STM32F1与STM32CubeIDE编程实例-矩阵键盘驱动

矩阵键盘驱动

1、矩阵键盘介绍

键盘是广泛用于各种电子和嵌入式项目的输入设备。 它们用于以数字和字母的形式获取输入,并将其输入系统以进行进一步处理。

矩阵键盘由一组相互连接的按钮组成。 在本次实例中使用 4X4 矩阵键盘,其中四行中的每一行都有 4 个按钮。 按钮端子按下图所示示连接。 在第一行,所有4个按钮的一个端子连接在一起,4个按钮的另一个端子代表4列中的每一列,每行也是如此。 所以一共有 8 根线来连接一个微控制器。

在这里插入图片描述

首先,需要配置 8 个 GPIO 引脚。 其中四个是输入,另外四个是输出引脚。 然后您可以将列引脚连接到输入或输出引脚,但为方便起见,我将它们连接到 4 个输出引脚。

最后但同样重要的是,4 行引脚将连接到微控制器的 4 个输入引脚。 如下图所示:

在这里插入图片描述

如何确定键盘中哪个按键按下?

1)查找列位置

  • 首先,将所有行都设置为逻辑级别 0,将所有列设置为逻辑级别 1。
  • 每当按下一个按键时,对应于该按钮的列和行就会短路,并使相应的列变为逻辑电平 0。因为该列连接(短路)到处于逻辑电平 0 的行。

在这里插入图片描述

2)查找行位置

  • 首先,将所有行都设置为逻辑级别1,将所有列设置为逻辑级别 0。
  • 当按下按键的行应为 0,因为它已连接(短路)到按下按钮的列,并且所有列都处于 0 逻辑。 所以我们已经扫描了所有行的 0。

在这里插入图片描述

因此,每当在逻辑 0 处找到 Row 时,就意味着这是按下按钮的行。 所以现在就可以将列号(在步骤 2 中得到)和行号确定按键的位置。

在前面的文章中,对矩阵键盘的驱动做了详细的描述,请参考:

2、矩阵键盘配置

开发环境搭建、系统时钟配置、调试配置及串口配置,请参考:

传感器配置如下:

在这里插入图片描述

保存配置并生成代码。

3、矩阵键盘驱动实现

1)基本定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/\*
\* keypad4x4.h
\*
\* Created on: Apr 24, 2022
\* Author: jenson
\*/

#ifndef \_\_KEYPAD\_H\_\_
#define \_\_KEYPAD\_H\_\_

#include <stdio.h>
#include <stm32f1xx\_hal.h>
#include "main.h"

typedef struct {
uint16\_t id;
uint16\_t rows;
uint16\_t cols;
GPIO_TypeDef \*\*row_ports;
uint16\_t \*row_pins;
GPIO_TypeDef \*\*column_ports;
uint16\_t \*column_pins;
uint8\_t \*keys_map;
} keypad\_t;

/\*\*\*
\* @brief Keypad初始化
\* @param keypad Keypad对象
\*/
void keypad\_init(keypad\_t \*keypad);
/\*\*\*
\* @brief 读取Keypad按键值
\* @param keypad Keypad对象
\*/
uint8\_t keypad\_read(keypad\_t \*keypad);
/\*\*\*
\* @brief Keypad 销毁
\* @param keypad Keypad对象
\*/
void keypad\_release(keypad\_t \*keypad);

#endif /\* \_\_KEYPAD\_H\_\_ \*/


2)矩阵键盘键值扫描

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/\*
\* keypad4x4.c
\*
\* Created on: Apr 24, 2022
\* Author: jenson
\*/

#include <keypad/keypad.h>

void keypad\_init(keypad\_t \*keypad) {

}

void keypad\_release(keypad\_t \*keypad) {

}
// 设置行
void \_\_keypad\_set\_column(keypad\_t \*keypad, uint16\_t column) {
for (int i = 0; i < keypad->cols; i++) {
HAL\_GPIO\_WritePin(keypad->column_ports[i], keypad->column_pins[i],
GPIO_PIN_RESET);
}
HAL\_GPIO\_WritePin(keypad->column_ports[column], keypad->column_pins[column],
GPIO_PIN_SET);
}

