STM32CUBEMX配置实现STM32F103C8T6矩阵键盘扫描
提示:某宝购买一矩阵键盘,通过STM32CUBEMX软件实现快速调试,基于成本考虑,实际使用国产CH32F103C8T6单片机。
文章目录
前言
通过上期基于STM32的USB鼠标https://blog.csdn.net/xhl9434826546/article/details/122391238的实现,对于STM32CUBEMX软件的使用有了比较深入的了解,本文需要实现矩阵键盘的驱动,配置难度就不值一提了,实践过程中却难免遇到小问题。本文核心是解决矩阵键盘原理及驱动程序的编写,其中驱动程序编写涉及引脚分配问题。
一、下载安装
1.下载
下载安装最新版本STM32CubeMX,本文使用的版本为V6.4.0,其他版本区别不大,下载链接:
https://download.csdn.net/download/XHL9434826546/75058920
(对于官网下载困难的用户可以下载,截止2022年1月发布的最新版本V6.4.0,适用于Windows平台,也可以访问官网进行下载:https://www.st.com/en/development-tools/stm32cubemx.html)
2.安装
全英文版,安装路径最后不要有任何中文,根据提示一路下一步,成功安装主界面如下:
二、配置步骤
1.创建项目
选择第一个,进入MCU选择器。
输入STM32F103C8筛选出对应的型号,点击开始项目按钮。
2.引脚配置
配置高速时钟源引脚与为外部晶振(默认8M)连接,
配置调试接口,
配置LED引脚(板载LED引脚为GPIOC13),
配置矩阵键盘连接引脚,4个输入口改为上拉输入,4个输出口,此处使用较多的引脚,必须注意该引脚是否支持GPIO功能,貌似都支持,实际上有些口会出问题,如之前使用了PB6和PB7引脚,反复调试一直有问题,发现CH32的这两个引脚比较特殊,是HUSB接口,无法作为通用GPIO,STM32应该没这个问题。
3.时钟配置
直接输入HCLK时钟为72,软件会自动匹配其他各个分频因子。
4.生成代码
输入项目名称,生成项目的路径(路径不能有任何中文,否则会报错),编译器类型以及最低版本,本文使用的IDE为Keil uVision V5.25.2.0,
生成后直接点击打开项目
点击编译,0错误,0警告,表示生成成功。
三、修改代码
1.向main.c文件添加矩阵键盘扫描函数
/* USER CODE BEGIN 4 */
uint8_t Key_Scan(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/*前4个端口输出低电平*/
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11, GPIO_PIN_RESET);
//前4个端口推挽输出
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
//后4个端口上拉输入
GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
HAL_Delay(10);
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_15)==GPIO_PIN_RESET)//读取第1行
{
/*后4个端口输出低电平*/
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET);
//后4个端口推挽输出
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
//前4个端口上拉输入
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_Delay(10);
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8)==GPIO_PIN_RESET)return 'A';
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_9)==GPIO_PIN_RESET)return '3';
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_10)==GPIO_PIN_RESET)return '2';
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_11)==GPIO_PIN_RESET)return '1';
}
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_14)==GPIO_PIN_RESET)//读取第2行
{
/*后4个端口输出低电平*/
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET);
//后4个端口推挽输出
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
//前4个端口上拉输入
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_Delay(10);
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8)==GPIO_PIN_RESET)return 'B';
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_9)==GPIO_PIN_RESET)return '6';
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_10)==GPIO_PIN_RESET)return '5';
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_11)==GPIO_PIN_RESET)return '4';
}
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_13)==GPIO_PIN_RESET)//读取第3行
{
/*后4个端口输出低电平*/
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET);
//后4个端口推挽输出
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
//前4个端口上拉输入
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_Delay(10);
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8)==GPIO_PIN_RESET)return 'C';
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_9)==GPIO_PIN_RESET)return '9';
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_10)==GPIO_PIN_RESET)return '8';
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_11)==GPIO_PIN_RESET)return '7';
}
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_12)==GPIO_PIN_RESET)//读取第4行
{
/*后4个端口输出低电平*/
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET);
//后4个端口推挽输出
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
//前4个端口上拉输入
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_Delay(10);
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8)==GPIO_PIN_RESET)return 'D';
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_9)==GPIO_PIN_RESET)return '#';
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_10)==GPIO_PIN_RESET)return '0';
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_11)==GPIO_PIN_RESET)return '*';
}
return 0;
}
添加函数声明
/* USER CODE BEGIN PFP */
uint8_t Key_Scan(void);
/* USER CODE END PFP */
2.向main.c文件添加测试程序
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
if(Key_Scan() == '#')
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
else
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
测试成功,按下#键LED亮,松开灭,同理可以测试其他按键。
四、内容拓展
1.矩阵键盘原理
如图所示,按下相应按键后,相应两个引脚会连通,因为材料原因,此键盘按键具有内阻,通过实践内阻在1K以内,因此当按键扫描方式读取时,要考虑输入内阻,通过将输入引脚配置为上拉输入,根据数据手册,上拉电阻大概为40K左右,远大于键盘内阻,故通过实际测试,能够准确响应。
2.矩阵键盘扫描逻辑
有两种方式:
- 一种是逐行扫描,因程序逻辑清晰,本文采用这种方式,先将4列都接低电平,也就是1、2、3、4端口,5、6、7、8端口配置为上拉输入,故默认电平为高,第一行(5端口)开始判断是否为低电平,是则5端口推挽输出低电平,高 1、2、3、4端口配置为上拉输入,逐一判断是否被拉低,从而确定该行哪个按键被按下,依次进行第2、3、4行。
- 一种是行列扫描,行扫描:先将4列都接低电平,也就是1、2、3、4端口,5、6、7、8端口配置为上拉输入,故默认电平为高,如某按键按下,则该行会被拉低,记录所在行。列扫描:先将4行都接低电平,也就是5、6、7、8端口,1、2、3、4端口配置为上拉输入,故默认电平为高,如某按键按下,则该列会被拉低,记录所在列。综合行列找出该按键。