目录
一、Keyboard
(1)资源介绍
🔅原理图
🔅扫描原理
(2)STM32CubeMX 软件配置
(3)代码编写
(4)实验现象
二、Keyboard接口函数封装
三、踩坑日记
(1)上拉电阻问题
一、Keyboard
(1)资源介绍
🔅原理图
蓝桥杯物联网竞赛实训平台提供了一个拓展接口 EXTEND MODULE,所有拓展模块均可直接安装在 Lora 终端上使用;

2×3 矩阵键盘模块电路原理图如下所示:

通过两张电路图连接可知,引脚资源配置情况为:
Keyboard | MCU |
---|---|
COLUMN_1 | PB3 |
COLUMN_2 | PA15 |
COLUMN_3 | PA0 |
ROW_1 | PA12 |
ROW_2 | PA11 |
🔅扫描原理
1️⃣行扫描:所有行线作为输入端口,列线作为输出端口。逐个拉低列线输出,检测对应行是否读到低电平,若有即可确定按键触发位置;
2️⃣列扫描:与行扫描相反。所有列线作为输入端口,行线作为输出端口。逐个拉低行线输出,检测对应列是否读到低电平,从而确定按键触发位置;
本文将通过列扫描方式使用矩阵键盘模块;
(2)STM32CubeMX 软件配置
🔅“工程建立、时钟树配置、Debug 串行线配置、代码生成配置” 在下文中有讲解,这里不再赘述❗️
【蓝桥杯——物联网设计与开发】Part1:GPIOhttps://blog.csdn.net/m0_63116406/article/details/144894900?spm=1001.2014.3001.55011️⃣点击引脚 PA12 和 PA11 → 选择 GPIO_Input 模式;
2️⃣点击"System Core" → "GPIO", 将 PA12 和 PA11 的 "GPIO Pull-up/Pull-down" 栏修改为 "Pull-up" ,即将 PA12 和 PA11 引脚初始化为带上拉电阻的输入引脚;

3️⃣点击引脚 PA0 、 PA15 和 PB3 → 选择 GPIO_Output 模式;
4️⃣点击"System Core" → "GPIO", 将 PA0 、 PA15 和 PB3 的"GPIO output level" 栏修改为 "High",即将 PA0 、 PA15 和 PB3 引脚初始化为高电平;

5️⃣配置 OLED;
6️⃣生成代码即可;
(3)代码编写
🟢️main 函数
/* USER CODE BEGIN Includes */
#include "oled.h"
#include <stdio.h>
/* USER CODE END Includes *//* USER CODE BEGIN PV */
uint8_t puc_oled[17];
/* USER CODE END PV *//*** @brief The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 */uint8_t key_val = 0;/* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_I2C1_Init();/* USER CODE BEGIN 2 */OLED_Init();/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){key_val = ' ';/* 按键扫描 */HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET); // 第一列HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12) == 0){HAL_Delay(10); // 延时消抖if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12) == 0){key_val = '1';while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12) == 0); // 抬手检测}}if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == 0){HAL_Delay(10); // 延时消抖if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == 0){key_val = '4';while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == 0); // 抬手检测}}HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET); // 第二列HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12) == 0){HAL_Delay(10); // 延时消抖if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12) == 0){key_val = '2';while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12) == 0); // 抬手检测}}if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == 0){HAL_Delay(10); // 延时消抖if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == 0){key_val = '5';while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == 0); // 抬手检测}}HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); // 第三列if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12) == 0){HAL_Delay(10); // 延时消抖if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12) == 0){key_val = '3';while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12) == 0); // 抬手检测}}if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == 0){HAL_Delay(10); // 延时消抖if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == 0){key_val = '6';while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == 0); // 抬手检测}}/* 键值显示 */sprintf((char*)puc_oled, " Key value:%c ", key_val);OLED_ShowString(0, 0, puc_oled, 16);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
(4)实验现象
按下按键后,OLED 屏幕上会显示对应键值。
二、Keyboard接口函数封装
🟡️Keyboard扫描函数
uint8_t key_read(void)
{uint8_t key_val = 0;/* 按键扫描 */HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET); // 第一列HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12) == 0)key_val = '1';else if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == 0)key_val = '4';HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET); // 第二列HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12) == 0)key_val = '2';else if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == 0)key_val = '5';HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); // 第三列if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12) == 0)key_val = '3';else if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == 0)key_val = '6';return key_val;
}
🔴Keyboard接口函数调用实例
/* 按键处理任务函数 */
void task_keys(void)
{uint8_t key_down, key_tmp;static uint8_t key_old = 0;// 10ms 调用一次if(cnt_10ms < 10) return;cnt_10ms = 0;// 边沿检测key_tmp = key_read();key_down = key_tmp & (key_tmp ^ key_old);key_old = key_tmp;// 下降沿逻辑处理if(key_down){OLED_Clear();sprintf((char*)puc_oled, " Key value:%c ", key_down);OLED_ShowString(0, 0, puc_oled, 16);}
}
三、踩坑日记
(1)上拉电阻问题
- 做列扫描时,行对应的引脚需配置为带上拉电阻的输入模式;
- 做行扫描时,列对应的引脚需配置为带上拉电阻的输入模式;
🔅如果不带有上拉电阻,则在按键未按下的时候,输入引脚的电平处于不确定的状态,导致读入数据不稳定;而带有上拉电阻,可将引脚电平钳制为高电平,在按键按下时才变化为低电平;