一、硬件连接与原理
1.1 硬件连接图
STM32F103 4×4 矩阵键盘
──────────────────────────────
PA0 ──────────── 行0 (ROW0)
PA1 ──────────── 行1 (ROW1)
PA2 ──────────── 行2 (ROW2)
PA3 ──────────── 行3 (ROW3)
PA4 ──────────── 列0 (COL0)
PA5 ──────────── 列1 (COL1)
PA6 ──────────── 列2 (COL2)
PA7 ──────────── 列3 (COL3)
键盘布局:
COL0 COL1 COL2 COL3
ROW0 1 2 3 A
ROW1 4 5 6 B
ROW2 7 8 9 C
ROW3 * 0 # D
1.2 扫描原理
1. 行扫描法:逐行输出低电平,读取列状态
2. 列扫描法:逐列输出低电平,读取行状态
3. 中断法:任意键按下产生外部中断
二、完整源码实现
2.1 头文件 (matrix_keyboard.h)
c
#ifndef __MATRIX_KEYBOARD_H
#define __MATRIX_KEYBOARD_H
#include "stm32f10x.h"
#include <stdint.h>
#include <stdbool.h>
// 键盘引脚定义
#define KEYBOARD_ROW_PORT GPIOA
#define KEYBOARD_COL_PORT GPIOA
#define ROW0_PIN GPIO_Pin_0
#define ROW1_PIN GPIO_Pin_1
#define ROW2_PIN GPIO_Pin_2
#define ROW3_PIN GPIO_Pin_3
#define COL0_PIN GPIO_Pin_4
#define COL1_PIN GPIO_Pin_5
#define COL2_PIN GPIO_Pin_6
#define COL3_PIN GPIO_Pin_7
// 键值定义
typedef enum {
KEY_NONE = 0x00,
// 数字键
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_ENTER = 0x0D,
KEY_ESC = 0x1B,
KEY_BACKSPACE = 0x08
} KeyValue_t;
// 键盘状态
typedef enum {
KEY_RELEASED = 0,
KEY_PRESSED,
KEY_LONG_PRESSED,
KEY_REPEAT
} KeyState_t;
// 键盘配置
typedef struct {
uint16_t debounce_time; // 消抖时间 (ms)
uint16_t long_press_time; // 长按时间 (ms)
uint16_t repeat_delay; // 重复延迟 (ms)
uint16_t repeat_rate; // 重复速率 (ms)
bool interrupt_enable; // 中断使能
} KeyboardConfig_t;
// 键盘事件
typedef struct {
KeyValue_t key_value;
KeyState_t key_state;
uint32_t press_time;
uint8_t repeat_count;
} KeyboardEvent_t;
// 函数声明
void MatrixKeyboard_Init(void);
void MatrixKeyboard_Config(KeyboardConfig_t *config);
KeyValue_t MatrixKeyboard_Scan(void);
bool MatrixKeyboard_GetKey(KeyboardEvent_t *event);
void MatrixKeyboard_IRQHandler(void);
void MatrixKeyboard_Process(void);
// 实用函数
const char* MatrixKeyboard_GetKeyName(KeyValue_t key);
bool MatrixKeyboard_IsDigit(KeyValue_t key);
uint8_t MatrixKeyboard_GetDigit(KeyValue_t key);
#endif /* __MATRIX_KEYBOARD_H */
2.2 核心驱动 (matrix_keyboard.c)
c
#include "matrix_keyboard.h"
#include "delay.h"
// 静态变量
static KeyboardConfig_t keyboard_config = {
.debounce_time = 20, // 20ms消抖
.long_press_time = 1000, // 1秒长按
.repeat_delay = 500, // 500ms重复延迟
.repeat_rate = 100, // 100ms重复速率
.interrupt_enable = false
};
static uint8_t current_row = 0;
static uint8_t key_buffer[16];
static uint8_t buffer_head = 0;
static uint8_t buffer_tail = 0;
// 键值映射表
static const KeyValue_t key_map[4][4] = {
{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}
};
// GPIO初始化
static void Keyboard_GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
// 使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置行引脚为推挽输出
GPIO_InitStructure.GPIO_Pin = ROW0_PIN | ROW1_PIN | ROW2_PIN | ROW3_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(KEYBOARD_ROW_PORT, &GPIO_InitStructure);
// 配置列引脚为上拉输入
GPIO_InitStructure.GPIO_Pin = COL0_PIN | COL1_PIN | COL2_PIN | COL3_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_Init(KEYBOARD_COL_PORT, &GPIO_InitStructure);
// 初始状态:所有行输出高电平
GPIO_SetBits(KEYBOARD_ROW_PORT, ROW0_PIN | ROW1_PIN | ROW2_PIN | ROW3_PIN);
}
// 设置指定行为低电平
static void SetRowLow(uint8_t row) {
switch(row) {
case 0: GPIO_ResetBits(KEYBOARD_ROW_PORT, ROW0_PIN); break;
case 1: GPIO_ResetBits(KEYBOARD_ROW_PORT, ROW1_PIN); break;
case 2: GPIO_ResetBits(KEYBOARD_ROW_PORT, ROW2_PIN); break;
case 3: GPIO_ResetBits(KEYBOARD_ROW_PORT, ROW3_PIN); break;
}
}
// 设置所有行为高电平
static void SetAllRowsHigh(void) {
GPIO_SetBits(KEYBOARD_ROW_PORT, ROW0_PIN | ROW1_PIN | ROW2_PIN | ROW3_PIN);
}
// 读取列状态
static uint8_t ReadColumns(void) {
uint8_t col_state = 0;
if (GPIO_ReadInputDataBit(KEYBOARD_COL_PORT, COL0_PIN) == 0) col_state |= 0x01;
if (GPIO_ReadInputDataBit(KEYBOARD_COL_PORT, COL1_PIN) == 0) col_state |= 0x02;
if (GPIO_ReadInputDataBit(KEYBOARD_COL_PORT, COL2_PIN) == 0) col_state |= 0x04;
if (GPIO_ReadInputDataBit(KEYBOARD_COL_PORT, COL3_PIN) == 0) col_state |= 0x08;
return col_state;
}
// 扫描单个按键
static KeyValue_t ScanSingleKey(uint8_t row, uint8_t col) {
KeyValue_t key = KEY_NONE;
// 设置指定行为低电平
SetRowLow(row);
Delay_us(10); // 等待电平稳定
// 读取列状态
uint8_t col_state = ReadColumns();
// 检查是否有按键按下
if (col_state & (1 << col)) {
key = key_map[row][col];
}
// 恢复行电平
SetAllRowsHigh();
Delay_us(10);
return key;
}
// 键盘扫描主函数
KeyValue_t MatrixKeyboard_Scan(void) {
KeyValue_t key = KEY_NONE;
uint8_t col_state;
// 逐行扫描
for (uint8_t row = 0; row < 4; row++) {
SetRowLow(row);
Delay_us(10);
col_state = ReadColumns();
if (col_state != 0) {
// 检测哪一列有按键按下
for (uint8_t col = 0; col < 4; col++) {
if (col_state & (1 << col)) {
key = key_map[row][col];
// 等待按键释放(可选,根据需求)
while (ReadColumns() != 0) {
Delay_ms(10);
}
SetAllRowsHigh();
return key;
}
}
}
SetAllRowsHigh();
Delay_us(10);
}
return key;
}
// 带消抖的键盘扫描
KeyValue_t MatrixKeyboard_ScanDebounced(void) {
static KeyValue_t last_key = KEY_NONE;
static uint32_t last_scan_time = 0;
KeyValue_t current_key;
// 检查消抖时间
if (HAL_GetTick() - last_scan_time < keyboard_config.debounce_time) {
return KEY_NONE;
}
current_key = MatrixKeyboard_Scan();
if (current_key != last_key) {
last_scan_time = HAL_GetTick();
last_key = current_key;
return current_key;
}
return KEY_NONE;
}
// 初始化键盘
void MatrixKeyboard_Init(void) {
Keyboard_GPIO_Init();
current_row = 0;
buffer_head = buffer_tail = 0;
printf("Matrix Keyboard Initialized\r\n");
}
// 配置键盘参数
void MatrixKeyboard_Config(KeyboardConfig_t *config) {
if (config != NULL) {
keyboard_config = *config;
}
}
// 获取键盘事件(带状态检测)
bool MatrixKeyboard_GetKey(KeyboardEvent_t *event) {
static KeyValue_t last_key = KEY_NONE;
static KeyState_t last_state = KEY_RELEASED;
static uint32_t press_start_time = 0;
static uint32_t last_repeat_time = 0;
static uint8_t repeat_count = 0;
KeyValue_t current_key = MatrixKeyboard_ScanDebounced();
if (event == NULL) return false;
event->key_value = current_key;
event->key_state = KEY_RELEASED;
event->press_time = 0;
event->repeat_count = 0;
if (current_key != KEY_NONE) {
if (last_key == KEY_NONE) {
// 新按键按下
last_state = KEY_PRESSED;
press_start_time = HAL_GetTick();
repeat_count = 0;
event->key_state = KEY_PRESSED;
event->press_time = press_start_time;
} else if (current_key == last_key) {
// 持续按下
uint32_t press_duration = HAL_GetTick() - press_start_time;
if (press_duration >= keyboard_config.long_press_time) {
last_state = KEY_LONG_PRESSED;
event->key_state = KEY_LONG_PRESSED;
event->press_time = press_duration;
}
// 重复按键检测
if (press_duration >= keyboard_config.long_press_time + keyboard_config.repeat_delay) {
if (HAL_GetTick() - last_repeat_time >= keyboard_config.repeat_rate) {
last_state = KEY_REPEAT;
event->key_state = KEY_REPEAT;
event->repeat_count = ++repeat_count;
last_repeat_time = HAL_GetTick();
}
}
}
} else {
last_state = KEY_RELEASED;
last_key = KEY_NONE;
}
last_key = current_key;
return (event->key_state != KEY_RELEASED);
}
// 键盘中断处理函数
void MatrixKeyboard_IRQHandler(void) {
KeyValue_t key = MatrixKeyboard_Scan();
if (key != KEY_NONE) {
// 将按键存入缓冲区
uint8_t next_head = (buffer_head + 1) % 16;
if (next_head != buffer_tail) {
key_buffer[buffer_head] = (uint8_t)key;
buffer_head = next_head;
}
}
}
// 处理键盘事件
void MatrixKeyboard_Process(void) {
KeyboardEvent_t event;
if (MatrixKeyboard_GetKey(&event)) {
switch(event.key_state) {
case KEY_PRESSED:
printf("Key Pressed: %c\r\n", event.key_value);
break;
case KEY_LONG_PRESSED:
printf("Key Long Pressed: %c (Duration: %lu ms)\r\n",
event.key_value, event.press_time);
break;
case KEY_REPEAT:
printf("Key Repeat: %c (Count: %d)\r\n",
event.key_value, event.repeat_count);
break;
default:
break;
}
}
}
// 实用函数实现
const char* MatrixKeyboard_GetKeyName(KeyValue_t key) {
switch(key) {
case KEY_0: return "0";
case KEY_1: return "1";
case KEY_2: return "2";
case KEY_3: return "3";
case KEY_4: return "4";
case KEY_5: return "5";
case KEY_6: return "6";
case KEY_7: return "7";
case KEY_8: return "8";
case KEY_9: return "9";
case KEY_A: return "A";
case KEY_B: return "B";
case KEY_C: return "C";
case KEY_D: return "D";
case KEY_STAR: return "*";
case KEY_HASH: return "#";
default: return "None";
}
}
bool MatrixKeyboard_IsDigit(KeyValue_t key) {
return (key >= KEY_0 && key <= KEY_9);
}
uint8_t MatrixKeyboard_GetDigit(KeyValue_t key) {
if (MatrixKeyboard_IsDigit(key)) {
return (uint8_t)(key - KEY_0);
}
return 0xFF; // 无效值
}
2.3 主程序示例 (main.c)
c
#include "stm32f10x.h"
#include "matrix_keyboard.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
// 密码验证示例
typedef struct {
uint8_t password[4];
uint8_t input_index;
bool password_correct;
} PasswordSystem_t;
PasswordSystem_t pwd_system = {
.password = {1, 2, 3, 4}, // 预设密码:1234
.input_index = 0,
.password_correct = false
};
// 键盘回调函数
void Keyboard_Callback(KeyboardEvent_t *event) {
if (event->key_state == KEY_PRESSED) {
LED_Toggle(LED0); // 按键提示
if (MatrixKeyboard_IsDigit(event->key_value)) {
// 数字输入
if (pwd_system.input_index < 4) {
uint8_t digit = MatrixKeyboard_GetDigit(event->key_value);
printf("Input digit: %d\r\n", digit);
// 验证密码
if (digit == pwd_system.password[pwd_system.input_index]) {
pwd_system.input_index++;
if (pwd_system.input_index >= 4) {
printf("Password Correct! Access Granted.\r\n");
LED_On(LED1); // 绿灯表示正确
pwd_system.password_correct = true;
pwd_system.input_index = 0;
}
} else {
printf("Password Wrong! Try again.\r\n");
LED_On(LED2); // 红灯表示错误
pwd_system.input_index = 0;
Delay_ms(1000);
LED_Off(LED2);
}
}
} else if (event->key_value == KEY_HASH) {
// #键确认
printf("Confirm pressed\r\n");
pwd_system.input_index = 0;
} else if (event->key_value == KEY_STAR) {
// *键清除
printf("Clear pressed\r\n");
pwd_system.input_index = 0;
}
}
}
int main(void) {
// 系统初始化
System_Init();
Delay_Init();
USART_Init(115200);
LED_Init();
// 初始化矩阵键盘
MatrixKeyboard_Init();
// 配置键盘参数
KeyboardConfig_t config = {
.debounce_time = 30, // 30ms消抖
.long_press_time = 2000, // 2秒长按
.repeat_delay = 800, // 800ms重复延迟
.repeat_rate = 150, // 150ms重复速率
.interrupt_enable = false
};
MatrixKeyboard_Config(&config);
printf("4x4 Matrix Keyboard Test Started\r\n");
printf("Password: 1234 (# to confirm, * to clear)\r\n");
while(1) {
// 方法1:简单扫描
KeyValue_t key = MatrixKeyboard_ScanDebounced();
if (key != KEY_NONE) {
printf("Key pressed: %s\r\n", MatrixKeyboard_GetKeyName(key));
}
// 方法2:事件驱动(推荐)
KeyboardEvent_t event;
if (MatrixKeyboard_GetKey(&event)) {
Keyboard_Callback(&event);
}
// 方法3:处理键盘事件
MatrixKeyboard_Process();
Delay_ms(50); // 主循环延时
}
}
2.4 中断方式实现 (keyboard_interrupt.c)
c
#include "matrix_keyboard.h"
#include "stm32f10x_exti.h"
// 外部中断初始化
void Keyboard_EXTI_Init(void) {
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 配置列引脚为外部中断
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource4);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource5);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource6);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource7);
// 配置EXTI线
EXTI_InitStructure.EXTI_Line = EXTI_Line4 | EXTI_Line5 | EXTI_Line6 | EXTI_Line7;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
// 配置NVIC
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 为其他EXTI线配置NVIC
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_Init(&NVIC_InitStructure);
}
// 外部中断服务函数
void EXTI4_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line4) != RESET) {
MatrixKeyboard_IRQHandler();
EXTI_ClearITPendingBit(EXTI_Line4);
}
}
void EXTI9_5_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line5) != RESET) {
MatrixKeyboard_IRQHandler();
EXTI_ClearITPendingBit(EXTI_Line5);
}
if (EXTI_GetITStatus(EXTI_Line6) != RESET) {
MatrixKeyboard_IRQHandler();
EXTI_ClearITPendingBit(EXTI_Line6);
}
if (EXTI_GetITStatus(EXTI_Line7) != RESET) {
MatrixKeyboard_IRQHandler();
EXTI_ClearITPendingBit(EXTI_Line7);
}
}
三、工程配置
3.1 Keil工程配置
Project Structure:
├── User/
│ ├── main.c
│ ├── matrix_keyboard.c
│ └── keyboard_interrupt.c
├── Hardware/
│ ├── led.c
│ ├── usart.c
│ └── delay.c
├── System/
│ ├── sys.c
│ └── delay.c
└── Libraries/
└── STM32F10x_StdPeriph_Driver/
3.2 编译选项
makefile
# 宏定义
USE_STDPERIPH_DRIVER
STM32F10X_MD
# 包含路径
-I../User
-I../Hardware
-I../System
-I../Libraries/CMSIS/CM3/CoreSupport
-I../Libraries/STM32F10x_StdPeriph_Driver/inc
参考代码 基于STM32的4X4矩阵键盘源码 www.youwenfan.com/contentcsu/60559.html
四、使用示例
4.1 基本使用
c
// 1. 初始化
MatrixKeyboard_Init();
// 2. 在主循环中扫描
while(1) {
KeyValue_t key = MatrixKeyboard_Scan();
if(key != KEY_NONE) {
// 处理按键
switch(key) {
case KEY_1: // 处理数字1
break;
case KEY_A: // 处理功能键A
break;
}
}
Delay_ms(100);
}
4.2 高级功能
c
// 密码锁系统
typedef struct {
char input_buffer[16];
uint8_t input_len;
bool locked;
} LockSystem_t;
LockSystem_t lock_sys = {.locked = true, .input_len = 0};
void ProcessLockSystem(KeyValue_t key) {
if(lock_sys.locked) {
if(MatrixKeyboard_IsDigit(key)) {
if(lock_sys.input_len < 16) {
lock_sys.input_buffer[lock_sys.input_len++] = key;
}
} else if(key == KEY_HASH) { // #确认
if(ValidatePassword(lock_sys.input_buffer, lock_sys.input_len)) {
lock_sys.locked = false;
printf("Unlocked!\r\n");
} else {
printf("Wrong password!\r\n");
lock_sys.input_len = 0;
}
} else if(key == KEY_STAR) { // *清除
lock_sys.input_len = 0;
}
}
}
五、常见问题解决
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 按键无反应 | GPIO配置错误 | 检查引脚定义和模式 |
| 按键抖动 | 消抖时间不够 | 增加消抖延时到30-50ms |
| 多键同时触发 | 扫描速度太快 | 降低扫描频率 |
| 中断不响应 | 中断未使能 | 检查EXTI和NVIC配置 |
| 键值错误 | 映射表不对 | 核对key_map数组 |