原创 一次内存错误调试总结(平台Cortex-M3)

2013-2-20 20:19 1813 8 22 分类: 消费电子

 

芯片:STM32F103CBT6
IDE:Keil
调试器:J-Link
操作系统:uCOS
 
从一开始怀疑硬件问题,到最后定义到内存错误的思路,
有些思维定式。
首先从最开始发现程序有一个地方通不过时,通过断点的逐步调试,
很快发现了是哪里,导致程序没有运行过去。
但是这里没有再继续缩小断点的范围,
从而漏掉了在最短的时间内,将问题解决的时机。
如果这个时候,再逐步缩小断点设置的范围,将程序通不过的地方,有几行代码,
缩小到一到两行代码,这时就比较容易去分析是什么问题,是什么原因导致了这个问题。
 
但是这时自己没有再去缩小断点的范围,而是将其运行通不过的范围,放在了9行代码上,
但是由于造成的这个通不过,是导致程序直接复位,这样就很难断定是什么问题。
虽然自己当时也常识分析,是什么原因导致代码,通不过这段程序,但是一直没有考虑到程序复位,
这个,应该是调试时的一个思维惯性,
 
为什么没有考虑到程序复位的情况?
应该是以为程序像其他步骤一样,没有接收到数据,或者接收到的数据不对,没有进行处理,
而是又回到了最初的步骤,开始执行。这点是在使用断点调试时很难发现的,因为没有在程序的运行开始处
设置断点,这样很难判断程序是否复位。
后来之所以发现这个复位的情况,是自己设置的一个用于调试使用的全局变量,这个值
在程序中有的地方进行赋值,这个调试使用的全局变量,只有在程序复位时才会是零。
由此,发现了程序复位的情况。
这个时候,使用串口调试可能更快的发现复位的情况。
或者就是,在程序的起始处设置一断点,如果程序复位就可以检测到。
 
于此得出一经验,在调试时最好在程序的起始端添加一断点,这样如果程序复位就可以马上检测到。
虽然一般,用不上,但是如果遇到了,可以节省我们很多的调试时间。
 
当检测到复位时,首先怀疑的是硬件原因。
因为程序已经在以前一个板子中跑了将近一年,而这次使用的是刚焊好的板子,
依照程序不会在两个板子中表现差异很大,因为毕竟这些都是数字电路。
没有模拟那么玄乎。所以首先怀疑是硬件问题,而在这一通不过的代码前,程序正常执行,
只有在这一段代码内出错,所以怀疑,是这一代码导致了电源不稳定导致了MCU复位。
但是金哥用示波器测试发现电源出来一只很稳定,即使是在程序通不过的地方,也没有出现什么异常。
 
这时,调试就有些陷入僵局。
 
于是,当程序运行到通不过的那一段代码时,暂停程序运行,查看代码在哪里停住,
发现程序运行到了
App_Fault_ISR       B       App_Fault_ISR
对于这个问题,以前并没有遇到过,于是在百度上搜了下,发现很多人碰到过这个问题。但是没有人说,是硬件引起的,
反而很多说是内存操作失误,或者栈溢出。
这样硬件导致复位的因素,可以暂时放一放,于是又回到了最早的那个断点的地方。
 
这个时候,终于想起来了,为什么不进一步缩小程序通不过的范围呢?
于是将第一个断点不变,将第二个断点,向前设置一行,发现依然没有运行到第二个断点,
于是在一次将第二个断点向前设置几行,直到程序运行到第二个断点处为止。
找到这一行后,
将第一个断点设置到这一行,
然后将第二个断点向后设置一行,发现这个时候,程序在第一个断点处开始运行后,没有走到第二个断点,
于是得出结论,程序在第一个断点处的代码导致了程序发生
App_Fault_ISR       B       App_Fault_ISR
于是在分析这一段代码,发现
是用的是strcat函数,这个函数是有可能造成内存错误(栈溢出的),因为如果它的第二个参数没有以'\0'结尾,
它就会以第二个参数的地址开始将内存中的内容复制到第一个参数中,直到遇到以第一个'\0',如果没有遇到就继续向第一个参数
写入。
于是这时在断点处查看,strcat的第二个参数的值,发现这个参数中的值全都是0XFF,这就导致了将第一个参数所在的栈内存破坏掉了。
至此找到了,程序复位的原因。
 
 
导致在新板子上出现问题的原因是,刚焊好的板子中Flash没有写入过参数,
而strcat的第二个参数的值,是从Flash中读取的,由于Flash默认读出的是0xFF,
所以造成了这个错误。
关于这个bug,有三个地方可以避免,
1、设置初始化默认参数,
2、在读出数据后,进行判断,如果不正确,将其设置为默认的参数。
3、在复制时,采用strncat,防止数据出错时,不影响堆栈。
 
在这里,分析下使用断点调试和串口调试
串口调试:
需要一定的技巧,
设置打印输出的地方,输出的打印信息,
在分析时,串口调试更直观的看到程序运行出现什么现象,
但是它很难判断到程序具体运行到什么地方,
所以串口调试的方法,有助于很快发现问题,
但是对于解决问题,它显得力不从心,
因为它很难判断程序运行到的具体代码行。
 
对于断点调试,
需要技巧,设置断点的位置,引入的调试的变量。
这一点上,断点调试显得没有串口调试那么直观,
因为对于程序运行的状态只能从设置的断点处去推测,
这时引入的调试变量,以及此时在断点处涉及到的变量的值就显得尤为重要,
调试变量的作用相当于串口调试中的打印输出,它的引入与设置,很关键,
直接影响到我们看到程序运行的轨迹。
当前的变量的值,指示了现在断点处的程序状态。
对于此,断点调试更适合在细节上使用,它有助于缩小问题的范围,
找到问题所在的代码行,能很快解决问题。
 
