串口监控导致TC1602A-21T(R)(3) LCD屏幕故障排查
问题:ATMega328p裸机编程中UART连接串口工具时LCD失效
问题背景
在Arduino UNO的ATMega328p上进行裸机编程(仅用C语言+Makefile+avrdude,不依赖Arduino库),目标实现简易类键盘设备:
- 按键循环切换TC1602A LCD(HD44780U驱动)显示的字符
- 按键将内容通过ATMega328p的UART经USB发送到PC的puTTY(监控/dev/ttyACM0)
异常现象
- 单独测试UART通信、LCD显示均正常
- 两者结合后,未连接串口工具时,LCD可正常切换字符,UART可发送内容
- 一旦连接puTTY或Arduino串口监控到/dev/ttyACM0:
- 按键输入逻辑正常(板载LED闪烁验证),但LCD无法切换字符
- UART仍能发送内容,但仅能发送默认字符'A'
- 将代码复制到Arduino IDE烧录后,所有功能正常,连接串口工具无异常
已排查项
- 按键配备4.7k下拉电阻,排除浮空电压问题
- 裸机Makefile可正常编译烧录,但存在上述异常
- 怀疑问题出在Makefile或与bootloader的交互逻辑,USB桥接协处理器为未知因素
代码片段
Makefile
CC = avr-gcc CFLAGS = -g -mmcu=atmega328p -DF_CPU=16000000UL OBJCOPY = avr-objcopy AVR = sudo avrdude AVRFLAGS = -F -v -p m328p -c arduino -P /dev/ttyACM0 -U flash:w:lcd.hex default: lcd.elf ${OBJCOPY} -O ihex lcd.elf lcd.hex lcd.o: ${CC} ${CFLAGS} -Os -c lcd.c -o lcd.o lcd.elf: lcd.o ${CC} ${CFLAGS} -o lcd.elf lcd.o flash: ${AVR} ${AVRFLAGS} lcd.hex clean: rm -rf *.o *.elf *.hex
UART初始化代码
void USART_Init() { /* set baud rate. Baud prescaler is #def'ed to (((F_CPU / (BAUD_RATE * 16UL))) - 1u) formula from Table 19-1 of ATMega328p datasheet */ UBRR0H = (uint8_t)(BAUD_PRESCALER >> 8u); UBRR0L = (uint8_t)(BAUD_PRESCALER & 0xFFu); /* bit format 8N1: 8data, 1stop, no parity */ UCSR0C |= (_BV(UCSZ01) | _BV(UCSZ00)); /* enable Tx line. Also tested with Rx line enabled with no difference */ UCSR0B |= _BV(TXEN0); }
主函数代码
int main() { /* LOW is #def to 0; HIGH #def to 1 in redacted code */ uint8_t lastCharCycleState = LOW; uint8_t lastSendState = LOW; uint8_t pos = 0u; uint8_t letter = (uint8_t)'A'; /* explicitly set PD7, PB1, PB0 to input */ DDRD &= ~_BV(DDD7); /* currently unused third button */ /* DDRB &= ~_BV(DDB1); */ DDRB &= ~_BV(DDB0); // arbitrarily large wait time for testing _delay_ms(1000u); LCD_Configure(); USART_Init(); // arbitrarily large wait time to ensure everything is setup right _delay_ms(1000u); while (1u) { /* button press defined as transition from low to high state. PROCESS macros are #def'ed in redacted code */ if ((LOW == lastCharCycleState) && (HIGH == PROCESS_CYCLE_INPUT)) { lastCharCycleState = HIGH; LCD_Write8Bits((uint8_t)DATA, letter); /* reset DDRAM address after it autoincrements from the above data write */ LCD_Write8Bits((uint8_t)INSTRUCTION, (uint8_t)(0x80U | pos)); /* using characters 32 - 126 to include spaces, letters, and common symbols */ letter = (++letter % 126u); if (32u > letter) { letter = 32u; } } else if ((HIGH == lastCharCycleState) && (LOW == PROCESS_CYCLE_INPUT)) {/* button release defined as transition from high to low state */ lastCharCycleState = LOW; } if ((LOW == lastSendState) && (HIGH == PROCESS_SEND_INPUT)) { USART_Transmit(letter); _delay_ms(5u); pos = 0u; lastSendState = HIGH; /* resets letter after display clear */ letter = (uint8_t)'A'; LCD_Write8Bits((uint8_t)INSTRUCTION, (uint8_t)CLEAR_DISPLAY); } else if ((HIGH == lastSendState) && (LOW == PROCESS_SEND_INPUT)) { lastSendState = LOW; } } // should not reach this point return 0; }
解决建议
1. 修正Makefile编译链接参数
Arduino IDE会添加默认优化和链接选项,当前Makefile缺少全局优化和垃圾代码清除,可能导致内存或时序异常:
- 将
-Os移到全局CFLAGS中,确保编译和链接阶段都启用优化 - 添加代码段优化参数,去除未使用的代码
- 移除
-F强制擦除参数,避免flash写入异常
修改后的Makefile:
CC = avr-gcc CFLAGS = -g -mmcu=atmega328p -DF_CPU=16000000UL -Os -ffunction-sections -fdata-sections LDFLAGS = -mmcu=atmega328p -Wl,--gc-sections OBJCOPY = avr-objcopy AVR = sudo avrdude AVRFLAGS = -v -p m328p -c arduino -P /dev/ttyACM0 -U flash:w:lcd.hex default: lcd.hex lcd.o: lcd.c ${CC} ${CFLAGS} -c $< -o $@ lcd.elf: lcd.o ${CC} ${LDFLAGS} -o $@ $< lcd.hex: lcd.elf ${OBJCOPY} -O ihex -R .eeprom $< $@ flash: lcd.hex ${AVR} ${AVRFLAGS} clean: rm -rf *.o *.elf *.hex
2. 处理串口连接触发的复位
PC连接串口时,USB桥接器会发送DTR信号触发ATMega328p复位,需确保复位后重新初始化硬件:
- 在
main开头清除复位标志,避免看门狗或复位状态影响 - 调整初始化延迟,确保电源和外设稳定
修改后的main开头:
int main() { // 清除复位标志,避免复位状态残留 MCUSR = 0; WDTCSR |= (1<<WDCE) | (1<<WDE); WDTCSR = 0; uint8_t lastCharCycleState = LOW; uint8_t lastSendState = LOW; uint8_t pos = 0u; uint8_t letter = (uint8_t)'A'; // 后续代码不变... }
3. 明确所有引脚的初始化状态
复位后引脚状态可能被改变,需明确设置LCD所有用到的引脚为输出:
- 例如LCD的RS、EN、D4-D7引脚,在
LCD_Configure前先设置对应的DDR寄存器为输出
4. 验证UART与LCD的时序冲突
检查LCD操作的延迟是否足够,串口连接后CPU负载变化可能影响时序,确保LCD指令和数据写入的延迟符合HD44780U的要求(比如指令写入后至少等待37us)。
内容的提问来源于stack exchange,提问作者C-h me
相关产品推荐
相关产品推荐

