单片机菜鸟求教,关于延迟和按键去抖的问题

单片机新手,想做一个篮球的计时计分器。计时没有问题,就是每次按键加分计分的时候,会有抖动,然后我就加了延迟。但是加完延迟后,每按一次加分的键,所有的数码管都会闪一下。因为自己对延迟不是很懂,求大神帮忙QAQ,在此谢过。
附上原理图和源程序
#include<reg51.h>
#define uint unsigned int
#define uchar unsigned char
uchar code Tab[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71};
uchar ge,shi,num,count,sec,min,ge1,ge2,shi1,shi2;
uchar fen1,fen2;
uchar ge3,shi3,bai3,ge4,shi4,bai4;
sbit flag=P3^0;
sbit res=P3^1;
sbit P1_0=P1^0;
sbit P1_1=P1^1;
sbit P1_2=P1^2;
sbit P1_3=P1^3;
sbit P1_4=P1^4;
sbit P1_5=P1^5;
sbit P1_6=P1^6;
sbit P1_7=P1^7;
void delay(uint);
void key()
{
if(P1_0!=1){delay(1000);fen1=fen1+1;}
if(P1_1!=1){delay(1000);fen1=fen1+2;}
if(P1_2!=1){delay(1000);fen1=fen1+3;}
if(P1_3!=1){delay(1000);fen1=fen1-1;}
if(P1_4!=1){delay(1000);fen2=fen2+1;}
if(P1_5!=1){delay(1000);fen2=fen2+2;}
if(P1_6!=1){delay(1000);fen2=fen2+3;}
if(P1_7!=1){delay(1000);fen2=fen2-1;}
}
void pause()
{if(flag!=1) TR0=0;else TR0=1;}
void res_1()
{
if(res!=1) num=24; delay(5);
}
void main()
{
fen1=0;
fen2=0;
count=0;
num=24;
sec=0;
min=10;
P2=0x00;
TMOD=0x01;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
EA=1;
ET0=1;
TR0=1;
while(1)
{ key();
pause();
shi=num/10;
ge=num%10;
shi1=sec/10;
ge1=sec%10;
shi2=min/10;
ge2=min%10;
bai3=fen1/100;
shi3=fen1/10%10;
ge3=fen1%10;
bai4=fen2/100;
shi4=fen2/10%10;
ge4=fen2%10;
P2=0x00;P0=Tab[shi];delay(20);
P2=0x01;P0=Tab[ge];delay(20);
P2=0x02;P0=Tab[shi1];delay(20);
P2=0x03;P0=Tab[ge1];delay(20);
P2=0x04;P0=Tab[shi2];delay(20);
P2=0x05;P0=Tab[ge2];delay(20) ;
P2=0x06;P0=Tab[bai3];delay(20);
P2=0x07;P0=Tab[shi3];delay(20);
P2=0x08;P0=Tab[ge3];delay(20);
P2=0x09;P0=Tab[bai4];delay(20);
P2=0x0a;P0=Tab[shi4];delay(20);
P2=0x0b;P0=Tab[ge4];delay(20);
res_1();
}
}
void T0_time()interrupt 1
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
count++;
if(count==20)
{
count=0;
sec--;num--;
if(num>=24)
{
num=24;
}
if(sec>=59)
{
sec=59;
min--;
if(min==0)
{
min=10;
num--;
}
}
}
}
void delay(uint c)
{
uint a,b;
for(;c>0;c--)
for(b=5;b>0;b--)
for(a=2;a>0;a--);
}

看到开头几行就知道你的情况了,这个是初学者常见的情况哈。
看到楼上已经解释得不错,我也说几句吧。

为什么数码管可以同时显示那么多个数字,你当然知道,用动态扫描。
动态扫描的意思就是,在很短的时间内,轮流让每个管显示一次。
只要频率够快,那么人眼的视觉暂留就会起作用,你就看不见数字在跳动。

闪烁的原因也很简单,比如你要显示1234
本来是:
1出现,1消失,2出现,2消失,3出现,3消失,4出现,4消失。。。
每个数字出现和消失的时间都很短,循环起来,周而复始,视觉上就是1234了

结果你按键程序里来个,延时1s。
这个1s远远超过了数字出现的时间,结果变成了:
1出现,1消失,2出现,2消失,3出现,3消失,4出现,4消失,1s。。。
同样周而复始。结果那1s时间内,什么都不显示,当然就看到数字不见了

解决办法就是采用定时器中断。不管是按键用中断也好,显示用中断也好,只要其中一个进入定时器中断,问题就解决了。

程序就不写了,描述一下意思就好
你的主程序保留键盘部分,但是显示那一大段不要了
中断本来是50ms,建议改成20ms

