问题描述
昨天打开.ioc文件改下STM32F4的芯片一些配置时,发现有新的固件包可以用,于是想着越新越好,就手抖更新下工程的固件包,之后在调试时就炸出了个bug,这个bug是:程序从IAP程序跳转到应用程序后,在进入freertos系统前(也就是进入osKernelStart()函数前)卡死在某个使用了HAL_Delay延时的函数中,卡死的位置就是HAL_Delay()延时函数。如下图:
调试
简要的说明下,本工程首先在CubeMx配置好F4型号芯片的一些引脚,生成工程文件,IDE用的是IAR8.30.1,调试器用的是J-Link_V9。
1.首先呢,简单的对比了旧的HAL固件包和新的固件包的两个工程文件,发现网络部分的LWIP初始化函数(MX_LWIP_Init())位置改变了,旧版的是在系统默认生成的线程中里调用来初始化的,而新版的直接紧跟外设初始化后面,也就是说:
旧版固件包:MX_LWIP_Init()在freertos启动后调用。
新版固件包:MX_LWIP_Init()在freertos启动前调用。
(刚好在MX_LWIP_Init()函数中,我添加了额外的函数调用了HAL_Delay,导致了该bug的出现)
2.经过简单的断点和变量的数值查看后,发现是systick没有进行每1ms的中断计数,导致全局变量“uwTick”一直为0,从而导致以此变量做延时的操作的HAL_Delay函数自然就卡死了。好了,问题转到为什么systick不起作用。(这里先说明下:在CubeMX中对于使用freertos后,其默认的内核基准频率用了systick timer(STK)来做,也就是内核外设STK被freertos用了,而且还不能修改,那HAL_Delay中的"uwTick"就需要用到其他定时器来计数,这里我选择了TIM5)
3.我查看了下htim5这个全局变量,其各个功能寄存器都正常,但是就是不触发其更新中断,然后我突然想起我的IAP程序,在应用跳转前我做了一步,如下:
__set_FAULTMASK(1);
jump_app(); //跳转到APP
我调用了__set_FAULTMASK函数,这个是用来关闭除了NMI以外的中断,为了避免在跳转前进入其他中断而用的。但是在应用程序的初始化步骤中我没调用!!!!!为什么没调用呢,因为在系统启动调度表时(也就是进入了osKernelStart()),里面会运行vPortStartFirstTask()函数,该函数里面有一个操作就是清除FAULTMASK的值,如下:
/* Call SVC to start the first task. */
cpsie i //开中断
cpsie f //开异常 ==> __set_FAULTMASK(0)
就是说在系统启动之后,内核FAULTMASK寄存器清空了标志位。所以HAL_Delay()能正常的工作。现在看来,如果要在系统启动之前进入中断,那就只要在应用程序初始化前调用 __set_FAULTMASK(0),就可以正常的使用HAL_Delay()函数了。
4.当加入了__set_FAULTMASK(0)后在调试时,发现程序一执行该函数清除FAULTMASK标志位时,就进入硬件错误中断,额,该来的中断不来,不该来的就来了。进入调试模式查看call_stack,发现调用了SysTick_Handler中断服务函数。这才想起,IAP程序中没上系统,所以系统的心跳直接配置了STK,跳转前没复位SysTick的控制寄存器CTRL,所以跳转到应用程序时,开启中断时第一个就进入了STK的中断了,从而导致硬件错误。
5.在跳转前复位下SysTick变量的CTRL寄存器,然后在跳转后清下内核寄存器FAULTMASK的标志位,程序就恢复正常了~
总的来说,不要轻易更新HAL驱动的固件包,以免出现意外的bug,除非迫不得已,且自己已经做好了足够的准备。
这篇文章是我第一篇写的CSDN文章,我本身也是小白,如有错误,还请大佬们指正~~