矩阵键盘的“一厢情愿”与“两情相悦”

2021-08-16 15:29:18 浏览数 (1)

前言

  日常设计中,不可避免的会使用到按键,像我们常见的 POS 机、计算器等设备用到的按键是非常多的,如果采用普通的 1 个 IO 1 个按键的设计方法,显然对单片机资源来说是非常浪费的,所以采用类似矩阵的设计思路,能够大大减少 MCU IO 的使用,也是我们所说的矩阵键盘。

应用场景

一、矩阵键盘的两种扫描方式

  矩阵键盘常用的有以下两种扫描方式,相比较之下,小飞哥更推荐第二种扫描方式,第二种扫描方式能够更有效地避免错误按键,本次分享的也是第二种扫描方式的代码。

  • 逐行扫描:高四位输出低电平来对矩阵键盘逐行扫描,当低四位接受数据不全为一,表示有按键按下,然后通过接收到的数据是哪一位为 0 来判断哪个按键被按下,可以简单理解为,“一厢情愿型”;
  • 行列扫描:高四位全部输出低电平,低四位输出高电平。当接受到的数据,低四位不全为高电平时,说明有按键按下。然后通过接收到的值判断是哪一列有按键按下,然后再反过来高四位输出高电平,第四位低电平,然后通过高四位接收到的值判断哪一行按键按下,可以简单理解为,“两情相悦型”;

二、“两情相悦型”按键扫描方式详解

  上面图片是 4*4 矩阵键盘电路设计,ROW1-ROW4 为键盘你的行,COL1-COL4 为键盘的列,8 个 IO 共 16 个按键,我们以 K1 按下为例,分析具体的工作流程:

  • 1、 先把行切换为上拉输入模式
  • 2、把列切换为输出模式,输出低电平
  • 3、获取当前行输入状态

即:ROW1 线上此时为低电平,按下之前 ROW1-ROW4 为:1111,即 0x0F,按下之后,ROW1 与 COL1 连通,ROW1-ROW4 的值变为:1110,即 0x0e,此时获取到按键所在行;

  • 4、把行切换为输出状态,输出低电平
  • 5、把列切换为上拉输入模式
  • 6、获取当前列输入状态

即:COL1 线上此时为低电平,按下之前 COL1-COL4 为:1111,即 0x0F,按下之后,ROW1 与 COL1 连通,COL1-COL4 的值变为:1110,即 0x0e,此时获取到按键所在列;

  • 7、两次行列状态获取完成之后,即确定了按键所在行列,得到按键编码:0xee,值并不是唯一的 0xee,具体跟硬件连接有关系。

硬件连接

  调试用的键盘为这种非常简单的,某宝几毛钱,邮费都不够的说~

MCU

键盘

PF0

ROW1

PF1

ROW2

PF2

ROW3

PF3

ROW4

PG0

COL1

PG1

COL2

PG2

COL3

PG3

COL4

软件实现

1、cubemx配置键盘IO

  行IO配置为输出模式,输出低电平

  列IO配置为输入模式,需要注意的是,配置为输入上拉模式

  配置完成的IO状态为:

  时钟配置、串口等配置见:HAL库使用章节

2、代码编写

  根据上一小节描述,按键按下之后是对应一个编码,根据上面的描述,我们可以计算出一个码表,也即按键真值表:

代码语言:javascript复制
const uint8_t KeyTrueLable[] =
 {
  0xEE, 0xDE, 0xBE, 0x7E,
  0xED, 0xDD, 0xBD, 0x7D,
  0xEB, 0xDB, 0xBB, 0x7B,
  0xE7, 0xD7, 0xB7, 0x77};

  真值表是硬件IO状态的一种表达,对用户来说并不是需要的,需要攻城狮转换为用户需要的信息,也即是,建立一个与之对应的用户信息表:

代码语言:javascript复制
const uint8_t KeyUserDefined[] =
 {
  1, 2, 3, 12,
  4, 5, 6, 13,
  7, 8, 9, 14,
  10, 0, 11, 15};

  根据上面原理介绍,我们需要封装4个函数,行输入、行输出、列输入、列输出:

代码语言:javascript复制

///===============================================================
/// 行输入
///===============================================================
void KeyPad_SetROW_IN(void)
{
 GPIO_InitTypeDef GPIO_InitStruct = {0};

 __HAL_RCC_GPIOF_CLK_ENABLE();
 //ROW0-ROW3设置成上拉输入
 GPIO_InitStruct.Pin = Key_ROW1_Pin | Key_ROW2_Pin | Key_ROW3_Pin | Key_ROW4_Pin; //KEY0-KEY3
 GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
 GPIO_InitStruct.Pull = GPIO_PULLUP;
 HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
}
///===============================================================
/// 行输出
///===============================================================
void KeyPad_SetROW_Out(void)
{
 GPIO_InitTypeDef GPIO_InitStruct = {0};
 __HAL_RCC_GPIOF_CLK_ENABLE();

 /*Configure GPIO pins : PFPin PFPin PFPin PFPin */
 GPIO_InitStruct.Pin = Key_ROW1_Pin | Key_ROW2_Pin | Key_ROW3_Pin | Key_ROW4_Pin;
 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
 HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
 /*Configure GPIO pin Output Level */
 HAL_GPIO_WritePin(GPIOF, Key_ROW1_Pin | Key_ROW2_Pin | Key_ROW3_Pin | Key_ROW4_Pin, GPIO_PIN_RESET);
}

