按键扫描:双击、短按、长按(非阻塞)

本文记录一种非阻塞按键的软件管处理思路。

引言:
  按键防抖大多使用延时来处理,一般延时阻塞10ms,就算对于51单片机的运行速度来说,10ms也能做很多很多任务,10ms的时间太浪费资源。
按键电路:

使用最简单的按键电路,组件仅包含一个单片机IO和一个按键。由于STC15W4K32S4的P7口内部有上拉电阻,所以也不需要在外面加什么东西,K1按下的时候单片机P7.0的电平是低电平,没按下P7.0就是高电平。

1、按键识别算法的作用:

        一般看来按键至少有3种状态短按、长按、双击(或多次点击)

        短按:一般定义为按键按下并快速释放的操作,持续时间较短,按下时不会立即触动动作,而是过一个较短的时间才触发动作。

        长按:通常是指按键保持按压状态超过短按时间阈值的操作,持续时间较长,松开时触发动作。

        双击:较短的时间内,连续按下两次才会触发动作。

2、按键机械抖动:

        我们将使用非阻塞式按键检测来过滤前后沿抖动。

3、短按 长按 双击定义:

         短按:如果检测出按键按下的时间>=90ms且<=250ms时,则会被认定为按键短按。程序则是进入短按的逻辑处理状态。

        长按:经过网上大量的资料查阅和实际按下的情况对比,长按的时间一般大于500ms,因此在本文的按键识别算法中,将临界时间设定为500ms。 

        双击:双击的第一次按下松开后到第二次按下开始的时间间隔较多分布在200ms以内,而考虑到一些用户操作动作较为缓慢,及误触发的风险、硬件和软件的响应能力。因此将双击检测时间设置为了250ms,如果250ms内没有检测到按下第二次短按,则是会判断为单击,否则就是触发双击。

4、实现代码:

        注:使用定时器进行分时调度 

/* Max compare value: u8/2 */
#define tim_cmp_uint8(cur, last)         (uint8_t)((s8)(cur) - (s8)(last))

/* Max compare value: uint16_t/2 */
#define tim_cmp_uint16(cur, last)        (uint16_t)((s16)(cur) - (s16)(last))

/* Max compare value: uint32_t/2 */
#define tim_cmp_uint32(cur, last)        (uint32_t)((s32)(cur) - (s32)(last))


以51为例子

static uint8_t time0_count;
static uint8_t led_count;
void main()
{
    clk_enable_timer0();
    timer0_set_overflow(249);//1 / (8000000 / 32) * 250 = 0.001 1ms
    timer0_enable_it();
    timer0_control(TIMER0_CTL_PSC_32 | TIMER0_CTL_START);
    while(1)
    {
        if ( tim_cmp_uint8(time0_count, led_count) >= 50 )//50ms进入一次
        {
            led_count = time0_count;
            led_Display();
            .......略
        }
        .......略
    }
}

中断处理
void isr() __interrupt
{
    if(IF0_BIT_T0F){
        time0_count +=1;
        IF0_BIT_T0F = 0;
    }
}
        1.定义各个状态:
typedef enum {
    KeyState_IDLE,               //空闲状态。
    KeyState_CUT,                //消抖状态。
    KeyState_PRESSED,            //按下状态。
    KeyState_WAIT_DOUBLE_CLICK,  //等待双击状态。
    KeyState_DOUBLE_CLICKED,     //双击状态。
    KeyState_LONG_PRESSED        //长按状态。
}KeyState;
        2.定义按键结构体:
typedef struct key_state{

    uint8_t key_flag;       //按键标志
    bool level_state;       //电平状态
    uint16_t start_time;    //按下时间
    uint16_t end_time;      //松开时间
    uint8_t key_cut;        //按键消抖
    uint8_t key_last;       //按键最终状态
}key_state_t;
        3.按键处理函数:
#define KEY_SCAN_CNT        (10-1)  //消抖 10ms
key_state_t g_key[4];    //按键状态全局结构体
 
