STM32独立按键实现单击双击长按功能

目录

前言

一、按键功能定义

二、使用步骤

1.按键初始化

2.按键扫描函数(重点)

总结


前言

在使用STM32或其他单片机开发项目时,经常需要用到独立按键进行控制。

通常一个独立按键需要使用一个IO口,如果项目需要按键实现多个功能,往往需要使用到多个按键,需要使用到多个IO口。在IO口资源紧张或不希望使用太多按键时。可以使用以下方法实现一个按键单击,双击,长按返回不同键值的功能,从而减少独立按键的使用。


提示:以下是本篇文章正文内容,下面案例可供参考

一、按键功能定义

这里我将按键按住3秒视为长按操作,两次按下间隔0.5s视为双击,否则为单击

二、使用步骤

1.按键初始化

初始化部分和正常的独立按键使用一样,可以参考正点原子的代码

代码如下(示例):

这里使用的是上拉模式

//按键初始化
void CtrlKey_Init(void) 
{ 
 	GPIO_InitTypeDef GPIO_InitStructure;
 
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能时钟

	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_12;//KEY0
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置为上拉
 	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化IO口

}
 

2.按键扫描函数(重点)

在key.h中对键值进行宏定义

代码如下(示例):

#define KEY0  GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)//读取按键

#define KEY0_PRES 	1	//单击
#define LONG_PRES	2	//长按
#define DOUBLE_PRES	3	//双击

在key.c中编写扫描函数,进行按键扫描并返回相应键值

代码如下(示例):

//为了代码不那么乱,这里我定义了一个结构体,不想使用结构体的可以直接定义全局变量
typedef struct {
  u32 u32time1;            //第一次按下后开始计时,主要用于判断长按,放在定时器中自加
  u32 u32time2;            //第一次松手后开始计时,用于判断双击或单击,放在定时器中自加
  u8 u8key_flag;           //第一次按下标志
  u8 u8key_double_flag;    //第二次标志
}Key_Tag;

Key_Tag skey;


u8 KEY_Scan()
{
    static u8 press = 0;
        
    if(KEY0==0)//按键按下
    {
        delay_ms(10);//消抖
        if(KEY0==0)
        { 
            if(skey.u8key_flag==0)
            {
                skey.u8key_flag=1;        //第一次按下,标志位置1。同时计数值归零
                skey.u32time1=0;
            }
            else if(skey.u8key_flag==1)
            {
                if(!press && skey.u32time1 > 3000)//如果第一次按下且时间超过3S,视为长按
                {
                    press = 1;
                    return LONG_PRES;//返回长按键值
                }
            }
        }
    }
    
    else if(KEY0==1)//按键松开
    {
        if(skey.u8key_flag==1)    //第一次按键松开
        {
            skey.u8key_flag=0;
            if(skey.u32time1>3000)//按下后超过3秒才松开,已经返回了键值,松手后把标志位都归零
            {
                press = 0;
                skey.u32time1 = 0;
                skey.u32time2 = 0;
                skey.u8key_flag=0;
                skey.u8key_double_flag=0;
            }
            else if(skey.u8key_double_flag==0)
            {
                skey.u8key_double_flag=1;    //第一次松开之后标志位置1开始等待第二次按键松开
                skey.u32time2=0;            
            }
            else if(skey.u8key_double_flag==1)
            {
                if(skey.u32time2<500)            //如果第二次松开时间间隔小于0.5S,视为双击
                {
                    skey.u8key_double_flag=0;

                    return DOUBLE_PRES;
                }
            }
        }
        else if(skey.u8key_double_flag==1)
        {
            if(skey.u32time2>=500)            
            {
                skey.u8key_double_flag=0;

                return KEY0_PRES;//如果第一次松开之后0.5s没有第二次按键操作,视为短按
            }
        }
    }
    return 0;//没有按键按下返回0
}

3.注意事项

time1和time2两个计时变量要放在定时器中自加,这里就不放出详细代码了。

在main函数中轮询调用按键扫描函数,获得键值之后再根据键值执行相应操作。和平时的按键扫描函数一样用法


总结

这个实现按键单击双击长按的方法比较简单,主要是在按键按下之后记录第一次按下持续时间,若达到我们要的长按标准则视为长按,建议长按时间不小于1秒。第一次按键松手之后记录第二个时间,若在规定间隔时间内有第二次按下并松手则视为双击,若达到规定时间还没第二次按下并松手,则为单击。

