51单片机 矩阵按键的扫描、消抖、动作分离

时间:2022-08-30 19:44:07
#include <reg52.h>

sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
sbit KEY_IN_1 = P2^4;
sbit KEY_IN_2 = P2^5;
sbit KEY_IN_3 = P2^6;
sbit KEY_IN_4 = P2^7;
sbit KEY_OUT_1 = P2^3;
sbit KEY_OUT_2 = P2^2;
sbit KEY_OUT_3 = P2^1;
sbit KEY_OUT_4 = P2^0;

code unsigned char LedChar[] = { //数码管显示字符转换表
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
unsigned char KeySta[4][4] = { //全部矩阵按键的当前状态
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1}
};

void main() {
unsigned char i, j;
unsigned char backup[4][4] = { //按键值备份,保存前一次的值
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1}
};

EA = 1; //使能总中断
ENLED = 0; //选择数码管DS1进行显示
ADDR3 = 1;
ADDR2 = 0;
ADDR1 = 0;
ADDR0 = 0;
TMOD = 0x01; //设置T0为模式1
TH0 = 0xFC; //为T0赋初值0xFC67,定时1ms
TL0 = 0x67;
ET0 = 1; //使能T0中断
TR0 = 1; //启动T0
P0 = LedChar[0]; //默认显示0

while (1) {
for (i=0; i<4; i++) { //循环检测4*4的矩阵按键
for (j=0; j<4; j++) {
if (KeySta[i][j] != backup[i][j]) { //检测按键动作
if (KeySta[i][j] == 0) { //按键按下时执行动作
P0 = LedChar[i*4 + j]; //将编号显示到数码管
}
backup[i][j] = KeySta[i][j]; //更新前一次的备份值
}
}
}
}
}
/* T0中断服务函数,扫描矩阵按键状态并消抖 */
void InterruptTimer0() interrupt 1 {
unsigned char j;
static unsigned char i = 0; //矩阵按键扫描输出索引
static unsigned char keybuf[4][4] = { //矩阵按键扫描缓冲区
{0xFF, 0xFF, 0xFF, 0xFF},
{0xFF, 0xFF, 0xFF, 0xFF},
{0xFF, 0xFF, 0xFF, 0xFF},
{0xFF, 0xFF, 0xFF, 0xFF}
};

TH0 = 0xFC; //重新加载初值
TL0 = 0x67;
//将一行的4个按键值移入缓冲区
keybuf[i][0] = (keybuf[i][0] << 1) | KEY_IN_1;
keybuf[i][1] = (keybuf[i][1] << 1) | KEY_IN_2;
keybuf[i][2] = (keybuf[i][2] << 1) | KEY_IN_3;
keybuf[i][3] = (keybuf[i][3] << 1) | KEY_IN_4;
//消抖后更新按键状态
for (j = 0; j < 4; j++) { //每行4个按键,所以循环4次
if ((keybuf[i][j] & 0x0F) == 0x00) { //连续4次扫描值为0,即4*4ms内都是按下状态时,可认为按键已稳定的按下
KeySta[i][j] = 0;
}
else if ((keybuf[i][j] & 0x0F) == 0x0F) { //连续4次扫描值为1,即4*4ms内都是弹起状态时,可认为按键已稳定的弹起
KeySta[i][j] = 1;
}
}
//执行下一次的扫描输出
switch (i) { //根据索引,释放当前输出引脚,拉低下次的输出引脚
case 0: KEY_OUT_1 = 1; KEY_OUT_2 = 0; break;
case 1: KEY_OUT_2 = 1; KEY_OUT_3 = 0; break;
case 2: KEY_OUT_3 = 1; KEY_OUT_4 = 0; break;
case 3: KEY_OUT_4 = 1; KEY_OUT_1 = 0; break;
default: break;
}
i = ++i & 0x03;
}