单片机程序调试小技巧
《匠人手记》网络版
程序调试(除错)过程中
的一些雕虫小技
作者:程序匠人
发布日期:2009-11-26
《匠人手记》封面
E-MAIL:[email protected]
《匠人的百宝箱》博客:http://cxjr.21ic.org/
《匠人手记》edn书友会:http://group.ednchina.com/628/
《匠人手记》21ic书友会:http://bbs.21ic.com/iclist-63.html
手 记 目 录
一、 前言.........................................................................................................................1
二、 磨刀不误砍柴功.....................................................................................................1
三、 优先调试人机界面.................................................................................................2
四、 慢镜头的威力.........................................................................................................2
五、 快镜头的威力.........................................................................................................3
六、 程序中的黑匣子.....................................................................................................3
七、 设卡伏击,拦截流窜犯.........................................................................................4
八、 向猎人学习挖坑设陷阱的技术.............................................................................5
九、 程序中的窃听器.....................................................................................................6
十、 拉闸睡觉!统一管理调试代码.............................................................................7
十一、 附录:《匠人手记》简介...................................................................................9
1、 《匠人手记》内容简介...................................................................................9
2、 《匠人手记》目录...........................................................................................9
3、 《匠人手记》封面及内页.............................................................................10
4、 《匠人手记》热销情况.................................................................................11
5、 《匠人手记》被各界推荐.............................................................................12
6、 《匠人手记》读者热评.................................................................................13
7、 购书渠道.........................................................................................................13
8、 相关连接.........................................................................................................14
9、 《匠人手记》书友会Q群............................................................................14
10、 《匠人手记》网络版 版权声明.................................................................14
程序调试(除错)过程中的一些雕虫小技
一、前言
调试程序,是软件开发过程中的一个必不可少的环节。这篇手记,匠人试着来整理一下日常调试过程中用到的技巧。
说到“技巧”,这个词自从被所长批臭之后,匠人就吓得不敢再提,生怕一不小心就暴露了思想的浅薄和眼光的局限,呵呵。所以咱们不叫“技巧”,干脆低调点,就叫“雕虫小技”吧。
这里所讨论的“调试”技巧,有些是必须结合开发工具本身的功能来实现,而有些可以通过烧录芯片来验证。
各种开发工具,提供的功能多少强弱也不尽相同,这些方法也未必都能套用。仅供参考吧。
最后说明一下,这是没有草稿的帖子,匠人仍然以不定期连载的方式,边写边发边改。可能结构会比较混乱。欢迎大家一起参与讨论。
二、磨刀不误砍柴功
在调试之前,需要掌握以下一些基本功:
1、熟悉当前的开发(调试)环境,比如:设置断点、单步运行、全速运行、终止运行,查看RAM、查看堆栈、查看IO口状态……总之,要熟练掌握基本操作的方法,并深刻了解其中意义。
2、了解芯片本身的资源和特性。
3、了解一点汇编语言的知识。(本来匠人是准备写“精通”的,但考虑到现状,还是“放低”这方面的要求罢了)。
4、掌握基本的电路知识和排错能力。(软件调试有时也会牵涉到硬件原因。总不能连三极管的好坏都不能识别吧?)
5、万用表、示波器、信号发生器……这些工具总该会用吧?
6、搜索、鉴别资料的能力。(内事问百度、外事问古狗、有事没事上21ic网)
7、与人沟通,描述问题的能力。(调试36计的最后一计——就是向他人讨教。当然,你得把话说明白才行)
差不多了,如果上述7把砍柴刀磨好了,就可以开始调试了。接下来,请调入你的程序……
——什么?你说你程序还没写?
——匠人倒塌……
三、优先调试人机界面
面对程序中的一大堆模块,无从下手是吗?好吧,匠人告诉你,先调显示模块,然后是键盘。
为什么要先调显示模块?道理很简单,我们说“眼睛是心灵的窗户”,同样,“显示是程序的窗户”。一旦把显示模块调试好了,就可以通过这个窗口,偷 窥 (天呐,这两个居然是敏感字!) 程序内部的数据和状态了。
然后紧接着,就是调试键盘模块。有了这个按键,我们就可以人工干预程序的运行了。 ——什么,你的程序没有显示和按键?
——这位童鞋,你真不幸,请去检查一下自己的人品和星座运程先。谢谢。
实在是没显示?再看看系统有蜂鸣器吗?如果侥幸有的话,也能凑合着发发提示声音吧?
或者,有串口吗?可以考虑借助PC 端的串口调试软件来收发数据,这也是一个间接的人机交流方法。
总而言之,要尽快建立人机交流界面。
四、慢镜头的威力
2009年春晚捧红了魔术师刘谦(这位老兄名“谦”,其实一点都不谦虚——长的帅不是错,出来拽就是罪过了!),也勾起了大家对魔术的浓厚兴趣,如何识破那些快速的眼花缭乱的魔术手法呢?很简单,用慢镜头回放即可。据说刘谦那个橡皮筋魔术的手法就是被人如此识破的。
回到我们单片机上来。我们知道,单片机的运行速度,一般都是在几M到几十M(当然,也有为了节能而采用几十K的低速)。不管怎么样,这个速度都远远超出了我们人眼能够分辨的速度。眼睛一眨,也许几M条指令已经执行过去了。
比如说数码管显示(假设有4位数码管)。平时我们看到数码管同时点亮着,但是实际上,这4个数码管是逐个扫描的。在任意一个时刻,只有一位数码管被点亮。在微观上,我们可以进一步把每位数码管的扫描动作细分为以下几个步骤:
1、关闭上一位数码管的位选信号;
2、输出当前位数码管的段选信号;
3、开启当前位数码管的位选信号;
4、启动1ms延时;
5、延时结束后,指针移动到下一位数码管,并重复上述4个步骤,如此周而复始。
你看,这样是不是就像用一个慢镜头在分解显示扫描的动作了?
那么如何实现这个慢镜头呢?方法很多:
1、单步运行(需要仿真器支持);
2、在每一步分动作之后设立断点(需要仿真器支持);
3、在每一步分动作之后插入足够的延时,让我们肉眼可以看清楚这些分动作(不需要仿真器,适合烧片测试)。
通过慢镜头的反复回放,我们就可以发现,到底是哪一个分动作出现了问题。
这个技巧,不仅仅适用于调试显示程序,也适用于按键扫描或其它模块。只要一个功能可以被细分为若干的动作,那么这一招“慢镜头分解法”都是可以使用的。
五、快镜头的威力
前面已经讲过慢镜头,这回再讲快镜头。
慢镜头的作用的把程序的运行节奏降低,以便我们能够“一帧一帧”地观测程序的运行状态。而快镜头的作用,则相反,就是让程序的运行节奏变快,让我们验证一些原本需要消耗较多等待时间的功能。
比如说,一个定时功能,定时范围是可调的,为1~24小时。如果我们要去验证,总不能傻等1~24小时吧?
怎么办呢?快镜头来了。
我们知道程序中的时间,是靠一级一级的计时器累计上来的。比如一个程序中分别有“时、分、秒”三个计时器单元。依次计数,逢60进一。“秒”计满60次了,则“分”+1;“分”计满60次了,则“时”+1;“时”计数超过设定值了,我们就可以判定定时结束。
那么我们只要修改一下“分”到“时”的进位关系。比如改成:“分”+1;“分”计满1次(原本是60次)了,则“时”+1。这样一来,整个定时系统速度就比原来提高60倍。测试起来就很省时间了。
当然,测试完成后,记得要把刚才做的测试代码改回原样哦。
举一反三,“快镜头”技巧,不仅仅用在定时方面,也可以用在计数方面。通过对数据的变化“加速”,来加快我们的测试速度。
——什么,你喜欢磨洋工,愿意花24小时去测试那个定时功能?
——哈哈,放心,我不会告诉你的老板的——除非他使出美人计来对付我。欧耶!
六、程序中的黑匣子
某年某月的某一天,一架飞机以优美的抛物线形状,一头栽到海里去了……几天后,人们找到了飞机的黑匣子,里面记录了飞行员的最后一句话:“天呐,我看到火星人了!……”
以上空难情节我们经常会通过新闻看到吧(当然,最后一句是匠人版的科幻情节)。看看,飞机的黑匣子可以记录并再现现场,多么神奇!欧耶!
我们在调试程序时,也可以借鉴这个方法,给程序按装一个黑匣子。程序中的黑匣子其实就是一个在内存中开辟的队列。队列的原理我们很清楚,先进先出,后进后出(与飞机黑匣子的特性相同)。
比如说吧,假设我们的系统在工作中,某个输入量的采样值经常受到不明原因的扰动。我们要摸清这种扰动的规律,以便对症下药。但是这种扰动稍纵即逝。
我们的困扰是:程序正常运行时看不出规律,单步走又难以捕捉扰动。怎么办?
有没有办法,把扰动记录下来?
当然可以。
我们可以利用系统里剩余的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指针错乱的原因。
当然,并不是每一次伏击守候都能一举擒获流窜犯(敌人是“狡猾”的,呵呵)。这就需要我们多一份耐心和技巧。通过不断调整断点位置来改变拦截地点。逐渐逼近并找到根源(流窜犯的老巢),然后一举拿下。
八、向猎人学习挖坑设陷阱的技术
上一回说到,在程序中设卡(断点),可以拦截流窜犯(程序流程错误)。实际上,断点的功能可强大了,不但可以拦截程序流程错误,也可以拦截数据错误。当然,这需要一些辅助手段。
还是以前面提到的一个例子来说。比如某个采样值(当然,也不一定是采样值,在这里也可以是RAM中任意单元中的值)受到未明因素影响,经常“乱跳”。这种数据出错的原因,可能如下:
1、计算错误(比如溢出),导致结果出错;
2、被其它程序段误改写;
3、其它原因……
当数据出错后,我们希望能够在最快时间内,让程序停下来,这样才能有效查出是哪一段程序出了问题。
有些调试环境本身可以捕捉数据错误,并产生断点中断。这当然最好不过。但是如果调试环境本身不提供这种捕捉功能,那么就需要我们自己来制造机关了。
看看猎人是是如何做的:他们会在猎物经过的地方,挖个坑,上面盖上浮土。当小型动物经过时,浮土不会塌陷。而当体重较大的动物经过时,它们的体重就会压垮浮土,掉进猎
人的陷阱。
猎人的这个陷阱机关,妙就妙在是它“智能”的,会根据动物的体重进行筛选。
轻巧的小白兔来了——放过,笨重的大狗熊来了——捕获!欧耶!
好了,回到程序中来,假设我们要监控的那个RAM单元,正常值域为0~9;那么我们可以写一段测试代码,判断数值是否>9,根据判断结果执行两个分支,并在那条错误的分支路径上设置断点。
如果数据没有出错,程序会一直运行(小白兔请放心过去);直到数据错误发生,断点会自动停下来(大狗熊给我拿下)。
我们可以把这段测试程序,插入在“狗熊出没”的地方,“守株待兔”(其实“守坑待熊”)。 接下来的事情,就跟上回说的抓流窜犯原理差不多了。
——什么,你喜欢吃兔肉?不喜欢吃熊掌?
——你也太没有爱心了,唉……
九、程序中的窃听器
1、你的定时中断频率是否等于设想的那个值?
2、你的主程序循环一次花了多少时间?
3、你的程序中某一次复杂计算需要耗费多少时间?
4、你的程序里某个动作发生的具体时刻是什么时候?
5、……
——也许你不关心这些时间,那么你就不必看这一回了。
但是——
1、当我们的计时时钟发生偏差时,我们希望知道定时中断是否正常发生了;
2、当我们的程序任务较多,并已经导致任务堵塞时,我们需要知道主程序运行一圈的时间是多少,以便我们合理分割任务,避免堵塞;
3、同样,为了避免任务堵塞,我们要了解那些复杂计算所消耗的时间,并采取必要的措施(优化算法、分时间片执行、调整执行频率)来保证系统的实时性;
4、当程序中某些动作与其它动作或状态存在时间上的关联时,我们必须严格控制它的执行时机,确保它在正确的时刻被执行到;
5、……
我们如何才能从外部,对这些这些发生在程序内部的时间(时刻)进行精准的测量? 我们当然不能钻到芯片里面去监视每一条指令的运行情况。但是,我们可以学习一下克格勃,给程序安装个窃听器。
具体方法:
1、首先,你需要一台示波器。没有的话,可以去偷、去抢、去骗。总之,最终你搞定了这台示波器,欧耶。
2、其次,你的芯片上要有一个空余的输出口用作测试口。没有的话,就拆东墙补西墙吧,先把不相关功能的IO口挪用一下啦。总之,最终你搞定了这个测试口,欧耶。
3、接下来,你可以在你要“监听”的程序段中,写一小段程序,对那个测试口取反(或者输出一个脉冲)。
4、最后让程序全速运行起来,你就可以用示波器来监听程序的运行状况了。
以本回开始举的几个例子来分析:
1、如果要测试定时中断频率,只要在中断中对这个测试口取反,即可通过示波器观测中断频率;
2、如果要测试主程序运行周期,只要把取反指令放在主程序循环圈中,即可;
3、如果要测试一次复杂计算(或其它动作)需要消耗多少时间,我们只需在计算之前把测试口变为高电平,等到计算结束后立即把输出口恢复到低电平,这段高电平的时间长度,即为计算消耗时间;
4、如果想知道两个动作之间的延时时间,我们也可以按照上一条方法一样,在两个动作发生前把测试口分别取一次反。就可以通过示波器轻松测试出来。
5、根据实际案例的具体情况,我们可以把这种窃听技术变换出更多花样。比如我们可以用两个IO口做测试口,同步检测两个事件的发生时刻,并测量其相互时间关系。等等……
6、引申开去,这个测试口不仅仅可以检测时间,也可以用来检测内部数据的变化。比如当某个数据的值发生“越界”时,输出一个高电平(平时为低电平)。
等到我们取得我们想要的测试数据,我们可以把这个临时的测试口功能撤销。同时,那些测试代码也可一并删除或屏蔽。
总结:把程序内在的、不直观的、快速的一些状态变化,通过IO口传递出来,以便我们观测。——这就是我们这一回所讲的“窃听器”调试技巧的精髓。
——警告,请勿把“窃听器”安装在女生宿舍哦!
——那样的话,匠人岂不就成为教唆犯了。罪过,罪过。。。。。
十、拉闸睡觉!统一管理调试代码
前面介绍的几种方法,需要在程序中增加一些临时性的调试代码。
有些调试代码是无害的,比如只是一些延时指令,或者是在不使用的IO口上有一些输出而已。
但另一些调试代码,与正式要求的程序功能是相冲突的。那么这些代码在完成调试之后就应该被删除或屏蔽掉。
那么会不会出现意外,把本该被删除的代码漏删了?结果埋下祸害?——如果调试代码少,出错的概率比较低,只要认真仔细点还好办;但是如果程序中的调试代码写得比较多,那么确实很担心会发生这种问题。
或者另一种情况,就是前脚把调试代码删除或屏蔽掉,后脚发现还需要再调试,又要重新输入或打开那些代码?
如何管理这些代码呢?这个我们要向宿舍管理员学习了。他们是这么做的,给所有房间安装一个总电闸。到了晚上11点就把总闸一拉,看书的、打牌的、喝酒的、胡侃的、泡妞的、夜游的、丫们都给我老老实实睡觉去吧!
程序中,这样的总闸也是可以通过条件编译的方式来实现的。就像这样:
//#define TEST_MD //调试状态标志(在调试时打开,正式烧录芯片时屏蔽) //在编写调试代码时,采用下面的形式:
#ifdef TEST_MD //如果是调试状态,则编译这段代码
……
……
#else //如果不是调试状态,则编译这段代码
……
……
#endif
一个总闸,把管理简单化了。欧耶!
下面是无事生非电视台广告时间,不喜欢广告的朋友可以换频道了……
十一、附录:《匠人手记》简介
1、《匠人手记》内容简介
本书是作者在从事单片机开发与应用的过程中,将实际经验教训和心得感悟加以总结,整理而成的工作手记。每篇手记论述一个专题,独立成篇、同时又相互关联。全书内容包含入门基础、经验技巧、设计案例、网络杂文等四个部分。
书中将网络中自由的语言艺术与现实中严谨的科学技术相结合。全书的风格以轻松诙谐的笔调为主。作者力图摆脱传统技术类书籍的说教式的表述形式,让读者耳目一新,在轻松的交流过程中获得共鸣。
本书的读者对象为单片机领域的开发工作者、以及有志于学习钻研单片机技术的人员。
z 书名:《匠人手记:一个单片机工作者的实践与思考》
z 定价:39元
z 作者:张俊(网名:程序匠人)
z 书号:978-7-81124-297-3
z 丛书名:博客藏经阁丛书
z 出版日期:200804
z 开本:787×960 1/16开
z 字数:549千字
z 页数:363页
2、《匠人手记》目录
第一部 入门基础
手记1 单片机入门知识与基本概念
手记2 单片机的汇编指令系统
手记3 编程思路漫谈
手记4 程序设计阶段漫谈
手记5 MC68HC908应用札记
手记6 天梯——MSP430学习札记
手记7 EMC单片机指令应用的误区与技巧
手记8 EMC单片机的伪指令与宏的应用
第二部 经验技巧
手记9 十种软件滤波方法
手记10 一阶滤波算法之深入研究
手记11 分段线性插值算法之深入研究
手记12 移位法在乘除运算及数制转换中的妙用
手记13 按键漫谈
手记14 单键多击的检测程序
手记15 串口七日之创世纪篇
手记16 用普通IO口实现单线单工通讯
手记17 用普通IO口检测模拟值
手记18 功率调节与过零检测
第三部 设计案例
手记19 梦幻时钟摇摇棒大揭秘
手记20 汽车组合仪表开发手记
手记21 空调遥控器开发手记
手记22 手机锂电池充电器设计白皮书
第四部 网络杂文
手记23 《大话篇》系列
手记24 《匠人夜话》系列
手记25 匠人的论坛文集
手记26 匠人的博客文集
手记27 21icbbs人物志
手记28 《网络心路》之匠人版(连载)
3、《匠人手记》封面及内页
图 1.1:《匠人手记》样书
图 1.2:
《匠人手记》在当当网同类图书的销售排名中第一
图 1.3:
《匠人手记》在互动出版网同类图书的销售排名中第一
图 1.4:《匠人手记》在END网站签名热卖
图 1.5:
《匠人手记》在上海书城被列为推荐图书
图 1.6:
《匠人手记》被北航出版社列为推荐图书
图 1.7:《匠人手记》被END杂志列为推荐图书
图 1.8:《匠人手记》被读者热评
7、购书渠道
z 互动出版网(china-pub):
z 当当网(dangdang): z 淘宝网(taobao):
z 更多购书渠道:
8、相关连接
z 《匠人的百宝箱》博客:
z 《匠人手记》edn书友会:
z 《匠人手记》21ic书友会:
9、《匠人手记》书友会Q群
备注:请勿重复申请!
z
z
z
z
z
z
z
z
z
z
z 《匠人的百宝箱》俱乐部:1109916 《匠人手记》书友会1:40177359 《匠人手记》书友会2:36337921 《匠人手记》书友会3:32426080 《匠人手记》书友会4:18931920 《匠人手记》书友会5:9084424 《匠人手记》书友会6:72049039 《匠人手记》书友会7:22260690 《匠人手记》书友会8:69629280 《匠人手记》书友会9:56038600 《匠人手记》书友会10:4866691
10、《匠人手记》网络版 版权声明
《匠人手记》网络版,相当于《匠人手记》纸媒版的尝鲜版和补充版。该系列文章都为匠人原创或精心整理,其中耗费了匠人的诸多心血。这些文章推出以来,一直受到网友的欢迎。也有许多网站给予转载和推荐,匠人对此由衷感谢。
但是我们发现个别网站有意将《匠人手记》网络版的内容拆开来发表,不但隐去了作者和出处等信息,而且还贴上他们自己网站原创的标签。匠人觉得这是一种不尊重原作者的行为。
在此,匠人特声明如下:
《匠人手记》网络版的正式发布版本均为PDF格式。匠人欢迎各方收藏或转载。但是,匠人要求转载者必须也以PDF格式提供。并且,转载者不得对PDF格式的《匠人手记》网络版文件内容进行修改。转载者不得利用《匠人手记》网络版谋取经济利益。《匠人手记》网络版中的设计案例(包含程序、电路及设计思路等)仅供学习,请勿应用在商业项目中。如有需求,请与匠人取得联系。
对原创作者的版权尊重,是鼓励作者继续创作的力量源泉。大伙能到《匠人的百宝箱》来做客,都是匠人的朋友。希望不要做伤害朋友的事情,呵呵。
图 1.9:《匠人的百宝箱》Logo