矩阵键盘

矩阵键盘通常由行(Row)和列(Column)组成,按键排列在行和列的交叉点处。例如,一个 4x4 矩阵键盘有 4 行 4 列,共 16 个按键。每个按键位于行和列的交点处,按下按键时会将该行和该列连接在一起。

工作原理

矩阵键盘的工作原理是通过扫描行和列来检测哪个按键被按下。每按下一个按键,就会将对应的行和列连接,使微控制器能够识别按键的具体位置。为了实现这一点,矩阵键盘的检测通常分为两部分:

  • 行扫描:微控制器将行设置为输出,逐行输出高或低电平信号。
  • 列检测:微控制器将列设置为输入,检查列引脚是否检测到电平的变化(即按键被按下)。

去抖处理

按键按下和释放时,电路中可能会出现抖动现象,导致信号不稳定。在嵌入式系统中,通常需要进行按键去抖动处理。常见的去抖动方法包括:

  • 软件去抖动:在检测到按键按下后,延时数毫秒后再次检测按键状态(或者通过额外的标志位,在检测到按下后立即置 0),以确保信号稳定。
  • 硬件去抖动:在按键电路中增加电容或使用专门的去抖动芯片来滤除噪声信号。

STC8H

引脚图

juzhenjpanyinjiaotu

实现

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// MatrixKey.h
#ifndef __MATRIXKEY_H
#define __MATRIXKEY_H

#include "Config"

void MK_Init();

void MK_Scan(void(*key_down) (u8, u8), void(*key_up) (u8, u8));

#endif
=========================================================================
// MatrixKey.c
#include "MatrixKey.h"

// 定义行列
#define COL1 P03
#define COL2 P06
#define COL3 P07
#define COL4 P17
#define ROW1 P34
#define ROW2 P35
#define ROW3 P40
#define ROW4 P41

// 检测按键上一个状态是否为弹起
#define is_up(status, row, col) ((status & (1 << 4 * row + col)) > 0)
// 检测按键上一个状态是否为按下
#define is_down(status, row, col) ((status & (1 << 4 * row + col)) == 0)
// 设置按键状态为弹起
#define set_up(status, row, col) (status |= (1 << 4 * row + col))
// 设置按键状态为按下
#define set_down(status, row, col) (status &= (1 << 4 * row + col))

u16 status = 0xFFFF; //默认 4x4 的按键状态都是弹起

void GPIO_Config_MatrixKey(void) {
GPIO_InitTypeDef GPIO_Init;
GPIO_Init.Mode = GPIO_PullUp;

GPIO_Init.Pin = GPIO_Pin_3 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_Inilize(GPIO_P0, &GPIO_Init);

GPIO_Init.Pin = GPIO_Pin_7;
GPIO_Inilize(GPIO_P1, &GPIO_Init);

GPIO_Init.Pin = GPIO_Pin_4 | GPIO_Pin_5;
GPIO_Inilize(GPIO_P3, &GPIO_Init);

GPIO_Init.Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_Inilize(GPIO_P4, &GPIO_Init);
}

void MK_Init(void) {
GPIO_Config_MatrixKey();
}

void set_row(u8 row) {
ROW1 = row == 0? 0 : 1;
ROW2 = row == 1? 0 : 1;
ROW3 = row == 2? 0 : 1;
ROW4 = row == 3? 0 : 1;
}

u8 get_col(u8 col) {
if(col == 0) return COL1;
else if(col == 1) return COL2;
else if(col == 2) return COL3;
else if(col == 3) return COL4;
}

int i, j;
// 扫描按键,如果需要感知按下或者弹起的状态,那么就传递进来按下和弹起的处理函数
void MK_Scan(void(*key_down) (u8, u8), void(*key_up) (u8, u8)) {
for( i = 0; i < 4; i++) { // 4行
set_row(i); // 逐行扫描
for(j = 0; j < 4; j++) { // 逐列判定
// 判断按键是否被按下,且按下之前的上一个状态时弹起(防抖)
if(get_col(j) == 0 && is_up(status, i, j)) {
set_down(status, i, j);
if (key_down != null) key_down(i j);
}else if(get_col(j) == 1 && is_down(status, i, j)) {
set_up(status, i, j);
if (key_up != null) key_down(i j);
}
}
}
}
=========================================================================
// main.c
#include "STC8G_H_GPIO.h"
#include "STC8G_H_NVIC.h"
#include "STC8G_H_UART.h"
#include "STC8G_H_Switch.h"
#include "STC8G_H_Delay.h"
#include "MatrixKey.h"

void UART_Config(void) {
COMx_InitDefine UART_Init;

UART_Init.UART_Mode = UART_8bit_BRTx;
UART_Init.UART_BRT_Use = BRT_Timer1;
UART_Init.UART_BaudRate = 115200ul;
UART_Init.Morecommunicate = DISABLE;
UART_Init.UART_RxEnable = ENABLE;
UART_Init.BaudRateDouble = DISABLE;

UART_Configuration(UART1, &UART_Init);

NVIC_UART1_Init(ENABLE, Priority_1);

UART1_SW(UART1_SW_P30_P31);
}

void down(u8 row, u8 col) {
printf("按下:%d-%d\n", (int)row, (int)col);
}

void up(u8 row, u8 col) {
printf("弹起:%d-%d\n", (int)row, (int)col);
}

void main() {

EA = 1;
UART_Config();
MK_Init();

while(1) {
MK_Scan(down, NULL);
delay_ms(20);
}
}