矩阵按键行列扫描法与反转扫描法:原理、代码实现

本文介绍了矩阵按键的行列扫描法和反转扫描法,包括原理、优缺点,以及如何在C语言代码中实现,如使用GPIO控制引脚和处理按键按下逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

矩阵按键:行列扫描法与反转扫描法
通常情况下,按键按下时会产生低电平信号,按键一般用低电平表示按下状态。

当按键没有被按下时,通常处于高电平状态,这是因为按键连接到电路时,内部的上拉电阻或外部的上拉电阻会将按键未按下的状态拉高为高电平。当按键被按下时,按键内部会导通,使连接到电路的引脚处于低电平状态,从而表示按键被按下。

这种低电平表示按下的设计方式是常见的,因为在数字电路中,低电平通常被认为是逻辑“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;        
}

 


 

### FreeRTOS 中实现按键矩阵 在嵌入式系统中,使用 FreeRTOS 实现按键矩阵能够有效管理多任务操作并提高系统的响应速度。为了实现这一目标,通常会创建一个专门的任务来负责扫描按键矩阵,并处理相应的按键事件。 #### 创建按键扫描任务 可以通过 `xTaskCreate` 函数创建一个新的任务来进行按键扫描: ```c void matrix_key_scan_task(void *pvParameters) { while (1) { // 执行按键扫描逻辑 scan_matrix_keys(); // 处理检测到的按键事件 process_key_events(); // 延迟一段时间再继续扫描 vTaskDelay(pdMS_TO_TICKS(50)); } } // 在初始化函数中启动该任务 BaseType_t xReturned; xReturned = xTaskCreate( matrix_key_scan_task, /* Task function */ "Matrix Key Scan", /* Name of task */ configMINIMAL_STACK_SIZE, /* Stack size in words */ NULL, /* Parameter passed into task */ tskIDLE_PRIORITY + 1, /* Priority at which the task is created */ NULL /* Used to pass out the handle by reference */ ); ``` #### 按键扫描去抖动 对于按键矩阵来说,重要的是要考虑到机械开关存在的弹跳现象。因此,在实际编写 `scan_matrix_keys()` 函数时应该加入软件消抖措施[^2]。 ```c #define DEBOUNCE_DELAY_MS 20 static uint8_t debounce_counter[MAX_ROWS][MAX_COLS]; bool read_and_debounce(uint8_t row, uint8_t col) { static bool last_state[MAX_ROWS][MAX_COLS]; bool current_state = !digitalRead(getPinFor(row, col)); // Active low if(current_state != last_state[row][col]) { debounce_counter[row][col] = DEBOUNCE_DELAY_MS / portTICK_PERIOD_MS; last_state[row][col] = current_state; return false; // Ignore transient state changes during debouncing period. } if(debounce_counter[row][col] > 0){ debounce_counter[row][col]--; return false; // Still within initial bounce window. } return current_state; } ``` #### 使用队列传递按键事件 为了让其他部分程序能及时得知按键状态的变化,可以利用 FreeRTOS 的消息队列机制将按键事件发送给感兴趣的模块: ```c QueueHandle_t key_event_queue; typedef struct KeyEvent { int row; int column; } KeyEvent_t; void sendKeyEvent(int r, int c) { KeyEvent_t event = {r, c}; BaseType_t xHigherPriorityTaskWoken = pdFALSE; xQueueSendToBackFromISR(key_event_queue, &event, &xHigherPriorityTaskWoken); } ``` 上述代码片段展示了如何构建基于 FreeRTOS 的按键矩阵解决方案框架。具体实现细节可能因硬件平台不同而有所差异,比如这里提到的 GPIO 配置方式可能会依据所使用的微控制器有所不同[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会创建对象

谢谢考官。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值