应用笔记 / 经验分享 · 2025年8月1日

ARM单片机看门狗重置导致局部变量初始值不确定引发问题的调试记录

项目背景:自家IOT设备,使用GD32E103作为主控,EC800 CAT1联网。板载EEPROM及国产SHT30温湿度传感器、电机控制及多个其它传感器;编译环境Keil,优化级别-O0,调试工具J-Link

故障现象:连续工作时意外挂起,即使开启看门狗也无法恢复正常工作状态。

由于上电后十几个小时甚至更长时间才会出故障,给排查和调试带了诸多不便。之前把代码捋了多次,并修改了自认为可能导致问题的代码,都没能解决问题。

昨天下定决心跟踪到底,早8点,设备进入调试状态,打开RTT日志并启动RTT Viewer,希望它能尽快出故障,直到下午六点设备一直正常工作,只好关掉屏幕,电脑开机状态下离开办公室。昨晚11点,睡觉前打开APP,设备能正常连接;今天早上7点,再开APP,发现设备无法远程控制,故障复现。

早上到办公室,发现KEIL已经无法调试,证明看门狗复位过了,调用堆栈显示重启前运行到sht30的I2C读写函数。RTT日志仍然输出,但EC800初始化流程卡在AT 第一条指令状态。

这两个问题可以分开考虑:看门狗之所以动作,大概率是硬件I2C死锁了,这个在之前使用STM32和GD32的时候都遇到过。将GD32官方I2C的dmo增加超时检测即可解决,这不是本文的讨论重点,不详细讨论。用镊子将I2C的 SCL和SDA短路,几秒后看门狗动作,证实刚刚的猜测。同时观察RTT日志,发现EC800初始化流程卡住,重复执行AT->OK->重启流程,这恰好复现了工作状态下出现故障的完整过程,于是决定保留I2C死锁的BUG,先解决EC800流程问题。

因为看门狗的存在,导致无法跟踪,所以暂停看门狗。发现断电重启、代码重启、使用调试器重启都不能使设备进入这个错误状态,证明必须看门狗重启才会复现故障。将看门狗启用,同时设置为超时时间32秒左右(256分频,窗口计数器4095),这样出现问题后,我有32秒时间下断点,并跟踪故障。

重点来了,经过反复调试,发现问题出在一个函数的局部变量上。该函数是AT返回解析函数,在UART中断函数里被调用,在处理返回字符串后快速返回。其中有个变量: uint8_t atState; 函数内定义,没有初始化,自认为编译器在每次调用时会自动初始化为0。但经过跟踪发现,正常启动进入函数时,此变量值的确为0,但看门狗重置后,每次进入此函数,atState变量值为3!不明白在看门狗重置后,栈空间是如何处理的。

将原代码从 uint8_t atState;改为uint8_t atState=0;问题解决。

正常情况下,即使函数内局部变量定义也会赋初值的,这次因为疏忽发现这个奇怪问题,特此记录。