按键电平的读取通常我们都是通过延时函数或者循环延时来降低抖动时间,来读取正确的按键电平。 
示例: 
#include "reg52.h" 
sbit SysKey P0^0 
sbit LED P0^1 
#define SET 1 
#define RESET 0 
void Delay1ms(unsigned int ms) 
{ 
unsigned char a,b,c; 
while(ms--) 
{ 
for(c=17;c>0;c--) 
for(b=134;b>0;b--) 
for(a=2;a>0;a--); 
} 
} 
bit ReadKey(void) 
{ 
if(SysKey == RESET)//有按键被按下 
{ 
Delay1ms(10);//延时10ms 
if(SysKey == RESET)// 再次判断按键被按下 
return 1; 
} 
return 0; 
} 
int main(void) 
{ 
SysKey = SET;//输出高电平 
LED = 1;//关闭LED 
while(1) 
{ 
if(ReadKey == SET)//读取到按键被按下 
LED = 0; //点亮LED小灯 
Delay1ms(1000)//延时1S 
LED = 1; //关闭LED小灯 
} 
} 
以上程序对于简单试验还使用,但对于稍微复杂一点的程序就显得太浪费单片机资源了。单片机在读取到第一个按键下降沿的时候就开始延时10ms,单片机处于循环状态,不能处理其它事件造成资源浪费。 
其实我们可以通过一个时间来扫描按键,当单片机读取到按键下降沿的时候,使用一个计数器来进行消抖操作。 
示例:www.dgzj.com 
#include 
sbit SysKey P0^0 
sbit LED P0^1 
#define SET 1 
#define RESET 0 
unsigned int TIMTABLE[2] = {0}; 
void InitTimer0(void) 
{ 
TMOD = 0x01; 
TH0 = 0x0FC; 
TL0 = 0x018; 
EA = 1; 
ET0 = 1; 
TR0 = 1; 
} 
bit ReadKey(void) 
{ 
static unsigned char KeyCnt = 0;//定义一个存储读取按键次数的静态变量 
switch(KeyCnt) 
{ 
case 0: 
{ 
if(SysKey == RESET)//读取到当前按键被按下 
KeyCnt = 1; 
} 
break; 
case 1: 
{ 
if(SysKey == RESET)//读取到当前按键被按下 
{ 
KeyCnt = 2; 
return 1; 
} 
} 
break; 
case 2: 
{ 
if(SysKey == SET)//读取到当前按键被被释放 
KeyCnt = 0; 
} 
break; 
default:KeyCnt = 0;break; 
} 
return 0; 
} 
void main(void) 
{ 
InitTimer0(); 
SysKey = SET; 
LED = 1;//关闭LED灯 
while(1) 
{ 
if(TIMTABLE[0] >= 10)//10ms扫描时间到了 
{ 
TIMTABLE[0] = 0;//必须清零 
if(ReadKey == SET)//读到按键被按下 
{ 
TIMTABLE[1] = 0; 
LED = 0;//点亮LED 
} 
} 
else if(TIMTABLE[1] >= 500)//500ms到了 
{ 
TIMTABLE[1] = 0; 
LED = 1;//关闭LED小灯 
} 
//添加其它代码,无软件延时 
} 
} 
void Timer0Interrupt(void) interrupt 1 //1ms定时中断 
{ 
unsigned char i = 0; 
TH0 = 0x0FC; 
TL0 = 0x018; 
for(i=0;i 
TIMTABLE++; 
} 
以上示例程序可以有效解决按键读取抖动,通过主函数每10ms扫描一次按键状态,读取到按键状态后跳出等待10ms判断按键是否被按下。可大大提高单片机资源使用效率。 
附原理图: 
