为了让大家能直观的了解《单片机那些事儿》的模块化编程篇,这里将软件仿真部分先贴出来。这部分图太多了,一张一张加入是在有点麻烦,所以就省了,具体见链接一文的附件,里面有详图哦。
http://bbs.ednchina.com/BLOG_ARTICLE_3020671.HTM
在真正讲述模块化编程之前,我们先来来补充一点Keil4的“软仿真”。所谓“软仿真”,就是用Keil4来做软件仿真,这样做,我们可以初步判断我们的程序是否正确,等正确之后,我们才可以将其编译生成HEX文件,最后下载到单片机中。当然不是所有的程序都先需要进行软仿真,再下载到单片机,而是对一些有问题的程序,我们首先可以做一下软件仿真,以便排除一些显而易见的“傻”问题。其实Keil4软件的仿真功能还是比较强,这里我们简单介绍几点,剩余的就留读者自行研究了。
说道Keil4软件的仿真,不得不提它还可以借助一些编程器实现“硬仿真”,这个这里不做介绍,等大家以后学习C8051F系列或STM32的单片机时再具体了解吧。
特别提醒:对Keil4不熟的,赶紧去复习一下第一章。我默认看此章节的人对Keil4已经很熟了。
仿真的步骤或者方法其实很简单,由编程界面进入仿真界面之前需检查一个选项设置,具体操作是:单击“Target Options ...”按钮,也即如图6-1所示的“4按钮”,打开“Options for Target ‘Targer 1’”对话框,对话框如图6-2所示,第二个选项卡(Target)下的“Xtal”处一定要设置为:11.0592,否则后续时间仿真的参数值会有出入。接着选择倒数第二个选项卡(Debug),其界面如图6-3所示。这里读者需要注意图中箭头所指的两个复选框,其意义大不相同。
图6-1 Keil4的编辑界面图
此处省略图一张
图6-2 Options for Target ‘Target1’界面图
此处省略图一张
图6-3 Debug选项卡设置对话框
第一个(Use Simulator):意思是软件模拟仿真,就是只在软件上做一些仿真动作,与硬件无关。
第二个(Use:...):这里是用硬件仿真,意思是软件上面的仿真动作也会对应到硬件上。51单片机需要借助一块特殊单片机芯片或仿真器;C8051F系列需要借助EC6等仿真器;STM32需要借助J-link等。这些读者只需要知道有这么回事就是了,不需深入的了解,以后学到了自然就会明白。
这里我们需要进行软件模拟仿真,因此选择软件默认的第一个选项就是了。别的选择默认项,最后单击“OK”按钮。
接着选择菜单项:Debug→Start/Stop Debug Session(或者选择如图6-1所示的7按钮),由编辑界面进入仿真界面,这时若Keil4软件没**,则会有一个“2K”的代码限制,软件的**,我在第一章中已经讲述过了,这里就不费口舌了。进入仿真界面的Keil4如图6-4所示。
图6-4 Keil4的仿真界面图
接下来笔者主要说明图中所标识的13个选项,其中12、13只是为了好说明,没有操作的地方不多。
X入广告。断点:这里的断点不是张敬轩唱的《断点》歌哈,而是在程序中添加一个断点,让程序运行到断点处时停止,以便读者作出别的操作,例如观察某一变量值,或者通过单击单步来运行程序等。插入的方法最直接,就是在想插入行的最后面双击鼠标左键,取消断点的方法也是双击。
1)Reset CPU:复位选项,意思是当程序执行一段以后,读者想让其重新开始,单击此处程序执行点就会回到开始处,即main函数的开头处。
2)Run:程序从头开始全速执行。当有断点时运行到断点处停止,没有时程序按程序规定一直运行。
3)Stop:顾名思义,停止运行的程序。
4)Step:单步运行。当碰见子函数时,会进入子函数。
5)Step Over:单步运行。碰见子函数时,不进入,将子函数当做一个整体来运行。
6)Step Out:单步运行。程序若在子函数内部运行,则会跳出子函数。
7)Run to Cursor Line:运行到光标处。
8)Serial Windows:串口输出窗口。
9)Analysis Windows:逻辑分析窗口。该窗口下有三个子选项。笔者这里以“Logic Analyzer”为例来讲解。别的两个读者自行研究。
10)变量等数值的观察窗口。
11)程序运行的时间。
12)反汇编窗口,这个对于新手来说,估计有些难,读者们只知道有这么回事就是了。
13)C语言程序窗口,可以观察程序此时运行到什么地方了。
当然该界面可操作的地方不止这些,例如观察左边寄存器的值、PC指针、内存数值等,这些就留读者慢慢去琢磨,笔者很懒就不说了。
这里以跑马灯为例,来仿真P2口的状态值。进入仿真界面后选择菜单项:Peripherals→I/O-Ports→Port 2,此时界面中会出现一个如图6-5所示的复选框。由于此时程序未运行,因此P2口的状态值都为高电平,因此界面显示为:0xFF。当单击“Step”或者“Step Over”按钮时程序运行:P2 = 0xfe,这时就变为:0xFE;界面如图6-6所示。之后就会依次变为:0xFD、0xFB...0x7F。别的端口仿真类似。
此处省略图一张 此处省略图一张
图6-5程序未运行是P2口的状态值 图6-6程序未运行是P2口的状态值
当程序运行到DelayMS(50)时有两种选择,一种是单击“Step”按钮进入DelayMS()函数,一种是单击“Step Over”不进入DelayMS()函数,直接将函数当做整体运行,这个望读者自行调试,加以区别,笔者就不再说了。至于这里的时间,稍后再说。
同样以跑马灯为例。进入仿真界面以后单击“Analysis Windows”(如图6-7的1处),则会默认选中第一个“Logic Analyzer”,这时仿真界面如图6-7所示。
此处省略图一张
图6-7 Logic Analyzer界面图
接着单击“Setup...”按钮(图6-7所示的序号2处),打开“Setup Logic Analyzer”对话框,如图6-8所示。
此处省略图一张
图6-8 Setup Logic Analyzer对话框
具体操作为:先单击图6-8“1”处的“New”选项,之后在“2”所示的地方填“PORT2.0”,接着再单击“New”按钮,再在“2”处填“PORT2.1”,这样依次再新建6个,在“2”处分别填:PORT2.2、PORT2.3...PORT2.7,最后如图6-8所示。
其中序号3用于以什么方式显示,这里选择位:Bit,当然还可以选择为:Analog和State。序号4是当单击选中该复选框时数值以十六进制方式显示。这些读者可以自行实验。
以上设置好之后单击“Close”按钮,接着选择如图6-4所示的“Run”按钮(全速运行),过几秒钟之后在单击“Stop”按钮,停止运行,这时就可得到如图6-9所示的波形图,界面是不是蛮漂亮的,读者们还不赶紧亲自试一试。
此处省略图一张
图6-9 Logic Analyzer的仿真波形图
意思是通过Keil4来观察程序运行中各个变量的数值变化是否正确,这里还是以实例4来做讲解。
回到仿真主界面,先在图8-20所示的序号10处添加两个变量:i、j,添加方法是双击或者按键盘上的F2,之后填写i、j,添加完变量之后“Watch1”窗口如图6-10所示。接着分别右键单击选择i、j行,之后选择:Number Base→Decimal,意思是数值以十进制的形式显示。
此处省略图一张
图6-10 “Watch1”窗口中添加完变量之后的界面图
用“Step Over”来运行程序到DelayMS(50)时改为“Step”运行程序,这样就可以进入到DelayMS()函数,这时细心的读者已经注意到变量后面的“Value”已经由“????????”变为了0,接着再单击“Step”运行程序,这时i、j是不是在有规律的变化,其实j变为113自后就再不会变化了,而i则一直再自增(肯定是有范围的)。这时读者还可以观察下面的t1时间如何变化,当读者单击50次“Step”之后,这里的时间是:0.05031250,读者可以想一想,这个时间与我们想要延时的50ms有没有联系,若有的话,又是怎么联系到一起的,或者说起到了延时的作用没?读者们带着这些问题,就可以进入下一章的学习了。
在玩单片机的过程中,有一种“功能”一直陪伴在左右,那就是——延时。原因很简单,玩单片机无非是玩一些人能看得见、能听得见、能用得着在一些东西。前面笔者说过,人很聪明,但是眼睛、耳朵等都很“慢”,速度远远跟不上单片机的速度,这样就需单片机在做某件事时等一等,这一等,给人一点反映的时间,这就引出了延时的概念。
笔者这里将延时分为两类:不精确的延时和精确的延时。这两者在概念和实现的方法上大不相同。接下来分别阐述一下这两种延时,希望对读者有所帮助。
在讲述精确延时和非精确延时之前,先来补补上章的内部,Keil4下的延时仿真。这里还是以实例4为例来讲解Keil4软件的时间仿真,在讲述过程中,读者需考虑一个问题,为什么执行DelayMS()函数就能起到延时的作用,难道执行别的函数就起不到延时作用吗?
接下来就打破沙锅问到底,为何DelayMS()函数能起到延时的作用,这里从仿真入手来,来看个究竟。读者按上章所讲述的操作方法,让其Keil4软件进入到仿真界面,这时记下右下角(如图8-20的序号11处)的时间值为:t1=0.00000000s,接着单击一次“Step Over”按钮运行程序,这时程序运行之进入了main()函数,则时间变为:t1=0.00042209s;再单击“Step Over”,程序运行了:P2 = 0xfe和for(;;),时间变为:t1=0.00042426s,这样运行时间为:t=0.00042426s-0.00042209s≈2us;再运行程序:P2 = _crol_(P2,1),时间变为:t1=0.00043837s,则程序运行的时间:t=0.00043837s-0.00042426s=0.00001411s≈14us。由此可得出以下三条结论:
(1)任何程序执行是需要时间的。例如这里的2us、14us等。
(2)C语言编写的程序,每条语句运行的时间是不确定的(2us≠14us)。至于汇编,笔者在这里就不说了,读者可以自行了解一下。
(3)程序编写中,一般将这些时间忽略不计。
接着再说“DelayMS(50)”为何能起到延时50ms的作用。操作步骤是先在DelayMS(50)和倒数第二个大括号后打两个断点(两行后面双击就可以打上断点),之后单击“Run”按钮,此时如图8-20所示的标号11处的时间值为:0.00043837s,也即4us多一点,再单击“Run”按钮,这时时间变为:0.05031467s,这样就可以算出执行程序:DelayMS(50)所用的时间,时间值为:0.05031467s-0.00043837s=0.0498763s≈50ms。为何上面的程序执行时间可以忽略,而这里又要借助程序执行的时间来达到延时的目的呢?那是因为,上面的程序执行次数少(先不考虑while(1)),而这里的DelayMS(50)执行了5650(50×113)次,这个时间就要算了,因为其花了50ms了,再不算就OUT了。
以上时间都是用软件来仿真,接下来残弈悟恩带领读者们看看这个50ms的真、假程度究竟有多高,是比天还高,还是比海底更低呢。这里借助逻辑分析仪(简易型的哈,高级的笔者买不起)或者示波器来抓取其时间值,两者得到时间值分别如图6-11和6-12所示,现在来对比一下这三个值,软件仿真得到的为:49.87ms,逻辑分析仪抓取的时间为:49.88ms,示波器获取的时间为:50ms,三个值几乎接近我们想要的理论值:50ms,这说明上面的延时函数(DelayMS())还是写的很“牛X”啊,但再牛也只是一个不精确的延时,要想有精确的延时,那就看下面精确延时的讲述了。
此处省略图一张 此处省略图一张
图6-11逻辑分析仪抓取的时间值 图6-12示波器抓取的时间值
俗话说,只有相对的,没有绝对的。这里的精确也是相对于上面的不精确而言,因为这里的精确也不是绝对的精确。笔者举个例子,这里所谓的精确的是用定时器和库函数_nop_()来产生,但这两者最终的参考源都是外部晶振,可晶振也是人生产的,怎么可能做到绝对准确的了,如MGMC-V1.0实验板上搭载的11.0592MHz晶振的实际频率为11.045MHz。即使有些高级的单片机内部集成了晶振,也是靠RC来产生的,这些也会受到温度等影响而不稳定。还有定时器在装初值、产生中断等时也需要时间,这些时间都是无法估算到里面的。因此读者要有个清醒地认识,不要一说精确,就要精确到皮秒、飞秒去。
关于定时器和库函数_nop_()的延时,笔者这里就不举例说明了,以后具体的例程中再详细讲解。当然读者朋友们也可以提前研究研究,何乐而不为呢?
loodao_812158451 2014-8-16 12:58
用户1678478 2014-6-13 18:02
用户862991 2014-5-21 15:26
残弈悟恩 2014-5-18 21:29
用户440246 2014-5-18 17:27
wangsenjn_132795942 2014-5-12 08:36
用户403664 2014-5-8 15:54
残弈悟恩 2014-5-7 21:05
残弈悟恩 2014-5-7 21:03
残弈悟恩 2014-5-7 21:02