矩阵键盘原理及程序设计

简介: 本文详细介绍了矩阵键盘是什么,主要应用场景,替代品,矩阵键盘的优缺点以及矩阵键盘的按键检测方法。最后附上了以STM32F103系列芯片为例的C程序。


🎀 文章作者:二土电子
🐸 期待大家一起学习交流!


一、前言

1、矩阵键盘是什么

矩阵键盘很好理解,就是排布类似矩阵的按键,大家可以想一下点阵的外形。大概长下面这个样子
89b820b2a3e41b86b44229c12bb1e950_454542c112624757b039bf827bbd1b32.png

2、矩阵键盘的应用场景

矩阵键盘一般应用在需要用到大量按键的场景,主要是为了节省按键使用的IO资源。比如一个项目需要用到16个按键,如果使用普通的独立按键,每一个按键都需要一个IO,16个按键就需要16个IO。对于珍贵的IO资源看来说,只是为了实现简单的按键检测就占用那么多,实在是一种浪费。而如果将这16个按键换成一个4*4的矩阵键盘,只需要8根线就可以实现16个按键的检测,能够节省很多IO资源。

3、矩阵键盘的替代品

相比于矩阵按键,也可以采用一些编码器来实现,原理于矩阵键盘相同,都是给每一个按键对应一个固定位数的二进制数。只不过矩阵键盘需要软件自己检测每一个IO上的高低电平信息,得到一个二进制数。而利用编码器就是将这一步交给硬件实现,硬件会直接根据按键按下的位置自己产生一个固定位数的二进制数,软件直接检测二进制数即可。利用编码器实现的话软件方面会减少一些工作,检测起来更加方便,而且通过硬件直接输出二进制数比软件自己检测更加可靠。

3、矩阵键盘的优缺点

1)使用矩阵键盘优点在于可以用相对较少的IO实现多个按键的检测。
2)缺点也较为明显,如果有多个按键同时按下无法检测出具体按下了哪几个,对于一些按键需求多,而且需要一次开启多个或者需要同时控制的应用场景,矩阵键盘并不太适用。

二、矩阵键盘按键检测原理

检测方法主要有两种,一种是逐行逐列扫描法,另一种是反线法,常用的是逐行逐列扫描法。接下来以4*4矩阵键盘为例,介绍一下这两种方法。在此之前可以先看一下矩阵键盘中按键的连接方法,带着下面这个硬件连接示意图去看软件检测原理。
c3cab8594517b6bdb1ca99e94083f127_5fba46536eef4e99816d23e482b90486.png

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;
}
相关文章
|
7月前
|
数据采集 数据处理 C语言
单片机:探索其原理、应用与编程实践
单片机:探索其原理、应用与编程实践
110 1
|
7月前
|
传感器 人工智能 物联网
单片机:编程实践与应用探索
单片机:编程实践与应用探索
82 4
|
7月前
|
数据安全/隐私保护
【51单片机】初学者必学的一个矩阵键盘基本项目——(矩阵键盘密码锁)(8)
【51单片机】初学者必学的一个矩阵键盘基本项目——(矩阵键盘密码锁)(8)
|
C语言 Perl
西门子S7-1200编程实例,电动机起保停控制梯形图如何编写?
本篇我们通过一个电动机起保停控制的实例,介绍S7-1200的使用方法,按下瞬时启动按钮I0.6,电动机Q0.0启动,按下瞬时停止按钮I0.7,电动机Q0.0停止。
西门子S7-1200编程实例,电动机起保停控制梯形图如何编写?
|
7月前
|
传感器 算法 IDE
如何灵活运用单片机
单片机可以通过编程来实现各种功能,例如控制外部设备、采集传感器数据、处理信号等。常见的单片机包括8051系列、PIC系列、AVR系列等,它们都具有不同的特点和适用场景。
91 0
|
缓存
蓝桥杯之单片机学习(十一)——串口通信的基本原理与应用
蓝桥杯之单片机学习(十一)——串口通信的基本原理与应用
408 0
蓝桥杯之单片机学习(十一)——串口通信的基本原理与应用
【数字IC手撕代码】Verilog无毛刺时钟切换电路|题目|原理|设计|仿真
【数字IC手撕代码】Verilog无毛刺时钟切换电路|题目|原理|设计|仿真
【数字IC手撕代码】Verilog无毛刺时钟切换电路|题目|原理|设计|仿真
【数字IC手撕代码】Verilog奇偶校验|题目|原理|设计|仿真
【数字IC手撕代码】Verilog奇偶校验|题目|原理|设计|仿真
【数字IC手撕代码】Verilog奇偶校验|题目|原理|设计|仿真
复习单片机:点亮LED(内含实物图+硬件设计+软件编程+原始代码)
复习单片机:点亮LED(内含实物图+硬件设计+软件编程+原始代码)
169 0
复习单片机:点亮LED(内含实物图+硬件设计+软件编程+原始代码)
|
存储 开发框架 算法
51单片机原理以及接口技术(四)--80C51的程序设计
单片机应用系统是合理的硬件与完善的软件的有机组合。软件就是各种指令依某种规律组合形成的程序。程序设计(或软件设计)的任务是利用计算机语言对系统预完成的任务进行描述和规定。
685 0
51单片机原理以及接口技术(四)--80C51的程序设计