【STM32F103笔记】3、按键与矩阵键盘

您所在的位置:网站首页 单片机矩阵按键电路图 【STM32F103笔记】3、按键与矩阵键盘

【STM32F103笔记】3、按键与矩阵键盘

#【STM32F103笔记】3、按键与矩阵键盘| 来源: 网络整理| 查看: 265

上一篇说完了STM32库开发的引脚输出控制,这一篇对其引脚输入控制方法进行说明,引脚设置为输入功能时能够感知引脚上的电平高低,具有模拟输入复用功能的引脚还可以结合芯片内部的A/D准确测量其电平值,后续在ADC章节再进行讨论。

这里通过按键改变引脚的输入电平高低,当引脚上接入高电平时,其输入数据寄存器对应的位将置1,而当引脚上接入低电平时,将清0;程序对引脚的输入数据进行判断,并控制LED的亮灭。

由于笔者蓝色的最小系统板没有设置按键,因此用黑色的最小系统板进行按键程序的演示。

按键电路原理图

在这里插入图片描述 从图中可以看出,黑色系统板的LED由GPIOC13引脚控制,低电平点亮;按键接在GPIOA0引脚,按键按下则引脚与地连接,即引脚接入低电平,在上一篇的基础上,可以开始写程序了。

程序设计 程序原理

程序思路是,初始化GPIOA0为上拉输入模式,即在STM32芯片内部使能上拉电阻,如图所示: 在这里插入图片描述 在STM32引脚内部,有输出驱动与输入驱动电路,通过Port Configuration Register(CRL、CRH)寄存器设置引脚输入还是输出,并设置相应的输入输出模式。

在这里将GPIOA0引脚设置为上拉输入模式,即上拉电阻的“开关”闭合,引脚在默认状态下,通过内部的上拉电阻接到VDD,也就是默认为高电平;这样,当按键按下时,GPIOA0通过按键接地,引脚切换为低电平。

检测到按键按下时,读取GPIOC13引脚的输出数据(0或1),并对其取反,重新写入GPIOC13引脚数据输出寄存器,改变引脚输出状态,控制LED状态反转。

同理复制模板工程文件到新工程文件夹Key下,打开Keil uVision5工程文件。这里直接给出核心程序:

