打印
[STM32F1]

按键轮询

[复制链接]
609|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
ccook11|  楼主 | 2024-11-21 11:00 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
按键监测一般有两种:按键扫描和按键中断。按键扫描是间隔很短时间反复查询GPIO状态,从而得知是否有按键动作,这种方式代码简单,但比较耗资源。按键中断而是通过按键产生中断信号,从而实现按键的检测,这种方式需要使用到中断机制,需要对MCU了解深入一点,效果是最好的。
本节先介绍按键扫描,理解按键的基本原理,下一章再介绍按键中断,同时了解STM32F103的中断使用方法。
按键一般占用一个GPIO口,通过监测该GPIO的电平变化得知按键操作,典型的电路如图 12.1.1 所示。当所需按键比较多时,则可以采用矩阵按键减少GPIO的占用。矩阵按键需要通过编程扫描等方式实现对多个按键的监控,这里以最简单的独立按键为基础进行介绍。
图 12.1.1 按键与MCU的连接方式示意图
可以看到,在没有按下按键时,电源3.3V通过电阻连接到MCU的PA0脚上,此时MCU读取PA0的电平就是3.3V的高电平。在按键按下时,电源3.3V经过电阻,再经过按键连接到了地,此时PA0连接到接地的一端,读到的电平就是0V的低电平。由此,MCU就可用过读取对应引脚的电平值,得知按键的变化。常用的按键都是机械触点式按键,机械式按键在按下或释放的过程中,由于机械弹性作用的影响,会伴随机械抖动,如图 12.1.2 所示。
图 12.1.2 机械按键抖动示意图
抖动的时长与机械开关特性相关,一般为5-10ms。在这抖动过程中,会产生多次高低电平,导致被识别为多次按键操作。为了避免机械触点按键检测误判,必须消抖处理。按键消抖可以硬件上处理,即在按键旁并联电容,吸收抖动的电平。也可以软件处理,即通过延时,避开抖动。
由此,首先获取对应引脚的电平得知按键状态,再硬件或软件消除抖动。
12.2 硬件设计
开发板上有两个按键,一个是复位按键(K1,红色),另一个是用户按键(K2,白色),这里只有用户按键能够编程控制。
如图 12.2.1 为开发板用户按键部分的原理图,电容C15用于硬件去抖,可以看作不存在。按键松开时,3V3经过上拉电阻R20到网络标号KEY,网络标号KEY另一端连接MCU的PA0,此时读取PA0电平为高电平;按键按下时,3V3经过上拉电阻R20,再通过按键接地,此时读取PA0电平为低电平。
由此可知,按键按下,GPIO引脚电平变低,反之为高,按键所接GPIO为PA0。
图 12.2.1 用户按键原理图
12.3 软件设计1.3.1 软件设计思路实验目的:本实验通过轮询读方式取GPIO的输入电平判断按键是否按下,并操作LED。1) 按键初始化:GPIO端口时钟使能、GPIO引脚设置为输入(PA0);2) 封装每个按键处理函数:读取按键GPIO状态,操作LED灯亮灭;3) 主函数轮询按键状态:一直检测是否有按键被按下;本实验配套代码位于“5_程序源码\5_GPIO—按键轮询\”。 1.3.2 软件设计讲解1) GPIO宏定义与接口宏定义代码段 12.3.1 引脚宏定义(driver_key.h)/*********************

* 按键引脚状态定义

**********************/

#define PUSH_DOWN                       GPIO_PIN_RESET

#define SPRING_UP                       GPIO_PIN_SET



/*********************

* 引脚宏定义

**********************/

#define KEY_GPIO_PIN                 GPIO_PIN_0

#define KEY_GPIO_PORT                GPIOA

#define KEY_GPIO_CLK_EN()            __HAL_RCC_GPIOA_CLK_ENABLE()



/*********************

* 函数宏定义

**********************/

/*

* 按键状态读取函数宏定义

*/

#define KEY                  HAL_GPIO_ReadPin(KEY_GPIO_PORT, KEY_GPIO_PIN)根据硬件设计选定的对应按键的引脚,将其宏定义命名为KEY,且对他们的读取函数进行重命名。其中“HAL_GPIO_ReadPin()”原型“GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)”,参数依次是:引脚组,引脚号,返回的是0(低电平)或1(高电平)。 2) GPIO初始化代码段 12.3.2 按键初始化(driver_key.c)/*

*  函数名:void KeyInit(void)

*  输入参数:无

*  输出参数:无

*  返回值:无

*  函数作用:初始化按键的引脚,配置为输入

*/

void KeyInit(void)

{

    // 定义GPIO的结构体变量

    GPIO_InitTypeDef GPIO_InitStruct = {0};

    // 使能按键的GPIO对应的时钟

    KEY_GPIO_CLK_EN();



    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;             // 设置为输入模式

    GPIO_InitStruct.Pull = GPIO_PULLUP;                 // 默认上拉

    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;       // 引脚反转速度设置为快

   

    // 初始化按键引脚配置

    GPIO_InitStruct.Pin = KEY_GPIO_PIN;              // 选择按键的引脚

    HAL_GPIO_Init(KEY_GPIO_PORT, &GPIO_InitStruct);

}
使能时钟,将引脚初始化为上拉输入。
3) 按键读取函数为了展示效果,编写代码查询按键是否按下,按下后切换LED灯亮灭状态。 代码段 12.3.3 按键读取函数(driver_key.c)/*

*  函数名:void KeyPolling(void)

*  输入参数:无

*  输出参数:无

*  返回值:无

*  函数作用:使用轮询方式查询按键是否按下,通过按下控制LED灯亮灭

*/

static bool key_flag = false;

void KeyPolling(void)

{

    if(KEY == PUSH_DOWN)          // 如果检测到按键被按下

    {

        HAL_Delay(8);             // 延时8ms防按键抖动

        if(KEY == PUSH_DOWN)      // 如果防抖动后按键依然是处于被按下的状态,就认为按键被按下过

        {

            key_flag = !key_flag; // 用一个标志位来判断按键被按下次数,按下一次灯亮,再按一次灯灭,如此反复

BLED(key_flag?OFF:ON);

        }

    }

}
  • 8行:定义了一个全局变量标志位“key_flag”,作为按键被按下的标志;
  • 11行:获取该按键状态;
  • 13行:延时5-10ms,软件去抖;
  • 14行:再次获取该按键状态,此时依旧按下,说明是正常按键操作,非抖动;
  • 16行:将标志位置反,按键按一次置反一次(即0->1->0->1这样循环);
  • 17行:根据标志位“key_flag”的值,让LED灯亮或灭;
此时每按下一次按键键,蓝色LED灯将亮灭交替。
4) 主函数测试代码段 12.3.4 主函数控制逻辑(main.c)    // 初始化LED

    LedGpioInit();

    // 初始化按键

    KeyInit();

   

    while(1)

    {

        // 轮询按键键

        KeyPolling();

     }
  • 1~4行:初始化LED灯和按键;
  • 6~10行:一直循环查询每个按键当前状态,从而判断对应按键是否按下;

使用特权

评论回复
沙发
yzzly| | 2024-11-26 14:53 | 只看该作者
软件滤波,连续几次(持续时间60毫秒以上)扫描都有效则认为有键按下。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

40

主题

1318

帖子

0

粉丝