第12章 GPIO—按键轮询

一、关于按键

  按键监测一般有两种:按键扫描和按键中断。按键扫描是间隔很短时间反复查询GPIO状态,从而得知是否有按键动作,这种方式代码简单,但比较耗资源。按键中断而是通过按键产生中断信号,从而实现按键的检测,这种方式需要使用到中断机制,需要对MCU了解深入一点,效果是最好的。
  按键一般占用一个GPIO口,通过监测该GPIO的电平变化得知按键操作,典型的电路如图 12.1.1 所示。当所需按键比较多时,则可以采用矩阵按键减少GPIO的占用。矩阵按键需要通过编程扫描等方式实现对多个按键的监控。
在这里插入图片描述
  常用的按键都是机械触点式按键,机械式按键在按下或释放的过程中,由于机械弹性作用的影响,会伴随机械抖动,如图 12.1.2 所示。
在这里插入图片描述
  抖动的时长与机械开关特性相关,一般为5-10ms。在这抖动过程中,会产生多次高低电平,导致被识别为多次按键操作。为了避免机械触点按键检测误判,必须消抖处理。按键消抖可以硬件上处理,即在按键旁并联电容,吸收抖动的电平。也可以软件处理,即通过延时,避开抖动。
  由此,首先获取对应引脚的电平得知按键状态,再硬件或软件消除抖动。

二、硬件设计

  如下图 12.2.1 所示,是一种常见轻触按键,该按键有四个脚,①和②脚连接,③和④脚连接,按钮按下后,四脚全相连,实现导通效果。
在这里插入图片描述
  开发板有四个用户按键,在开发板左下角,如图 12.2.2 所示。为了方便区分,以按键所处位置命名,分别为KEY1_U(up,上)、KEY2_D(down,下)、KEY3_L(left,左)、KEY4_R(right,右)。
在这里插入图片描述
  左边的KEY1为例,E2的TVS二极管用于静电保护,可以看作不存在;C36的电容用于硬件去抖,也可以看作不存在。按键松开时,VDD_3V3经过上拉电阻R25,再经过限流电阻R29到GPIO KEY1(PA0),此时PA0读取电平为高电平;按键按下时,VDD_3V3经过上拉电阻R25,再通过按键接地,此时PA0读取电平为低电平。
  由此可知,按键按下,GPIO引脚电平变低,反之为高。四个按键所接GPIO分别为:KEY1(PA0)、KEY2 (PG15)、KEY3(PC13)、KEY4(PE3)。
在这里插入图片描述

三、软件设计

3.1 软件设计思路

  实验目的:本实验通过轮询读方式取GPIO的输入电平判断按键是否按下,并操作对应LED。

  1. 按键初始化:GPIO端口时钟使能、GPIO引脚设置为输入(PA0, PG15, PC13, PE3);
  2. 封装每个按键处理函数:读取按键GPIO状态,操作对应LED灯亮灭;
  3. 主函数轮询按键状态:一直检测是否有按键被按下;

3.2 软件设计讲解

3.2.1 GPIO宏定义与接口宏定义

/*********************
 * 按键引脚状态定义
**********************/
#define PUSH_DOWN                       GPIO_PIN_RESET
#define SPRING_UP                       GPIO_PIN_SET

/*********************
 * 引脚宏定义
**********************/
#define KEY_UP_GPIO_PIN                 GPIO_PIN_0
#define KEY_UP_GPIO_PORT                GPIOA
#define KEY_UP_GPIO_CLK_EN()            __HAL_RCC_GPIOA_CLK_ENABLE()

#define KEY_DOWN_GPIO_PIN               GPIO_PIN_15
#define KEY_DOWN_GPIO_PORT              GPIOG
#define KEY_DOWN_GPIO_CLK_EN()          __HAL_RCC_GPIOG_CLK_ENABLE()

#define KEY_LEFT_GPIO_PIN               GPIO_PIN_13
#define KEY_LEFT_GPIO_PORT              GPIOC
#define KEY_LEFT_GPIO_CLK_EN()          __HAL_RCC_GPIOC_CLK_ENABLE()

#define KEY_RIGHT_GPIO_PIN              GPIO_PIN_3
#define KEY_RIGHT_GPIO_PORT             GPIOE
#define KEY_RIGHT_GPIO_CLK_EN()         __HAL_RCC_GPIOE_CLK_ENABLE()

