1.现象
在基于STM32开发一个项目过程中,遇到一个比较奇葩的现象:经常会时不时出现修改上层的应用代码导致程序运行不起来,进不去main函数。这个STM32程序是分为bootloader层和APP层,出现这个奇葩现象的时候,bootloader层是可以正常运行的,但是跳转到APP层的时候,就发现进不了main函数。
2.分析
一开始也是找不到原因何在,通过网上搜索发现也有类似出现STM32进不去main函数的,原因多数是因为printf函数的应用导致的。但通过代码检查发现我遇到的这个奇葩现象,并非printf函数运用导致。这里也借此了解一下printf函数在stm32中使用注意事项,避免出现这个错误。
printf函数:
printf之类的函数,使用了半主机模式,所以要利用目标ARM器件的输入输出设备,首先要关掉半主机机制,然后再将输入输出重定向到ARM器件上,如printf和scanf,需重写fputc和fgetc.
具体代码实现可参考如下(重写fputc):
#if 1
#pragma import(__use_no_semihosting) //确保没有从C库链接使用半主机的函数
//标准库需要支持的函数
struct __FILE
{
int handle;
};
FILE __stdout;
//以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->ISR & 0X40) == 0); //循环发送,直到发送完毕
USART1->TDR = (u8) ch;
return ch;
}
#endif
如果不重写fputc等函数,也可直接勾选keil工具里面的Use MicroLIB.
3. 仿真调试
当经过检查后,发现并非是printf函数使用不当导致程序进不去main函数,于是采用在线仿真的方式一步步查找原因。
第一步:先是全速运行,发现程序直接进入了HardFault_Handler。
接着分析启动文件startup_stm32xx.s:在执行进main函数之前,会先执行SystemInit函数,进行系统初始化。
在SystemInit函数里面打断点,单步执行调试,看程序能否执行进来:发现程序可以执行进来。
于是结合上面分析的printf函数原因, 于是尝试在fputc函数打一断点,看是否会跑进fputc函数:发现程序确实跑进了fputc函数
通过SystemInit函数单步执行跑到fputc函数可以分析到,应该是程序哪里出错后,执行了打印输出,而此时串口并未初始化。发现从SystemInit到fputc这样顺序执行下来,不可能会出现打印的函数,那此时有可能就是发生了某种中断,导致在进入main函数之前,跑进了一个中断处理函数里面去了。
那有可能是哪个中断导致的呢?: 像定时器、RTC这些中断,都是进入main函数初始化后才会开启的,排除了这类中断,那应该就是某种系统中断导致的。该stm32程序用到了FreeRtos系统,那可能中断就是systick中断,svc中断,pendsv中断。而svc、pendsv中断都是FreeRtos系统启动后才会执行的。那最大可能就是systick中断了。
接着在systick中断函数打断点,重新单步执行,看是否进入该中断:确实进入了该中断。
通过对这个中断函数一步步执行,发现最终在此处出错了:进入了HardFault_Handler。
定位到是系统滴答中断systick导致进不去main函数的原因了,那哪里开启了这个中断呢?通过代码分析发现是bootloader程序开启了这个中断,在跳转到main函数之前并未关闭这个中断,导致出现了这中异常错误。
4.解决办法
既然是系统滴答中断导致的进不去main函数,那就要在进入main函数之前关闭这个滴答中断。
方法一: 在bootloader程序跳转到app层时就关闭系统滴答中断:SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
方法二:在SystemInit函数关闭系统滴答中断:SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; (如下图所示)
5.总结
在实际项目运用过程中,遇到类似这些奇葩现象,可以尝试通过仿真调试,一步步尝试可能出现错误的地方,进行打断点分析,找出根因所在,你的点赞是我最大的动力。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)