【STM32】按键单、双、三击、长按

1 前言

        本人于去年这个时候准备了蓝桥杯嵌入式的比赛,其中涉及了按键的单击和长按,但是似乎双击和三击一直没考过。

        一直基于火花页大佬的文章进行备战,其有一篇讲述了如何实现按键的双击与长按,链接如下:【STM32G431RBTx】备战蓝桥杯嵌入式→基本模块→KEY→长按(持续响应)以及双击_stm32怎么按键按一下就持续-CSDN博客

        最近由于项目需要使用按键三击功能,网上众多代码无法真正实现,因此基于双击的逻辑进行了进一步扩展。

2 CUBEMX配置

以STM32G474VET6为例进行配置,配置一个定时器和按键输入引脚即可,主频32MHz。

定时器配置

配置定时器TIM3,10ms进行一次中断,记得打开NVIC中断!

按键输入引脚配置

配置PD6为按键输入引脚,按键另一侧接地,按下则输入为低电平。

3 代码部分

myinterrupt.c文件:
#include "myinterrupt.h"

#define LONG_PRESS_TIME 70            // 长按时间阈值(700ms)
#define DOUBLE_CLICK_WINDOW 20        // 双击时间窗口(200ms)
#define TRIPLE_CLICK_WINDOW 20        // 三击时间窗口(200ms)

struct keys key[4] = {0};
extern bool tri_sta;

unsigned char _10ms_cnt;

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	_10ms_cnt++;
	if(_10ms_cnt>=10)
	{
		HAL_ADC_Start_DMA(&hadc1,(uint32_t*)adc_result,10);
		_10ms_cnt = 0;
	}
	
    if (htim->Instance == TIM3)
    {
        key[0].key_sta = HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_6);

        switch (key[0].judge_sta)
        {
            case 0: // 初始状态
            {
                if (key[0].key_sta == 0) 
                {
                    key[0].judge_sta = 1;
                    key[0].key_time = 0; // 初始化按键持续时间
                }
            }
            break;

            case 1: // 按键按下确认状态
            {
                if (key[0].key_sta == 0)
                {
                    key[0].judge_sta = 2; // 确认按下,进入下一个状态
                }
                else
                {
                    key[0].judge_sta = 0; // 按键抖动或松开,回到初始状态
                }
            }
            break;

            case 2: // 检测按键持续状态
            {
                if (key[0].key_sta == 1 && key[0].key_time < LONG_PRESS_TIME)
                {
                    if (key[0].triple_click_timerEN == 1 && key[0].triple_click_time <= TRIPLE_CLICK_WINDOW) 
                    {
                        key[0].triple_key_flag = 1;       // 触发三击事件
                        key[0].triple_click_timerEN = 0; // 停止三击计时器
                    }
                    else if (key[0].double_click_timerEN == 0) 
                    {
                        key[0].double_click_timerEN = 1;  // 开启双击计时器
                        key[0].double_click_time = 0;
                    }
                    else if (key[0].triple_click_timerEN == 0 && key[0].double_click_timerEN == 1)
                    {
                        key[0].triple_click_timerEN = 1;  // 开启三击计时器
                        key[0].triple_click_time = 0;
                    }
                    key[0].judge_sta = 0; // 回到初始状态
                }
                else if (key[0].key_sta == 1 && key[0].key_time >= LONG_PRESS_TIME)
                {
                    key[0].long_key_flag = 1; // 标记长按事件
                    key[0].judge_sta = 0; // 长按松开,回到初始状态
                }
                else
                {
                    key[0].key_time++; // 按键持续时间累加
                }
            }
            break;
        }

        // 双击计时器逻辑
        if (key[0].double_click_timerEN == 1)
        {
            key[0].double_click_time++;
            if (key[0].double_click_time >= DOUBLE_CLICK_WINDOW)
            {
                // 超时判定单击,但需要确保未进入三击状态
                if (key[0].triple_click_timerEN == 0) 
                {
                    key[0].single_key_flag = 1;  // 触发单击
                }
                key[0].double_click_timerEN = 0; // 停止双击计时器
            }
        }

        // 三击计时器逻辑
        if (key[0].triple_click_timerEN == 1)
        {
            key[0].triple_click_time++;
            if (key[0].triple_click_time >= TRIPLE_CLICK_WINDOW)
            {
                key[0].double_key_flag = 1;    // 超时判定双击
                key[0].triple_click_timerEN = 0; // 停止三击计时器
            }
        }
    }
}
myinterrupt.h头文件:
#ifndef __MYINTERRUPT_H_
#define __MYINTERRUPT_H_

#include "main.h"

#include "main.h"
#include "stdbool.h" 

struct keys
{
    unsigned char judge_sta;           // 状态机状态
    bool key_sta;                      // 按键状态
    bool single_key_flag;              // 单击事件标志
    unsigned int key_time;             // 按键持续时间
    bool double_click_timerEN;         // 双击计时器使能
    unsigned int double_click_time;    // 双击计时器时间
    bool double_key_flag;              // 双击事件标志
    bool long_key_flag;                // 长按事件标志

    bool triple_click_timerEN;         // 三击计时器使能
    unsigned int triple_click_time;    // 三击计时器时间
    bool triple_key_flag;              // 三击事件标志
};


void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);



#endif

        文件中配置了key结构体有四个,可以实现4个按键的检测,但代码中仅演示了1个按键,需要增加按键数量的只需要在定时器回调函数中添加for循环进行判断即可,前言中的链接中有相关代码,不会的可参考。

main.c文件:
#include "myinterrupt.h"
#include "stdbool.h"

extern struct keys key[4];
void key_func(void);



int main(void)
{
  HAL_TIM_Base_Start_IT(&htim3);

  while (1)
  {

	 key_func();
  }
}

/* USER CODE BEGIN 4 */
void key_func()
{
	if(key[0].single_key_flag==1)//单击
	{
		//写逻辑
		key[0].single_key_flag = 0;
	}
	if(key[0].double_key_flag==1)//双击
	{
		//写逻辑
		key[0].double_key_flag = 0;
	}
	if(key[0].triple_key_flag==1)//三击
	{
		//写逻辑
		key[0].triple_key_flag = 0;
	}
	if(key[0].long_key_flag==1)//长按
	{
		//写逻辑
		key[0].long_key_flag = 0;
	}
}

上述main.c删去了官方生成的代码,只放了需要自己写的代码,逻辑部分只需要在key_func中写 ,然后把此函数放进while(1) 循环。

4 实现

以一个LED为例进行演示,运行稳定。

按键单击、双击、三击演示视频

作者:tjd6666

物联沃分享整理
物联沃-IOTWORD物联网 » 【STM32】按键单、双、三击、长按

发表回复