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

ATmega328P USART串口重复发送字符问题排查求助

ATmega328P USART通信异常:传递字符串参数时无限重复输出问题

根本原因

  1. 哈佛架构的存储区隔离:ATmega328P采用哈佛架构,Flash(程序存储区)和RAM(数据存储区)是独立地址空间。字符串字面量(如"HE")默认被编译器存入Flash,而普通char*指针默认指向RAM地址。直接用char*访问Flash中的字符串会触发未定义行为,读取到无效数据。
  2. 固定索引访问的越界风险:你的print_serial函数强制访问data[0]、data[1]、data[2],但由于Flash访问方式错误,data[1]和data[2]的读取值无效,可能导致程序执行流程混乱(如跳转到错误地址反复执行发送data[0]的代码),最终出现无限重复输出的现象。
  3. 不同场景的存储处理差异:
    • 函数内部定义char *data = "HE"时,编译器可能通过优化将字符串复制到RAM,此时普通指针访问有效,输出正常。
    • malloc分配的内存本身在RAM中,数据访问自然正确。

修复方案

方案1:正确访问Flash中的字符串

使用AVR GCC的PROGMEM属性声明Flash字符串,并通过专用函数读取:

#include <avr/pgmspace.h>

#define FOSC 16000000UL
#define BAUD 9600
#define UBRR FOSC/16/BAUD-1

void USART_init(unsigned int ubrr)
{
    UBRR0H = (unsigned char)(ubrr >> 8);
    UBRR0L = (unsigned char)ubrr;
    UCSR0B = (1 << RXEN0) | (1 << TXEN0);
    UCSR0C = (1 << USBS0) | (1 << UCSZ00) | (1 << UCSZ01);
}

void USART_Transmit(unsigned char data)
{
    while(!(UCSR0A & (1 << UDRE0)));
    UDR0 = data;
}

void print_serial(const char* data)
{ 
    USART_Transmit(pgm_read_byte(&data[0]));
    USART_Transmit(pgm_read_byte(&data[1]));
    USART_Transmit(pgm_read_byte(&data[2]));
}

// 声明存放在Flash中的字符串
const char str_HE[] PROGMEM = "HE";

int main()
{
    USART_init(UBRR);
    print_serial(str_HE);
    return 0;
}

方案2:将字符串复制到RAM后访问

在传递参数前,把Flash字符串复制到RAM缓冲区:

#include <string.h>

#define FOSC 16000000UL
#define BAUD 9600
#define UBRR FOSC/16/BAUD-1

// 省略USART_init和USART_Transmit函数(同原代码)

void print_serial(char* data)
{ 
    USART_Transmit(data[0]);
    USART_Transmit(data[1]);
    USART_Transmit(data[2]);
}

int main()
{
    USART_init(UBRR);
    char buf[3];
    strcpy(buf, "HE"); // 自动将Flash字符串复制到RAM
    print_serial(buf);
    return 0;
}

方案3:改为字符串遍历(推荐)

修改print_serial函数,遍历字符串直到终止符,避免固定索引的越界访问:

// 针对RAM字符串的版本
void print_serial(char* data)
{ 
    while(*data != '\0')
    {
        USART_Transmit(*data);
        data++;
    }
}

// 针对Flash字符串的版本(需包含avr/pgmspace.h)
void print_serial_progmem(const char* data)
{ 
    char c;
    while((c = pgm_read_byte(data)) != '\0')
    {
        USART_Transmit(c);
        data++;
    }
}

int main()
{
    USART_init(UBRR);
    // 调用RAM版本
    char buf[] = "HE";
    print_serial(buf);
    // 或调用Flash版本
    print_serial_progmem(PSTR("HE"));
    return 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 07:52:04