///===============================================================
///列输入
///===============================================================
void KeyPad_SetCol_IN(void)
{
 GPIO_InitTypeDef GPIO_InitStruct = {0};
 __HAL_RCC_GPIOG_CLK_ENABLE();

 //C0-3设置成上拉输入
 /*Configure GPIO pins : PCPin PCPin PCPin PCPin */
 GPIO_InitStruct.Pin = Key_COL1_Pin | Key_COL2_Pin | Key_COL3_Pin | Key_COL4_Pin;
 GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
 GPIO_InitStruct.Pull = GPIO_PULLUP;
 HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
}
///===============================================================
///列输出
///===============================================================
void KeyPad_SetCol_Out(void)
{
 GPIO_InitTypeDef GPIO_InitStruct = {0};
 __HAL_RCC_GPIOC_CLK_ENABLE();

 GPIO_InitStruct.Pin = Key_COL1_Pin | Key_COL2_Pin | Key_COL3_Pin | Key_COL4_Pin; //KEY0-KEY3
 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
 HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);

 HAL_GPIO_WritePin(GPIOG, Key_COL1_Pin | Key_COL2_Pin | Key_COL3_Pin | Key_COL4_Pin, GPIO_PIN_RESET);
}

然后编写我们的按键真值获取函数:

代码语言:javascript复制
///===============================================================
//按键扫描程序
//先C低读R状态,再R低读C状态,判断是那个按键按下
//
///===============================================================

uint8_t KeyPad_KEY_Scan(void)
{
 uint8_t keycode, keycode_temp;

 //C低读ROW
 KeyPad_SetCol_Out();
 keycode = KEYPADROW & 0x0f;
 //R低读COL
 KeyPad_SetROW_Out();
 KeyPad_SetCol_IN();

 keycode_temp = KEYPADCOL;

 keycode |= (keycode_temp << 4 & 0xf0);

 KeyPad_SetROW_IN();

 return keycode;
}

前面说过,直接获取到的是按键IO的状态组合,需要转换为实际需要的信息。封装一下函数:

代码语言:javascript复制
///===============================================================
//键值转换用户定义键值
///===============================================================
uint8_t KeyPad_KeyTrueLableGet(uint8_t key)
{
 uint8_t i = 0, R_key_user = 0xE0;

 for (i = 0; i < D_Key_Count; i  )
 {
  if (key == KeyTrueLable[i])
  {
   R_key_user = KeyUserDefined[i];
  }
 }
 return R_key_user;
///===============================================================
//得到键值
///===============================================================
uint8_t KeyPad_GetKeyValue(void)
{
 uint8_t KeyValue = 0;

 // HAL_Delay(10);
 if (KeyPad_KEY_Scan() == D_NO_KEY_CODE) //没有按键/按键抬起
 {

  if (keypad_status.KeyPress == 1)
  {
   keypad_status.KeyValue_New = 0;
   keypad_status.KeyValue_Old = 0;
   keypad_status.KeyPress = 0;
  }
  return 0xF0;
 }
 HAL_Delay(10);

 KeyValue = KeyPad_KEY_Scan();
 if (KeyValue == D_NO_KEY_CODE)
 {
  if (keypad_status.KeyPress == 1)
  {
   //解决按键“0” 的问题
   keypad_status.KeyValue_New = 0xF2;
   keypad_status.KeyValue_Old = 0xF2;
   keypad_status.KeyPress = 0;
  }
  return 0xF0;
 }
 KeyValue = KeyPad_KeyTrueLableGet(KeyValue);
 keypad_status.KeyPress = 1;
 //判断是否是第一次按下
 keypad_status.KeyValue_New = KeyValue;
 if (keypad_status.KeyValue_New == keypad_status.KeyValue_Old)
 {
  return 0xF1;
 }
 keypad_status.KeyValue_Old = KeyValue;
 printf("rnkeycode is:%0xrn", KeyValue);

 return KeyValue;
}

在keypad.h中定义:

代码语言:javascript复制
#ifndef __KEYPAD_H
#define __KEYPAD_H

#include "main.h"
#include "stm32f4xx.h"

#define KEYPADROW GPIOF->IDR
#define KEYPADCOL GPIOG->IDR

#define D_Key_Count 16

#define D_ERR_KEY_CODE 0xFF
#define D_NO_KEY_CODE 0xFF

///===============================================================
typedef struct keypad
{
    uint8_t KeyStatus;
    uint8_t KeyValue_New;
    uint8_t KeyValue_Old;
    uint8_t KeyPress;
    
}Keypad_para;

uint8_t
KeyPad_KEY_Scan(void);
uint8_t KeyPad_GetKeyValue(void);

#endif

测试结果

  本次的介绍就到这里啦,后面有更精彩的内容,欢迎大家持续关注嵌入式实验基地,来这里还可以学习HAL库 cubemx的更多精彩内容哦!

  如果你觉得对自己有帮助的话,给个赞,点个关注,点个在看,感谢前进的道路上有你的陪伴!

io

0 人点赞