然后呢,设置一个变量,每次进入中断就把变量的值加1
加完以后,根据变量当前的值,控制数码管显示/关掉一位
大概就是这样的
void T0_time()interrupt 1
{
TH0=(65536-20000)/256;
TL0=(65536-20000)%256;
//从这里开始是我加的
aaa++;
if(aaa==13) aaa=1;
switch(aaa)
{
case 1:P2=0x00;P0=Tab[shi];break;
case 2:P2=0x01;P0=Tab[ge];break;
中间略
case 12:P2=0x0b;P0=Tab[ge4];break;
}
//从这里开始结束,以下是原文
count++;

if(count==50)//差点忘了,20要改50,因为定时器被我改成20ms了




这样就行了,每次20ms到了,就会刷新一个数追问

同样非常很感谢你,解释地非常详细周到。

追答

不客气,偶就是教这个的,所以话比较多哈。
其实你现在的主要问题是,对程序和物理现象的关系,理解得还不够
比如楼上的
if(P1_0!=1)
{
delay(10);
fen1=fen1+1;
while(!P1_0);
}

while(!P1_0);理解起来也没什么啊,

先看while(P1_0);
没有循环体的哦,所以是空循环
意思是,如果P1.0为“真”,就空循环
什么是为真,不等于0就是为真嘛
所以翻译起来就是,如果(P1.0==1),就空循环

那while(!P1_0);
翻译成中文就是,如果(P1.0==0)就空循环
再翻译一下,就是,如果(开关一直按着不放),就空循环

这是什么意思,就是如果开关如果没有松开,就卡在这个小小的空循环里等开关松开
那么,如果你一小时都不松开开关呢?那CPU就什么都不做,在这里等着
OK,那你的数码管当然不显示了

加上一句循环内容不一样了
if(P1_0!=1)
{
delay(10);
fen1=fen1+1;
while(!P1_0)
display();
}
这时候就不是空循环了,循环有了内容
什么内容呢?
每次循环,就调用一次显示函数
好理解吧
这样的话,如果按着开关不放,
程序还是会卡在这个循环里
可是不同之处在于,这个循环会调用显示函数
所以照样会显示数码管的内容。

其实这个很简单的
这样的理解过程是必须的,想多了,就容易了
学习单片机,学习别人的程序
一定要多思考
你做得项目越大,就会有越复杂越隐蔽的问题
需要你去考虑周全。

还有一点要提的是
你的程序,太乱了
可读性和可移植性都很差
程序的基本思路是,尽可能的条理清楚

不要把什么都写在main函数的主循环里
这样会很乱
要把各种功能都封装到函数里面
每个函数都有自己特定的功能
而主函数呢,在特定的时间,调用特定的函数完成任务

看起来就会清晰很多
程序流程一目了然,这就叫可读性强

而且下次你做别的任务时
以前的函数还是可以拿过来用,这就叫可移植性

追问

都看完了,好多字啊,真的非常感谢您的指导和建议。

追答

没事儿,现在不是放假么,我比较闲。

温馨提示:内容为网友见解,仅供参考
第1个回答  推荐于2016-06-04
数码管动态显示频率需要大于50Hz人眼才能看不到数码管闪,像你在按键中延时1s肯定会造成数码管闪,有两种解决方法:
第一种:在延时中加入数码管的动态扫描函数,这样延时的过程中不会造成数码管闪的问题;
第二种:如果你的定时器充足,可以把数码管动态扫描放到定时器里(20ms显示一次),这样无论主程序中怎样延时都不会造成数码管的显示问题。

其实按键消抖动只需要延时10ms即可,你延时1s应该是因为按一次会重复进入按键扫描,所以增加了延时,其实只需加上判断按键释放即可,比如:
if(P1_0!=1){delay(10);fen1=fen1+1;while(!P1_0);}
但是这样如果按键按得时间比较长的话也会出现闪的问题,解决方法可以把数码管显示独立出来写个显示函数(比如display();),把while(!P1_0);改成while(!P1_0)display();即可追问

问题解决了,非常感谢你。能问问你为什么加了while(!P1_0)display();数码管就不会闪烁了?有点想不明白。

追答

因为当你按住按键不松的时候while(!P1_0)一直为真,也就一直执行display(); 而display();是我常用的数码管显示函数
像你程序display()就是
void display(void)
{
shi=num/10;
ge=num%10;
shi1=sec/10;
ge1=sec%10;
shi2=min/10;
ge2=min%10;
bai3=fen1/100;
shi3=fen1/10%10;
ge3=fen1%10;
bai4=fen2/100;
shi4=fen2/10%10;
ge4=fen2%10;
P2=0x00;P0=Tab[shi];delay(20);
P2=0x01;P0=Tab[ge];delay(20);
P2=0x02;P0=Tab[shi1];delay(20);
P2=0x03;P0=Tab[ge1];delay(20);
P2=0x04;P0=Tab[shi2];delay(20);
P2=0x05;P0=Tab[ge2];delay(20) ;
P2=0x06;P0=Tab[bai3];delay(20);
P2=0x07;P0=Tab[shi3];delay(20);
P2=0x08;P0=Tab[ge3];delay(20);
P2=0x09;P0=Tab[bai4];delay(20);
P2=0x0a;P0=Tab[shi4];delay(20);
P2=0x0b;P0=Tab[ge4];delay(20);
}

本回答被提问者采纳
第2个回答  2013-08-21
你要做的是在延时程序中调用显示程序,另外要在检测到按键弹起后才能计数和退出键盘扫描程序,否则容易出现按下一次后加几次的现象

单片机菜鸟求教,关于延迟和按键去抖的问题
同样周而复始。结果那1s时间内,什么都不显示,当然就看到数字不见了 解决办法就是采用定时器中断。不管是按键用中断也好,显示用中断也好,只要其中一个进入定时器中断,问题就解决了。程序就不写了,描述一下意思就好 你的主程序保留键盘部分,但是显示那一大段不要了 中断本来是50ms,建议改成20ms 然...

单片机 按键去抖啥意思???
单片机 按键去抖, 就是单片机的 按键 在正常操作中 在按压过程时 不能一下 完全接通,就是按压抖动,单片机在接收这种 信息时 会判断错误,所有要去掉这种抖动因素,去抖 有 硬件 去抖 就是在 按键与单片机连接的 IO 口 加 消抖电容 。还有 用 软件 去抖 就是 在单片机 接收按键 信息时 多次 ...

问一个关于单片机按键延时防抖方面的问题,使用软件防抖(汇编、C语言...
D2:DJNZ R6,D2 ;DJNZ R7,D1 ;RET D10mS就是一个延时程序,调用它后再进行判断电平判断,就避开了按键按下时的抖动.

单片机按键去抖动有哪些方法
在按键上增加电容去除干扰,在按键上串一个电阻也可以解决静电或者是其他干扰对按键造成的干扰。软件去抖动也是一种非常常见的去抖动的方法,就是对按键进行多次的检测,每次检测都被按下,才认为按键被按下。软件去抖动要采用多次采集的方法,中间的间隔可以使用延时或者使用分时的思想来进行去抖动,一般的...

单片机按键防抖动延时多长才合适
抖动一般是5~10ms,10ms可以,对时间没太大要求,大于10ms也可以比如20ms,不过太大了程序运行就费时间,看需求做吧。

在线采纳51单片机按键去抖的问题
我感觉多半是你得到的信息有误,应该没有这个技术。关于扫描,矩阵键盘需要用扫描的方式去监控,但是处理抖动仍然靠延时,延时至少是当下最有效的办法。抖动的实质是输入信号有高频杂波(相对于按键操作频率),目前只有滤波技术能消掉高频杂波。延时算一种数字滤波技术,当然还有硬件滤波技术,最简单的就是...

单片机键盘去抖动到底该用什么算法??!!
延时5-10ms再判按键

单片机的按键开关抖动的产生原因,抖动的特点和如何消除抖动
消抖的目的就是为了要除去信号在高低电位之间弹跳所造成的不正确输入,就是交错出现的01那段,一般抖动持续的时间是3-10毫秒。所以简单的去抖动操作,只要在这段抖动出现的时间跨度两端进行两次按钮状态检测即可:假设放开按钮信号为1,按下为0,那么当首次检测到按钮为0,过10ms再次检测,若依然为0,按...

单片机在扫描是否有键按下时,为什么要做这么多次的判断?有什么作用...
是为了防止键盘抖动,键盘抖动的时间是很短的,通过延时一段时间后,如果再判断键盘是按下的,这是才真正能说明键盘按下了,相反,则是抖动,不能说明键盘按下!所以,软件中一般都需要两次判断键盘按下!第一次之后,加段延时,之后再判断

AT89C51单片机:简述在使用普通按键的时候,为什么要进行去抖动处理,怎 ...
去除机械按键这种抖动的措施有硬件方法,还有软件方法,硬件方法就是在按键上增加电容去除干扰。在按键上串一个电阻也可以解决静电或者是其他干扰对按键造成的干扰。软件去抖动也是一种非常常见的去抖动的方法,就是对按键进行多次的检测,每次检测都被按下才认为按键被按下。

相似回答