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

串口监控导致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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 13:09:52