当前位置:   article > 正文

STM32 (基于HAL库)4×4矩阵按键驱动详细教程_4*4矩阵模块hal

4*4矩阵模块hal

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();
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134

这份驱动文件里面可能大家不太明白,我们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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

主函数代码:

/* 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 */
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99

运行效果:
在这里插入图片描述

以上就是矩阵键盘的原理和驱动代码。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/431129
推荐阅读
相关标签
  

闽ICP备14008679号