STM32矩阵键盘驱动(库函数版)实现

4x4矩阵键盘驱动代码,包含扫描、去抖、多键检测、长按短按识别等功能,适用于STM32全系列。

一、硬件连接与原理

1.1 4x4矩阵键盘连接

复制代码
行线(输出):Row0-Row3
列线(输入):Col0-Col3

STM32连接示例(GPIOB):
行线:Row0-PB0, Row1-PB1, Row2-PB2, Row3-PB3
列线:Col0-PB4, Col1-PB5, Col2-PB6, Col3-PB7

内部上拉电阻:列线启用内部上拉

1.2 扫描原理

复制代码
逐行扫描法:
1. 设置当前行为低电平,其他行为高电平
2. 读取所有列线状态
3. 如果有列线为低电平,说明对应交叉点的按键被按下
4. 循环扫描4行

二、核心代码实现

2.1 头文件定义

c 复制代码
/**
 * @file matrix_keypad.h
 * @brief 4x4矩阵键盘驱动(STM32库函数版)
 */

#ifndef __MATRIX_KEYPAD_H
#define __MATRIX_KEYPAD_H

#include "stm32f1xx_hal.h"
#include <stdint.h>
#include <string.h>

/* 键盘配置 */
#define KEYPAD_ROWS     4
#define KEYPAD_COLS     4
#define KEY_BUFFER_SIZE 16

/* 按键值定义 */
typedef enum {
    KEY_NONE = 0,
    KEY_0 = '0', KEY_1 = '1', KEY_2 = '2', KEY_3 = '3',
    KEY_4 = '4', KEY_5 = '5', KEY_6 = '6', KEY_7 = '7',
    KEY_8 = '8', KEY_9 = '9', 
    KEY_A = 'A', KEY_B = 'B', KEY_C = 'C', KEY_D = 'D',
    KEY_STAR = '*', KEY_HASH = '#',
    KEY_UP = 0x80, KEY_DOWN, KEY_LEFT, KEY_RIGHT,  // 方向键
    KEY_OK, KEY_CANCEL, KEY_MENU, KEY_BACK
} KeyCode;

/* 按键状态 */
typedef enum {
    KEY_STATE_IDLE = 0,     // 空闲
    KEY_STATE_PRESSED,      // 按下
    KEY_STATE_HOLD,         // 保持
    KEY_STATE_RELEASED      // 释放
} KeyState;

/* 按键事件 */
typedef enum {
    KEY_EVENT_NONE = 0,     // 无事件
    KEY_EVENT_PRESS,        // 按下事件
    KEY_EVENT_RELEASE,      // 释放事件
    KEY_EVENT_SHORT_PRESS,  // 短按事件
    KEY_EVENT_LONG_PRESS,   // 长按事件
    KEY_EVENT_REPEAT        // 重复按下事件
} KeyEvent;

/* 按键结构体 */
typedef struct {
    KeyCode code;           // 按键编码
    KeyState state;         // 当前状态
    KeyEvent event;         // 当前事件
    uint32_t press_time;    // 按下时间戳
    uint32_t hold_time;     // 保持时间
    uint8_t repeat_count;   // 重复次数
} KeyInfo;

/* 键盘配置结构体 */
typedef struct {
    GPIO_TypeDef* row_port[KEYPAD_ROWS];   // 行线端口
    uint16_t row_pin[KEYPAD_ROWS];         // 行线引脚
    
    GPIO_TypeDef* col_port[KEYPAD_COLS];   // 列线端口
    uint16_t col_pin[KEYPAD_COLS];         // 列线引脚
    
    uint16_t debounce_time;    // 去抖时间(ms)
    uint16_t long_press_time;  // 长按时间(ms)
    uint16_t repeat_delay;     // 重复延迟(ms)
    uint16_t repeat_rate;      // 重复速率(ms)
} Keypad_Config;

/* 键盘句柄 */
typedef struct {
    Keypad_Config config;      // 配置参数
    KeyInfo keys[KEYPAD_ROWS * KEYPAD_COLS];  // 按键状态数组
    KeyCode key_map[KEYPAD_ROWS][KEYPAD_COLS]; // 键值映射表
    uint8_t key_buffer[KEY_BUFFER_SIZE];       // 键值缓冲区
    uint8_t buffer_head;       // 缓冲区头
    uint8_t buffer_tail;       // 缓冲区尾
    uint32_t last_scan_time;   // 上次扫描时间
    uint8_t scan_enabled;      // 扫描使能
} Keypad_Handle;

/* 函数声明 */
void Keypad_Init(Keypad_Handle *hkeypad, Keypad_Config *config);
void Keypad_Scan(Keypad_Handle *hkeypad);
uint8_t Keypad_GetKey(Keypad_Handle *hkeypad, KeyCode *key);
uint8_t Keypad_GetKeyEvent(Keypad_Handle *hkeypad, KeyCode *key, KeyEvent *event);
uint8_t Keypad_IsKeyPressed(Keypad_Handle *hkeypad, KeyCode key);
uint8_t Keypad_IsKeyReleased(Keypad_Handle *hkeypad, KeyCode key);
uint8_t Keypad_GetKeyHoldTime(Keypad_Handle *hkeypad, KeyCode key, uint32_t *time);
void Keypad_ClearBuffer(Keypad_Handle *hkeypad);
uint8_t Keypad_BufferFull(Keypad_Handle *hkeypad);
uint8_t Keypad_BufferEmpty(Keypad_Handle *hkeypad);

