ATmega8a与RealTerm通信乱码问题及方程求解功能实现求助
解决ATmega8a USART串口乱码及方程计算返回异常问题
问题背景
已将代码烧录至ATmega8a,使用CH340G USB转TTL模块连接电脑,在RealTerm终端接收数据时出现乱码(如“•Ăeâ‚ ÂŤń•Ăeâ™ ÂŤń”),需求是实现终端发送数字后,设备计算方程y = 0.3187x² + 2x -7并返回正常格式的结果。
乱码核心原因排查
乱码主要由串口参数不匹配、波特率误差过大、浮点打印未适配AVR环境导致,按以下步骤验证:
- 串口参数一致性:确保RealTerm与代码参数完全一致(波特率、数据位、停止位、奇偶校验)
- 硬件连接:确认CH340G的TX接ATmega8的RX(PD0)、RX接ATmega8的TX(PD1),必须共地
- 波特率合理性:ATmega8用1MHz时钟跑57600波特率时误差超过允许范围,会导致通信不同步
- 浮点打印支持:AVR GCC默认未启用浮点
sprintf支持,直接使用会导致打印异常
修复后的完整代码
#define F_CPU 1000000UL #include <avr/io.h> #include <stdio.h> #include <avr/interrupt.h> #include <math.h> // 接收相关变量 volatile unsigned char odb_x; // 接收的ASCII格式数字 volatile unsigned char odb_flaga = 0; // 接收完成标志 // 发送缓冲区 volatile unsigned int usart_bufor_ind; // 发送缓冲区索引 char usart_bufor[30] = "y = 0.3187x^2 + 2x - 7"; // 初始提示文本 void usart_inicjuj(void) { // 改用9600波特率,1MHz时钟下误差仅0.16%,稳定性更高 #define BAUD 9600 #include <util/setbaud.h> // 设置波特率寄存器 UBRRH = UBRRH_VALUE; UBRRL = UBRRL_VALUE; #if USE_2X UCSRA |= (1<<U2X); #else UCSRA &= ~(1<<U2X); #endif // 配置USART帧格式:8数据位,1停止位,无校验 UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0); // 启用发送、接收及接收中断 UCSRB = (1<<TXEN) | (1<<RXEN) | (1<<RXCIE); } ISR(USART_RXC_vect) { // 接收中断:读取数据并设置标志 odb_x = UDR; odb_flaga = 1; } ISR(USART_UDRE_vect) { // 发送缓冲区空中断:持续发送缓冲区内容 if(usart_bufor[usart_bufor_ind] != 0){ UDR = usart_bufor[usart_bufor_ind++]; }else{ // 发送完成,关闭该中断 UCSRB &= ~(1<<UDRIE); } } void wyslij_wynik(void) { // 添加换行符CR+LF,使终端输出换行 unsigned char z; for(z=0; z<30; z++){ if(usart_bufor[z] == 0){ usart_bufor[z] = 13; // 回车符CR usart_bufor[z+1] = 10; // 换行符LF usart_bufor[z+2] = 0; break; } } // 等待发送缓冲区为空 while (!(UCSRA & (1<<UDRE))); usart_bufor_ind = 0; // 启用发送缓冲区空中断,开始发送 UCSRB |= (1<<UDRIE); } // 自定义浮点转字符串函数,适配AVR无浮点sprintf的环境 void float_to_str(float num, char* buf, int precision) { int integer_part = (int)num; float fractional_part = num - integer_part; // 处理负数情况 if(fractional_part < 0){ fractional_part *= -1; } // 转换整数部分 sprintf(buf, "%d.", integer_part); // 转换指定精度的小数部分 int frac = (int)(fractional_part * pow(10, precision)); for(int i=0; i<precision; i++){ buf[strlen(buf)] = (frac / (int)pow(10, precision-1-i)) + '0'; frac %= (int)pow(10, precision-1-i); } buf[strlen(buf)] = '\0'; } int main(void) { usart_inicjuj(); sei(); // 开启全局中断 wyslij_wynik(); // 启动后发送方程提示文本 while(1){ if(odb_flaga){ odb_flaga = 0; // 将ASCII字符转换为整数:终端发送的'0'-'9'对应ASCII码0x30-0x39 int x = odb_x - '0'; // 计算方程结果 float y = 0.3187 * x * x + 2 * x - 7; // 转换浮点数为字符串,保留4位小数 float_to_str(y, usart_bufor, 4); wyslij_wynik(); // 发送计算结果 } } }
关键修复说明
- 波特率优化
将波特率从57600改为9600,1MHz时钟下误差仅0.16%,远低于2%的允许误差阈值,彻底解决因波特率不匹配导致的乱码 - 浮点打印适配
替换原代码中依赖AVR GCC默认不支持的浮点sprintf,改用自定义float_to_str函数实现浮点数转字符串,避免打印异常 - 输入数据修正
终端发送的数字是ASCII字符,添加x = odb_x - '0'转换为整数,避免用ASCII码直接参与计算导致结果错误 - 代码精简
移除无用头文件,优化变量逻辑,提升代码可读性
验证步骤
- 将修复后的代码重新烧录至ATmega8a
- 打开RealTerm,配置串口参数:波特率9600、8数据位、1停止位、无校验
- 发送数字0-9,终端将返回格式为
x.xxxx的方程计算结果,自动换行
内容的提问来源于stack exchange,提问作者Wojciech Stolarz
相关产品推荐
相关产品推荐

