🎀 文章作者:二土电子
🐸 期待大家一起学习交流!
一、前言
1、矩阵键盘是什么
矩阵键盘很好理解,就是排布类似矩阵的按键,大家可以想一下点阵的外形。大概长下面这个样子
2、矩阵键盘的应用场景
矩阵键盘一般应用在需要用到大量按键的场景,主要是为了节省按键使用的IO资源。比如一个项目需要用到16个按键,如果使用普通的独立按键,每一个按键都需要一个IO,16个按键就需要16个IO。对于珍贵的IO资源看来说,只是为了实现简单的按键检测就占用那么多,实在是一种浪费。而如果将这16个按键换成一个4*4的矩阵键盘,只需要8根线就可以实现16个按键的检测,能够节省很多IO资源。
3、矩阵键盘的替代品
相比于矩阵按键,也可以采用一些编码器来实现,原理于矩阵键盘相同,都是给每一个按键对应一个固定位数的二进制数。只不过矩阵键盘需要软件自己检测每一个IO上的高低电平信息,得到一个二进制数。而利用编码器就是将这一步交给硬件实现,硬件会直接根据按键按下的位置自己产生一个固定位数的二进制数,软件直接检测二进制数即可。利用编码器实现的话软件方面会减少一些工作,检测起来更加方便,而且通过硬件直接输出二进制数比软件自己检测更加可靠。
3、矩阵键盘的优缺点
1)使用矩阵键盘优点在于可以用相对较少的IO实现多个按键的检测。
2)缺点也较为明显,如果有多个按键同时按下无法检测出具体按下了哪几个,对于一些按键需求多,而且需要一次开启多个或者需要同时控制的应用场景,矩阵键盘并不太适用。
二、矩阵键盘按键检测原理
检测方法主要有两种,一种是逐行逐列扫描法,另一种是反线法,常用的是逐行逐列扫描法。接下来以4*4矩阵键盘为例,介绍一下这两种方法。在此之前可以先看一下矩阵键盘中按键的连接方法,带着下面这个硬件连接示意图去看软件检测原理。
1、逐行逐列扫描法
4根行线,4根列线。首先MCU给4个列线的IO输出低电平,4个行线的IO输出高电平。当没有按键按下时,四条行线所连接的IO引脚读取到的将全部是高电平。而当有按键按下时,由于按键按下,导致该按键所在的行列线接通,本身高电平的行线电平被拉低。此时读取所有行线的IO电平可以得知有按键按下。
判断有按键按下后行线IO依旧保持高电平,逐列将列线IO电平置低,读取行线IO电平,如果在一条列线为低电平的时候检测到某一条行线为高电平。此时可以根据行线列线8个IO的电平状态得到一个特定的16进制数,根据这个16进制数可以确定具体是哪个按键按下。
2、反线法
反线法也较为简单,判断是否有按键按下的方法与逐行伫列扫描法相同,首先MCU给4个列线的IO输出低电平,4个行线的IO输出高电平。当没有按键按下时,四条行线所连接的IO引脚读取到的将全部是高电平。而当有按键按下时,由于按键按下,导致该按键所在的行列线接通,本身高电平的行线电平被拉低。此时读取所有行线的IO电平,可以得知有按键按下,同时也能知道被按下的按键位于哪一行。
不同的是后续反线法会将行线IO电平拉低,列线电平IO拉高,这也是这种方法叫做反线法的原因。同样的方法会检测到列线有一条与行线接通,导致电平被拉低。这样又能得出被按下的按键所在的列。如此一来行列确定后就能够确定被按下按键的具体位置。
个人还是更加倾向于逐行逐列扫描法的。
三、矩阵键盘按键检测程序实现
矩阵键盘的检测整理来讲还是比较简单的,这里以STM32F103系列单片机为例,附上一个4*4矩阵键盘检测的程序。
#include "keyboad.h"
#include "delay.h"
void keyboad_Init()
{
GPIO_InitTypeDef GPIO_InitStructure;//定义结构体变量
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; //选择你要设置的IO口
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; //设置推挽输出模式
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz; //设置传输速率
GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化GPIO */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7; //选择你要设置的IO口
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD; //设置下拉输入模式
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz; //设置传输速率
GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化GPIO */
GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);
GPIO_ResetBits(GPIOA,GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);
}
u8 Read_KeyValue()
{
u8 KeyValue=0;
if((GPIO_ReadInputData(GPIOA)&0xff)!=0x0f)
{
delay_ms(10);
if((GPIO_ReadInputData(GPIOA)&0xff)!=0x0f)
{
GPIO_SetBits(GPIOA,GPIO_Pin_0);
GPIO_ResetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);
switch(GPIO_ReadInputData(GPIOA)&0xff)
{
case 0x11: KeyValue=1;break;
case 0x21: KeyValue=5;break;
case 0x41: KeyValue=9;break;
case 0x81: KeyValue=13;break;
}
GPIO_SetBits(GPIOA,GPIO_Pin_1);
GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_2|GPIO_Pin_3);
switch(GPIO_ReadInputData(GPIOA)&0xff)
{
case 0x12: KeyValue=2;break;
case 0x22: KeyValue=6;break;
case 0x42: KeyValue=10;break;
case 0x82: KeyValue=14;break;
}
GPIO_SetBits(GPIOA,GPIO_Pin_2);
GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_3);
switch(GPIO_ReadInputData(GPIOA)&0xff)
{
case 0x14: KeyValue=3;break;
case 0x24: KeyValue=7;break;
case 0x44: KeyValue=11;break;
case 0x84: KeyValue=15;break;
}
GPIO_SetBits(GPIOA,GPIO_Pin_3);
GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2);
switch(GPIO_ReadInputData(GPIOA)&0xff)
{
case 0x18: KeyValue=4;break;
case 0x28: KeyValue=8;break;
case 0x48: KeyValue=12;break;
case 0x88: KeyValue=16;break;
}
GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);
GPIO_ResetBits(GPIOA,GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);
while((GPIO_ReadInputData(GPIOA)&0xff)!=0x0f);
return KeyValue;
}
}
return 0;
}