利用这种方法,就可以避免通过直接延时按键消抖占用CPU时间,而是转化成了一种按键状态判定而非按键过程判断,我们只对当前按键的连续16ms的8次状态进行判断,而不再关心它在这16ms内都做了什么事情,我们来看看这个程序怎么写。 #include
sbit KEY1 = P2^4;
sbit KEY2 = P2^5;
sbit KEY3 = P2^6;
sbit KEY4 = P2^7;
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
unsigned char code LedChar[] = {
0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,
0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8e
};
bit KeySta = 1; //当前按键状态
void main(void)
{
bit backup = 1; //按键值备份,保存前一次的值
unsigned char counter = 0; //计数器记录按键按下的次数
//选择最右边的数码管进行显示
P0 = LedChar[counter];
ADDR0 = 0;
ADDR1 = 0;
ADDR2 = 0;
ADDR3 = 1;
ENLED = 0;
//选中第一行按键以进行扫描
P2 = 0xF7;
//配置T0工作在模式1,定时2ms
TMOD = 0x01;
TH0 = 0xF8;
TL0 = 0xCD;
TR0 = 1;
ET0 = 1;
EA = 1;
while(1)
{
if (KeySta != backup) //当前值与前一次值不相等说明此时按键有动作
{
if (backup == 0) //如果前一次的值为0,则说明当前状态是由0变为1,即按键弹起
{
counter++; //计数器+1
if (counter >= 10)
{ //只用1个数码管显示,所以记到10就清零重新开始
counter = 0;
}
P0 = LedChar[counter]; //计数值显示到数码管上
}
backup = KeySta; //更新备份为当前值,以备进行下次比较
}
}
}
void InterruptTimer0() interrupt 1
{
static unsigned char keybuf = 0xFF; //按键扫描缓冲区,保存一段时间内的扫描值
TH0 = 0xF8; //溢出后进入中断重新赋值
TL0 = 0xCD;
keybuf = (keybuf << 1) | KEY4; //只取KEY4为例,缓冲区左移一位,并将当前扫描值移入最低位
if (keybuf == 0x00)
{ //当连续8次扫描值都为0,即16ms内都只检测到按下状态时,可认为按键已按下
KeySta = 0; //按键状态值为按下
}
else if (keybuf == 0xFF)
{ //当连续8次扫描值都为1,即16ms内都只检测到弹起状态时,可认为按键已弹起
KeySta = 1; //按键状态值为弹起
}
else
{} //其它情况下则说明按键状态尚未稳定,则不对KeySta变量值进行更新
}
这个算法是我们在工程中经常使用按键所总结的一个比较好的方法,介绍给大家,今后都可以用这种方法消抖了。当然,按键消抖也还有其它的方法,程序实现更是多种多样,大家也可以再多考虑下其它的算法,拓展下思路。这个程序有一个新知识点,就是bit类型的变量,这个在标准C语言里边是没有的。51单片机有一种特殊的变量类型就是bit型,比如unsigned char型是定义了一个无符号的8位的数据,它占用一个字节(Byte)的内存,而bit型是1位数据,只占用1个位(bit)的内存,用法和标准C中其他的基本数据类型是一致的。它的优点就是节省内存空间,8个bit型变量才相当于1个char型变量所占用的空间。虽然它只有0和1两个值,但也已经可以表示很多东西了,比如:按键的按下和弹起、LED灯的亮和灭、三极管的导通与关断、开关的闭合与断开,联想一下已经学过的内容,它是不是能用最小的内存代价来完成很多工作呢?
|
电工学习网 ( )
GMT+8, 2023-4-5 16:02