/** * @brief Main program. * @param None * @retval None */ int main(void) { // BitAction是库文件提供的enum枚举类型 BitAction status; // 配置引脚 GPIOConfig(); while(1) { // 读取GPIOA0输入状态 if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)==0) { // 延时去抖 delay_u(1000); if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)==0) { // 等待按键释放,即松手检测 while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)==0); // 读取LED状态并取反 status = (BitAction)(1-GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_13)); // 设置LED反转 GPIO_WriteBit(GPIOC, GPIO_Pin_13, status); } } } } /** * @brief Configure GPIO * @param None * @retval None */ void GPIOConfig(void) { GPIO_InitTypeDef GPIOInitStruct; // GPIOA与GPIOC均挂载在APB2总线上 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); // 设置GPIOA为上拉输入模式,输入模式下无需设置引脚输出速率 GPIOInitStruct.GPIO_Pin = GPIO_Pin_0; GPIOInitStruct.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOA, &GPIOInitStruct); // 设置GPIOC13为推挽输出模式 GPIOInitStruct.GPIO_Pin = GPIO_Pin_13; GPIOInitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIOInitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIOInitStruct); GPIO_SetBits(GPIOC, GPIOInitStruct.GPIO_Pin); }

其中BitAction是库文件中提供的枚举类型,表示引脚的位清0与置1:

/** * @brief Bit_SET and Bit_RESET enumeration */ typedef enum { Bit_RESET = 0, Bit_SET }BitAction;

uint8_t GPIO_ReadOutputDataBit( GPIO_TypeDef * GPIOx, uint16_t GPIO_Pin ) 为引脚输入数据读取函数,可以在库文件帮助文档中找到其用法。

在利用官方库进行STM32程序开发时,需要时刻结合帮助文档,查找相关函数。

运行结果

编译程序并下载运行,可以看到按键改变LED亮灭状态: 在这里插入图片描述

矩阵键盘

这里用的矩阵键盘规格为4x4,即4行4列共16个按键,通过8个端子就可以进行控制和读取,电路原理图及实物如下: 在这里插入图片描述 从电路原理图可以看出,按键的每一行、每一列均分别相连并接入P1的一个端子,其控制方式为扫描读取(行扫描列读取或者列扫描行读取),这里以行扫描为例: 在这里插入图片描述 P1的1-4端子为列,用于读取数据,5-8端子为行,进行扫描;假设K4被按下:

控制STM32的引脚,首先使P1的5端子输出低电平,6-8端子为高电平,并读取1-4端子的电平数据,可以知道1端子为低电平,2-4端子为高电平;由此可以推知K4按下;

依次使P1的5-8端子分别输出低电平,并读取列数据,可以获取按下的按键位置;

但进行行扫描时矩阵键盘无法识别同一列同时按下的两个按键,举例若K4、K8同时按下:

第一行扫描时,P1的5端子输出低电平,6端子输出高电平此时6端子的高电平通过K8、K4进入5端子,形成回路;而由上面的引脚内部结构可以看出,引脚配置成推挽输出时,输出高电平时P-MOS导通,而输出低电平时N-MOS导通,且这两个MOS管采用的是对称设计,即参数近似,故此时VDD通过两个MOS管接到VSS,引脚输出的电压为 1 2 V D D \frac{1}{2}V_{DD} 21​VDD​,近似于高电平;从而1端子的输入引脚的电平被干扰,导致无法正确读取数据 程序

矩阵键盘列接到PA0-PA3,行接到PA4-PA7,而上一篇中的流水灯接到PB8-PB15

矩阵键盘扫描程序:

/** * @brief Key Scan * @param None * @retval sum of key values */ uint8_t MatrixKeyScan(void) { uint8_t i, temp, pin = 0; // 进行行扫描 for(i=0; i // 延时去抖 delay_u(1000); temp = (uint8_t)(GPIO_ReadInputData(GPIOA) & 0x000F); // 这里不进行松手检测,直接将该行中所有按下的键值相加 if(temp !=0x0F) { pin += ~(temp|0xF0)+i*4; } } } // 返回计算的键值 return pin; }

这里通过8个LED以二进制的方式显示按下的键的值的和,如按下K1、K3,则LED1、LED3亮,二进制值为0b00000101,即4

完整程序

(不含上一篇中写好的延时程序)如下:

/* Includes ------------------------------------------------------------------*/ #include "stm32f10x.h" /* Private functions Declaration ---------------------------------------------*/ void GPIOConfig(void); uint8_t MatrixKeyScan(void); void delay_m(uint32_t t); void delay_u(uint32_t t); /** * @brief Main program. * @param None * @retval None */ int main(void) { uint8_t keys; GPIOConfig(); while(1) { keys = MatrixKeyScan(); GPIO_SetBits(GPIOB, 0xFF00); GPIO_ResetBits(GPIOB, keys GPIO_SetBits(GPIOA, 0xF0); GPIO_ResetBits(GPIOA, 0x10 pin += ~(temp|0xF0)+i*4; } } } return pin; } /** * @brief Configure GPIO * @param None * @retval None */ void GPIOConfig(void) { GPIO_InitTypeDef GPIOInitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //GPIOInitStruct.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; GPIOInitStruct.GPIO_Pin = 0x00F0; GPIOInitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIOInitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIOInitStruct); //GPIOInitStruct.GPIO_Pin = |GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7; GPIOInitStruct.GPIO_Pin = 0x0F; GPIOInitStruct.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOA, &GPIOInitStruct); //GPIOInitStruct.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; GPIOInitStruct.GPIO_Pin = 0xFF00; GPIOInitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIOInitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIOInitStruct); GPIO_ResetBits(GPIOC, GPIOInitStruct.GPIO_Pin); delay_m(500); GPIO_SetBits(GPIOC, GPIOInitStruct.GPIO_Pin); }

注意在GPIO初始化和控制GPIO输出时,并没有用GPIO_Pin_x的形式(被注释的部分),查看GPIO_Pin_x的定义(如在GPIO_Pin_0上右键Go To Definition…)可知,是用二进制位表示的GPIO引脚编号,因此可以直接用二进制数来表示引脚,更为方便(虽然没有那么直观)。

运行结果

编译程序并下载,现象如下(一根手指按下了K1-K4,难道可以用这个练大横按~啊哈哈哈哈哈): 在这里插入图片描述

完结撒花✿✿ヽ(°▽°)ノ✿


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3