//按键处理函数
void key_timer_callback(void)//注意该函数以每1ms 调用一次
{

    g_key[1].level_state =    GET_KEY_STATE(KEY1_GPIO_PIN);        
    g_key[2].level_state =    GET_KEY_STATE(KEY1_GPIO_PIN);        
    g_key[3].level_state =    GET_KEY_STATE(KEY1_GPIO_PIN);    
    
    for(int i=0; i<4; i++)
    {
        switch(g_key[i].key_flag)
        {
            case KeyState_IDLE:          //空闲状态
                if(g_key[i].level_state == GPIO_PIN_RESET)              //按下
                {
                    g_key[i].key_flag = KeyState_CUT;                   //将状态定义为消抖状态,在下次进入时开始消抖
                    g_key[i].key_cut = KEY_SCAN_CNT;                    //消抖
                }
                break;
            case KeyState_CUT:          //消抖状态
                if ( g_key[i].key_cut == 0 )
                {
                     if(g_key[i].level_state == GPIO_PIN_RESET)         //10ms消抖后按键状态为按下
                        g_key[i].key_flag = KeyState_PRESSED;           //将状态定义为按下状态
                    else                                                //消抖后按键未按下认为是误触
                        g_key[i].key_flag = KeyState_IDLE;     
                }
                else
                    g_key[i].key_cut--;
                break;
            case KeyState_PRESSED:    //按下状态
                if ( g_key[i].level_state == GPIO_PIN_RESET )           //按键按下
                {
                    g_key[i].press_time++;                              //按下时间加1
                    if ( g_key[i].press_time > (500-1) )                //避免按键长时间触发 由于16位最大65535,以免溢出导致错误
                    {
                        g_key[i].key_last = KeyState_LONG_PRESSED;      //如果长时间按下将一直为长按状态,可以选择其他方式优化
                        g_key[i].press_time = 0;
                    }
                }
                else                                                    //按键松开
                {
                    if ( g_key[i].press_time > (500-1) )                //长按(按下持续时间 >= 500ms)
                    {
                        g_key[i].key_flag = KeyState_IDLE;              //恢复到空闲状态
                        g_key[i].key_last = KeyState_LONG_PRESSED;      //长按
                        g_key[i].press_time = 0;
                        
                    }else if(g_key[i].press_time > (90-1))              //短按(持续时间>=90ms)
                    {
                        g_key[i].key_flag = KeyState_WAIT_DOUBLE_CLICK; //等待双击
                        g_key[i].press_time = 0;
                        g_key[i].key_cut = KEY_SCAN_CNT;                //释放消抖
                    }
                }
                break;
            
            case KeyState_WAIT_DOUBLE_CLICK:                            //释放按键消抖,只在双击时释放消抖
                if ( g_key[i].key_cut == 0 )
                {
                    g_key[i].end_time++;                                //等待双击
                    if(g_key[i].end_time > (250-1))                     //超出等待双击时间
                    {
                         g_key[i].key_last = KeyState_PRESSED;          //按键状态最终为短按
                         g_key[i].key_flag = KeyState_IDLE;             //空闲
                         g_key[i].end_time = 0;
                    }
                    else
                    {
                        if(g_key[i].level_state == GPIO_PIN_RESET)      //按键按下
                        {
                            g_key[i].key_flag = KeyState_DOUBLE_CLICKED;//双击状态
                            g_key[i].end_time = 0;
                            g_key[i].key_cut = KEY_SCAN_CNT;            //按下消抖
                        }
                    }
                }
                else
                {
                    g_key[i].key_cut--;
                }
                break;
            case KeyState_DOUBLE_CLICKED:
                if ( g_key[i].key_cut == 0 )                            //双击消抖
                {
                    if(g_key[i].level_state == GPIO_PIN_RESET)          //按键按下
                    {
                        g_key[i].press_time++; 
                    }
                    else
                    {
                        g_key[i].key_last = KeyState_DOUBLE_CLICKED;     //双击
                        g_key[i].key_flag = KeyState_IDLE;
                        g_key[i].press_time = 0;
                        g_key[i].end_time = 0;
                    }
                }
                else
                    g_key[i].key_cut--;
        }
    
    }
}

  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: stm32是一款嵌入式微控制器,具有多达数十个GPIO口。在使用它来控制花样彩灯时,可以利用这些GPIO口来实现单击、双击短按按和组合键等多种控制方式。 对于单击和双击的控制方式,可以通过对GPIO口进行中断编程的方式来实现。当单击或双击时,通过读取GPIO口的状态来判断按键的动作并做出相应的操作。 短按按的控制方式可以利用定时器产生定时中断实现。通过设置定时器的计数周期和中断时间,来判断按键按下的时间短,从而分别实现短按按的控制。 除了以上基本的控制方式,还可以通过组合键的方式实现更多的控制方法。例如,通过同时按下两个或多个按键,来实现多种操作的组合键控制。 最终在程序设计时,需要将以上所有的控制方式进行整合,使其结合起来,来实现更加灵活、多样的花样彩灯控制。因此,对于单击、双击短按按和组合键等控制方式的实现,需要在编程设计中将它们有机地结合起来,以实现更加丰富和灵活的控制。 ### 回答2: STM32是一款微控制器,其支持多种按键控制方式,包括单击、双击短按按和组合键等。利用这些控制方式,我们可以在STM32上实现花样彩灯的控制。 单击控制可以让灯在各种颜色之间切换,双击控制可以让灯的颜色快速变化,短按控制可以让灯的亮度调节,按控制则可以开启或关闭灯。 同时,我们还可以实现组合键控制,例如:双击+短按可以让灯维持在某一特定的颜色,按+短按可以将灯的颜色复位等。 通过这些控制方式的运用,可以让花样彩灯实现更加多样化的效果,丰富了灯的功能性和趣味性。同时,STM32作为一款高性能的微控制器,还可以实现更加复杂的功能控制,根据实际需要,我们可以利用其提供的功能,加入更多有趣的控制方式,让灯的效果更加丰富多彩。 ### 回答3: STM32是一种单片微控制器,它具有丰富的功能和灵活的控制能力。在进行花样彩灯的控制时,可以利用STM32的不同点击方式来实现不同的操作。其中,单击可以控制灯的开关,双击可以切换灯的颜色,短按可以调节亮度,按可以切换灯的模式,组合键可以实现更为复杂的操作。 要实现这些控制功能,需要在STM32中编写相应的程序。具体实现步骤如下: 1. 定义输入引脚:首先需要设置花样彩灯的各种控制引脚。可以将单击、双击短按按和组合键分别定义为不同的输入引脚。 2. 编写中断程序:在STM32中,可以利用中断程序来处理各种输入事件。可以编写不同的中断程序来处理单击、双击短按按和组合键等事件。 3. 实现各种控制功能:根据不同的输入事件,可以实现各种不同的控制功能。比如,单击可以控制灯的开关,双击可以切换灯的颜色,短按可以调节亮度,按可以切换灯的模式,组合键可以实现更为复杂的操作。 4. 调试和优化:在实现控制功能后,需要进行调试和优化,确保程序能够正常运行,并且能够满足各种不同的控制需求。 总体来说,STM32单击、双击短按按和组合键控制花样彩灯,可以实现非常灵活、多样化的控制效果,使得控制更为智能化、便捷化。同时,也需要对STM32的编程技能有一定的了解和掌握,才能够实现各种功能的控制。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值