小白入行没多久,第一次写博客,可能写得很一般,希望各位大神勿喷。如果有什么错误欢迎指正。

  • 37
    点赞
  • 279
    收藏
    觉得还不错? 一键收藏
  • 32
    评论
好的,以下是一个简单的实现单击双击功能的程序,使用的是STM32的HAL库: 首先,我们需要定义一些常量和变量: ```c #define DEBOUNCE_TIME 20 // 消抖时间,单位ms #define LONG_PRESS_TIME 1000 // 按时间,单位ms #define DOUBLE_CLICK_TIME 250 // 双击间隔时间,单位ms GPIO_TypeDef* KEY_PORT = GPIOB; // 按键所在的GPIO端口 uint16_t KEY_PIN = GPIO_PIN_0; // 按键所在的GPIO引脚 typedef enum { KEY_IDLE, // 按键空闲状态 KEY_PRESS, // 按键按下状态 KEY_RELEASE, // 按键释放状态 KEY_LONG_PRESS, // 按状态 KEY_DOUBLE_CLICK_WAIT, // 等待双击状态 KEY_DOUBLE_CLICK // 双击状态 } key_state_t; key_state_t key_state = KEY_IDLE; // 当前按键状态 uint32_t key_press_time = 0; // 按键按下时间 uint32_t key_release_time = 0; // 按键释放时间 uint32_t key_last_release_time = 0; // 上一次按键释放时间 uint8_t click_count = 0; // 点击次数 ``` 然后,我们可以在主函数中初始化按键所在的GPIO引脚: ```c HAL_GPIO_WritePin(KEY_PORT, KEY_PIN, GPIO_PIN_SET); // 拉高按键引脚 GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = KEY_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(KEY_PORT, &GPIO_InitStruct); ``` 在主函数的`while`循环中,我们可以编写按键状态机的代码: ```c // 获取当前时间 uint32_t t = HAL_GetTick(); // 检测按键状态 switch (key_state) { case KEY_IDLE: if (HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN) == GPIO_PIN_RESET) { // 按键按下 key_press_time = t; key_state = KEY_PRESS; } break; case KEY_PRESS: if (HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN) == GPIO_PIN_SET) { // 按键释放 key_release_time = t; if (key_release_time - key_press_time > DEBOUNCE_TIME) { // 消抖 click_count++; if (click_count == 1) { // 第一次单击 key_last_release_time = key_release_time; key_state = KEY_IDLE; } else if (click_count == 2) { // 第二次单击 if (key_release_time - key_last_release_time < DOUBLE_CLICK_TIME) { // 双击 click_count = 0; key_state = KEY_DOUBLE_CLICK; } else { // 第一次单击后超时,重新开始计数 click_count = 1; key_last_release_time = key_release_time; key_state = KEY_IDLE; } } else { // 连续单击 key_last_release_time = key_release_time; key_state = KEY_IDLE; } } } else if (t - key_press_time > LONG_PRESS_TIME) { // 按 click_count = 0; key_state = KEY_LONG_PRESS; } break; case KEY_RELEASE: if (HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN) == GPIO_PIN_RESET) { // 按键重新按下 key_press_time = t; key_state = KEY_PRESS; } else { // 按键完全释放 key_state = KEY_IDLE; } break; case KEY_LONG_PRESS: if (HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN) == GPIO_PIN_SET) { // 按释放 key_state = KEY_RELEASE; } break; case KEY_DOUBLE_CLICK_WAIT: if (t - key_release_time > DOUBLE_CLICK_TIME) { // 双击超时 if (click_count == 1) { // 单击 key_state = KEY_RELEASE; } else { // 连续单击 key_last_release_time = key_release_time; key_state = KEY_IDLE; } } else if (HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN) == GPIO_PIN_RESET) { // 双击按下 key_press_time = t; key_state = KEY_PRESS; } break; case KEY_DOUBLE_CLICK: // 双击完成 key_state = KEY_IDLE; break; } // 执行按键状态的相应操作 switch (key_state) { case KEY_LONG_PRESS: // 按操作 break; case KEY_DOUBLE_CLICK: // 双击操作 break; } ``` 以上就是一个简单的实现单击双击功能的程序,可以根据需要进行修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 32
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值