STM32F407 + HT1621 段码 LCD 偶发白屏:为什么硬件复位无效,断电却能恢复?
事先叠甲,问题是我遇到的真实问题,bug又比较重要,进而由AI代笔写的文章
前言
项目使用 STM32F407 驱动 HT1621(32×4 RAM 映射 LCD 驱动器)和一块定制段码 LCD。设备长期运行后偶尔会出现近乎完全白屏,只残留少量不完整段码;此时 STM32 仍在正常采集、联网和输出日志,按硬件复位键也无法恢复,只有整机断电后重新上电才可能恢复。
本文记录一次从“怀疑应用刷新”到“确认外部 LCD 控制器状态异常”的完整排查过程,以及最终采用的渐进式修复方案。
图片来源说明:第一张图是在另一台设备上复现的同类故障,仅用于展示故障现象;第二张图才是本次目标设备修复后的实际画面。两张图不是同一台设备的严格前后对比。
测试环境:
- 主控:STM32F407VET6;
- LCD 控制器:HT1621 兼容控制器,4COM、32SEG;
- 操作系统:FreeRTOS;
- 调试器:J-Link,SWD 接口;
- 运行日志:COM7,460800 baud。
故障现象

图 1:另一台设备复现的同类故障。屏幕接近完全空白,仅残留几个不完整段码。
故障具有以下特征:
- LCD 正常工作一段时间后突然白屏,偶尔短暂出现部分段码。
- STM32 主程序没有死机,温湿度采样、RTC、MQTT、CDC 心跳仍在运行。
- 只复位 STM32 无法恢复。
- 完全断电再上电可以恢复,但运行一段时间后可能再次出现。
- 较早版本的设备也出现过类似现象,因此不能简单归因于后来增加的 ESP32-C3 RTS/CTS 信号。
系统结构
LCD 控制链路如下:
显示刷新任务 ─┐
├─> CHIP1621 驱动 ─> CS / WR / RD / DATA ─> HT1621 ─> COM/SEG ─> 段码屏
蜂鸣器任务 ─┘
HT1621 不在 STM32 芯片内部,它拥有独立的系统振荡器、LCD 偏压发生器、串行接口状态和显示 RAM。STM32 的 NRST 只复位 MCU;如果 HT1621 的 VDD 没有同时断开,它的内部状态不会得到真正的掉电复位。
这正好解释了一个关键现象:为什么按硬件复位无效,而断电重新上电有效。
先确认:白屏时 MCU 是否仍在运行
故障期间通过 COM7、460800 baud 观察日志,显示刷新和系统心跳仍然存在:
[CDC_DIAG]: [CDC_TASK] Heartbeat | tick=20161 | mode=0 |
q_waiting=0/512 | paused=0 | cmd_idx=0
[DisplayDataUpdateTask]: RTC 时间: 2026/08/04 13:45:05
因此首先排除了以下方向:
- STM32 整机卡死;
- FreeRTOS 调度完全停止;
- 显示任务没有被周期唤醒;
- 固件整体运行失败。
问题范围被缩小到 STM32 与 HT1621 之间的总线、HT1621 内部状态、LCD 偏压/时钟以及物理连接。
第一轮修复:消除共享总线竞争
显示刷新和蜂鸣器音调命令共享同一组 HT1621 GPIO。原实现没有互斥保护,两项任务如果同时操作 CS、WR 和 DATA,可能把两个合法事务交错成一个非法帧。
第一轮修改包括:
- 为所有 RAM 写入、RAM 读取、初始化和蜂鸣器命令增加同一把静态互斥锁;
- MCU 启动时把 CS、WR、RD 设置为高电平安全空闲状态;
- 每次初始化前先拉高 CS,重新同步 HT1621 串行接口;
- 保证任何失败路径都会释放总线锁。
核心结构如下:
th_result_t Chip1621WriteRam(uint8_t start_address,
const uint8_t *data,
size_t length)
{
/* 参数校验和完整帧构建省略,bits/written 已在加锁前生成。 */
th_result_t result = chip1621_board_bus_lock();
if (result != TH_RESULT_OK) {
return result;
}
CS_SET(CHIP1621_ENABLE);
Chip1621Driver_SendBits(bits, written);
CS_SET(CHIP1621_DISABLE);
return Chip1621Driver_UnlockBus(TH_RESULT_OK);
}
但烧录后,已经处于白屏状态的控制器仍未恢复。这个结果并不代表互斥锁无效,而是说明:防止产生新错误,与恢复已经卡住的外部控制器,是两个不同问题。
第二轮排查:逐层排除应用和 RAM 写帧
为了避免凭感觉修改,依次进行了以下真机隔离测试:
| 诊断步骤 | 结果 | 排除内容 |
|---|---|---|
| 禁用写后 RAM 回读 | 无改善 | 白屏并非单次回读校验失败造成 |
停止业务刷新与蜂鸣器,仅写一次 128 个 1 |
只显示“状态”相关残留段 | 应用层显存内容不是主因 |
| 把 16 字节连续写改成每个 RAM 地址独立写 | 仍只有相同残留段 | 地址自动递增和长连续帧不是主因 |
| 执行完整控制器恢复序列后再次全段写入 | 全部段码点亮 | LCD 玻璃、COM/SEG、GPIO 和 RAM 写入链路正常 |
其中最关键的是最后一步:没有更换屏幕、排线或控制器,只改变初始化序列,128 个段码就全部能够点亮。这直接证明硬件链路具备正常显示能力,故障核心位于 HT1621 的内部运行状态。
真正有效的恢复序列
旧初始化只在一个连续命令帧中发送:
SYS EN → BIAS → LCD ON
这足以启动处于正常复位状态的 HT1621,却不足以覆盖错误时钟源、测试模式、LCD 偏压状态或命令失步等异常情况。
最终采用的恢复序列是:
SYS DIS → LCD OFF → RC 256K → NORMAL → BIAS 1/3 + 4COM → SYS EN → LCD ON
并且每条命令都使用独立 CS 帧。CS 回到高电平后,HT1621 会结束当前事务并初始化串行接口;即使某一帧此前发生错位,也不会继续污染后面的命令。
命令定义:
#define CHIP1621_SYS_DIS 0x00
#define CHIP1621_SYS_EN 0x01
#define CHIP1621_DISPLAY_OFF 0x02
#define CHIP1621_Display_ON 0x03
#define CHIP1621_RC_256K 0x18
#define CHIP1621_BIAS 0x29
#define CHIP1621_NORMAL_MODE 0xE3
独立命令事务:
static void Chip1621Driver_SendCommandTransaction(uint8_t command)
{
CS_SET(CHIP1621_ENABLE);
Chip1621Driver_SendCommandMode();
Chip1621Driver_SendCommand(command);
CS_SET(CHIP1621_DISABLE);
Chip1621Driver_DelayWriteHigh();
}
最终初始化:
Chip1621Driver_SendCommandTransaction(CHIP1621_SYS_DIS);
Chip1621Driver_SendCommandTransaction(CHIP1621_DISPLAY_OFF);
Chip1621Driver_SendCommandTransaction(CHIP1621_RC_256K);
Chip1621Driver_SendCommandTransaction(CHIP1621_NORMAL_MODE);
Chip1621Driver_SendCommandTransaction(CHIP1621_BIAS);
Chip1621Driver_SendCommandTransaction(CHIP1621_SYS_EN);
Chip1621Driver_SendCommandTransaction(CHIP1621_Display_ON);
为什么还要暂时禁用 RAM 回读
项目曾增加写后回读校验。读取 HT1621 RAM 时,需要把 DATA 从输出切换为输入,并驱动 RD 时钟;读取完成后再恢复 DATA 输出方向。
这套逻辑在主机桩测试中可以验证位序,但真实硬件尚未通过逻辑分析仪确认方向切换、总线释放和采样窗口。它又与显示写入、蜂鸣器命令共用同一组线路,因此最终固件暂时不绑定 RAM 回读回调,只保留经过真机验证的写入路径。
这不是宣称“回读一定是唯一根因”,而是在证据不足时移除一个没有必要进入生产路径的风险点。
官方数据手册给出的重要提示
Holtek 的 HT1621 数据手册给出了两个与本次现象高度一致的信息:
- 如果上下电时序不满足要求,内部 POR 可能无法正常工作;
- 当 VDD 曾低于工作电压时,应降到 0V 并保持至少 20ms,之后再重新上升;同时建议主控在上电后主动初始化 HT1621。
此外,CS 高电平主要负责终止通信并初始化串行接口,并不等价于切断 HT1621 电源。
官方资料:Holtek HT1621/HT1621G 数据手册
因此,“MCU 硬复位无效、整机断电有效”不是随机现象,而是外部控制器未随 MCU 一起掉电复位的直接结果。
已证实的故障机制与可能的触发因素
需要区分“故障机制”和“进入故障状态的最初触发因素”。
已经通过真机实验确认的是:
- 白屏时 STM32 主程序仍正常运行;
- 普通 RAM 连续写和逐地址写都无法让已经异常的控制器恢复;
- 完整恢复序列可以在不更换硬件、不整机断电的情况下重新点亮全部段码;
- 恢复后正常显存可以持续刷新到屏幕。
尚未通过长时间逻辑分析仪抓取唯一确认的触发因素包括:
- 显示任务和蜂鸣器任务曾经发生总线事务交错;
- RAM 回读时 DATA/RD 方向切换使接口进入异常状态;
- 电源瞬态下降没有满足 HT1621 的完整 POR 条件;
- 上述因素共同作用。
因此最终修复同时覆盖“预防”和“恢复”:用互斥锁防止帧交错,用安全空闲电平和独立 CS 帧保证同步,禁用未验证回读,并在每次启动时显式重建 HT1621 的时钟、模式、偏压和显示状态。
构建、烧录与运行日志
主机测试和 STM32 固件构建通过,最终资源占用如下:
Memory region Used Size Region Size %age Used
CCMRAM 65160 B 64 KB 99.43%
RAM 130712 B 128 KB 99.73%
FLASH 443892 B 458240 B 96.87%
[100%] Built target F407_TH02_V3.elf
J-Link 烧录并回读校验成功:
Device "STM32F407VE" selected.
VTref=3.236V
Loading binary file lcd_recovery_normal.fwpk.bin
Reading 444404 bytes data from target memory @ 0x08010000.
Verify successful.
恢复正常版本后,COM7 日志显示显示任务继续按 5 秒周期刷新:
[DisplayDataUpdateTask]: RTC 时间: 2026/08/04 14:25:43
[DisplayDataUpdateTask]: RTC 时间: 2026/08/04 14:25:48
修复结果

