一、磨刀不误砍柴功
在调试之前,需要掌握以下一些基本功:
1、熟悉当前的开发(调试)环境,比如:设置断点、单步运行、全速运行、终止运行,查看RAM、查看堆栈、查看IO口状态……总之,要熟练掌握基本操作的方法,并深刻了解其中意义。
2、了解芯片本身的资源和特性。
3、了解一点汇编语言的知识。(本来是准备写“精通”的,但考虑到现状,还是“放低”这方面的要求罢了)。
4、掌握基本的电路知识和排错能力。(软件调试有时也会牵涉到硬件原因。总不能连三极管的好坏都不能识别吧?)
5、万用表、示波器、信号发生器……这些工具总该会用吧?
6、搜索、鉴别资料的能力。(内事问百度、外事问古狗)
7、与人沟通,描述问题的能力。(调试36计的最后一计——就是向他人讨教。当然,你得把话说明白才行)
差不多了,如果上述7把砍柴刀磨好了,就可以开始调试了。接下来,请调入你的程序……
——什么?你说你程序还没写?
——倒塌……
二、优先调试人机界面
面对程序中的一大堆模块,无从下手是吗?好吧,告诉你,先调显示模块,然后是键盘。
为什么要先调显示模块?道理很简单,我们说“眼睛是心灵的窗户”,同样,“显示是程序的窗户”。一旦把显示模块调试好了,就可以通过这个窗口,偷 窥 (天呐,这两个居然是敏感字!) 程序内部的数据和状态了。
然后紧接着,就是调试键盘模块。有了这个按键,我们就可以人工干预程序的运行了。
——什么,你的程序没有显示和按键?
——这位童鞋,你真不幸,请去检查一下自己的人品和星座运程先。谢谢。
实在是没显示?再看看系统有蜂鸣器吗?如果侥幸有的话,也能凑合着发发提示声音吧?
或者,有串口吗?可以考虑借助PC 端的串口调试软件来收发数据,这也是一个间接的人机交流方法。
总而言之,要尽快建立人机交流界面。
三、慢镜头的威力
2009年春晚捧红了魔术师刘谦(这位老兄名“谦”,其实一点都不谦虚——长的帅不是错,出来拽就是罪过了!),也勾起了大家对魔术的浓厚兴趣,如何识破那些快速的眼花缭乱的魔术手法呢?很简单,用慢镜头回放即可。据说刘谦那个橡皮筋魔术的手法就是被人如此识破的。
回到我们单片机上来。我们知道,单片机的运行速度,一般都是在几M到几十M(当然,也有为了节能而采用几十K的低速)。不管怎么样,这个速度都远远超出了我们人眼能够分辨的速度。眼睛一眨,也许几M条指令已经执行过去了。
比如说数码管显示(假设有4位数码管)。平时我们看到数码管同时点亮着,但是实际上,这4个数码管是逐个扫描的。在任意一个时刻,只有一位数码管被点亮。在微观上,我们可以进一步把每位数码管的扫描动作细分为以下几个步骤:
1、关闭上一位数码管的位选信号;
2、输出当前位数码管的段选信号;
3、开启当前位数码管的位选信号;
4、启动1ms延时;
5、延时结束后,指针移动到下一位数码管,并重复上述4个步骤,如此周而复始。
你看,这样是不是就像用一个慢镜头在分解显示扫描的动作了?
那么如何实现这个慢镜头呢?方法很多:
1、单步运行(需要仿真器支持);
2、在每一步分动作之后设立断点(需要仿真器支持);
3、在每一步分动作之后插入足够的延时,让我们肉眼可以看清楚这些分动作(不需要仿真器,适合烧片测试);
通过慢镜头的反复回放,我们就可以发现,到底是哪一个分动作出现了问题。
这个技巧,不仅仅适用于调试显示程序,也适用于按键扫描或其它模块。只要一个功能可以被细分为若干的动作,那么这一招“慢镜头分解法”都是可以使用的。
四、给程序安装个黑匣子
某年某月的某一天,一架飞机以优美的抛物线形状,一头栽到海里去了……几天后,人们找到了飞机的黑匣子,里面记录了飞行员的最后一句话:“天呐,我看到火星人了!……”
以上空难情节我们经常会通过新闻看到吧(当然,最后一句是版的科幻情节)。看看,飞机的黑匣子可以记录并再现现场,多么神奇!欧耶!
我们在调试程序时,也可以借鉴这个方法,给程序按装一个黑匣子。程序中的黑匣子其实就是一个在内存中开辟的队列。队列的原理我们很清楚,先进先出,后进后出(与飞机黑匣子的特性相同)。
比如说吧,假设我们的系统在工作中,某个输入量的采样值经常受到不明原因的扰动。我们要摸清这种扰动的规律,以便对症下药。但是这种扰动稍纵即逝。
我们的困扰是:程序正常运行时看不出规律,单步走又难以捕捉扰动。怎么办?
有没有办法,把扰动记录下来?当然可以。
我们可以利用系统里剩余的RAM,开辟一块单元,做成队列。并写段测试程序,定时把新采样值压入队列。
然后我们让程序运行,在需要的(任意)时刻,让程序停下来。这时,队列里记录的就是最新一批采样数据。
只要队列的深度足够大,我们就可以找出扰动的规律来。
——什么,你问我什么叫队列?
——天呐,我看到火星人了!……
五、在程序中设卡伏击,拦截流窜犯
警察抓流窜犯的场面我们都很熟悉了。一般的方法,就是以案发现场为中心,在犯罪分子逃窜的必经路口,设卡盘查。有道是天网恢恢疏而不漏,叫你插翅也飞不过去。
有时,程序中也会出现这样一个“流窜犯”,它就是PC指针。
对于一个未经调试的不成熟的程序来说,导致PC指针跑飞的因素很多,我们逐条列举并分析之:
1、电磁干扰(如果不是在现场,那么这一条可以暂时不考虑。因为在调试环境下一般不会有干扰);
2、程序结构错乱(喜欢用jmp或goto类指令的尤其要注意这点);
3、堆栈溢出或错乱,导致PC指针出错;
4、PC指针被错误改写(有些芯片PC指针存储单元和其它RAM单元的访问方法是一样的,很容易被误写);
5、数据错误,导致程序没有按照预期路径运行;
6、看门狗溢出(原因一般是因为看门狗设置不当、喂狗不及时、程序堵塞或者程序死循环);
7、中断被意外触发;
8、外部电路问题,比如电源不稳等等;
9、其它……
当我们开始怀疑PC指针时,我们首先要做的是确认PC指针是否跑飞了,其次要找到PC指针跑飞的证据。
我们可以在不同的分支路口,或者在我们怀疑的地方,设立断点,看程序是否走了不该经过的路径。
举个例子,比如我们怀疑程序运行中看门狗发生了溢出复位,那么很简单,我们只需要在初始化入口设立一个断点,让程序运行。正常情况下,程序只会经过一次该断点。如果再次经过该断点被拦截,那么我们就可以初步确诊“看门狗发生了溢出复位”。
再举个例子,比如程序中某个环节有A、B两个分支,正常时只走A分支,不正常时才走B分支。那么我们可以在B分支设立断点,程序一旦异常,走入B分支,就可以被拦截下来。
程序被拦截下来后,我们可以勘察现场,查看RAM区内容和程序刚走过的路径,从中分析导致程序PC指针错乱的原因。
当然,并不是每一次伏击守候都能一举擒获流窜犯(敌人是“狡猾”的,呵呵)。这就需要我们多一份耐心和技巧。通过不断调整断点位置来改变拦截地点。逐渐逼近并找到根源(流窜犯的老巢),然后一举拿下。