嵌入式开发—矩阵键盘原理及程序设计

文章目录

  • 一、前言
  • 1、矩阵键盘是什么
  • 2、矩阵键盘的应用场景
  • 3、矩阵键盘的替代品
  • 3、矩阵键盘的优缺点
  • 二、矩阵键盘按键检测原理
  • 1、逐行逐列扫描法
  • 2、反线法
  • 三、矩阵键盘按键检测程序实现
  • 一、前言

    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;
    }
    
    
    物联沃分享整理
    物联沃-IOTWORD物联网 » 嵌入式开发—矩阵键盘原理及程序设计

    发表评论