解决了按键控制数码管加1时数码管闪灭问题。
- #include<reg52.h>
- #define uchar unsigned char
- #define uint unsigned int
- uchar code table[]={
- 0x3f,0x06,0x5b,0x4f,
- 0x66,0x6d,0x7d,0x07,
- 0x7f,0x6f,0x77,0x7c,
- 0x39,0x5e,0x79,0x71,
- 0x77,0x1F,0x4E,0x2D,
- 0x4F,0x47 };
- sbit button = P3^0;
- sbit dula = P2^6;
- sbit wela = P2^7;
- sbit lcden = P3^4;
- uchar count = 0;
- void buttonScan();
- void display(uchar count);
- void delayms(uint ms);
- void main()
- {
- lcden = 0;
- TMOD = 0x01;
- TH0 = (65536 - 9174)/256;
- TL0 = (65536 - 9174)%256;
- EA = 1;
- ET0 = 1;
- TR0 = 1;
- while(1)
- {
- buttonScan();
- //display(count);
- }
- }
- void buttonScan()
- {
- if(button == 0)
- {
- delayms(20);
- if(button == 0)
- {
- while(!button);
- count++;
- }
- }
- }
- void display(uchar count)
- {
- uchar w = count/10000;
- uchar q = count%10000/1000;
- uchar b = count%10000%1000/100;
- uchar s = count%10000%1000%100/10;
- uchar g = count%10;
- dula = 1;
- P0 = table[w];
- dula = 0;
- P0 = 0xff;
- wela = 1;
- P0 = 0xfe;
- wela = 0;
- delayms(1);
- dula = 1;
- P0 = table[q];
- dula = 0;
- P0 = 0xff;
- wela = 1;
- P0 = 0xfd;
- wela = 0;
- delayms(1);
- dula = 1;
- P0 = table[b];
- dula = 0;
- P0 = 0xff;
- wela = 1;
- P0 = 0xfb;
- wela = 0;
- delayms(1);
- dula = 1;
- P0 = table[s];
- dula = 0;
- P0 = 0xff;
- wela = 1;
- P0 = 0xf7;
- wela = 0;
- delayms(1);
- dula = 1;
- P0 = table[g];
- dula = 0;
- P0 = 0xff;
- wela = 1;
- P0 = 0xef;
- wela = 0;
- delayms(1);
-
- }
- void Interrupt_T0()interrupt 1
- {
- TH0 = (65536 - 9174)/256;
- TL0 = (65536 - 9174)%256;
- display(count);
- }
- void delayms(uint ms)
- {
- uint i,j;
- for(i=ms;i>0;i--)
- for(j=110;j>0;j--);
- }
显示函数放在主函数里,由于按键检测代码中delayms(20);这句话,严重干扰了显示的时效性,所以会出现数码管的闪烁,如果注释掉按键这一部分可能就会恢复正常。 你做的改进是把显示放在定时器中断中,中断就是会严格按照你需要的时间来执行你的显示,所以代码效果就正常了。
还可以进一步优化就是利用定时器延时来消抖,而不是delayms(20);这句话来消抖。你可以想象一下20Ms对CPU来说时间已经非常长了,它耽误了很多很多条有效指令的执行。
数码管动态扫描,其实就使数码管的闪烁频率大于人眼识别的高频率(50Hz),超过这个频率人眼就分辨不出来了,所以看到的数码管的不闪的。
程序里你用的定时器中断来扫描数码管,定时器中断时间10ms,那么数码管闪烁频率就是1/0.01=100Hz,就是每秒点亮和熄灭100次,所以人眼分辨不出来;如果你把中断时间改为50ms,那么频率就是20Hz,这时候你就可以明显感觉到数码管在闪烁了
你讲的很清楚,我现在明白了!
你说的要我用定时来做延时,是觉得用delayms(20)不够精确还是说不用延时这么长时间,如果不延时这么长的时间,那么按键消抖可能就不明显了。
定时器消抖,是否可以定义一个number,在定时器中断一次就+1,然后在第一次按键检测后面当number=2的时候,再检测按键一次,是吗?
另一个问题:数码管虽然不闪了,但是前4个数码管比第五个数码管都要暗一点,这个也算是一点瑕疵,不知道有没有解决办法呢?
先说第一个延时的问题,在按键扫描中延时不用精确延时,跟delayms(20)的精确度关系不大,也不是说这个延时时间不长,而是在主函数main()里,可以不使用这种delayms(20)
void delayms(uint ms)
{
uint i,j;
for(i=ms;i>0;i--)
for(j=110;j>0;j--);
}
这个延时就是利用CPU消耗一些指令来累计时间达到延时的效果,简单有效,缺点就是在执行这些指令的同时CPU其他任务全部照顾不到,根本不能去执行(除了中断里的,但是中断也不能写一大堆任务)。所以利用定时器中断来做这个延时就OK了,单片机的定时器外设和单片机主程序的运行是真正的并行执行,定时器计时时间到了告诉主程序一下,主程序只需要去处理一下就可以。定时器的消抖跟你理解的意思是差不多的,论坛中有很多参考代码我就不写了。
第二个问题,我刚才才发现你的display里面是一次性全部做的显示,并且中间通过了一些小延时做了间隔,这个延时也要删掉,原因同上。数码管的显示方法有很多,要看程序框架如何规划了,我说一个你试试。
void display(uchar count)里的count可以认为是缓冲数据buf,每次执行显示的时候传入缓冲数据去做显示就可以了。按键或者其他外部因素仅仅是更新这个数据buf,并不需要立即就去调用显示。
显示可以按照一个封闭的状态机的模式去写,扔在定时器中断里,比如10ms中断一次,第一次也就是状态1显示第一位数码管,第二次中断显示第二位数码管,第三次中断……状态就是一个unsigned char 型的变量做指示就可以的。这样中断每次执行的代码就是显示其中一位数码管并且更新状态,代码量也不大,也不应delay去做延时了。
我居然打了这么多字……
10ms中断一次显示一位,能不能保证数码管都同时点亮额。那display函数里面应该还有可以控制数码管显示位的才行,或者说每隔10ms,数码管显示控制位循环左移一位。
10Ms能不能就实际检验一下好了,要显示的位数越多,这个中断时间就要设置的越短一些,满足人眼的视觉暂留最低显示频率。display函数里面为了实现每次进去显示不同的位,所以肯定需要一个只是标志位,static也可以,全局变量也可以,然后每次进去执行一次++,然后判别,去显示对应的位就可以了。
额,明白。非常感谢你!
