所以最近我尝试为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中断一次:
谁能告诉,为什么中断方法不能按预期工作?
更多信息:
所以最近我尝试为ATTiny85实现软件UART(仅TX)。我想用内部Timer1驱动它。计时器应以波特率的频率中断。每个ISR一位...