综上,
对于串口调试与断点调试,最好的方法是结合使用,
在宏观的调试上使用串口调试,在微观的调试上,
使用断点调试。
当然,在不支持断点调试的情况下,使用串口,led,液晶等显示
我们需要的信息,也是一种方法,判断的思路是一致的。
从宏观到微观。
 
Bug导致的原因分析:
程序在ETCPIP处复位(GPRS激活PDP)。
1、是否因为拨号时,电流过大,造成单片机电流出现问题。
2、最后发现,是程序内存溢出造成的。
因为,新板子中Flash没有配置参数,而原来程序中没有默认的初始化参数,
导致当从Flash中读取错误的数据时,读到的错误数据没有进行处理,
同时在复制数据时,使用的是strcat,由于数据错误,结尾没有'\0'
结果就一直在复制,造成了内存错误。
这里有三个地方可以,防止这个bug,
但是都没有做,
1、设置初始化默认参数,
2、在读出数据后,进行判断,如果不正确,进行相应的处理
3、在复制时,采用strncat,防止数据出错时,不影响堆栈。
 
由此知,在程序中,关于读出数据,和写入数据要进行Assert,
保证数据正确。

文章评论14条评论)

登录后参与讨论

用户1515636 2013-3-1 14:38

学习了

1989tie_959541171 2013-2-28 07:57

之前的帖子没有表达清楚,GPRS和MCU确实是分开的电源供电。但是电压不稳的情况,确实存在。有些不是很明白,为什么电压的瞬间变化会导致板子MCU复位。按理说里边的大电容应该会起到一定的作用,但似乎不是这样。

1989tie_959541171 2013-2-28 07:53

现在用的STM32感觉都还可以,环境可能没有那个帖子里讨论的那么糟糕。但也是在强电附近工作,没有发生什么意外情况。NXP的据说抗干扰性很好,只是价格比STM32的要贵一些,没有实际用过,以前用过NXP的ARM7,它的M3没有用过。

用户1039176 2013-2-27 19:52

STM32稳定性不错的

用户1406868 2013-2-27 16:57

不错

allen_zhan_752827529 2013-2-27 15:59

顺便问一下 STM32 的稳定程度如何? 我还没用过 M3. 有点想选 NXP 来用. 一般网络上资源似乎不推荐 TI 的 M3. 关于这个说法, 请见: http://bbs.21ic.com/icview-107346-1-1.html 因为我都没用过, 所以只是给资料出处你参考.

allen_zhan_752827529 2013-2-27 15:56

GPRS 模块一般占用单独的电源 Channel. 无论拨号上线或者发送数据, 都不应影响 MCU. 你说的现象, 似乎应该留心下, 是不是瞬间发送功率过大, 拉低了主 Channel(比如 5v), 又导致从 5v 降压得到的 MCU 的 channel 不正常? 我给一个我们的经验值, 一般我们认为无论是 GPRS 模块第一次上线(登录PPP, 可能为最高脉冲), 或者是其他发送数据瞬间, 我们觉得在 GPRS 模块的 channel 上, 瞬间负载导致的压降似乎不应超过 100mV. 导致拉动 main channel 如 5v, 似乎不应超过 200mV. 在上述参数下, MCU 理论上不应收到 GPRS 模块工作的任何影响.

1989tie_959541171 2013-2-27 12:59

拿示波器看过电压波动,确实如你所言,在拨号时电压波动有些大。这或许就是现在板子有时候插头接触不是很好时,电压有较大的瞬间波动,就导致了板子的CPU重启。

1989tie_959541171 2013-2-27 12:56

确实是这样,相对于strcpy,strcat如果使用strncpy,strncat,memcpy将size参数加进去会好很多。很多就是我们在开始学的时候的习惯,造成了现在潜在的隐患。

1989tie_959541171 2013-2-27 12:54

呵呵,谢谢提醒。 这个整理完,没有注意格式。 以后会注意的。
相关推荐阅读
catch2000 2015-07-19 11:44
信号线小电阻的作用
在一块新的PCB上,测试系统能否正常运行的时候,发现系统上电后没有正常启动。  系统框图如下:   在上电的时刻,CPU A(GPIO电平2.6V)会向串口发送启动日志数据,CPU A启动后,...
catch2000 2015-07-05 17:04
协议设计中ACK机制的影响
在TCP/IP中,延时ACK和Nagle算法。  TCP为了同时处理成块数据(通常为512字节的用户数据)和交互数据(通常用户数据比较少,例如不大于10个字节),采用了延时ACK和Nagle算法...
catch2000 2015-05-23 15:48
话说物联网操作系统
最近好多家都宣布推出自己的物联网操作系统。   1. Google将要在Google I/O大会发布的Brillo; 2. 三星推出的Artik芯片搭载Mentor Graphics的...
catch2000 2015-03-31 23:52
不要采用异或来交换两个变量
在进行两个变量的时候,经常会看到有些书误人子弟的推荐使用异或的方式: 方式一 {   x = x ^ y;   y = x ^ y;   x = x ^ y; } 而不是...
catch2000 2014-10-09 07:28
为什么要测试先行
在产品的研发过程中,测试一项至关重要。不论是软件还是硬件。   软件的测试先行,在研发过程中,就做到质量的保证,因为在出现Bug的时候,容易定位Bug,而且即使是在客户端出现Bug,也能够...
catch2000 2014-10-09 07:26
C语言的面向对象编程(一)
一、前言 对于编程而言,重要的是解决问题的方式,而不是语言本身。面向对象与面向过程是解决问题和思考问题的方式。C语言常说是面向过程开发的语言,因为其缺少很多对于面向对象特性的支持。但,这并不影...
我要评论
14
8
关闭 站长推荐上一条 /2 下一条