STM32CUBEMX配置实现STM32F103C8T6矩阵键盘扫描

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端口配置为上拉输入,故默认电平为高,如某按键按下,则该列会被拉低,记录所在列。综合行列找出该按键。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

简单|纯粹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值