#endif /* __MATRIX_KEYPAD_H */

2.2 键盘驱动实现

c 复制代码
/**
 * @file matrix_keypad.c
 * @brief 4x4矩阵键盘驱动实现
 */

#include "matrix_keypad.h"

/* 默认键值映射 */
static const KeyCode default_keymap[KEYPAD_ROWS][KEYPAD_COLS] = {
    {KEY_1, KEY_2, KEY_3, KEY_A},
    {KEY_4, KEY_5, KEY_6, KEY_B},
    {KEY_7, KEY_8, KEY_9, KEY_C},
    {KEY_STAR, KEY_0, KEY_HASH, KEY_D}
};

/**
 * @brief 初始化矩阵键盘
 * @param hkeypad 键盘句柄
 * @param config 配置参数
 */
void Keypad_Init(Keypad_Handle *hkeypad, Keypad_Config *config)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    /* 复制配置参数 */
    memcpy(&hkeypad->config, config, sizeof(Keypad_Config));
    
    /* 初始化按键状态数组 */
    for(int i = 0; i < KEYPAD_ROWS * KEYPAD_COLS; i++)
    {
        hkeypad->keys[i].code = KEY_NONE;
        hkeypad->keys[i].state = KEY_STATE_IDLE;
        hkeypad->keys[i].event = KEY_EVENT_NONE;
        hkeypad->keys[i].press_time = 0;
        hkeypad->keys[i].hold_time = 0;
        hkeypad->keys[i].repeat_count = 0;
    }
    
    /* 初始化键值映射表 */
    memcpy(hkeypad->key_map, default_keymap, sizeof(default_keymap));
    
    /* 初始化缓冲区 */
    memset(hkeypad->key_buffer, 0, KEY_BUFFER_SIZE);
    hkeypad->buffer_head = 0;
    hkeypad->buffer_tail = 0;
    
    /* 初始化扫描状态 */
    hkeypad->last_scan_time = 0;
    hkeypad->scan_enabled = 1;
    
    /* 初始化行线为推挽输出 */
    for(int i = 0; i < KEYPAD_ROWS; i++)
    {
        if(config->row_port[i] != NULL)
        {
            /* 使能GPIO时钟 */
            if(config->row_port[i] == GPIOA) __HAL_RCC_GPIOA_CLK_ENABLE();
            else if(config->row_port[i] == GPIOB) __HAL_RCC_GPIOB_CLK_ENABLE();
            else if(config->row_port[i] == GPIOC) __HAL_RCC_GPIOC_CLK_ENABLE();
            else if(config->row_port[i] == GPIOD) __HAL_RCC_GPIOD_CLK_ENABLE();
            else if(config->row_port[i] == GPIOE) __HAL_RCC_GPIOE_CLK_ENABLE();
            
            GPIO_InitStruct.Pin = config->row_pin[i];
            GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
            GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
            HAL_GPIO_Init(config->row_port[i], &GPIO_InitStruct);
            
            /* 默认输出高电平(按键未按下) */
            HAL_GPIO_WritePin(config->row_port[i], config->row_pin[i], GPIO_PIN_SET);
        }
    }
    
    /* 初始化列线为输入上拉 */
    for(int i = 0; i < KEYPAD_COLS; i++)
    {
        if(config->col_port[i] != NULL)
        {
            /* 使能GPIO时钟 */
            if(config->col_port[i] == GPIOA) __HAL_RCC_GPIOA_CLK_ENABLE();
            else if(config->col_port[i] == GPIOB) __HAL_RCC_GPIOB_CLK_ENABLE();
            else if(config->col_port[i] == GPIOC) __HAL_RCC_GPIOC_CLK_ENABLE();
            else if(config->col_port[i] == GPIOD) __HAL_RCC_GPIOD_CLK_ENABLE();
            else if(config->col_port[i] == GPIOE) __HAL_RCC_GPIOE_CLK_ENABLE();
            
            GPIO_InitStruct.Pin = config->col_pin[i];
            GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
            GPIO_InitStruct.Pull = GPIO_PULLUP;
            HAL_GPIO_Init(config->col_port[i], &GPIO_InitStruct);
        }
    }
}

/**
 * @brief 扫描矩阵键盘(需要在主循环中定期调用)
 * @param hkeypad 键盘句柄
 */
