单片机按键不阻塞检测(单击双击长按)

该驱动用于检测按键,方便拓展,不限平台,只要是用c语言的均可运行。可实现的功能有:单击(按下立即执行和按下松开再执行),双击,长按(支持连续触发长按,也可触发一次)。单击和双击的时间间隔可配置,长按触发时间可配置,长按连续触发间隔时间可配置。话不多说,看代码。

//hal_btn.c
#include "hal_btn.h"
/**
 * @brief  按键扫描
 * @param  *btn:按键扫描的参数配置结构体,以及按键事件输出参数
 * @retval none
 */
void btn_scan(btn_module_t *btn)
{
    btn->btn_event = btn_null;
    switch (btn->index)
    {
    case 0:
        if (btn->pin_state == btn->pree_state)
        {
            btn->index = 1;
        }
        btn->Press_count = 0;
        btn->Press_num_count = 0;
        break;
    case 1:
        if (btn->pin_state == btn->pree_state)
        {
            btn->index = 2;
            btn->btn_event = btn_pree;
        }
        else
        {
            btn->index = 0;
        }
        break;
    case 2:
        if (btn->pin_state == btn->pree_state)
        {
            if (++btn->Press_count > LONG_PREE_TIME)
            {
                btn->Press_count = 0;
                btn->index = 3;
                btn->btn_event = btn_long_pree;
                btn->Press_num_count = 0;
            }
        }
        else
        {
            //按键松开
            if (btn->Press_count && btn->Press_count < LONG_PREE_TIME)
            {
                btn->Press_count = 0;
                btn->index = 4;
                btn->Press_num_count++;
            }
            else
            {
                btn->index = 0;
            }
        }
        break;
    case 3:
        if (btn->pin_state == btn->pree_state)
        {
#if BTN_LONGPREE_CONTINUITY
            if (++btn->Press_count >= LONG_PREE_CONTINUITY_TIME)
            {
                btn->Press_count = 0;
                //连续触发长按间隔
                btn->btn_event = btn_long_pree;
            }
#endif
        }
        else
        {
#if BTN_LONGPREE_CONTINUITY
            btn->Press_count = 0;
#endif
            btn->index = 0;
        }
        break;
    case 4:
        if (btn->pin_state == btn->pree_state)
        {
            btn->index = 1;
            btn->Press_count = 0;
        }
        else
        {
            //按键松开
            if (++btn->Press_count >= INTERVAL_TIMEOUT_TIME)
            {
                btn->Press_count = 0;
                //多击间隔超时
                btn->index = 5;
            }
        }
        break;
    case 5:
        if (btn->Press_num_count == 1)
        {
            btn->btn_event = btn_short_pree;
        }
        else if (btn->Press_num_count == 2)
        {
            btn->btn_event = btn_double_pree;
        }

        btn->Press_num_count = 0;
        btn->index = 0;
        break;
    default:
        btn->index = 0;
        btn->btn_event = btn_null;
        break;
    }
}
/******************使用方法如下*******************/
//例1:该方式将直接处理按键事件,整体较为直观,但会使得按键驱动变得臃肿
/*10ms循环调用该函数 btn1_handle(); ,函数名可自定义,调用的时间决定了配置的时间参数,因使用 unsigned char 类型,所以10ms调用可以满足大部分需求。当然,可以根据需求更改成员变量的类型*/
btn_module_t btn1 = {0};
void btn1_handle(void)
{
    btn1.pree_state = 0;//按键按下的电平
    btn1.pin_state = PIN_BTN1;//按键对应的IO口
    btn_scan(&btn1);

    if (btn1.btn_event == btn_pree)
    {
        //按下立即执行
    }
    if (btn1.btn_event == btn_short_pree)
    {
        //短按松开执行
    }
    if (btn1.btn_event == btn_double_pree)
    {
        //双击
    }
    if (btn1.btn_event == btn_long_pree)
    {
        //长按
    }
}
//例2:事件函数调用,该方式的按键响应可在其他文件定义,使得按键驱动变得简洁
/*10ms循环调用该函数 btn2_handle(); ,函数名可自定义调用的时间决定了配置的时间参数,因使用 unsigned char 类型,所以10ms调用可以满足大部分需求。当然,可以根据需求更改成员变量的类型*/
btn_module_t btn2 = {0};
void btn2_handle(void)
{
    btn2.pree_state = 0;//按键按下的电平
    btn2.pin_state = PIN_BTN2;//按键对应的IO口
    btn_scan(&btn2);

    if (btn2.btn_event == btn_pree)
    {
        //按下立即执行
         btn2_pree_event();
    }
    if (btn2.btn_event == btn_short_pree)
    {
        //短按松开执行
        btn2_short_event();
    }
    if (btn2.btn_event == btn_double_pree)
    {
        //双击
        btn2_double_event();
    }
    if (btn2.btn_event == btn_long_pree)
    {
        //长按
        btn2_long_event(); 
    }
}

