见鬼,过年回来后板子就 hardfault 了?

来源:公众号【鱼鹰谈单片机】

作者:鱼鹰Osprey

ID   :emOsprey

快一个月没有更新了,回家之前给自己安排了很多任务,然后回到家之后电脑就没有打开过,啥也没干,不知道有多少人回家后的状态和鱼鹰是一样的~~

回来之后,准备搞个脚本,自动读取芯片 ID、修改头文件 ID、自动编译、下载。

折腾了一晚上,读取 ID 是搞定了,但是发现有个板子读取 ID 后下载进去发现直接 hardfault 了,可自己年前的时候明明代码没动、板子没动,年前都是没问题的啊。

想不通,反正还有其他板子,直接换一块,下载进去,完全运行正常。因为太晚了,就暂时不管它。

第二天晚上,还是没想明白,本想不管的,想想反正没事情,不如深入研究一下,万一下次遇到这种问题,那不是很快就能定位了嘛。

第一步,打开错误窗口(怎么打开的,自己看往期系列文章吧):

85fafa4cdabb3554cc6f2f2ef250ad03.png

可以看出,总线错误,IMPRECISERR 、STKERR 置位(下面的是 BKPT 是人为给出, FORCED 是因为本来是总线错误,但因为没有开启该中断,直接上访成了 Hardfault,所以真正来说,这次异常应该由总线异常中断处理的)。

突然想起年前好像也发生类似的事情,因为当时程序能正常运行,换板子后不能运行,立马就想到了是芯片差异导致,改了代码就好了;而现在时间过去太久,把这事情忘记了,就闹了这一出(当时解决问题太快,也没深入研究,就没有多少印象了)。

其实这种事情很多工程师估计都遇到过,本来一个程序需要内存 RAM 40 K,在一块板子运行的好好的,换一块就不行,然后发现芯片实际只有 20K。

一般建工程选芯片型号的时候,就会规定好 ROM、RAM大小,只要能正常编译通过,问题都不大,但万一搞错了,就可能遇到鱼鹰的情况了。

a5d4c842863ac2e8ca131092f6f2b511.png

工程选择 STM32F103RET6,实际芯片 STM32F103RCT6,内存一个 64 K,一个 48K,偏偏我这个工程内存用量接近满了(如果 RAM 在 48  K 以内即使下载了也是没有问题的),程序也能正常下载,但运行之后立刻 Hardfault 伺候。

很多人遇到这个错误,束手无策,实际上掌握方法,并不是很难的事情。今天鱼鹰教大家一步步排查定位这个错误,大家遇到了其他错误,也可以按鱼鹰的思路解决。

首先,《权威指南》肯定要有,然后就是在线调试了。

从前面的错误信息可以了解到这是一个总线错误,但是我们看到地址这一栏,发现并不像有效地址(是否有效可以看 BFARVALID 是否置位),所以先不管它:

3130a54736572875f102d5edccce5199.png

就看那两个打钩的位置啥意思了。看看《Cortex-M3 权威指南》咋说的:

df29da03a8332a033fb8035bcd2c989b.png

了解这些就差不多了。

此时,我们就要通过在线调试的方式定位错误代码的位置了,毕竟上面的内容只是给我们一个排查方向,但具体怎么解决还看代码情况。

通过单步运行(代码量大的话,可以用二分法排查),并且追踪到汇编级别的代码发现,在即将运行下面代码时 LDR,[pc, #76],直接跳转到了 Hardfault:

PUSH {r4,lr}   // 执行完成
LDR,[pc, #76]  // 未执行

执行 PUSH {r4,lr}  前,未报错(黄色光标位置代表即将执行但未执行的代码):

865787a760d45099ab5b0818fe7e002e.png

执行后报错,但是还没有运行到 hard fault (单步调试,芯片可能还没反应过来 -_-):

58e91e5b168ef3700fae267cc060720b.png

当想单步执行 LDR,[pc, #76] 时,直接进入了 Hardfault 中断:

fc1be36c02b068d0972b91018d181a8e.png

这样问题就很明确了,PUSH {r4,lr}  执行有问题,这个和前面从权威指南中得到的信息是一致的。

那么为什么压栈操作会导致问题?

此时我们可以看左边的寄存器,压栈前 0x2000F338 ,压栈后 0x2000F330。

如果对这个数据不敏感,可能不知道这个值超出了 48 K (49152,0xC000)范围,没那么快定位。

没关系,我们可以继续看右边鱼鹰给出的内存窗口,压榨后 0x2000F330 和 0x2000F334 的值应该和 R4、LR 值一一对应,实际上并没有(先减 4,再赋值,图片打开后很清晰,认真分析)。

于是很容易可以得出压栈失败的结论,进而得出内存访问问题,从而发现芯片型号不对导致。

后面鱼鹰又查了英文版《Cortex M3与M4权威指南.pdf》,更详细了一些:

66aac2d78ff005e714bd26fa12faef30.png

IMPRECISERR   bit 解释了:访问无效内存空间。

49c870d6d9f39063cf14a02b655b9593.png

如此一来,hardfault 问题就顺利解决了 ^_^ ^_^

工作时间久了,这种问题很容易遇到,那么我们的代码是否能自动根据某个寄存器来识别芯片型号,从而确定大小,继而拒绝运行呢?

鱼鹰发现在用 MDK 更新  ST-LINK 固件的时候,它居然知道我芯片型号的准确容量大小(RAM 不行),从而拒绝下载,ST-LINK Utility 也是,就不知道它是通过读取哪个寄存器得到这些信息的了,有知道的道友不如留言分享一下。

362fae4df36510a3458aa35b0657ab06.png

另外上面提到的两本手册如果没有,可以公众号后台回复:“权威指南”  自行领取。也可以自己人为制造该错误,自行分析、研究、学习。

推荐阅读:

KEIL 调试经验总结

佛祖保佑,永无 BUG,永不修改 | KEIL 调试系列总结篇

更多关于调试的技术请参考鱼鹰历史文章和 MDK 官方文档。安富莱最近也出了一个视频教程,可以研究看看,鱼鹰看完了,感觉还不错,推荐大家看一看(B 站 1.5倍速看效果更好)。

版权声明:本文为CSDN博主「EmbeddedOsprey」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42876465/article/details/123059490

生成海报
点赞 0

EmbeddedOsprey

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

单次事件触发DMA读取多字节SPI数据

有人想使用定时器事件触发DMA读取SPI数据,希望每一次更新事件仅触发一轮DMA传输,借助DMA实现N字节的SPI数据收发。要完成这个功能,这里推荐两种做法供参考。第一种,先做好SPI的D

AD9528芯片介绍及配置详解

AD9528是ADI(亚德诺半导体技术有限公司, Analog Devices, Inc. 简称ADI )出品的一款双级PLL,集成JESD204B SYSREF发生器,可用于多器件同步。第一级锁相环(PLL) (P

时隔10年,STM32标准外设库再次升级

关注星标公众号,不错过精彩内容作者 | strongerHuang微信公众号 | strongerHuang有一个读者问了我这样一个问题:能请教一个问题吗,就是STM32F071的PORTE端口怎么用&