void Keypad_Scan(Keypad_Handle *hkeypad)
{
    uint8_t row, col;
    uint32_t current_time = HAL_GetTick();
    
    /* 检查扫描间隔 */
    if((current_time - hkeypad->last_scan_time) < 10)  // 10ms扫描一次
        return;
    
    hkeypad->last_scan_time = current_time;
    
    if(!hkeypad->scan_enabled)
        return;
    
    /* 逐行扫描 */
    for(row = 0; row < KEYPAD_ROWS; row++)
    {
        if(hkeypad->config.row_port[row] == NULL)
            continue;
            
        /* 设置当前行为低电平,其他行为高电平 */
        for(uint8_t r = 0; r < KEYPAD_ROWS; r++)
        {
            if(hkeypad->config.row_port[r] != NULL)
            {
                if(r == row)
                    HAL_GPIO_WritePin(hkeypad->config.row_port[r], 
                                     hkeypad->config.row_pin[r], GPIO_PIN_RESET);
                else
                    HAL_GPIO_WritePin(hkeypad->config.row_port[r], 
                                     hkeypad->config.row_pin[r], GPIO_PIN_SET);
            }
        }
        
        /* 延时等待稳定(可选) */
        // for(volatile int i = 0; i < 10; i++);
        
        /* 读取列线状态 */
        for(col = 0; col < KEYPAD_COLS; col++)
        {
            if(hkeypad->config.col_port[col] == NULL)
                continue;
                
            /* 计算按键索引 */
            uint8_t key_index = row * KEYPAD_COLS + col;
            KeyCode key_code = hkeypad->key_map[row][col];
            
            /* 读取列线状态(低电平表示按下) */
            GPIO_PinState pin_state = HAL_GPIO_ReadPin(hkeypad->config.col_port[col], 
                                                      hkeypad->config.col_pin[col]);
            
            /* 更新按键状态 */
            KeyInfo *key = &hkeypad->keys[key_index];
            
            if(pin_state == GPIO_PIN_RESET)  // 按键按下
            {
                switch(key->state)
                {
                    case KEY_STATE_IDLE:
                        /* 首次检测到按下 */
                        key->state = KEY_STATE_PRESSED;
                        key->event = KEY_EVENT_PRESS;
                        key->press_time = current_time;
                        key->hold_time = 0;
                        key->repeat_count = 0;
                        
                        /* 存入缓冲区 */
                        if(!Keypad_BufferFull(hkeypad))
                        {
                            hkeypad->key_buffer[hkeypad->buffer_head] = key_code;
                            hkeypad->buffer_head = (hkeypad->buffer_head + 1) % KEY_BUFFER_SIZE;
                        }
                        break;
                        
                    case KEY_STATE_PRESSED:
                        /* 去抖处理 */
                        if((current_time - key->press_time) > hkeypad->config.debounce_time)
                        {
                            key->state = KEY_STATE_HOLD;
                            key->event = KEY_EVENT_NONE;
                            
                            /* 计算保持时间 */
                            key->hold_time = current_time - key->press_time;
                            
                            /* 检查长按 */
                            if(key->hold_time > hkeypad->config.long_press_time)
                            {
                                key->event = KEY_EVENT_LONG_PRESS;
                                
                                /* 检查重复按下 */
                                if(key->repeat_count == 0 || 
                                   (current_time - key->press_time) > 
                                   (hkeypad->config.repeat_delay + 
                                    key->repeat_count * hkeypad->config.repeat_rate))
                                {
                                    key->repeat_count++;
                                    key->event = KEY_EVENT_REPEAT;
                                    
                                    /* 存入缓冲区 */
                                    if(!Keypad_BufferFull(hkeypad))
                                    {
                                        hkeypad->key_buffer[hkeypad->buffer_head] = key_code;
                                        hkeypad->buffer_head = (hkeypad->buffer_head + 1) % KEY_BUFFER_SIZE;
                                    }
                                }
                            }
                        }
                        break;
                        
                    case KEY_STATE_HOLD:
                        key->hold_time = current_time - key->press_time;
                        key->event = KEY_EVENT_NONE;
                        break;
                        
                    case KEY_STATE_RELEASED:
                        key->state = KEY_STATE_PRESSED;
                        key->event = KEY_EVENT_PRESS;
                        key->press_time = current_time;
                        break;
                }
            }
            else  // 按键释放
            {
                switch(key->state)
                {
                    case KEY_STATE_PRESSED:
                    case KEY_STATE_HOLD:
                        /* 检查是否为短按 */
                        if((current_time - key->press_time) < hkeypad->config.long_press_time)
                        {
                            key->event = KEY_EVENT_SHORT_PRESS;
                        }
                        else
                        {
                            key->event = KEY_EVENT_RELEASE;
                        }
                        
                        key->state = KEY_STATE_RELEASED;
                        key->hold_time = 0;
                        break;
                        
                    case KEY_STATE_RELEASED:
                        /* 释放后回到空闲状态 */
                        if((current_time - key->press_time) > 50)  // 50ms防抖
                        {
                            key->state = KEY_STATE_IDLE;
                            key->event = KEY_EVENT_NONE;
                        }
                        break;
                }
            }
        }
    }
    
    /* 所有行恢复高电平 */
    for(row = 0; row < KEYPAD_ROWS; row++)
    {
        if(hkeypad->config.row_port[row] != NULL)
        {
            HAL_GPIO_WritePin(hkeypad->config.row_port[row], 
                             hkeypad->config.row_pin[row], GPIO_PIN_SET);
        }
    }
}

/**
 * @brief 从缓冲区获取按键值
 * @param hkeypad 键盘句柄
 * @param key 返回的按键值
 * @return 1:成功, 0:缓冲区为空
 */