/*********************
 * 函数宏定义
**********************/
/*
 * 按键状态读取函数宏定义
*/
#define KEY_UP                  HAL_GPIO_ReadPin(KEY_UP_GPIO_PORT, KEY_UP_GPIO_PIN)
#define KEY_DOWN                HAL_GPIO_ReadPin(KEY_DOWN_GPIO_PORT, KEY_DOWN_GPIO_PIN)
#define KEY_LEFT                HAL_GPIO_ReadPin(KEY_LEFT_GPIO_PORT, KEY_LEFT_GPIO_PIN)
#define KEY_RIGHT               HAL_GPIO_ReadPin(KEY_RIGHT_GPIO_PORT, KEY_RIGHT_GPIO_PIN)

  根据硬件设计选定的四个按键的引脚,将其宏定义命名UP/DOWN/LEFT/RIGHT,且对他们的读取函数进行重命名。其中“HAL_GPIO_ReadPin()”原型“GPIO_PinState   HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)”,参数依次是:引脚组,引脚号,返回的是0(低电平)或1(高电平)。

3.2.2 GPIO初始化

/*
 *  函数名:void KeyInit(void)
 *  输入参数:无
 *  输出参数:无
 *  返回值:无
 *  函数作用:初始化按键的引脚,配置为输入
*/
void KeyInit(void)
{
    // 定义GPIO的结构体变量
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    // 使能按键的GPIO对应的时钟
    KEY_UP_GPIO_CLK_EN();
    KEY_DOWN_GPIO_CLK_EN();
    KEY_LEFT_GPIO_CLK_EN();
    KEY_RIGHT_GPIO_CLK_EN();

    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;             // 设置为输入模式
    GPIO_InitStruct.Pull = GPIO_PULLUP;                 // 默认上拉
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;       // 引脚反转速度设置为快
    
    // 初始化'Up'键引脚配置 
    GPIO_InitStruct.Pin = KEY_UP_GPIO_PIN;              // 选择按键的引脚
    HAL_GPIO_Init(KEY_UP_GPIO_PORT, &GPIO_InitStruct);
    // 初始化'Down'键引脚配置
    GPIO_InitStruct.Pin = KEY_DOWN_GPIO_PIN;            // 选择按键的引脚
    HAL_GPIO_Init(KEY_DOWN_GPIO_PORT, &GPIO_InitStruct);
    // 初始化Left'键引脚配置
    GPIO_InitStruct.Pin = KEY_LEFT_GPIO_PIN;            // 选择按键的引脚
    HAL_GPIO_Init(KEY_LEFT_GPIO_PORT, &GPIO_InitStruct);
    // 初始化'Right'键引脚配置
    GPIO_InitStruct.Pin = KEY_RIGHT_GPIO_PIN;           // 选择按键的引脚
    HAL_GPIO_Init(KEY_RIGHT_GPIO_PORT, &GPIO_InitStruct);
}

  将引脚初始化为上拉输入,此处使用了一个小技巧,因为各个按键的除了引脚号不同之外其余参数都是一致的,所以将GPIO结构体除引脚号外的参数只赋值一遍,最后只改变引脚号的那个成员参数的值进行初始化就可以了,不需要每个按键都将所有的成员参数重新赋值一遍,简化了代码量。

3.2.3 按键读取函数

  因为四个按键这个函数的处理都几乎一致,所以此处只对KEY1,即UP键进行具体举例,其余的请查看具体代码。

/*
 *  函数名:void UpKeyPolling(void)
 *  输入参数:无
 *  输出参数:无
 *  返回值:无
 *  函数作用:使用轮询方式查询向上键是否按下,通过按下控制三色灯绿灯亮灭
*/
static bool up_flag = false;
void UpKeyPolling(void)
{
    if(KEY_UP == PUSH_DOWN)     // 如果检测到向上键被按下
    {
        HAL_Delay(8);           //延时8ms防按键抖动
        if(KEY_UP == PUSH_DOWN) // 如果防抖动后向上键依然是处于被按下的状态,就认为向上键被按下过
        {
            up_flag = !up_flag; // 用一个标志位来判断向上键被按下次数,按下一次绿灯亮,再按一次绿灯灭,如此反复
            RLED(OFF);
            GLED(up_flag?OFF:ON);
            BLED(OFF);
        }
    }
}

  此时每按下一次UP键,绿色LED灯将亮灭交替。剩下的三个按键的效果分别是:DOWN->三个灯同时亮灭;LEFT->红灯亮灭;RIGHT->蓝灯亮灭。

3.2.4 主函数测试

    // 初始化LED
    LedGpioInit();
    // 初始化按键
    KeyInit();
    
    while(1)
    {
        // 轮询向上键
        UpKeyPolling();
        // 轮询向下键
        DownKeyPolling();
        // 轮询向左键
        LeftKeyPolling();
        // 轮询向右键
        RightKeyPolling();
    }

四、代码工程

工程链接:GPIO-按键轮询

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值