You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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(); // 发送计算结果
        }
    }
}

关键修复说明

  1. 波特率优化
    将波特率从57600改为9600,1MHz时钟下误差仅0.16%,远低于2%的允许误差阈值,彻底解决因波特率不匹配导致的乱码
  2. 浮点打印适配
    替换原代码中依赖AVR GCC默认不支持的浮点sprintf,改用自定义float_to_str函数实现浮点数转字符串,避免打印异常
  3. 输入数据修正
    终端发送的数字是ASCII字符,添加x = odb_x - '0'转换为整数,避免用ASCII码直接参与计算导致结果错误
  4. 代码精简
    移除无用头文件,优化变量逻辑,提升代码可读性

验证步骤

  1. 将修复后的代码重新烧录至ATmega8a
  2. 打开RealTerm,配置串口参数:波特率9600、8数据位、1停止位、无校验
  3. 发送数字0-9,终端将返回格式为x.xxxx的方程计算结果,自动换行

内容的提问来源于stack exchange,提问作者Wojciech Stolarz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 14:38:10