uint8_t Keypad_GetKey(Keypad_Handle *hkeypad, KeyCode *key)
{
    if(Keypad_BufferEmpty(hkeypad))
        return 0;
    
    *key = hkeypad->key_buffer[hkeypad->buffer_tail];
    hkeypad->buffer_tail = (hkeypad->buffer_tail + 1) % KEY_BUFFER_SIZE;
    
    return 1;
}

/**
 * @brief 获取按键事件
 * @param hkeypad 键盘句柄
 * @param key 按键编码
 * @param event 按键事件
 * @return 1:有事件, 0:无事件
 */
uint8_t Keypad_GetKeyEvent(Keypad_Handle *hkeypad, KeyCode *key, KeyEvent *event)
{
    /* 查找有事件的按键 */
    for(int i = 0; i < KEYPAD_ROWS * KEYPAD_COLS; i++)
    {
        if(hkeypad->keys[i].event != KEY_EVENT_NONE)
        {
            *key = hkeypad->keys[i].code;
            *event = hkeypad->keys[i].event;
            
            /* 清除事件标志 */
            hkeypad->keys[i].event = KEY_EVENT_NONE;
            
            return 1;
        }
    }
    
    return 0;
}

/**
 * @brief 检查指定按键是否按下
 * @param hkeypad 键盘句柄
 * @param key 要检查的按键
 * @return 1:按下, 0:未按下
 */
uint8_t Keypad_IsKeyPressed(Keypad_Handle *hkeypad, KeyCode key)
{
    for(int i = 0; i < KEYPAD_ROWS * KEYPAD_COLS; i++)
    {
        if(hkeypad->keys[i].code == key && 
           (hkeypad->keys[i].state == KEY_STATE_PRESSED || 
            hkeypad->keys[i].state == KEY_STATE_HOLD))
        {
            return 1;
        }
    }
    
    return 0;
}

/**
 * @brief 检查指定按键是否释放
 * @param hkeypad 键盘句柄
 * @param key 要检查的按键
 * @return 1:释放, 0:未释放
 */
uint8_t Keypad_IsKeyReleased(Keypad_Handle *hkeypad, KeyCode key)
{
    for(int i = 0; i < KEYPAD_ROWS * KEYPAD_COLS; i++)
    {
        if(hkeypad->keys[i].code == key && 
           hkeypad->keys[i].state == KEY_STATE_RELEASED)
        {
            return 1;
        }
    }
    
    return 0;
}

/**
 * @brief 获取按键保持时间
 * @param hkeypad 键盘句柄
 * @param key 按键编码
 * @param time 返回保持时间(ms)
 * @return 1:成功, 0:按键未按下
 */
uint8_t Keypad_GetKeyHoldTime(Keypad_Handle *hkeypad, KeyCode key, uint32_t *time)
{
    for(int i = 0; i < KEYPAD_ROWS * KEYPAD_COLS; i++)
    {
        if(hkeypad->keys[i].code == key && 
           hkeypad->keys[i].state == KEY_STATE_HOLD)
        {
            *time = hkeypad->keys[i].hold_time;
            return 1;
        }
    }
    
    return 0;
}

/**
 * @brief 清空按键缓冲区
 * @param hkeypad 键盘句柄
 */
void Keypad_ClearBuffer(Keypad_Handle *hkeypad)
{
    hkeypad->buffer_head = 0;
    hkeypad->buffer_tail = 0;
    memset(hkeypad->key_buffer, 0, KEY_BUFFER_SIZE);
}

/**
 * @brief 检查缓冲区是否已满
 * @param hkeypad 键盘句柄
 * @return 1:已满, 0:未满
 */
uint8_t Keypad_BufferFull(Keypad_Handle *hkeypad)
{
    return ((hkeypad->buffer_head + 1) % KEY_BUFFER_SIZE) == hkeypad->buffer_tail;
}

/**
 * @brief 检查缓冲区是否为空
 * @param hkeypad 键盘句柄
 * @return 1:为空, 0:非空
 */
uint8_t Keypad_BufferEmpty(Keypad_Handle *hkeypad)
{
    return hkeypad->buffer_head == hkeypad->buffer_tail;
}

三、高级功能扩展

3.1 多键组合检测

c 复制代码
/**
 * @file keypad_combo.c
 * @brief 键盘组合键检测
 */

#include "matrix_keypad.h"

#define MAX_COMBO_KEYS   3
#define COMBO_TIMEOUT    1000  // 组合键超时时间(ms)

/* 组合键定义 */
typedef struct {
    KeyCode keys[MAX_COMBO_KEYS];  // 按键序列
    uint8_t key_count;             // 按键数量
    uint32_t last_time;            // 上次按键时间
    uint8_t matched;               // 已匹配数量
} ComboDetector;

/* 全局组合键检测器 */
ComboDetector combo_detector = {0};

/* 预定义组合键 */
typedef enum {
    COMBO_NONE = 0,
    COMBO_RESET,        // *#0
    COMBO_MENU,         #123#
    COMBO_DEBUG,        #789*
} ComboType;

/**
 * @brief 初始化组合键检测
 */
void Combo_Init(void)
{
    memset(&combo_detector, 0, sizeof(combo_detector));
}

