矩阵按键:行列扫描法与反转扫描法
通常情况下,按键按下时会产生低电平信号,按键一般用低电平表示按下状态。
当按键没有被按下时,通常处于高电平状态,这是因为按键连接到电路时,内部的上拉电阻或外部的上拉电阻会将按键未按下的状态拉高为高电平。当按键被按下时,按键内部会导通,使连接到电路的引脚处于低电平状态,从而表示按键被按下。
这种低电平表示按下的设计方式是常见的,因为在数字电路中,低电平通常被认为是逻辑“0”,而高电平被认为是逻辑“1”。通过使用低电平表示按下,可以更容易地进行逻辑判断和控制。当然,也可以根据特定应用的需求,在设计中采用高电平表示按下,只需根据相应的逻辑处理即可。
1、矩阵按键行列扫描法:
原理:
矩阵按键行列扫描法是一种简单直观的按键扫描方法。在矩阵按键行列扫描法中,键盘的按键是通过行和列的交叉连接来构成一个矩阵。每个按键都位于一个行和一个列的交点上。通过轮询扫描的方式,逐个检测按键的状态。当有按键按下时,通过判断对应的行和列,可以确定按下的是哪一个按键。
一般来说:
行扫描,行线为低电平,列线为高电平。(就STC89C5类型的机子来说,一般都是用列扫描,以为行线端口有可能被其他的引脚占用(复用)(B站江协大的教学视频中的51单片机不建议用行扫描))
列扫描,行线为高电平,列线为低电平。
优点:
矩阵按键行列扫描法简单易懂,实现成本较低,适用于较小规模的键盘矩阵。
缺点:
随着键盘规模的增大,扫描效率会降低,因为需要逐一扫描每个按键。
矩阵按键行列扫描法(假设使用GPIO控制引脚):
#include <stdio.h>
#include <stdbool.h>// 定义行列数量
#define NUM_ROWS 4
#define NUM_COLS 4// 定义行列引脚
int rows[NUM_ROWS] = {R0, R1, R2, R3}; //数组中为具体引脚
int cols[NUM_COLS] = {C0, C1, C2, C3}; //数组中为具体引脚// 初始化引脚状态
void setup() {
for (int i = 0; i < NUM_ROWS; i++) {
pinMode(rows[i], OUTPUT); //行配置为输出模式,
digitalWrite(rows[i], LOW); //行初始化电平为低电平
}
for (int i = 0; i < NUM_COLS; i++) {
pinMode(cols[i], INPUT_PULLUP); //列配置为输入模式、上拉
}
}// 扫描键盘状态
void scanKeypad() {
for (int col = 0; col < NUM_COLS; col++) {
// 将当前列引脚设置为低电平
digitalWrite(cols[col], LOW);
for (int row = 0; row < NUM_ROWS; row++) {
// 检测行引脚状态,如果为低电平,则说明该按键被按下
if (digitalRead(rows[row]) == LOW) {
// 在这里处理按键按下的逻辑
// ...
}
}
// 将当前列引脚设置为高电平,准备扫描下一列
digitalWrite(cols[col], HIGH);
}
}int main() {
setup();
while (true) {
scanKeypad();
}
return 0;
2. 反转扫描法
原理:
反转扫描法是另一种常见的按键扫描方法。在反转扫描法中,将行和列的引脚分开连接,行引脚设置为输出,列引脚设置为输入。然后,将行引脚逐个设置为高电平,检测列引脚的状态来确定按键的状态。
优点:
反转扫描法的扫描效率相对较高,适用于大规模的键盘矩阵,因为只需逐一扫描行引脚。
缺点:
相比矩阵按键行列扫描法,反转扫描法在硬件连接上稍微复杂一些。
反转扫描法(假设使用GPIO控制引脚):
#include <stdio.h>
#include <stdbool.h>// 定义行列数量
#define NUM_ROWS 4
#define NUM_COLS 4// 定义行列引脚
int rows[NUM_ROWS] = {R0, R1, R2, R3};
int cols[NUM_COLS] = {C0, C1, C2, C3};// 初始化引脚状态
void setup() {
for (int i = 0; i < NUM_COLS; i++) {
pinMode(cols[i], OUTPUT);
digitalWrite(cols[i], HIGH);
}
for (int i = 0; i < NUM_ROWS; i++) {
pinMode(rows[i], INPUT_PULLUP);
}
}// 扫描键盘状态
void scanKeypad() {
for (int row = 0; row < NUM_ROWS; row++) {
// 将当前行引脚设置为低电平
digitalWrite(rows[row], LOW);
for (int col = 0; col < NUM_COLS; col++) {
// 检测列引脚状态,如果为低电平,则说明该按键被按下
if (digitalRead(cols[col]) == LOW) {
// 在这里处理按键按下的逻辑
// ...
}
}
// 将当前行引脚设置为高电平,准备扫描下一行
digitalWrite(rows[row], HIGH);
}
}int main() {
setup();
while (true) {
scanKeypad();
}
return 0;
}
实例:
有一51矩阵按键的原理图如下:
#define KEY_MATRIX_PORT P1
//行列选中法:
unsigned char key_matrix_ranks_scan(void)
{
unsigned char key_value=0;KEY_MATRIX_PORT=0xf7;//给第一列赋值0
if(KEY_MATRIX_PORT!=0xf7)//判断第一列按键是否按下
{
delay_10us(1000);//消抖
switch(KEY_MATRIX_PORT)//保存第一列按键按下后的键值
{
case 0x77: key_value=1;break;
case 0xb7: key_value=5;break;
case 0xd7: key_value=9;break;
case 0xe7: key_value=13;break;
}
}
while(KEY_MATRIX_PORT!=0xf7);//等待按键松开
KEY_MATRIX_PORT=0xfb;//给第二列赋值0,其余全为1
if(KEY_MATRIX_PORT!=0xfb)//判断第二列按键是否按下
{
delay_10us(1000);//消抖
switch(KEY_MATRIX_PORT)//保存第二列按键按下后的键值
{
case 0x7b: key_value=2;break;
case 0xbb: key_value=6;break;
case 0xdb: key_value=10;break;
case 0xeb: key_value=14;break;
}
}
while(KEY_MATRIX_PORT!=0xfb);//等待按键松开
KEY_MATRIX_PORT=0xfd;//给第三列赋值0,其余全为1
if(KEY_MATRIX_PORT!=0xfd)//判断第三列按键是否按下
{
delay_10us(1000);//消抖
switch(KEY_MATRIX_PORT)//保存第三列按键按下后的键值
{
case 0x7d: key_value=3;break;
case 0xbd: key_value=7;break;
case 0xdd: key_value=11;break;
case 0xed: key_value=15;break;
}
}
while(KEY_MATRIX_PORT!=0xfd);//等待按键松开
KEY_MATRIX_PORT=0xfe;//给第四列赋值0,其余全为1
if(KEY_MATRIX_PORT!=0xfe)//判断第四列按键是否按下
{
delay_10us(1000);//消抖
switch(KEY_MATRIX_PORT)//保存第四列按键按下后的键值
{
case 0x7e: key_value=4;break;
case 0xbe: key_value=8;break;
case 0xde: key_value=12;break;
case 0xee: key_value=16;break;
}
}
while(KEY_MATRIX_PORT!=0xfe);//等待按键松开
return key_value;
}//线反转法:
unsigned char key_matrix_flip_scan(void)
{
static unsigned char key_value=0;KEY_MATRIX_PORT=0x0f;//给所有行赋值0,列全为1
if(KEY_MATRIX_PORT!=0x0f)//判断按键是否按下
{
delay_10us(1000);//消抖
if(KEY_MATRIX_PORT!=0x0f)
{
//测试列
KEY_MATRIX_PORT=0x0f;
switch(KEY_MATRIX_PORT)//保存行为0,按键按下后的列值
{
case 0x07: key_value=1;break;
case 0x0b: key_value=2;break;
case 0x0d: key_value=3;break;
case 0x0e: key_value=4;break;
}
//测试行
KEY_MATRIX_PORT=0xf0;
switch(KEY_MATRIX_PORT)//保存列为0,按键按下后的键值
{
case 0x70: key_value=key_value;break;
case 0xb0: key_value=key_value+4;break;
case 0xd0: key_value=key_value+8;break;
case 0xe0: key_value=key_value+12;break;
}
while(KEY_MATRIX_PORT!=0xf0);//等待按键松开
}
}
else
key_value=0;
return key_value;
}