【蓝桥杯嵌入式】定时器实现按键单击,双击,消抖以及长按的代码实现

简介: 【蓝桥杯嵌入式】定时器实现按键单击,双击,消抖以及长按的代码实现

目录


原理图解析

设置STM32CubeMX

按键配置

定时器配置

手搓代码

中断回调(服务)函数

按键判断函数

按键单击判断函数

按键双击判断函数

按键长按判断函数


正文


原理图解析


我们以PB1为例来分析,假如按键没有被按下,那么PB1的电平就与左上角的VDD相等,也就是PB1 = 1;如果按键被按下,那么右下角的接地就会被导通,PB1的电平3就与GND相等,也就是PB1 = 2。


这样我们就能通过以上原理对按键进行判断,但是按键判断是一个事件触发程序,所以我们要使用定时器来使开发板能在任意时间都能对按键进行判断。因此我们再下一步设置STM32CubeMX中需要对定时器初始化。

123.png


设置STM32CubeMX


按键配置


首先我们先根据按键的原理图配置好引脚,需要注意的是,再前几篇文章中讲LCD与LED我们讲引脚都是设置为GPIO_Output,但是按键的四个引脚,我们需要设置为GPIO_input,如图:

然后在左侧选择GPIO中的按键的四个引脚,上下拉模式设为上拉,为以下状态:

321.png


定时器配置


如果大家有去做过省赛或国赛题目,就都会看到过对按键响应时间是有要求的,一般都是响应时间在0.1秒内,所以我们的定时器可以就设置为0.01秒。

以下是使能中断

231.png


手搓代码


中断回调(服务)函数


void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);

以下就是中断回调函数,很长,记不住怎么办。别担心,还有办法,我们打开keil5,在Project下找到stm32g4xx_it.c,在里面找到stm32g4xx_hal-tim.h并打开他,翻到最下面,再往上滑一点点,大概两千五百多行的位置,就可以找到这个函数啦,我们就可以直接复制使用。

132.png133.png


按键判断函数


实现创建一个按键的结构体,其含义写在注释中了:

struct keys
{
  uchar judge_sta;  //判断按键按键按下的动作到了第几步
  bool key_sta;   //如果按键被按下,为0
  bool key_flag;  //如果确认被按下,为1
};

具体实现按键判断函数的思路:

1.判断中断回调函数收到的中断信号是不是我们刚刚给按键设置的定时器3的信号,如果是就进入按键判断函数

2.读取每个按键这一时刻的电平

3.判断有哪些按键为按下的状态(低电平)

4.按键抖动判断

5.状态重置


按键单击判断函数


struct keys key[4]={0,0,0,0};
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  if(htim->Instance==TIM3)      //判断中断信号是否来自定时器3
  {
    key[0].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);    //读按键PB0现在的状态,如果被按下,PB0  = 0;如果没有被按下,PB0 = 1;
    key[1].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
    key[2].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
    key[3].key_sta=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
    for(int i = 0;i < 4; i++ )    //确认是哪个或哪些按键被按下了
    {
      switch (key[i].judge_sta)
      {
        case 0:
        {
          if(key[i].key_sta==0) key[i].judge_sta = 1; //第一次判断是否按下
        }
        break;
        case 1:
        {
          if(key[i].key_sta==0) //进入下一次定时器扫描,按键还是按下状态,那么就确认为按下,以此来消抖
          {
            key[i].judge_sta = 2;
            key[i].key_flag = 1;
          }
          else    //否则就是抖动,本次不算按键被按下
            key[i].judge_sta = 0;
        }
        break;
        case 2:
        {
          if(key[i].key_sta==1) key[i].judge_sta = 0; //判断是否松手,松手后按键状态重置
        }
        break;
      }
    }
  }
}


按键双击判断函数