/**
 * @brief 检测组合键
 * @param hkeypad 键盘句柄
 * @return 检测到的组合键类型
 */
ComboType Combo_Detect(Keypad_Handle *hkeypad)
{
    KeyCode key;
    uint32_t current_time = HAL_GetTick();
    
    /* 获取新按键 */
    if(Keypad_GetKey(hkeypad, &key))
    {
        /* 检查超时 */
        if((current_time - combo_detector.last_time) > COMBO_TIMEOUT)
        {
            /* 超时重置 */
            memset(&combo_detector, 0, sizeof(combo_detector));
        }
        
        /* 记录按键 */
        if(combo_detector.matched < MAX_COMBO_KEYS)
        {
            combo_detector.keys[combo_detector.matched++] = key;
            combo_detector.last_time = current_time;
            
            /* 检查预定义组合 */
            if(combo_detector.matched == 3)
            {
                /* 检查组合 *#0 */
                if(combo_detector.keys[0] == KEY_STAR &&
                   combo_detector.keys[1] == KEY_HASH &&
                   combo_detector.keys[2] == KEY_0)
                {
                    memset(&combo_detector, 0, sizeof(combo_detector));
                    return COMBO_RESET;
                }
                
                /* 检查组合 123# */
                if(combo_detector.keys[0] == KEY_1 &&
                   combo_detector.keys[1] == KEY_2 &&
                   combo_detector.keys[2] == KEY_3 &&
                   key == KEY_HASH)  // 第4个键
                {
                    memset(&combo_detector, 0, sizeof(combo_detector));
                    return COMBO_MENU;
                }
            }
        }
    }
    
    return COMBO_NONE;
}

3.2 按键宏定义

c 复制代码
/**
 * @file keypad_macro.c
 * @brief 键盘宏功能
 */

#include "matrix_keypad.h"

#define MAX_MACROS       10
#define MACRO_BUFFER_SIZE 32

/* 宏定义结构体 */
typedef struct {
    KeyCode trigger;                // 触发键
    KeyCode sequence[MACRO_BUFFER_SIZE];  // 宏序列
    uint8_t seq_len;                // 序列长度
    uint8_t enabled;                // 启用标志
} KeyMacro;

/* 宏定义数组 */
KeyMacro key_macros[MAX_MACROS] = {0};
uint8_t macro_count = 0;

/**
 * @brief 注册宏定义
 * @param trigger 触发键
 * @param sequence 宏序列
 * @param seq_len 序列长度
 * @return 宏ID,0xFF表示失败
 */
uint8_t Macro_Register(KeyCode trigger, KeyCode *sequence, uint8_t seq_len)
{
    if(macro_count >= MAX_MACROS || seq_len > MACRO_BUFFER_SIZE)
        return 0xFF;
    
    key_macros[macro_count].trigger = trigger;
    memcpy(key_macros[macro_count].sequence, sequence, seq_len);
    key_macros[macro_count].seq_len = seq_len;
    key_macros[macro_count].enabled = 1;
    
    return macro_count++;
}

/**
 * @brief 执行宏
 * @param hkeypad 键盘句柄
 * @param key 按下的键
 * @return 1:执行了宏, 0:未执行
 */
uint8_t Macro_Execute(Keypad_Handle *hkeypad, KeyCode key)
{
    for(int i = 0; i < macro_count; i++)
    {
        if(key_macros[i].enabled && key_macros[i].trigger == key)
        {
            /* 将宏序列存入缓冲区 */
            for(int j = 0; j < key_macros[i].seq_len; j++)
            {
                if(!Keypad_BufferFull(hkeypad))
                {
                    hkeypad->key_buffer[hkeypad->buffer_head] = key_macros[i].sequence[j];
                    hkeypad->buffer_head = (hkeypad->buffer_head + 1) % KEY_BUFFER_SIZE;
                }
            }
            return 1;
        }
    }
    
    return 0;
}

四、应用示例

4.1 简单密码锁示例

c 复制代码
/**
 * @file keypad_example.c
 * @brief 矩阵键盘应用示例 - 密码锁
 */

#include "main.h"
#include "matrix_keypad.h"
#include "lcd.h"

/* 硬件定义 */
#define KEYPAD_ROW0_PORT  GPIOB
#define KEYPAD_ROW0_PIN   GPIO_PIN_0
#define KEYPAD_ROW1_PORT  GPIOB
#define KEYPAD_ROW1_PIN   GPIO_PIN_1
#define KEYPAD_ROW2_PORT  GPIOB
#define KEYPAD_ROW2_PIN   GPIO_PIN_2
#define KEYPAD_ROW3_PORT  GPIOB
#define KEYPAD_ROW3_PIN   GPIO_PIN_3

#define KEYPAD_COL0_PORT  GPIOB
#define KEYPAD_COL0_PIN   GPIO_PIN_4
#define KEYPAD_COL1_PORT  GPIOB
#define KEYPAD_COL1_PIN   GPIO_PIN_5
#define KEYPAD_COL2_PORT  GPIOB
#define KEYPAD_COL2_PIN   GPIO_PIN_6
#define KEYPAD_COL3_PORT  GPIOB
#define KEYPAD_COL3_PIN   GPIO_PIN_7