图 2:本次目标设备修复后的实际画面。时间、温度、湿度、信号和电池段码均恢复正常。
对应 Git 提交:
2fa0a390 修复CHIP1621总线竞争与复位白屏
33740aa0 修复CHIP1621异常状态恢复失败
总结
这次问题最容易误判的地方,是把“LCD 白屏”直接等同于“MCU 没有刷新”或“屏幕硬件损坏”。实际情况是 MCU 一直在正常运行,真正失效的是独立供电的 LCD 控制器状态。
排查这类问题时,可以按下面的顺序缩小范围:
- 先用独立日志证明主控和显示任务是否仍在运行;
- 固定显存并停止其他总线使用者,排除应用层覆盖;
- 分别验证连续写和逐地址写,排除地址自动递增;
- 使用全段常亮验证 RAM、COM/SEG 和 LCD 玻璃;
- 检查 MCU 复位是否真的覆盖了外部芯片;
- 按数据手册显式恢复振荡器、工作模式、偏压和显示开关;
- 对共享 GPIO 总线增加事务级互斥,不让多个任务各写一半帧。
最重要的经验是:外部芯片仍有电时,复位 MCU 不等于复位整个系统。 对没有独立 RESET 引脚的器件,必须预留一套能够从未知内部状态恢复的完整初始化序列。
建议标签:STM32、HT1621、FreeRTOS、嵌入式、LCD、J-Link、故障排查
浙公网安备 33010602011771号