单片机死机问题是一个令广大工程师头疼的问题,最近小编又遇到一起实际案例,让我们来看看是怎么回事。

根据该工程师描述,当被测产品被大功率对讲机的发射信号干扰时,会出现死机现象,程序开了看门狗也没有复位。
我首先让他做了一个实验,通过一个IO口把看门狗的内部低速时钟输出,观察在死机的时候该时钟是否还存在。工程师测试后反馈,死机的时候IO口时钟就不再输出了。另外他提到程序里用到一个定时器(使用的是内部高速时钟作为时钟源),做了一个周期翻转IO的操作,死机时IO口也不再翻转。
这听起来似乎有点严重,难不成是对讲机把MCU彻底干崩溃了?小编第一时间购买火车票,不顾当地台风,紧急赶往客户现场。
遇到这种情况,首先要稳定的复现问题。到了现场之后,该工程师给我做了演示,一开始也确实是对讲机开启后,很快就会让板子接出来的一个原本正常闪烁的指示灯不再闪烁。

最初想到的是对讲机可能干扰到了MCU的电源,实际接上示波器观察3.3V输出相对稳定,干扰时约有200mv左右的波动。Vcore引脚电压也有波动(如图所示黄色波形)。Vcore正常输出应为1.5V

将Vcore信号放大后,可见干扰波形频率为434MHz,这与对讲机显示屏上显示的频率基本一致。

另外我用开发板简单写了一个翻转IO的程序,使用对讲机测试,未出现问题。实际测了VDD和Vcore波形,和上述他的板子基本一致。这基本可以说明不是电源引起的问题。
随后我们进行了多次实验。期间,他曾将板子拿到电脑旁修改代码并重新下载了几次程序,但之后突然无法再次复现问题,即使我将对讲机按键按到“冒烟”也无法复现。这引起了我的高度警觉,我隐约感觉问题可能与软件代码有关。于是我们又焊接了一块板子进行测试,这个板子也是怎么也复现不了。
此时我更加确信是软件上的差异导致了此问题,因为同样的硬件和软件,不可能之前很容易复现,现在却完全无法复现。
后来我坐到他的工位旁,和他一起看了下代码,我们先将他的业务代码精简至最简状态,然后逐个添加功能模块进行测试,最终定位到了问题点。只有当添加SPI通信的代码块时才会出现问题;若无此代码块,则问题不会出现。
后来进一步定位,所谓的死机是卡在下图中while(1)循环里了,程序实际还在运行

分析至此,引出了两个关键问题,一是为什么看门狗没有起作用?难不成是外部干扰影响到了内部32K时钟或是让看门狗功能工作异常了? 二是为什么干扰会引起SPI RXFNE标志位无法置位?
最终还是搞清楚了这两个问题。先回答第一个问题,查看main函数可见:

第一个问题出在87行看门狗初始化上:看门狗初始化得太晚了!在单片机程序中,看门狗定时器的初始化位置通常非常关键。最佳位置是将其放在程序启动初期,尽早启用看门狗可以确保如果程序在后续初始化或主循环之前的启动代码中就发生跑飞或卡死,也能被及时复位。
在85行调用的这个函数里就会进行SPI的通信,这个时候出现了上述情况的卡死,而看门狗这时还没有起作用,因此就会一直卡住。另外客户IO口输出内部32K是89行,周期翻转IO是在while(1)里 97行,所以整体的外在表现就是死机。若周期翻转IO操作是在定时器中断服务程序中进行,则也不会表现出彻底死机的现象。
程序98行也会有SPI的通信,如果是在这里引起的SPI while(1)卡住,看门狗会起作用并触发复位,这也与实际测试中有时能观察到复位的表现一致
只需将看门狗初始化代码的位置提前,即可避免此类卡死问题。
这里多提一点,有的单片机是上电复位后默认就使能看门狗的,这时要注意及时喂狗,避免前期初始化的时候就引起复位。
第二个问题的原因在于:对讲机会干扰SPI时钟信号。当SPI时钟受到干扰时,SPI通信无法正常进行,导致RXFNE(接收缓冲区非空)标志位无法置位。这很好理解:SPI时钟持续受扰,必然导致通信失败,自然无法正确接收数据置位RXFNE。
我后来单独做了测试,直接在SPI CLK信号上施加干扰,即可轻易复现RXFNE标志位无法置位的问题。

因此,有经验的工程师除了main()主循环外,通常不会在代码中编写其他while(1)死循环,因为这确实存在较大的风险!