/* 密码设置 */
#define PASSWORD_LENGTH   6
const char correct_password[PASSWORD_LENGTH + 1] = "123456";
char input_password[PASSWORD_LENGTH + 1] = {0};
uint8_t password_index = 0;

/* 键盘句柄 */
Keypad_Handle hkeypad;
Keypad_Config keypad_config;

/**
 * @brief 初始化键盘配置
 */
void Keypad_Config_Init(void)
{
    /* 配置行线 */
    keypad_config.row_port[0] = KEYPAD_ROW0_PORT;
    keypad_config.row_pin[0] = KEYPAD_ROW0_PIN;
    keypad_config.row_port[1] = KEYPAD_ROW1_PORT;
    keypad_config.row_pin[1] = KEYPAD_ROW1_PIN;
    keypad_config.row_port[2] = KEYPAD_ROW2_PORT;
    keypad_config.row_pin[2] = KEYPAD_ROW2_PIN;
    keypad_config.row_port[3] = KEYPAD_ROW3_PORT;
    keypad_config.row_pin[3] = KEYPAD_ROW3_PIN;
    
    /* 配置列线 */
    keypad_config.col_port[0] = KEYPAD_COL0_PORT;
    keypad_config.col_pin[0] = KEYPAD_COL0_PIN;
    keypad_config.col_port[1] = KEYPAD_COL1_PORT;
    keypad_config.col_pin[1] = KEYPAD_COL1_PIN;
    keypad_config.col_port[2] = KEYPAD_COL2_PORT;
    keypad_config.col_pin[2] = KEYPAD_COL2_PIN;
    keypad_config.col_port[3] = KEYPAD_COL3_PORT;
    keypad_config.col_pin[3] = KEYPAD_COL3_PIN;
    
    /* 配置参数 */
    keypad_config.debounce_time = 20;     // 20ms去抖
    keypad_config.long_press_time = 1000; // 1秒长按
    keypad_config.repeat_delay = 500;     // 500ms重复延迟
    keypad_config.repeat_rate = 200;      // 200ms重复速率
}

/**
 * @brief 密码锁主程序
 */
void PasswordLock_Example(void)
{
    KeyCode key;
    KeyEvent event;
    uint8_t authenticated = 0;
    
    /* 初始化LCD */
    LCD_Init();
    LCD_Clear();
    
    /* 初始化键盘 */
    Keypad_Config_Init();
    Keypad_Init(&hkeypad, &keypad_config);
    
    /* 显示欢迎信息 */
    LCD_SetCursor(0, 0);
    LCD_PrintString("Password Lock");
    LCD_SetCursor(1, 0);
    LCD_PrintString("Enter Code: ");
    
    while(1)
    {
        /* 扫描键盘 */
        Keypad_Scan(&hkeypad);
        
        /* 处理按键事件 */
        if(Keypad_GetKeyEvent(&hkeypad, &key, &event))
        {
            if(event == KEY_EVENT_SHORT_PRESS)
            {
                /* 数字键处理 */
                if(key >= '0' && key <= '9')
                {
                    if(password_index < PASSWORD_LENGTH)
                    {
                        input_password[password_index] = (char)key;
                        password_index++;
                        
                        /* 显示星号 */
                        LCD_SetCursor(1, 12 + password_index - 1);
                        LCD_PrintChar('*');
                    }
                }
                /* 确认键 (#) */
                else if(key == KEY_HASH)
                {
                    if(password_index == PASSWORD_LENGTH)
                    {
                        input_password[password_index] = '\0';
                        
                        /* 验证密码 */
                        if(strcmp(input_password, correct_password) == 0)
                        {
                            LCD_Clear();
                            LCD_SetCursor(0, 0);
                            LCD_PrintString("Access Granted!");
                            authenticated = 1;
                            
                            /* 控制门锁等 */
                            HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
                            HAL_Delay(2000);
                            HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
                        }
                        else
                        {
                            LCD_Clear();
                            LCD_SetCursor(0, 0);
                            LCD_PrintString("Access Denied!");
                            HAL_Delay(2000);
                        }
                        
                        /* 重置输入 */
                        password_index = 0;
                        memset(input_password, 0, sizeof(input_password));
                        
                        /* 重新显示界面 */
                        if(!authenticated)
                        {
                            LCD_Clear();
                            LCD_SetCursor(0, 0);
                            LCD_PrintString("Password Lock");
                            LCD_SetCursor(1, 0);
                            LCD_PrintString("Enter Code: ");
                        }
                    }
                }
                /* 清除键 (*) */
                else if(key == KEY_STAR)
                {
                    if(password_index > 0)
                    {
                        password_index--;
                        input_password[password_index] = '\0';
                        
                        /* 清除显示 */
                        LCD_SetCursor(1, 12 + password_index);
                        LCD_PrintChar(' ');
                    }
                }
            }
            /* 长按清除键清除所有输入 */
            else if(event == KEY_EVENT_LONG_PRESS && key == KEY_STAR)
            {
                password_index = 0;
                memset(input_password, 0, sizeof(input_password));
                
                LCD_SetCursor(1, 12);
                LCD_PrintString("      ");
                LCD_SetCursor(1, 12);
            }
        }
        
        HAL_Delay(10);
    }
}