// 检查列状态,并确定按键位置
uint8\_t \_\_keypad\_check\_row(keypad\_t \*keypad, uint16\_t column) {
uint8\_t key = 0;

for (int i = 0; i < keypad->rows; i++) {
if (HAL\_GPIO\_ReadPin(keypad->row_ports[i], keypad->row_pins[i])) {
//HAL\_Delay(50);
while (HAL\_GPIO\_ReadPin(keypad->row_ports[i], keypad->row_pins[i]))
; // 等待松开
key = keypad->keys_map[i \* keypad->rows + column];

break;
}
}

return key;
}

uint8\_t keypad\_read(keypad\_t \*keypad) {
uint8\_t key = 0;

for (int i = 0; i < keypad->cols; i++) {
\_\_keypad\_set\_column(keypad, i);
key = \_\_keypad\_check\_row(keypad, i);
if (key) {
break;
}
}
return key;
}


3)主程序

main.c文件中,添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/\* Private includes ----------------------------------------------------------\*/
/\* USER CODE BEGIN Includes \*/
#include <keypad/keypad.h>
/\* USER CODE END Includes \*/

/\* Private variables ---------------------------------------------------------\*/

/\* USER CODE BEGIN PV \*/
GPIO_TypeDef \*column_ports[] = { KEYPAD_C1_GPIO_Port, KEYPAD_C2_GPIO_Port,
KEYPAD_C3_GPIO_Port, KEYPAD_C4_GPIO_Port };
uint16\_t column_pins[] = { KEYPAD_C1_Pin, KEYPAD_C2_Pin, KEYPAD_C3_Pin,
KEYPAD_C4_Pin };
GPIO_TypeDef \*row_ports[] = { KEYPAD_R1_GPIO_Port, KEYPAD_R2_GPIO_Port,
KEYPAD_R3_GPIO_Port, KEYPAD_R4_GPIO_Port };
uint16\_t row_pins[] = { KEYPAD_R1_Pin, KEYPAD_R2_Pin, KEYPAD_R3_Pin,
KEYPAD_R4_Pin };
// 按键值
uint8\_t keys_map[4][4] = {
{ '1', '2', '3', 'A' },
{ '4', '5', '6', 'B' },
{'7', '8', '9', 'C' },
{ '\*', '0', '#', 'D' }
};

keypad\_t keypad;
/\* USER CODE END PV \*/

int main(void) {
/\* USER CODE BEGIN 1 \*/

/\* USER CODE END 1 \*/

/\* MCU Configuration--------------------------------------------------------\*/

/\* Reset of all peripherals, Initializes the Flash interface and the Systick. \*/
HAL\_Init();

/\* USER CODE BEGIN Init \*/

/\* USER CODE END Init \*/

/\* Configure the system clock \*/
SystemClock\_Config();

/\* USER CODE BEGIN SysInit \*/

/\* USER CODE END SysInit \*/

/\* Initialize all configured peripherals \*/
MX\_GPIO\_Init();
MX\_USART1\_UART\_Init();
/\* USER CODE BEGIN 2 \*/
keypad.id = 1;
keypad.rows = 4;
keypad.cols = 4;
keypad.column_ports = column_ports;
keypad.column_pins = column_pins;
keypad.row_ports = row_ports;
keypad.row_pins = row_pins;
keypad.keys_map = (uint8\_t\*) &keys_map[0];

printf("\*\*\*\*STM32CubeIDE:Keypad4x4 \*\*\*\*\r\n");
/\* USER CODE END 2 \*/

/\* Infinite loop \*/
/\* USER CODE BEGIN WHILE \*/
while (1) {
/\* USER CODE END WHILE \*/

/\* USER CODE BEGIN 3 \*/
uint8\_t key = keypad\_read(&keypad);
if (key) {
printf("key %c pressed\r\n", key);
}
}
/\* USER CODE END 3 \*/
}

运行结果:

在这里插入图片描述

文章来源: https://iotsmart.blog.csdn.net/article/details/125500171