"); //-->
快一个月没有更新了,回家之前给自己安排了很多任务,然后回到家之后电脑就没有打开过,啥也没干,不知道有多少人回家后的状态和鱼鹰是一样的~~
回来之后,准备搞个脚本,自动读取芯片 ID、修改头文件 ID、自动编译、下载。
折腾了一晚上,读取 ID 是搞定了,但是发现有个板子读取 ID 后下载进去发现直接 hardfault 了,可自己年前的时候明明代码没动、板子没动,年前都是没问题的啊。
想不通,反正还有其他板子,直接换一块,下载进去,完全运行正常。因为太晚了,就暂时不管它。
第二天晚上,还是没想明白,本想不管的,想想反正没事情,不如深入研究一下,万一下次遇到这种问题,那不是很快就能定位了嘛。
第一步,打开错误窗口(怎么打开的,自己看往期系列文章吧):
可以看出,总线错误,IMPRECISERR 、STKERR 置位(下面的是 BKPT 是人为给出, FORCED 是因为本来是总线错误,但因为没有开启该中断,直接上访成了 Hardfault,所以真正来说,这次异常应该由总线异常中断处理的)。
突然想起年前好像也发生类似的事情,因为当时程序能正常运行,换板子后不能运行,立马就想到了是芯片差异导致,改了代码就好了;而现在时间过去太久,把这事情忘记了,就闹了这一出(当时解决问题太快,也没深入研究,就没有多少印象了)。
其实这种事情很多工程师估计都遇到过,本来一个程序需要内存 RAM 40 K,在一块板子运行的好好的,换一块就不行,然后发现芯片实际只有 20K。
一般建工程选芯片型号的时候,就会规定好 ROM、RAM大小,只要能正常编译通过,问题都不大,但万一搞错了,就可能遇到鱼鹰的情况了。
工程选择 STM32F103RET6,实际芯片 STM32F103RCT6,内存一个 64 K,一个 48K,偏偏我这个工程内存用量接近满了(如果 RAM 在 48 K 以内即使下载了也是没有问题的),程序也能正常下载,但运行之后立刻 Hardfault 伺候。
很多人遇到这个错误,束手无策,实际上掌握方法,并不是很难的事情。今天鱼鹰教大家一步步排查定位这个错误,大家遇到了其他错误,也可以按鱼鹰的思路解决。
首先,《权威指南》肯定要有,然后就是在线调试了。
从前面的错误信息可以了解到这是一个总线错误,但是我们看到地址这一栏,发现并不像有效地址(是否有效可以看 BFARVALID 是否置位),所以先不管它:
就看那两个打钩的位置啥意思了。看看《Cortex-M3 权威指南》咋说的:
了解这些就差不多了。
此时,我们就要通过在线调试的方式定位错误代码的位置了,毕竟上面的内容只是给我们一个排查方向,但具体怎么解决还看代码情况。
通过单步运行(代码量大的话,可以用二分法排查),并且追踪到汇编级别的代码发现,在即将运行下面代码时 LDR,[pc, #76],直接跳转到了 Hardfault:
PUSH {r4,lr} // 执行完成LDR,[pc, #76] // 未执行
执行 PUSH {r4,lr} 前,未报错(黄色光标位置代表即将执行但未执行的代码):
执行后报错,但是还没有运行到 hard fault (单步调试,芯片可能还没反应过来 -_-):
当想单步执行 LDR,[pc, #76] 时,直接进入了 Hardfault 中断:
这样问题就很明确了,PUSH {r4,lr} 执行有问题,这个和前面从权威指南中得到的信息是一致的。
那么为什么压栈操作会导致问题?
此时我们可以看左边的寄存器,压栈前 0x2000F338 ,压栈后 0x2000F330。
如果对这个数据不敏感,可能不知道这个值超出了 48 K (49152,0xC000)范围,没那么快定位。
没关系,我们可以继续看右边鱼鹰给出的内存窗口,压榨后 0x2000F330 和 0x2000F334 的值应该和 R4、LR 值一一对应,实际上并没有(先减 4,再赋值,图片打开后很清晰,认真分析)。
于是很容易可以得出压栈失败的结论,进而得出内存访问问题,从而发现芯片型号不对导致。
后面鱼鹰又查了英文版《Cortex M3与M4权威指南.pdf》,更详细了一些:
IMPRECISERR bit 解释了:访问无效内存空间。
如此一来,hardfault 问题就顺利解决了 ^_^ ^_^
工作时间久了,这种问题很容易遇到,那么我们的代码是否能自动根据某个寄存器来识别芯片型号,从而确定大小,继而拒绝运行呢?
鱼鹰发现在用 MDK 更新 ST-LINK 固件的时候,它居然知道我芯片型号的准确容量大小(RAM 不行),从而拒绝下载,ST-LINK Utility 也是,就不知道它是通过读取哪个寄存器得到这些信息的了,有知道的道友不如留言分享一下。
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。