解决了按键控制数码管加1时数码管闪灭问题。

2019-07-14 20:30发布

       我们公司自己做了一个攻牙机,我想着只是简单的攻牙也不行,得知道一天生产了多少数量,所以就想要设计一个单片机程序来实现按键开关启动一次,数码管计数一次。但是程序还不能实现不良的不计数,“空”转的也不计数,这些实现起来相对而言比较复杂了(软件还好,硬件可能要求比较高)。以下程序足以满足我们小公司了,程序详见下(希望大家一起探讨)。最开始写的代码,我把按键扫描和显示功能都放到了main函数里面,但是按键一次数码管会闪一次,觉得不好,后面网上查了下资料,就把显示功能放在了定时器0里面,每隔10ms扫描一次就对了。扫描时间不能太短也不能太长,效果是实现了,但是始终还是不明白为什么。代码详见下:

  1. #include<reg52.h>
  2. #define uchar unsigned char
  3. #define uint unsigned int

  4. uchar code table[]={
  5. 0x3f,0x06,0x5b,0x4f,
  6. 0x66,0x6d,0x7d,0x07,
  7. 0x7f,0x6f,0x77,0x7c,
  8. 0x39,0x5e,0x79,0x71,
  9. 0x77,0x1F,0x4E,0x2D,
  10. 0x4F,0x47 };

  11. sbit button = P3^0;
  12. sbit dula = P2^6;
  13. sbit wela = P2^7;
  14. sbit lcden = P3^4;

  15. uchar count = 0;

  16. void buttonScan();
  17. void display(uchar count);
  18. void delayms(uint ms);

  19. void main()
  20. {
  21.         lcden = 0;
  22.         TMOD = 0x01;
  23.         TH0 = (65536 - 9174)/256;
  24.         TL0 = (65536 - 9174)%256;
  25.         EA = 1;
  26.         ET0 = 1;
  27.         TR0 = 1;
  28.         while(1)
  29.         {
  30.                 buttonScan();
  31.                 //display(count);
  32.         }
  33. }

  34. void buttonScan()
  35. {
  36.         if(button == 0)
  37.         {
  38.                 delayms(20);
  39.                 if(button == 0)
  40.                 {
  41.                         while(!button);
  42.                         count++;
  43.                 }
  44.         }
  45. }

  46. void display(uchar count)
  47. {
  48.         uchar w = count/10000;
  49.         uchar q = count%10000/1000;
  50.         uchar b = count%10000%1000/100;
  51.         uchar s = count%10000%1000%100/10;
  52.         uchar g = count%10;

  53.         dula = 1;
  54.         P0 = table[w];
  55.         dula = 0;
  56.         P0 = 0xff;
  57.         wela = 1;
  58.         P0 = 0xfe;
  59.         wela = 0;
  60.         delayms(1);

  61.         dula = 1;
  62.         P0 = table[q];
  63.         dula = 0;
  64.         P0 = 0xff;
  65.         wela = 1;
  66.         P0 = 0xfd;
  67.         wela = 0;
  68.         delayms(1);

  69.         dula = 1;
  70.         P0 = table[b];
  71.         dula = 0;
  72.         P0 = 0xff;
  73.         wela = 1;
  74.         P0 = 0xfb;
  75.         wela = 0;
  76.         delayms(1);

  77.         dula = 1;
  78.         P0 = table[s];
  79.         dula = 0;
  80.         P0 = 0xff;
  81.         wela = 1;
  82.         P0 = 0xf7;
  83.         wela = 0;
  84.         delayms(1);

  85.         dula = 1;
  86.         P0 = table[g];
  87.         dula = 0;
  88.         P0 = 0xff;
  89.         wela = 1;
  90.         P0 = 0xef;
  91.         wela = 0;
  92.         delayms(1);
  93.        
  94. }

  95. void Interrupt_T0()interrupt 1
  96. {
  97.         TH0 = (65536 - 9174)/256;
  98.         TL0 = (65536 - 9174)%256;

  99.         display(count);
  100. }

  101. void delayms(uint ms)
  102. {
  103.         uint i,j;
  104.         for(i=ms;i>0;i--)
  105.                 for(j=110;j>0;j--);
  106. }
复制代码


友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
5条回答
mcqueen
1楼-- · 2019-07-15 01:21
 精彩回答 2  元偷偷看……
HARRY007
2楼-- · 2019-07-15 07:13
本帖最后由 HARRY007 于 2016-8-13 10:31 编辑
mcqueen 发表于 2016-8-13 00:29
你讲的很清楚,我现在明白了!
你说的要我用定时来做延时,是觉得用delayms(20)不够精确还是说不用延时这么长时间,如果不延时这么长的时间,那么按键消抖可能就不明显了。
定时器消抖,是否可以定义一个number,在定时器中断一次就+1,然后在第一次按键检测后面当number=2的时候,再检测按键一次,是吗?

先说第一个延时的问题,在按键扫描中延时不用精确延时,跟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去做延时了。
我居然打了这么多字……
mcqueen
3楼-- · 2019-07-15 12:45
HARRY007 发表于 2016-8-13 10:27
先说第一个延时的问题,在按键扫描中延时不用精确延时,跟delayms(20)的精确度关系不大,也不是说这个延时时间不长,而是在主函数main()里,可以不使用这种delayms(20)
void delayms(uint ms)
{

10ms中断一次显示一位,能不能保证数码管都同时点亮额。那display函数里面应该还有可以控制数码管显示位的才行,或者说每隔10ms,数码管显示控制位循环左移一位。。。。。。
HARRY007
4楼-- · 2019-07-15 17:36
mcqueen 发表于 2016-8-14 01:00
10ms中断一次显示一位,能不能保证数码管都同时点亮额。那display函数里面应该还有可以控制数码管显示位的才行,或者说每隔10ms,数码管显示控制位循环左移一位。。。。。。

10Ms能不能就实际检验一下好了,要显示的位数越多,这个中断时间就要设置的越短一些,满足人眼的视觉暂留最低显示频率。display函数里面为了实现每次进去显示不同的位,所以肯定需要一个只是标志位,static也可以,全局变量也可以,然后每次进去执行一次++,然后判别,去显示对应的位就可以了。
mcqueen
5楼-- · 2019-07-15 21:34
HARRY007 发表于 2016-8-15 19:54
10Ms能不能就实际检验一下好了,要显示的位数越多,这个中断时间就要设置的越短一些,满足人眼的视觉暂留最低显示频率。display函数里面为了实现每次进去显示不同的位,所以肯定需要一个只是标志位,static也可以,全局变量也可以,然后每次进去执行一次++,然后判别,去显示对应的位就可以了。 ...

额,明白。非常感谢你!

一周热门 更多>