簡體   English   中英

ATTiny85 - 帶 Timer1 的軟件 UART

[英]ATTiny85 - Software UART with Timer1

所以最近我嘗試為 ATTiny85 實現軟件 UART(僅限 TX)。 我想用內部Timer1驅動它。

定時器應以波特率的頻率中斷。 每個 ISR 都會發送一位,直到沒有任何東西可以發送並且中斷將再次被禁用。

(注:F_CPU=1000000 ;保險絲為出廠默認值(E:FF, H:DF, L:62))

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdint.h>

#define SET(reg, pos) (reg |= 1<<(pos))
#define FLP(reg, pos) (reg ^= 1<<(pos))
#define CLR(reg, pos) (reg &= ~(1<<(pos)))
#define GET(reg, pos) (reg &  1<<(pos))

#define UART_TX_BIT PB1
#define UART_BAUDRATE 600

static volatile uint16_t txframe;

/* Timer1A interrupt at BAUDRATE */
ISR(TIM1_COMPA_vect)
{
        /* Write current bit */
        if(txframe & 1) SET(PORTB, UART_TX_BIT);
        else            CLR(PORTB, UART_TX_BIT);

        /*
         * If the 1 mark at the end of txframe is reached,
         * disable interrupts (stop transmitting)
         */
        if(txframe == 1) CLR(TIMSK, OCIE1A);

        txframe >>= 1;
}

static void uart_putc(const char c)
{
        /* Wait until pending txframe is transmitted */
        do {
                sei();
                __asm__ __volatile__ ("nop");
                cli();
        }while(txframe);

        /* MARK . STOP | DATA | START */
        txframe = (0b11<<9) | ((uint16_t) c<<1) | 0;

        /* Enable timer interrupt and clear flag */
        SET(TIMSK, OCIE1A);
        SET(TIFR, OCF1A);
        sei();
}

static void uart_init()
{
        uint8_t sreg = SREG;

        cli();

        /* Set timer1 (CK) to CTC with divisor of 8 */
        TCCR1 = _BV(CTC1) | _BV(CS12);

        /* Set BAUDRATE clock divisor */
        OCR1A = (uint8_t) ((uint32_t) (F_CPU/8)/UART_BAUDRATE)-1;

        /* Enable and pull TX Pin to HIGH */
        SET(DDRB, UART_TX_BIT);
        SET(PORTB, UART_TX_BIT);

        txframe = 0;

        SET(TIFR, OCF1A);
        sreg = SREG;
}

int main()
{
        uart_init();

        for(;;) {
                uart_putc('A');
                _delay_ms(2000);
        }
}

使用此設置,接收器僅接收 0x00 或 0xFF,偶爾還會收到其他一些垃圾(取決於波特率)

最終,我嘗試在沒有中斷的情況下實現相同的目標:

#define UART_FALLBACK_DELAY() _delay_us(1000000UL/UART_BAUDRATE)

static void uart_putc_fallback(uint8_t c)
{
        uint8_t sreg = SREG;
        cli();

        /* Start */
        CLR(PORTB, UART_TX_BIT);
        UART_FALLBACK_DELAY();
        /* Data */
        for(int i = 0; i < 8; i++, c>>=1) {
                if(c&1) SET(PORTB, UART_TX_BIT);
                else    CLR(PORTB, UART_TX_BIT);
                UART_FALLBACK_DELAY();
        }
        /* Stop */
        SET(PORTB, UART_TX_BIT);
        UART_FALLBACK_DELAY();

        SREG = sreg;
}

static void uart_putc_fallback2(const char c)
{
        uint8_t sreg = SREG;
        cli();

        txframe = (0b11<<9) | ((uint16_t) c<<1) | 0;

        while(txframe) {
                if(txframe & 1) SET(PORTB,UART_TX_BIT);
                else            CLR(PORTB,UART_TX_BIT);
                txframe >>= 1;
                UART_FALLBACK_DELAY();
        }

        SREG = sreg;
}

令人驚訝的是,這兩個函數都按預期工作,所以我想我在搞砸 Timer1。 遺憾的是我沒有示波器,所以我無法手動檢查信號。 但總的來說,使用 Timer1 時信號似乎有點慢。 設置應該每 1664µs 中斷一次:

  • 波特率 = 600Hz
  • CK = 1MHz
  • 定時器 1DIV = 8
  • Timer1CK = CK/Timer1DIV = 125kHz
  • OCR1A = Timer1CK/波特率 = 208
  • 延遲 = (Timer1DIV * OCR1A)/CK = (8*208)/1MHz = 1664µs

誰能告訴,為什么中斷方法沒有按預期工作?


更多信息:

我想我發現了一個可能導致您所看到的問題。

在 CTC 模式下,該定時器在達到OCR1C后清除計數器...

在此處輸入圖片說明

您似乎沒有在代碼中設置OCR1C ,因此計時器的周期將基於OCR1C發生的任何事情(通電時為0 )。

這是一件容易錯過的事情( OCR1COCR1A )!

我認為您可以通過在此處添加一行來設置此寄存器來使您的代碼工作...

    /* Set BAUDRATE clock divisor */
    uint8_t match = (uint8_t) ((uint32_t) (F_CPU/8)/UART_BAUDRATE)-1;

    OCR1A = match;    // Generate interrupt on match to drive ISR 
    OCR1C = match;    // Clear counter on match to define period

如果是這樣,請報告 - 如果不是,我們將繼續尋找!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM