那么搞明白了原理后,解决起来就不是困难的事情了,我们只要避开这个瞬间错误就可以了。不产生瞬间错误的方法是,在进行位选切换期间,避免一切数码管的赋值即可。方法有两个,一个方法是刷新之前关闭所有的段,改变好了位选后,再打开段即可;第二个方法是关闭数码管的位,赋值过程都做好后,再重新打开即可。这个不是很难,答案我都公布一下。
关闭段:在switch(i)这句程序之前,加一句P0=0xFF;这样就把数码管所有的段都关闭了,当把“ADDR”的值全部搞定后,再给P0赋对应的值即可。
关闭位:在switch(i)这句程序之前,加上一句ENLED=1;等到把ADDR2=0;ADDR1=0;ADDR0=0;i++;P0=LedBuff[0];这几条刷新程序全部写完后,再加上一句ENLED=0;然后再进行break操作即可。
这个地方逻辑思路上稍微有点复杂,大家一定要理解深刻,深刻理解,彻底弄明白,把这个瞬间的问题弄明白了,后边很多牵扯到此类情况的问题,我们都可以一并搞定。
上边的数码管程序还有第二个问题,大家仔细看,我们的数码管上的数字每一秒变化一次,变化的时候,不参加变化的数码管可能出现一次抖动,这个抖动没有什么专业的名字,我们就称之为数码管抖动吧。这种数码管抖动是什么原因造成的呢?为何在数据改变的时候才抖动呢?
来分析一下我们的程序,程序在定时到1秒的时候,执行了“秒数+1并转换为数码管显示字符”这个操作,一个32位整型数的除法运算,实际上是比较耗费时间的,至于这一段程序究竟耗费了多少时间,大家可以通过第四章讲的调试方法来看看这段程序运行用了多少时间。由于每次定时到1秒的时候,程序都多运行了这么一段,导致了某个数码管的点亮时间比其他情况下要长一些,总时间就变成了1ms+本段程序运行时间,于此同时,其它的数码管就熄灭了5ms+本段程序运行时间,如果这段程序运行时间非常短,那么可以忽略不计,但很明显,现在这段程序运行时间已经比较长了,以致于严重影响到视觉效果了,所以我们要采取另外一种思路去解决这个问题。
1.5单片机中断系统
1.5.1中断的产生背景
请设想这样一个场景:此刻我正在厨房用煤气烧一壶水,而烧开一壶水刚好需要10分钟,我是一个主体,烧水是一个目的,而且我只能时时刻刻在这里烧水,因为一旦水开了,溢出来浇灭煤气的话,有可能引发一场灾难。但就在这个时候呢,我又听到了电视里传来《天龙八部》的主题歌,马上就要开演了,我真想夺门而出,去看我最喜欢的电视剧。然而,听到这个水壶发出的“咕嘟”的声音,我清楚:除非等水烧开了,否则我是无法享受我喜欢的电视剧的。
这里边主体只有一个我,而我要做的有两件事情,一个是看电视,一个是烧水,而电视和烧水是两个独立的客体,它们是同时进行的。其中烧水需要10分钟,但不需要了解烧水的过程,只需要得到水烧开的这样一个结果就行了,提下水壶和关闭煤气只需要几秒的时间而已。所以我们采取的办法就是:烧水的时候,定上一个闹钟,定时10分钟,然后我就可以安心看电视了。当10分钟时间到了,闹钟响了,此刻水也烧开了,我就过去把煤气灭掉,然后继续回来看电视就可以了。
这个场景和单片机有什么关系呢?
在单片机的程序处理过程中也有很多类似的场景,当单片机正在专心致志的做一件事情(看电视)的时候,总会有一件或者多件紧迫或者不紧迫的事情发生,需要我们去关注,有一些需要我们停下手头的工作去马上去处理(比如水开了),只有处理完了,才能回头继续完成刚才的工作(看电视)。这种情况下单片机的中断系统就该发挥它的强大作用了,合理巧妙的利用中断,不仅可以使我们获得处理突发状况的能力,而且可以使单片机能够“同时”完成多项任务。
1.5.2定时器中断的应用
在第五章我们学过了定时器,而实际上定时器一般用法都是采取中断方式来做的,我是故意在第五章用查询法,就是使用if(TF0==1)这样的语句先用定时器,目的是明确告诉同学们,定时器和中断不是一回事,定时器是单片机模块的一个资源,确确实实存在的一个模块,而中断,是单片机的一种运行机制。尤其是初学者们,很多人会误以为定时器和中断是一个东西,只有定时器才会触发中断,但实际上很多事件都会触发中断的,除了“烧水”,还有“有人按门铃”,“来电话了”等等。
标准51单片机中控制中断的寄存器有两个,一个是中断使能寄存器,另一个是中断优先级寄存器,这里先介绍中断使能寄存器,如表6-1和表6-2所示。随着一
|