赞
踩
矩阵的工作原理:
矩阵原理图如下:
矩阵键盘由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 */ }
运行效果:
以上就是矩阵键盘的原理和驱动代码。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。