ATmega328P USART串口重复发送字符问题排查求助
ATmega328P USART通信异常:传递字符串参数时无限重复输出问题
根本原因
- 哈佛架构的存储区隔离:ATmega328P采用哈佛架构,Flash(程序存储区)和RAM(数据存储区)是独立地址空间。字符串字面量(如
"HE")默认被编译器存入Flash,而普通char*指针默认指向RAM地址。直接用char*访问Flash中的字符串会触发未定义行为,读取到无效数据。 - 固定索引访问的越界风险:你的
print_serial函数强制访问data[0]、data[1]、data[2],但由于Flash访问方式错误,data[1]和data[2]的读取值无效,可能导致程序执行流程混乱(如跳转到错误地址反复执行发送data[0]的代码),最终出现无限重复输出的现象。 - 不同场景的存储处理差异:
- 函数内部定义
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
相关产品推荐
相关产品推荐

