1,首先先要知道矩阵键盘的引脚,以下是按键与芯片引脚的关系,由于PA8引脚于oled屏幕连接,我个人感觉B3和B6按键的电气特性是要不同于另外四个引脚的。主要体现同一个代码,其他四个按键都没问题,但是B3和B6会出现双击或者键值相反的问题(我做了按键消抖)。
2,我是借鉴了gpt的代码,个人感觉代码写的很简洁,本来都搞不明白按键检测的原理,看过这个代码之后就懂了矩阵检测代码的原理。
#include "stm32f4xx_hal.h"
// 定义键盘矩阵的行和列
#define ROWS 2
#define COLS 3
// 定义矩阵键盘的GPIO引脚
GPIO_TypeDef* rows_GPIO_Port[ROWS] = {GPIOA, GPIOA};
uint16_t rows_Pin[ROWS] = {GPIO_PIN_0, GPIO_PIN_1};
GPIO_TypeDef* cols_GPIO_Port[COLS] = {GPIOB, GPIOB, GPIOB};
uint16_t cols_Pin[COLS] = {GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2};
int scanKeyboard() {
// 初始化GPIO引脚
for (int i = 0; i < ROWS; i++) {
HAL_GPIO_Init(rows_GPIO_Port[i], &GPIO_InitStruct);
HAL_GPIO_WritePin(rows_GPIO_Port[i], rows_Pin[i], GPIO_PIN_RESET);
}
for (int j = 0; j < COLS; j++) {
HAL_GPIO_Init(cols_GPIO_Port[j], &GPIO_InitStruct);
HAL_GPIO_WritePin(cols_GPIO_Port[j], cols_Pin[j], GPIO_PIN_RESET);
}
// 扫描键盘状态
for (int i = 0; i < ROWS; i++) {
HAL_GPIO_WritePin(rows_GPIO_Port[i], rows_Pin[i], GPIO_PIN_SET);
for (int j = 0; j < COLS; j++) {
if (HAL_GPIO_ReadPin(cols_GPIO_Port[j], cols_Pin[j]) == GPIO_PIN_SET) {
// 返回按下键的键值
return i * COLS + j + 1;
}
}
HAL_GPIO_WritePin(rows_GPIO_Port[i], rows_Pin[i], GPIO_PIN_RESET);
}
// 没有按键按下
return 0;
}
int main() {
// 初始化HAL库
HAL_Init();
// 配置GPIO引脚
GPIO_InitTypeDef GPIO_InitStruct = {0};
for (int i = 0; i < ROWS; i++) {
GPIO_InitStruct.Pin = rows_Pin[i];
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(rows_GPIO_Port[i], &GPIO_InitStruct);
}
for (int j = 0; j < COLS; j++) {
GPIO_InitStruct.Pin = cols_Pin[j];
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(cols_GPIO_Port[j], &GPIO_InitStruct);
}
int key = scanKeyboard();
// 处理按下的键值...
}
3,以下是我的代码,因为要用到串口调试,我还重写了一下打印函数。
#include "main.h"
#include "gpio.h"
#include "stdio.h"
#include "usart.h"
GPIO_TypeDef* row_GPIO_Port[2] = {GPIOB, GPIOB};
uint16_t row_Pin[2] = {GPIO_PIN_6, GPIO_PIN_7};
GPIO_TypeDef* col_GPIO_Port[3] = {GPIOB, GPIOB, GPIOA};
uint16_t col_pin[3] = {GPIO_PIN_1, GPIO_PIN_0, GPIO_PIN_8};
int scanKeyBorad(void)
{
for (int i =0; i < 2; i ++)
{
HAL_GPIO_WritePin(row_GPIO_Port[i], row_Pin[i], GPIO_PIN_SET);
HAL_Delay(40); //按键消抖
for (int j = 0; j < 3; j++)
{
if (HAL_GPIO_ReadPin(col_GPIO_Port[j], col_pin[j]) == GPIO_PIN_SET)
{
HAL_Delay(40); //按键消抖
return i * 3 + j + 1;
}
}
HAL_GPIO_WritePin(row_GPIO_Port[i], row_Pin[i], GPIO_PIN_RESET);
}
return 0;
}
int fputc(int ch, FILE *f)
{
unsigned char temp[1] = {ch};
HAL_UART_Transmit(&huart2, temp, 1, 0xFF);
return ch;
}
4,说一下大概思路,就是两行引脚PB6和PB7设为输出模式,竖着的三列引脚PB1,PB0和PA7设置为输入模式,首先将第一行的引脚就是PB6设为高电平,然后去读取列引脚的电平,如果第一行的某一个键被按下,就可以读到那个键的引脚为高电平,再通过 行数*3+列数+1 这个公式算出按键的键值(1到6)。
5,要注意的是cubemx的配置,除了选择输出输入模式外,还要讲GPIO Pull选择成我这样,三个输入的改成Pull-down,两个输出的选择为Pull-up。一定不能错这一步,不然按键就会疯狂的输出1