学习目标
- 了解矩阵键盘的原理和基本结构
- 学会矩阵键盘的按键扫描方法
- 学会矩阵键盘的编程实现
学习内容
矩阵按键
矩阵键盘是一种常见的数字输入设备,由多行多列的按键组成。每个按键都有一个唯一的行列坐标,通过行列坐标可以确定按键的编号,从而实现对数字或字母的输入。原理图
矩阵键盘的基本结构包括按键、行引脚和列引脚。按键一般是机械按键或触摸按键,行引脚和列引脚分别与矩阵键盘的行和列相连,用于检测按键的输入状态。按键状态检测
单个按键状态检测
- 输出端的电平
- 输入端的状态
- 按键抬起
通过按键抬起时的状态,我们分析输入端的电平信号,来确定抬起时输入端的默认电平状态。
通过按键按下时的状态,我们分析输入端的电平信号,来确定按下时输入端的默认电平状态。
通过分析确认,默认输出端和输入端都是高电平;
- 当输出端输出低电平时,输入端为高电平,则开关为抬起状态;
- 当输出端输出低电平时,输入端为低电平,则开关为按下状态;
单行按键状态检测
通过逐一检测输入端的状态,来判断按键是否按下。
多行按键状态检测
首先,将第一行输出低电平,其余行设置为高电平。目的是为了只测试第一行的按键状态。
然后,将第二行输出低电平,其余行设置为高电平。目的是为了只测试第二行的按键状态。
接着,将第三行输出低电平,其余行设置为高电平。目的是为了只测试第三行的按键状态。
最后,将第四行输出低电平,其余行设置为高电平。目的是为了只测试第四行的按键状态。
状态记录
通过自定义状态来记录按键状态
// 记录16个按键状态,0为按下,1为抬起
u16 key_state = 0xFFFF;
......
void scan() {
// 初始都是 高电平
ROW_COL_RESET();
NOP1();
// ROW1
// 给 row1 低电平,读取COL1的值
ROW1 = 0;
NOP1();
// 当前是UP,当之前是DOWN,则为UP
// 当前是DOWN,当之前是UP,则为DOWN
if(COL1 != (key_state & 0x01) >> 0) {
if(COL1) {
// 修改当前状态为UP
key_state |= 0x01;
printf("K1 Up\r\n");
} else {
// 修改当前状态为DOWN
key_state &= ~0x01;
printf("K1 Down\r\n");
}
}
......
}
状态优化
通过define
优化一些数值的操作,方便在后续看代码时方便理解,提高代码的阅读性。
// 记录16个按键状态,0为按下,1为抬起
u16 key_state = 0xFFFF;
#define KEY_UP 1
#define KEY_DOWN 0
// 第n个按键的状态
#define KEY_STATE(n) ((key_state & (1 << n)) >> n)
#define SET_KEY_UP(n) (key_state |= (1 << n))
#define SET_KEY_DOWN(n) (key_state &= ~(1 << n))
#define ROW_COL_RESET() {ROW1=1,ROW2=1,ROW3=1,ROW4=1;COL1=1,COL2=1,COL3=1,COL4=1;}
......
void scan() {
// 初始都是 高电平
ROW_COL_RESET();
NOP1();
// ROW1
// 给 row1 低电平,读取COL1的值
ROW1 = 0;
NOP1();
// 当前是UP,当之前是DOWN,则为UP
// 当前是DOWN,当之前是UP,则为DOWN
if(COL1 != KEY_STATE(0)) {
if(COL1) {
// 修改当前状态为UP
SET_KEY_UP(0);
printf("K1 Up\r\n");
} else {
// 修改当前状态为DOWN
SET_KEY_DOWN(0);
printf("K1 Down\r\n");
}
}
......
}
循环优化
操作的按键众多,通过循环的方式来操控每一个按键,减少代码量,方便维护。
#define ROW 4
#define COL 4
// 记录16个按键状态,0为按下,1为抬起
u16 key_state = 0xFFFF;
#define KEY_UP 1
#define KEY_DOWN 0
// 第n个按键的状态
#define KEY_STATE(r, c) ((key_state & (1 << (r * ROW + c))) >> (r * ROW + c))
#define SET_KEY_UP(r, c) (key_state |= (1 << (r * ROW + c)))
#define SET_KEY_DOWN(r, c) (key_state &= ~(1 << (r * ROW + c)))
#define ROW_COL_RESET() {ROW1=1,ROW2=1,ROW3=1,ROW4=1;COL1=1,COL2=1,COL3=1,COL4=1;}
void scan() {
u8 i, j;
for(i = 0; i < ROW; i++) {
// 初始都是 高电平
ROW_COL_RESET();
NOP1();
ROW_ON(i);
for(j = 0; j < COL; j++) {
// 当前是UP,当之前是DOWN,则为UP
// 当前是DOWN,当之前是UP,则为DOWN
if(COL_STATE(j) != KEY_STATE(i, j)) {
if(COL_STATE(j)) {
// 修改当前状态为UP
SET_KEY_UP(i, j);
printf("(%d, %d) Up\r\n", (int)i, (int)j);
} else {
// 修改当前状态为DOWN
SET_KEY_DOWN(i, j);
printf("(%d, %d) Down\r\n", (int)i, (int)j);
}
}
}
}
}
练习题
- 按键扫描检测
- 优化按键状态扫描
- 驱动封装