相信很多初学 51 单片机的同学都会遇到矩阵键盘这个拦路虎。刚开始的时候,我也是各种头大,扫描不准、按键抖动等等问题层出不穷。本文就以江协科技的教程为基础,结合我多年的嵌入式开发经验,带你彻底搞懂 51 单片机的矩阵键盘,并分享一些实战中的避坑经验。
矩阵键盘的原理深度剖析
矩阵键盘相比独立按键,最大的优势在于节省 IO 口资源。比如,要实现 16 个按键,如果使用独立按键,需要 16 个 IO 口,而使用 4x4 的矩阵键盘,只需要 8 个 IO 口。矩阵键盘的原理就是行线和列线交叉,通过扫描行线或列线,来判断哪个按键被按下。
行列扫描的实现方式
通常有两种扫描方式:行扫描和列扫描。
- 行扫描: 将行线设置为输出,列线设置为输入。依次将行线输出低电平,然后读取列线的电平。如果某列线为低电平,则表示该行与该列交叉的按键被按下。
- 列扫描: 将列线设置为输出,行线设置为输入。依次将列线输出低电平,然后读取行线的电平。如果某行线为低电平,则表示该列与该行交叉的按键被按下。
这两种方式本质上是一样的,选择哪一种取决于你的硬件连接方式和个人习惯。
消抖处理的重要性
按键按下或释放时,会产生抖动,如果不进行消抖处理,可能会导致误判。常用的消抖方法有两种:硬件消抖和软件消抖。
- 硬件消抖: 使用电容等元件,滤除抖动信号。这种方法简单有效,但会增加硬件成本。
- 软件消抖: 通过延时一段时间,等待抖动稳定后再读取按键状态。这种方法不需要额外的硬件,但会占用 CPU 资源。
在实际应用中,根据具体情况选择合适的消抖方法。
基于江协科技的代码示例及解析
下面是一个基于江协科技教程的简单的 4x4 矩阵键盘扫描代码:
#include <reg52.h>
#define KEY_PORT P1 // 定义键盘端口
sbit L1 = P2^0;
sbit L2 = P2^1;
sbit L3 = P2^2;
sbit L4 = P2^3;
void delay_ms(unsigned int ms) {
unsigned int i, j;
for (i = ms; i > 0; i--)
for (j = 110; j > 0; j--);
}
unsigned char KeypadScan() {
unsigned char keyValue = 0;
// 行扫描
KEY_PORT = 0xF0; // 行输出低电平,列输入高电平
if ((KEY_PORT & 0xF0) != 0xF0) { // 有按键按下
delay_ms(10); // 软件消抖
if ((KEY_PORT & 0xF0) != 0xF0) { // 再次确认
// 判断是哪一行
KEY_PORT = 0xFE; // 第一行置低
if ((KEY_PORT & 0xF0) != 0xF0) {keyValue = 1; while((KEY_PORT & 0xF0) != 0xF0);}
KEY_PORT = 0xFD; // 第二行置低
if ((KEY_PORT & 0xF0) != 0xF0) {keyValue = 2; while((KEY_PORT & 0xF0) != 0xF0);}
KEY_PORT = 0xFB; // 第三行置低
if ((KEY_PORT & 0xF0) != 0xF0) {keyValue = 3; while((KEY_PORT & 0xF0) != 0xF0);}
KEY_PORT = 0xF7; // 第四行置低
if ((KEY_PORT & 0xF0) != 0xF0) {keyValue = 4; while((KEY_PORT & 0xF0) != 0xF0);}
// 判断是哪一列
KEY_PORT = 0xF0;
if ((KEY_PORT & 0xEF) == 0xEE) keyValue = keyValue * 4 - 3; // 第一列
if ((KEY_PORT & 0xDF) == 0xDE) keyValue = keyValue * 4 - 2; // 第二列
if ((KEY_PORT & 0xBF) == 0xBE) keyValue = keyValue * 4 - 1; // 第三列
if ((KEY_PORT & 0x7F) == 0x7E) keyValue = keyValue * 4; // 第四列
}
}
return keyValue;
}
void main() {
unsigned char key;
while (1) {
key = KeypadScan();
if (key != 0) {
switch(key){
case 1: L1=~L1;break;
case 2: L2=~L2;break;
case 3: L3=~L3;break;
case 4: L4=~L4;break;
case 5: L1=~L1;L2=~L2;break;
case 6: L1=~L1;L3=~L3;break;
case 7: L1=~L1;L4=~L4;break;
case 8: L2=~L2;L3=~L3;break;
case 9: L2=~L2;L4=~L4;break;
case 10: L3=~L3;L4=~L4;break;
case 11: L1=~L1;L2=~L2;L3=~L3;break;
case 12: L1=~L1;L2=~L2;L4=~L4;break;
case 13: L1=~L1;L3=~L3;L4=~L4;break;
case 14: L2=~L2;L3=~L3;L4=~L4;break;
case 15: L1=~L1;L2=~L2;L3=~L3;L4=~L4;break;
case 16: L1=0;L2=0;L3=0;L4=0;break;
}
}
}
}
代码解析:
KEY_PORT定义了键盘端口,这里使用了P1口。delay_ms函数是一个简单的延时函数,用于软件消抖。KeypadScan函数是键盘扫描函数,通过行列扫描的方式,获取按键值。- 在主函数中,不断调用
KeypadScan函数,获取按键值,并根据按键值执行相应的操作。 - 这里为了方便观察结果,使用四个LED灯的亮灭作为结果展示
实战避坑经验总结
- IO 口驱动能力不足: 51 单片机的 IO 口驱动能力有限,如果键盘连接的电阻过小,可能会导致 IO 口输出高电平时电压不足,从而导致扫描不准确。解决方法是适当增大电阻。
- 干扰问题: 在复杂的电磁环境下,键盘扫描可能会受到干扰,导致误判。解决方法是使用屏蔽线,或者增加滤波电路。
- 消抖时间不合理: 消抖时间过短,可能无法消除抖动;消抖时间过长,会影响按键响应速度。需要根据实际情况调整消抖时间。
- 行列线接反: 矩阵键盘的行列线如果接反了,会导致扫描结果错误。解决方法是仔细检查硬件连接。
- 上拉电阻: 如果列线没有上拉电阻,可能会导致列线电平不稳定,从而导致扫描不准确。解决方法是在列线上加上拉电阻。
掌握了以上技巧,相信你就能轻松驾驭 51 单片机的矩阵键盘了。希望本文能够帮助到正在学习 51 单片机的你!记住,实践是检验真理的唯一标准,多动手实践才能真正掌握知识。
冠军资讯
代码小能豆