STM32矩阵键盘:
矩阵的工作原理:
矩阵原理图如下:
矩阵键盘由16个按键组成,也就是4*4矩阵键盘,按照传统的按键接法,16个按键需要16个单片机io口,按照矩阵键盘接法,16个按键只需要八个io口,我选择了PA口的0~7号引脚,前四个依次接矩阵键盘的行,后四个依次接矩阵键盘的列;矩阵键盘的每一个按键都是接了两个io口,也就是一端必须是输出高电平或者低电平,另一端去检测是否是高电平或者低电平;简单来说,也就是一端配置为GPIO输出,另一端配置为GPIO输入或者外部中断检测。所以只需要配置行线或者列线为输出,则另外一端就配置为中断检测。这个只是为了方便才选择连续的引脚,也可以选择不连续的引脚。
下面是STM32CubeMax配置引脚图:
我把行线配置为输出,列线配置为中断;
PA0-PA3配置为开漏输出低电平,其实推挽输出低电平也行。大家不明白腿玩输出与开漏输出的可以自己去查一下。
PA4-PA7配置为外部中断,下降沿触发,上拉,之所以这样配置,是因为我们PA0-PA3配置的是输出低电平,所以我们需要先上拉,让他那个引脚默认输入高电平,只有按下按键,两引脚想通才会由高电平变为低电平,所以我们需要按下按键触发的话也就是由高到低的时候,也就是下降沿触发。
配置完了矩阵键盘,还需要配置一下串口1,完成输出显示。
记得勾选中断,设置一下中断优先级
以上就是相关配置,点击右上角生成代码即可。
为了方便,给大家准备好了一份驱动代码:
keyboard_exit.c文件:
#include "keybord_exit.h"
#include "stm32f1xx_hal.h"
#define KEY_DELAY 10 //中断去抖动间隔
volatile uint8_t key;
volatile uint32_t key_tic;
static void Key_EXTI_Mode(void)// A4-7exti 0-3output
{
GPIO_InitTypeDef GPIO_InitStruct;
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
//__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, GPIO_PIN_RESET);
/*Configure GPIO pins : PA4 PA5 PA6 PA7 */
GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : PA0 PA1 PA2 PA3 */
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI4_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(EXTI4_IRQn);
HAL_NVIC_SetPriority(EXTI9_5_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
}
static void Key_Input_Mode(void)// A4-7output 0-3input
{
GPIO_InitTypeDef GPIO_InitStruct;
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_RESET);
/*Configure GPIO pins : PA4 PA5 PA6 PA7 */
GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : PA0 PA1 PA2 PA3 */
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
static uint8_t Get_Key(uint8_t num)//按键两引脚值相加(引脚只用到了前八个所以只有低 八位)
{
switch(num)
{
case 0x11:key='0';break;
case 0x21:key='1';break;
case 0x41:key='2';break;
case 0x81:key='3';break;
case 0x12:key='4';break;
case 0x22:key='5';break;
case 0x42:key='6';break;
case 0x82:key='7';break;
case 0x14:key='8';break;
case 0x24:key='9';break;
case 0x44:key='A';break;
case 0x84:key='B';break;
case 0x18:key='C';break;
case 0x28:key='D';break;
case 0x48:key='E';break;
case 0x88:key='F';break;
}
return key;
}
//非阻塞方式获取按键,返回0,没有键值,非0,获得键值
//该函数在中断中调用
uint8_t Get_KeyNum(uint16_t GPIO_Pin)
{
uint8_t i=0,num=0;
num = (uint8_t)GPIO_Pin;
if(HAL_GetTick() - key_tic < KEY_DELAY) //中断间隔过小,放弃
{
key_tic = HAL_GetTick();
return 0;
}
HAL_Delay(1);
key_tic = HAL_GetTick();
//略微延时后,读取管脚状态,去除上升沿抖动
if( HAL_GPIO_ReadPin(GPIOA,GPIO_Pin) != GPIO_PIN_RESET )
return 0;
if(num)
{
Key_Input_Mode();
for(i=0;i<4;i++)
{
if(HAL_GPIO_ReadPin(GPIOA,(1<<i)) == GPIO_PIN_RESET)
num |= (1<<i);
}
Key_EXTI_Mode();
return Get_Key(num);
}
else
{
Key_EXTI_Mode();
return 0;
}
}
void Key_Bord_Init(void)
{
Key_EXTI_Mode();
key = 0;
key_tic = HAL_GetTick();
}
这份驱动文件里面可能大家不太明白,我们cubemax配置了引脚,怎么还有引脚配置的操作,是这样的,这个文件的引脚配置是为了让轮询和中断同时使用,后面完成一个相互切换的配置。Key_EXTI_Mode函数是中断配置;
Key_Input_Mode函数是轮询配置。只不过转成轮询之后输出端改变了一下,变成了列输出,行输入。
还有一个地方就是Get_Key函数里面的这些十六进制,大家不太明白,我解释一下,在Get_KeyNum函数中有下图的一段代码
这段代码先是轮询检测是哪个引脚按下了,就是一个移位运算,后边num|=(1<<i)简单来讲,就是把一个按键两端同时接的两个引脚的值相加起来;而这些值就是stm32f1xx_hal_gpio.h里的这些引脚的宏值,如下:
比如说,我上边PA0和PA4接的第一个按键,因为我们用的0——7引脚,八位全是0,只用到了低八位,那就是0x01(PA0)与0x10(PA4)相加,之后结果就是0x11,依次类推,也可以算出其他按键被按下的值;所以1-16按键按下,依次返回’0’-'F’的字符。
keyboard_exit.h文件:
#ifndef __KEY_BORD_EXTI_H
#define __KEY_BORD_EXTI_H
#include "stm32f1xx_hal.h"
extern volatile uint8_t key;
void Key_Bord_Init(void);
//非阻塞方式获取按键,返回0,没有键值,非0,获得键值
//中断中调用
uint8_t Get_KeyNum(uint16_t GPIO_Pin);
#define ReadPin_Port GPIOA
#endif
主函数代码:
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "keybord_exit.h"
#include <stdio.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
//输出重定向
int fputc(int ch,FILE *f){
uint8_t temp[1]={ch};
HAL_UART_Transmit(&huart1,temp,1,2);
return ch;
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)//实现一下回调函数
{
char c;
c = Get_KeyNum(GPIO_Pin);
if(c != 0)//判断按键是否真正有按键按下
{
printf("按键值:%c\n",c);
}
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* 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_USART1_UART_Init();
/* USER CODE BEGIN 2 */
//Key_Bord_Init();//如果cubemax直接配置了,就不用调用这一句
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
运行效果:
以上就是矩阵键盘的原理和驱动代码。