这两个月没有怎么更新博文,最近换工作了,根据新工作安排,大半年内都做MCU开发(就不要叫单片机了,太老土了)。
入职新工作了,需重构拳头产品的软件,所以每天加班加点。
单片机与linux应用开发,开发过程中有什么区别之近日个人感悟:
第一点,单片机往往配合仿真器调试,linux应用开发往往使用串口打印,或者经由以太网的gdb调试、生成coredump文件来定位常见的段错误。
linux应用开发调试手段,可以参考我的博文:
嵌入式交叉编译GDB,结合vscode图形化调试C和C++代码 coredump定位段错误
第二点,单片机开发会涉及中断,而linux应用开发不会涉及中断的概念。
(linux驱动开发会涉及中断,但是一般驱动开发工作量少,某个SPI或IIC外设的驱动开发好了以后就可以一直被使用)
近日遇到问题,迷糊了挺久,最后得以解决,在此总结心得。
我的硬件环境: MDK-ARM下调试stm32
我的软件环境: RT-Thread PS:以前也常在业余时间了解RT-Thread,这次终于有机会用它做项目了,跃跃欲试。
我遇到的问题:
翻车在一个GPIO上 开启pin中断 导致程序卡死
具体描述:开启pin中断,就会导致程序卡死,rt_pin_mode()参数配置调整了几次也没用。 翻车在一个GPIO上了。
硬件触发的信号是正常的高低变化的信号,示波器看过。 同时,我当前使用的是gitee-master版本,不是发布版。
1. 仿真后运行程序,外界给入高低电平的信后,这时程序就会卡死,之后停止运行程序,MDK下并没有箭头指示当前C代码运行到哪里去了。
但是这里显示PC是0x08004EE4.
我不知道是否可以根据此PC值定位出当前运行到哪里,以及如何根据此PC值定位出当前运行到了哪里。
需要补充一个技能:根据当前的汇编代码定位出当前运行的C代码。
2. RT-Thread版本,当前是 gitee-master版本,不是发布版。这个有点疑虑,不排除怀疑gitee-master版本的可靠性。但是基本认为gitee-master版本是可靠的。如果实在不行,可以换成release版本去试试。
由于能力有限,目前这个仿真效果对我来说,没什么参考意义,所以我就只能想其他办法了。
图省事,我直接在原工程上屏蔽了其他也操作了该PIN操作相关的地方,然后再次测试,还是卡死。《== 假设这是我改的一个测试程序,称呼其为测试程序1吧。
问题解决后,回顾该调试过程,重现了下该测试过程,
这里我犯错了,因为总共就涉及到三四句代码而已,我图省事,直接在原工程上屏蔽一些代码,然而当时我确实没有屏蔽完全!那么这样,造成的最终效果还是卡死!
即我在修改出测试程序1的过程中又犯下了低级错误, 所以导致我又加深了对这个难点O的疑惑。
展示下我的低级错误:
编译程序并烧录,由于没有进行规范的外部中断脚的配置,造成的最终效果还是卡死!
接着说当时的调试,偶然又发现如果烧录该工程到0x08000000的flash起始地址上,我的中断可以正常跑。
我的项目是使用了OTA的,我当前所编写的是APP代码,所以我就开始怀疑是我的OTA或者称为IAP导致的这个问题。
Q群讨论,知道了如何查看当前的异常向量表的地址,如下图
重新实操了下,回顾再次回顾该步骤调试过程:
我当时肯定又进行了一些代码的修改,假设这是测试程序2 ,测试程序2直接烧录到0x08000000后外部中断正常,之前测试程序1是个有问题的程序,当时这个测试程序2恰好又被我改对了而已!
我认为当时这个测试程序2作为APP,烧录到我的APP的0X08020000这个地址肯定也是可以正常运行和响应外部中断的。
所以当时,我又没能调试好我的这个中断响应后卡死的问题。
后来是怎么调试好的呢?接着说
因为我使用的外部中断脚是PD11,那么它的中断服务函数归属于EXTI15_10_IRQHandler
我一看,是个弱定义,又没搜索到函数定义,虽然我以前还阅读分析过RT-Thread的GPIO驱动框架的源码,但那都是一年半之前的一点兴趣爱好而已,早就忘干净了。
于是又没继续下去了,现在回顾之前在MDK下的搜索方式,正确的方式应该是这样:
全都勾选上。
我当时只勾选了左侧两个,所以没有搜索到RT-Thread的drv_gpio.c内的 EXTI15_10_IRQHandler()函数定义。
后来我在此处打上断点,然后外界给入高低电平,发现可以进入到这里!一步步运行, 能进入我的中断服务程序,在中断服务程序内打断点,发现一直在里面打转出不来。
终于定位问题了。
最后的问题原因在于:
中断服务程序内一个链表头忘记初始化了,导致ISR内没出来,双向循环链表没初始化,for循环出不来了。
总结:
1. 习惯要好。 如果要编写测试程序,哪怕只是三四句代码,最好也重新搞一个工程,或者,假设我要修改main函数,就屏蔽掉原来的main函数,重新搞一个main函数,而不是直接在main函数内部去修改。
并且最好做到屏蔽掉无关的代码,即使自己以为很有把握,但是一句话说得好:难得糊涂。一糊涂可就完事了,完蛋了。难得糊涂是不被允许的。
2. 要注重加强打断点调试,遇到相关的问题,从源头开始打断点,一步步仿真执行下去。仿真不是点击一个开始允许,和一个停止运行。
例如我遇到的问题是STM32的基于HAL库的中断问题,那么就从对应引脚的HAL库中断服务函数处开始设置第一个断电,然后一步步进去。
MDK的各个调试按钮、各类观察窗口要熟悉。
3. 单片机上也会遇到段错误,没有coredump咋搞? RT-Thread官网介绍的CmBacktrace可以了解一下。
.