4.2 菜单导航示例

c 复制代码
/**
 * @brief 菜单系统示例
 */
void MenuSystem_Example(void)
{
    KeyCode key;
    KeyEvent event;
    uint8_t menu_index = 0;
    const char *menu_items[] = {
        "1. System Info",
        "2. Settings",
        "3. Calibration",
        "4. Test Mode",
        "5. Exit"
    };
    uint8_t menu_count = sizeof(menu_items) / sizeof(menu_items[0]);
    
    /* 初始化LCD */
    LCD_Init();
    LCD_Clear();
    
    /* 初始化键盘 */
    Keypad_Config_Init();
    Keypad_Init(&hkeypad, &keypad_config);
    
    /* 显示菜单 */
    LCD_SetCursor(0, 0);
    LCD_PrintString("Main Menu:");
    LCD_SetCursor(1, 0);
    LCD_PrintString(menu_items[menu_index]);
    
    while(1)
    {
        Keypad_Scan(&hkeypad);
        
        if(Keypad_GetKeyEvent(&hkeypad, &key, &event))
        {
            if(event == KEY_EVENT_SHORT_PRESS)
            {
                switch(key)
                {
                    case KEY_2:  // 下方向键
                    case KEY_8:  // 上方向键
                        if(key == KEY_2)  // 下
                        {
                            menu_index = (menu_index + 1) % menu_count;
                        }
                        else  // 上
                        {
                            menu_index = (menu_index == 0) ? menu_count - 1 : menu_index - 1;
                        }
                        
                        LCD_SetCursor(1, 0);
                        LCD_PrintString("                ");  // 清空行
                        LCD_SetCursor(1, 0);
                        LCD_PrintString(menu_items[menu_index]);
                        break;
                        
                    case KEY_5:  // 确认键
                        ExecuteMenuItem(menu_index);
                        break;
                        
                    case KEY_0:  // 返回键
                        LCD_Clear();
                        LCD_SetCursor(0, 0);
                        LCD_PrintString("Return to menu");
                        HAL_Delay(1000);
                        
                        LCD_Clear();
                        LCD_SetCursor(0, 0);
                        LCD_PrintString("Main Menu:");
                        LCD_SetCursor(1, 0);
                        LCD_PrintString(menu_items[menu_index]);
                        break;
                }
            }
        }
        
        HAL_Delay(10);
    }
}

/**
 * @brief 执行菜单项
 */
void ExecuteMenuItem(uint8_t index)
{
    LCD_Clear();
    
    switch(index)
    {
        case 0:  // System Info
            LCD_SetCursor(0, 0);
            LCD_PrintString("System Info:");
            LCD_SetCursor(1, 0);
            LCD_PrintString("Ver 1.0 2024");
            break;
            
        case 1:  // Settings
            LCD_SetCursor(0, 0);
            LCD_PrintString("Settings Menu");
            break;
            
        case 2:  // Calibration
            LCD_SetCursor(0, 0);
            LCD_PrintString("Calibration");
            break;
            
        case 3:  // Test Mode
            LCD_SetCursor(0, 0);
            LCD_PrintString("Test Mode");
            break;
            
        case 4:  // Exit
            LCD_SetCursor(0, 0);
            LCD_PrintString("Exiting...");
            HAL_Delay(1000);
            break;
    }
    
    HAL_Delay(2000);
    LCD_Clear();
}

参考代码 Stm32矩阵键盘程序(库函数) www.youwenfan.com/contentcsv/70644.html

五、中断扫描版本

5.1 定时器中断扫描

c 复制代码
/**
 * @file keypad_timer.c
 * @brief 定时器中断扫描键盘
 */

#include "main.h"
#include "matrix_keypad.h"

/* 定时器句柄 */
TIM_HandleTypeDef htim2;
Keypad_Handle hkeypad;

/**
 * @brief 初始化定时器扫描
 */
void Keypad_Timer_Init(void)
{
    TIM_ClockConfigTypeDef sClockSourceConfig = {0};
    TIM_MasterConfigTypeDef sMasterConfig = {0};
    
    /* 使能定时器时钟 */
    __HAL_RCC_TIM2_CLK_ENABLE();
    
    /* 配置定时器 */
    htim2.Instance = TIM2;
    htim2.Init.Prescaler = 7200 - 1;  // 72MHz/7200 = 10kHz
    htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim2.Init.Period = 100 - 1;      // 10kHz/100 = 100Hz (10ms)
    htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
    HAL_TIM_Base_Init(&htim2);
    
    /* 配置时钟源 */
    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
    HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig);
    
    /* 主输出使能 */
    sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig);
    
    /* 启动定时器中断 */
    HAL_TIM_Base_Start_IT(&htim2);
    
    /* 配置NVIC */
    HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(TIM2_IRQn);
}

/**
 * @brief 定时器中断处理
 */
void TIM2_IRQHandler(void)
{
    if(__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_UPDATE) != RESET)
    {
        if(__HAL_TIM_GET_IT_SOURCE(&htim2, TIM_IT_UPDATE) != RESET)
        {
            __HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE);
            
            /* 扫描键盘 */
            Keypad_Scan(&hkeypad);
        }
    }
}