void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  if(htim->Instance==TIM3)      //判断中断信号是否来自定时器3
  {
    key[0].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);    //读按键PB0现在的状态,如果被按下,PB0  = 0;如果没有被按下,PB0 = 1;
    key[1].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
    key[2].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
    key[3].key_sta=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
    for(int i = 0;i < 4; i++ )    //确认是哪个或哪些按键被按下了
    {
      switch (key[i].judge_sta)
      {
        case 0:
        {
          if(key[i].key_sta==0) key[i].judge_sta = 1; //第一次判断是否按下
        }
        break;
        case 1:
        {
          if(key[i].key_sta==0) //进入下一次定时器扫描,按键还是按下状态,那么就确认为按下,以此来消抖
          {
            if(a == i && key[a].key_time < 70)  //小于70,说明上次按下后到这次按下时间间隔小于0.7秒
            {
              key[i].double_key_flag = 1;   //这是一次双击事件
            }
            else
            {
              key[i].key_flag = 1;
              a = i;              //记录这一次是上面按键被按下
            }
            key[i].judge_sta = 2;
          }
          else    //否则就是抖动,本次不算按键被按下
            key[i].judge_sta = 0;
        }
        break;
        case 2:
        {
          if(key[i].key_sta==1) key[i].judge_sta = 0; //判断是否松手,松手后按键状态重置
          key[i].key_time = 0;
        }
        break;
      }
    }
    key[a].key_time++;    //第一次被按下之后,开始计时
  }
}


按键长按判断函数


void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  if(htim->Instance==TIM3)
  {
    key[0].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
    key[1].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
    key[2].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
    key[3].key_sta=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
    for(int i = 0;i < 4; i++ )
    {
      switch (key[i].judge_sta)
      {
        case 0:
        {
          if(key[i].key_sta==0) 
          {
            key[i].judge_sta = 1; //第一次判断是否按下
            key[i].key_time = 0;
          }
        }
        break;
        case 1:
        {
          if(key[i].key_sta==0) //进入下一次定时器扫描,按键还是按下状态,那么就确认为按下,以此来消抖
          {
            key[i].judge_sta = 2;
          }
          else
            key[i].judge_sta = 0;
        }
        break;
        case 2:
        {       
          if(key[i].key_sta==1)     //判断是否松手
          {
            if(key[i].key_time < 100)
            {
              key[i].key_flag = 1;
            }
//            if(key[i].key_time > 100)       //一次扫描10毫秒,100次1000毫秒,就是判断是否长按超过1000毫秒
//                                            //松手后,才会执行相应反应
//            {
//              key[i].long_flag = 1;
//            }
            key[i].judge_sta = 0;   
          }
          else
          {
            key[i].key_time++;
            if(key[i].key_time > 100)       //一次扫描10毫秒,100次1000毫秒,就是判断是否长按超过1000毫秒//未松手时,就会执行相应反应
            {
              key[i].long_flag = 1;
            }
          }
        }
        break;
      }
    }
  }
}


相关文章
|
2月前
蓝桥杯嵌入式第十二届省赛
蓝桥杯嵌入式第十二届省赛
45 0
|
2月前
蓝桥杯嵌入式第十一届(第一场)省赛
蓝桥杯嵌入式第十一届(第一场)省赛
107 1
|
2月前
蓝桥杯嵌入式第十一届(第二场)省赛
蓝桥杯嵌入式第十一届(第二场)省赛
57 0
|
2月前
|
芯片
蓝桥杯嵌入式创建第一个工程(点亮led灯)
蓝桥杯嵌入式创建第一个工程(点亮led灯)
42 0
蓝桥杯 stm32 按键点灯 CubeMX
从图中我们可以看到有 4个按键,B1 , B2 , B3 , B4。 对应的 GPIO 端口是 PB0 , PB1, PB2 ,PA0。 当按键按下,IO 口读到低电平;按键弹起,IO 口读到高电平。 LED 的原理图,CubeMX 配置,以及代码讲解都在我的上篇文章 里详细讲解了,这 篇文章主要为大家讲解 按键 的使用。 蓝桥杯嵌入式之 LED 闪烁
102 0
|
芯片
蓝桥杯嵌入式之 LED 闪烁
蓝桥杯嵌入式之 LED 闪烁
177 0
|
内存技术
蓝桥杯嵌入式之 Keil 仿真与调试
蓝桥杯嵌入式之 Keil 仿真与调试
290 1
蓝桥杯使用定时器代替延时控制按键
蓝桥杯使用定时器代替延时控制按键
42 0
|
存储 数据采集
【蓝桥杯嵌入式】蓝桥杯嵌入式第十四届省赛程序真题,真题分析与代码讲解
【蓝桥杯嵌入式】蓝桥杯嵌入式第十四届省赛程序真题,真题分析与代码讲解
1111 1
STM32 PWM模式与输出比较模式的区别。PWM占空比不生效,在STM32CubeMX中配置PWM的两种模式——蓝桥杯嵌入式
STM32 PWM模式与输出比较模式的区别。PWM占空比不生效,在STM32CubeMX中配置PWM的两种模式——蓝桥杯嵌入式
650 0