头文件的声明,可配置的参数在此处声明,以及IO的相关宏定义

//hal_btn.h
#ifndef _HAL_BTN_H
#define _HAL_BTN_H
#include "main.h"
typedef enum
{
    btn_null = 0x00,
    btn_pree = 0x02,
    btn_short_pree = 0x03,
    btn_double_pree = 0x04,
    btn_long_pree = 0x05,
} btn_event_t;
typedef struct
{
    btn_event_t btn_event;
    unsigned char index;
    unsigned char Press_count;
    unsigned char Press_num_count;
    unsigned char pin_state : 1;
    unsigned char pree_state : 1;
} btn_module_t;

#define LONG_PREE_TIME 90        //触发长按时间,触发长按事件的时间,注:该参数是触发长按事件的时间,跟长按连续触发间隔有区别
#define INTERVAL_TIMEOUT_TIME 5  //多击间隔超时时间,该值影响单击松开执行和双击的判定

#define BTN_LONGPREE_CONTINUITY 0 //连续触发长按使能
#if BTN_LONGPREE_CONTINUITY
#define LONG_PREE_CONTINUITY_TIME 60 //连续触发长按间隔,影响连续触发的间隔
#endif

#define PIN_BTN1 P20
void btn1_handle(void);

#define PIN_BTN2 P21
void btn2_handle(void);

#endif

按键检测的大致流程图,流程图有些细节可能没有体现,或者是流程图有问题,仅作参考
请添加图片描述

如有问题可留言,如有问题,麻烦告知,多谢您的支持!

独立按键是指在代码中只要检测按键被按下或松开就能触发相应的操作。在C语言中,可以使用外部中断引脚来实现独立按键单击双击按功能。 首先,需要使用适当的库函数来初始化和配置外部中断引脚。例如,如果使用的是8051单片机系列,可以使用相应的库函数来配置外部中断引脚。 然后,在主程序中,可以通过判断中断标志位的变化来实现按键单击双击按功能。以下是一个简单的示例代码: #include "reg51.h" //引入相关的头文件,具体根据使用的单片机而定 volatile unsigned int click_count = 0; //单击次数计数 volatile unsigned int long_press_count = 0; //按计数 //中断服务函数 void interrupt_func() interrupt 0 //中断号根据具体的中断配置而定 { if (/* 判断按键被按下 */) { click_count++; if (click_count == 2) { /* 双击操作代码 */ } } else if (/* 判断按键被松开 */) { if (click_count == 1) { /* 单击操作代码 */ } click_count = 0; } if (/* 判断按键按 */) { long_press_count++; if (long_press_count >= /* 设置按阈值 */) { /* 按操作代码 */ } } else { long_press_count = 0; } } 这是一种简单的实现方法,具体的代码根据使用的单片机和外部中断配置有所差异。在这个例子中,通过计数器变量来记录按键单击次数和按的时间,根据计数器的值来判断触发相应的操作。需要根据具体的需求和硬件平台来调整和完善代码。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值