ATmega88无法通过USART更新OCR2A寄存器的问题排查
ATmega88 Timer2方波USART控制异常问题解决
问题核心分析
- 额外串口数据干扰:多数串口工具(如Cutecom)默认会自动添加CR/LF换行符,导致你发送单个目标字节时,实际接收了多个无效字节,OCR2A被反复修改为非预期值,表现为频率随机变化。
- 无数据有效性校验:未对接收数据做范围过滤,无效字节直接写入OCR2A,引发异常频率。
- 未处理USART接收错误:帧错误、数据溢出等异常会导致UDR0返回错误数据,未做过滤会影响结果。
解决步骤
- 关闭Cutecom的自动换行功能,确保仅发送你指定的HEX字节,无额外附加数据。
- 对接收数据做范围校验,仅当数据落在
minOCR2A到maxOCR2A区间内时,才更新OCR2A。 - 在USART接收函数中添加错误检查,过滤帧错误、溢出错误的无效数据。
- 调整OCR2A与TCNT2的赋值顺序,先更新OCR2A再清零计数器,避免出现异常计数周期。
修改后的代码
#include <avr/io.h> #include <util/delay.h> #include <stdbool.h> #define FOSC 16000000 // 时钟频率 #define BAUD 19200 #define MYUBRR FOSC / 16 / BAUD - 1 #define CLR(x, y) (x &= (~(1 << y))) #define SET(x, y) (x |= (1 << y)) #define maxOCR2A 255 #define minOCR2A 15 #define waitTime 20 unsigned char USART_Receive(void); void USART_Init(unsigned int ubrr); void blink(char x); void loop(void); int main() { USART_Init(MYUBRR); // LED引脚配置 DDRB |= _BV(PORTB0); blink(3); // OC2A引脚配置(PB3) DDRB |= _BV(PORTB3); _delay_ms(150); // 重置Timer2控制寄存器 TCCR2B = 0; TCCR2A = 0; // CTC模式,匹配时翻转OC2A引脚 TCCR2A = (1 << COM2A0) | (1 << WGM21); // 预分频64,启动定时器 TCCR2B = (1 << CS22); // 初始OCR2A值,对应3205Hz方波 OCR2A = 38; loop(); } void loop(void) { unsigned char t; while (true) { t = USART_Receive(); // 验证收到40时翻转LED if(t == 40) PORTB ^= _BV(PORTB0); // 仅在数据有效范围内更新OCR2A if(t >= minOCR2A && t <= maxOCR2A) { // 先更新OCR2A,再清零计数器,避免异常周期 OCR2A = t; TCNT2 = 0; } } } unsigned char USART_Receive(void) { // 等待数据接收完成 while (!(UCSR0A & (1 << RXC0))) ; // 检查接收错误:帧错误、数据溢出、奇偶错误 if(UCSR0A & ((1 << FE0) | (1 << DOR0) | (1 << UPE0))) { // 清空错误数据,重新等待 (void)UDR0; return USART_Receive(); } // 返回接收到的数据 return UDR0; } void USART_Init(unsigned int ubrr) { // 设置波特率 UBRR0H = (unsigned char)(ubrr >> 8); UBRR0L = (unsigned char)ubrr; // 启用接收器和发送器 UCSR0B = (1 << RXEN0) | (1 << TXEN0); // 设置帧格式:8数据位,1停止位 UCSR0C = ((1 << UCSZ01) | (1 << UCSZ00)); } void blink(char x) { for (char i = 0; i < x; i++) { SET(PORTB, PORTB0); _delay_ms(200); CLR(PORTB, PORTB0); _delay_ms(200); } }
内容的提问来源于stack exchange,提问作者cemox
相关产品推荐
相关产品推荐