六、调试与测试

6.1 键盘测试程序

c 复制代码
/**
 * @file keypad_test.c
 * @brief 键盘测试程序
 */

void Keypad_Test(void)
{
    KeyCode key;
    KeyEvent event;
    char display_str[20];
    
    /* 初始化串口 */
    UART_Init(115200);
    printf("Matrix Keypad Test\n");
    
    /* 初始化键盘 */
    Keypad_Config_Init();
    Keypad_Init(&hkeypad, &keypad_config);
    
    printf("Press any key...\n");
    
    while(1)
    {
        /* 扫描键盘 */
        Keypad_Scan(&hkeypad);
        
        /* 获取按键事件 */
        if(Keypad_GetKeyEvent(&hkeypad, &key, &event))
        {
            switch(event)
            {
                case KEY_EVENT_PRESS:
                    printf("Key 0x%02X Pressed\n", key);
                    break;
                    
                case KEY_EVENT_SHORT_PRESS:
                    printf("Key 0x%02X Short Press\n", key);
                    break;
                    
                case KEY_EVENT_LONG_PRESS:
                    printf("Key 0x%02X Long Press\n", key);
                    break;
                    
                case KEY_EVENT_RELEASE:
                    printf("Key 0x%02X Released\n", key);
                    break;
                    
                case KEY_EVENT_REPEAT:
                    printf("Key 0x%02X Repeat\n", key);
                    break;
            }
        }
        
        /* 检查特定按键 */
        if(Keypad_IsKeyPressed(&hkeypad, KEY_1))
        {
            printf("Key 1 is holding\n");
        }
        
        HAL_Delay(10);
    }
}

七、性能优化

7.1 硬件优化

c 复制代码
/* 使用位带操作提高扫描速度 */
#ifdef USE_BITBAND
    /* 定义位带地址 */
    #define BITBAND(addr, bit) ((addr & 0xF0000000) + 0x02000000 + ((addr & 0xFFFFF) << 5) + (bit << 2))
    
    /* 快速读写函数 */
    #define GPIO_READ_BIT(gpio, pin) (*(volatile uint32_t*)BITBAND((uint32_t)&gpio->IDR, __builtin_ctz(pin)))
    #define GPIO_WRITE_BIT(gpio, pin, value) *(volatile uint32_t*)BITBAND((uint32_t)&gpio->ODR, __builtin_ctz(pin)) = value
    
    /* 快速扫描函数 */
    void Keypad_FastScan(Keypad_Handle *hkeypad)
    {
        for(uint8_t row = 0; row < KEYPAD_ROWS; row++)
        {
            /* 设置当前行为低 */
            GPIO_WRITE_BIT(hkeypad->config.row_port[row], hkeypad->config.row_pin[row], 0);
            
            /* 读取列线 */
            for(uint8_t col = 0; col < KEYPAD_COLS; col++)
            {
                if(GPIO_READ_BIT(hkeypad->config.col_port[col], hkeypad->config.col_pin[col]) == 0)
                {
                    /* 按键按下处理 */
                }
            }
            
            /* 恢复行线 */
            GPIO_WRITE_BIT(hkeypad->config.row_port[row], hkeypad->config.row_pin[row], 1);
        }
    }
#endif

八、常见问题解决

问题 可能原因 解决方案
按键无响应 1. GPIO配置错误 2. 上拉电阻未启用 3. 扫描频率过低 1. 检查GPIO模式 2. 启用内部上拉 3. 提高扫描频率
按键抖动 1. 机械抖动 2. 接触不良 1. 增加去抖时间 2. 检查硬件连接
多键同时按下错误 1. 行线冲突 2. 扫描逻辑错误 1. 使用二极管隔离 2. 改进扫描算法
耗电过高 1. 上拉电阻过小 2. 扫描过频繁 1. 使用内部上拉 2. 降低扫描频率或使用中断
相关推荐
oo哦哦2 小时前
全域矩阵系统的技术架构拆解:从单点效率到链路闭环
人工智能·矩阵·架构
嵌入式小站3 小时前
STM32 零基础可移植教程 05:按键消抖,为什么按一次会触发好几次
chrome·stm32·嵌入式硬件
2601_957786774 小时前
拆解矩阵系统的底层逻辑:从“人海战术“到“一套系统管所有“
大数据·人工智能·矩阵
拾知_H4 小时前
STM32/Delay延时函数编程思路
stm32·单片机·时钟·延时
2zcode5 小时前
基于STM32的智能扫地机器人设计与实现
stm32·嵌入式硬件·机器人
晚霞的不甘6 小时前
CANN Catlass 矩阵乘模板库深度解析:高性能矩阵运算的进阶之路
人工智能·python·线性代数·矩阵
jllllyuz6 小时前
单相并网逆变器控制代码实现(STM32版)
stm32·单片机·嵌入式硬件
2601_957786776 小时前
矩阵系统深度解析:从冷启动困局到智能化运营的技术演进
大数据·人工智能·矩阵
AI_yangxi6 小时前
短视频矩阵系统技术强的公司
线性代数·矩阵