ATTiny85-带Timer1的软件UART

问题描述 投票:0回答:1

所以最近我尝试为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
  • Timer1DIV = 8
  • Timer1CK = CK / Timer1DIV = 125kHz
  • OCR1A = Timer1CK /波特= 208
  • 延迟=(Timer1DIV * OCR1A)/ CK =(8 * 208)/ 1MHz = 1664µs
  • 谁能告诉,为什么中断方法不能按预期工作?


更多信息:

  • Vcc =〜4.52V(〜25°C)Oscillator应该足够准确]]
  • Timer1 Prescale, Table 12-5
  • Timer1 Overview
  • 默认情况下,Timer1使用CK(1MHz)代替PCK(我已经尝试过手动将其设置为CK)
  • 工具链:avr-gcc
  • 所以最近我尝试为ATTiny85实现软件UART(仅TX)。我想用内部Timer1驱动它。计时器应以波特率的频率中断。每个ISR一位...

c avr uart avr-gcc attiny
1个回答
0
投票

我认为我发现了一个可能导致您看到的问题。

在CTC模式下,该计时器在到达OCR1C ...后清除计数器。>

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.