使用AVR-C在ATmega328P Arduino Uno上UART串口打印乱码求助
解决ATmega328P裸机串口打印乱码问题
1. 修正波特率配置顺序
util/setbaud.h的宏依赖严格的定义顺序——必须先指定F_CPU,再包含该头文件。把F_CPU定义移到代码最顶部,避免编译时宏展开顺序错误:
#define F_CPU 16000000UL #include <avr/io.h> #include <stdio.h> #include <util/delay.h> #define BAUD 9600 #include <util/setbaud.h>
同时可以手动计算验证UBRR值:16MHz时钟、9600波特率下,UBRR应为(16000000/(16*9600))-1 = 103,直接替换代码中的UBRR赋值,跳过头文件自动计算:
UBRR0H = (103 >> 8); UBRR0L = 103;
2. 修复Makefile链接参数
你的编译链接命令缺少-mmcu=atmega328p参数,会导致生成的二进制指令集与芯片不匹配,间接引发时序/波特率异常。修改链接行:
default: avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p -c -o serial-print.o serial-print.c avr-gcc -mmcu=atmega328p -o serial-print.bin serial-print.o avr-objcopy -O ihex -R .eeprom serial-print.bin serial-print.hex sudo avrdude -F -V -c arduino -p ATMEGA328P -P /dev/ttyACM0 -b 115200 -U flash:w:serial-print.hex
3. 显式指定串口帧格式
明确配置帧格式,避免默认值歧义,确保与接收端(串口监视器、screen)一致:
UCSR0C = (1<<UCSZ01) | (1<<UCSZ00) | (0<<USBS0); // 8位数据,1位停止位,无校验
4. 确认芯片实际时钟频率
克隆版Arduino Uno常使用8MHz内部时钟代替16MHz晶振,这会直接导致波特率误差超出允许范围。用avrdude读取熔丝位确认:
sudo avrdude -c arduino -p atmega328p -P /dev/ttyACM0 -b 115200 -U lfuse:r:-:h
- LFUSE值为
0xFF:对应16MHz外部晶振(标准Uno配置) - LFUSE值为
0xE2:对应8MHz内部时钟
如果是8MHz时钟,需将代码和Makefile中的F_CPU改为8000000UL,同时UBRR值调整为51。
5. 排除printf的干扰
简化代码直接发送单字符,验证底层串口功能是否正常:
int main(void) { uart_init(); while(1) { loop_until_bit_is_set(UCSR0A, UDRE0); UDR0 = 'A'; _delay_ms(500); } return 0; }
如果能正常接收字符'A',说明问题出在printf的流配置环节,检查stdout赋值是否在uart_init之后,且FDEV_SETUP_STREAM参数正确。
内容的提问来源于stack exchange,提问作者Troglobyte
相关产品推荐
相关产品推荐

