一个强大的嵌入式按键处理驱动库-AhFeiButton(C语言嵌入式版)长按,短按,双击,单机,自锁按键等

AhFeiButton

         是一个用于小型嵌入式平台的按键处理库。采用了类似MVC的架构:驱动层, 逻辑层,应用层。理论上支持普通按键与自锁按键,并可以无限扩展按键数量, 支持的事件有:单击,双击,短按,长按,多次点按,按下,松开。另外,AhFeiButton使用扫描的方式一次性读取所有所有的按键状态,然后通过事件回调机制上报按键事件。最强大的一点是可以为每个button做个性化配置,比如说,button1按下多长时间为短按,多长时间为长按,间隔多长时间为双击,每个button单独配置,灵活性强,能适应强复杂度场景,如果按照平常写法,多几个有特色的按键,你的if else 和标志位就是满天飞了,逻辑还不好维护。

该按键库使用 C 语言编写,驱动与应用程序解耦,便于灵活应用,比如用户可以方便地在应用层增加按键中断、处理按键功耗、定义按键事件处理方式,而无需修改API源码。支持32位平台,以及所有的OS平台,示例为STM32G474+RT-Thread+IO设备驱动.

原文链接

        http://t.csdn.cn/rgppP

代码讲解

        代码中已经写了足够的注释,这里就不再赘述,请看代码。

AhFeiButton.c

/*********************************************************************************
  *Copyright(C) -
  *FileName: ahfei_button.c
  *Author: 我不是阿沸
  *Version: 1.1
  *Date: 2023.08.28
  *Description:  主要用于复杂的按键操作场景,比如说一个按键需要实现加加减减菜单确认等多种功能,都可以用这个库  
  *Others:   
  *History:  
     1.Date:2023/08/29
       Author:我不是阿沸 
       Modification:修改双击相关处理流程。
	 2.其他修改
  *Copyright Notice:本文为CSDN博主「我不是阿沸」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
  *Original link:https://blog.csdn.net/qq_53828053/article/details/132497784	 
**********************************************************************************/
 

#include "ahfeiible_button.h"
#include <string.h>
#include <stdio.h>

static ahfei_button_t *btn_head = NULL;

#define EVENT_CB_EXECUTOR(button) if(button->cb) button->cb((ahfei_button_t*)button)
#define MAX_BUTTON_CNT 16

static uint16_t trg = 0;
static uint16_t cont = 0;
static uint16_t keydata = 0xFFFF;
static uint16_t key_rst_data = 0xFFFF;
static uint8_t button_cnt = 0;

/**
 * @brief 	按键注册
 * 
 * @param 	flex_button_t 结构体实例的地址
 * @return 	返回一个按键索引
*/
int8_t ahfei_button_register(ahfei_button_t *button)
{
    ahfei_button_t *curr = btn_head;
    
    if (!button || (button_cnt > MAX_BUTTON_CNT))
    {
        return -1;
    }

    while (curr)
    {
        if(curr == button)
        {
            return -1; 
        }
        curr = curr->next;
    }

    button->next = btn_head;
    button->status = 0;
    button->event = AHFEI_BTN_PRESS_NONE;
    button->scan_cnt = 0;
    button->click_cnt = 0;
    btn_head = button;
    key_rst_data = key_rst_data << 1;
    button_cnt ++;
    return button_cnt;
}


/**
 * @brief 	按键读取
 * 
 * @param 	void
 * @return 	none
*/
static void ahfei_button_read(void)
{
    ahfei_button_t* target;
    uint16_t read_data = 0;
    keydata = key_rst_data;
    int8_t i = 0;

    for(target = btn_head, i = 0;
        (target != NULL) && (target->usr_button_read != NULL);
        target = target->next, i ++)
    {
        keydata = keydata |
                  (target->pressed_logic_level == 1 ?
                  ((!(target->usr_button_read)()) << i) :
                  ((target->usr_button_read)() << i));
    }

    read_data = keydata^0xFFFF;
    trg = read_data & (read_data ^ cont);
    cont = read_data;
}


/**
 * @brief 	按键扫描
 * @param 	void
 * @return 	None
*/
static void ahfei_button_process(void)
{
    int8_t i = 0;
    ahfei_button_t* target;

    for (target = btn_head, i = 0; target != NULL; target = target->next, i ++)
    {
        if (target->status > 0)
        {
            target->scan_cnt ++;
        }

        switch (target->status)
        {
            case 0: 
                if (trg & (1 << i)) 
                {
                    target->scan_cnt = 0;
                    target->click_cnt = 0;
                    target->status = 1;
                    target->event = AHFEI_BTN_PRESS_DOWN;
                    EVENT_CB_EXECUTOR(target);
                }
                else
                {
                    target->event = AHFEI_BTN_PRESS_NONE;
                }
                break;

            case 1: 
                if (!(cont & (1 << i))) 
                {
                    target->status = 2;
                }
                else if ((target->scan_cnt >= target->short_press_start_tick) &&
                        (target->scan_cnt < target->long_press_start_tick))
                {
                    target->status = 4;
                    target->event = AHFEI_BTN_PRESS_SHORT_START;
                    EVENT_CB_EXECUTOR(target);
                }
                break;

            case 2: 
                if ((target->scan_cnt < target->click_start_tick))
                {
                    target->click_cnt++; // 1
                    
                    if (target->click_cnt == 1)
                    {
                        target->status = 3;  
                    }
                    else
                    {
                        target->click_cnt = 0;
                        target->status = 0;
                        target->event = AHFEI_BTN_PRESS_DOUBLE_CLICK;
                        EVENT_CB_EXECUTOR(target);
                    }
                }
                else if ((target->scan_cnt >= target->click_start_tick) &&
                    (target->scan_cnt < target->short_press_start_tick))
                {
                    target->click_cnt = 0;
                    target->status = 0;
                    target->event = AHFEI_BTN_PRESS_CLICK;
                    EVENT_CB_EXECUTOR(target);
                }
                else if ((target->scan_cnt >= target->short_press_start_tick) &&
                    (target->scan_cnt < target->long_press_start_tick))
                {
                    target->click_cnt = 0;
                    target->status = 0;
                    target->event = AHFEI_BTN_PRESS_SHORT_UP;
                    EVENT_CB_EXECUTOR(target);
                }
                else if ((target->scan_cnt >= target->long_press_start_tick) &&
                    (target->scan_cnt < target->long_hold_start_tick))
                {
                    target->click_cnt = 0;
                    target->status = 0;
                    target->event = AHFEI_BTN_PRESS_LONG_UP;
                    EVENT_CB_EXECUTOR(target);
                }
                else if (target->scan_cnt >= target->long_hold_start_tick)
                {
                   
                    target->click_cnt = 0;
                    target->status = 0;
                    target->event = AHFEI_BTN_PRESS_LONG_HOLD_UP;
                    EVENT_CB_EXECUTOR(target);
                }
                break;

            case 3: 
                if (trg & (1 << i))
                {
                    target->click_cnt++;
                    target->status = 2;
                    target->scan_cnt --;
                }
                else if (target->scan_cnt >= target->click_start_tick)
                {
                    target->status = 2;
                }
                break;

            case 4: 
                if (!(cont & (1 << i)))
                {
                    target->status = 2;
                }
                else if ((target->scan_cnt >= target->long_press_start_tick) &&
                        (target->scan_cnt < target->long_hold_start_tick))
                {
                    target->status = 5;
                    target->event = AHFEI_BTN_PRESS_LONG_START;
                    EVENT_CB_EXECUTOR(target);
                }
                break;

            case 5: 
                if (!(cont & (1 << i))) 
                {
                    target->status = 2;
                }
                else if (target->scan_cnt >= target->long_hold_start_tick)
                {
                    target->status = 6;
                    target->event = AHFEI_BTN_PRESS_LONG_HOLD;
                    EVENT_CB_EXECUTOR(target);
                }
                break;

            case 6:
                if (!(cont & (1 << i))) 
                {
                    target->status = 2;
                }
                break;
        }
    }
}


/**
 * @brief 	获取指定按键的事件。
 * @param 	需要获取的按键.
 * @return 	返回按键事件
*/
ahfei_button_event_t ahfei_button_event_read(ahfei_button_t* button)
{
    return (ahfei_button_event_t)(button->event);
}


/**
 * @brief 	按键扫描任务。
 * @param 	void
 * @return 	none
*/
void ahfei_button_scan(void)
{
    ahfei_button_read();
    ahfei_button_process();
}

AhFeiButton.h

/*********************************************************************************
  *Copyright(C) -
  *FileName: ahfei_button.c
  *Author: 我不是阿沸
  *Version: 1.1
  *Date: 2023.08.28
  *Description:  主要用于复杂的按键操作场景,比如说一个按键需要实现加加减减菜单确认等多种功能,都可以用这个库  
  *Others:   
  *History:  
     1.Date:2023/08/29
       Author:我不是阿沸 
       Modification:修改双击相关处理流程。
	 2.其他修改
  *Copyright Notice:本文为CSDN博主「我不是阿沸」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
  *Original link:https://blog.csdn.net/qq_53828053/article/details/132497784	 
**********************************************************************************/

#ifndef __AHFEIIBLE_BUTTON_H__
#define __AHFEIIBLE_BUTTON_H__

#include "stdint.h"
#include "string.h"

typedef void (*ahfei_button_response_callback)(void*);

typedef enum
{
    AHFEI_BTN_PRESS_DOWN = 0,        // 按下事件
    AHFEI_BTN_PRESS_CLICK,           // 单击事件
    AHFEI_BTN_PRESS_DOUBLE_CLICK,    // 双击事件
    AHFEI_BTN_PRESS_SHORT_START,     // 短按开始事件
    AHFEI_BTN_PRESS_SHORT_UP,        // 短按抬起事件
    AHFEI_BTN_PRESS_LONG_START,      // 长按开始事件
    AHFEI_BTN_PRESS_LONG_UP,         // 长按抬起事件
    AHFEI_BTN_PRESS_LONG_HOLD,       // 长按保持事件
    AHFEI_BTN_PRESS_LONG_HOLD_UP,    // 长按保持的抬起事件
    AHFEI_BTN_PRESS_MAX,			 //最大
    AHFEI_BTN_PRESS_NONE,            //
} ahfei_button_event_t;

/**
 * ahfei_button_t
 * 
 * @brief  按钮数据结构,需要在扫描前使用init的成员。
 * @member pressed_logic_level:    设置按键按下的逻辑电平。1:高电平为按下状态;0:低电平为按下状态
 *                                 Must be inited by 'ahfei_button_register' API                                    before start button scan.
 * @member debounce_tick:          消抖时间,默认为 0,可以不配置,依靠扫描间隙进行消抖            
 * @member click_start_tick:       设置触发按键按下事件的起始 tick,有消抖效果         
 * @member short_press_start_tick: 设置短按事件触发的起始 tick                   
 * @member long_press_start_tick:  设置长按事件触发的起始 tick                    
 * @member long_hold_start_tick:   设置长按保持事件触发的起始 tick                         
 * @member usr_button_read:        用户设备的按键引脚电平读取函数
 * @member cb:                     设置按键事件回调,用于应用层对按键事件的分类处理
 *                                 如果使用“ahfei_button_event_read”api,您不需要初始化“cb”成员。
 * @member next :                  按键库使用单向链表串起所有的按键配置
*/
typedef struct ahfei_button
{
    uint8_t pressed_logic_level : 1; /* need user to init */

    /**
     * @event
     * The event of button in ahfei_button_evnt_t enum list.
     * Automatically initialized to the default value ahfei_BTN_PRESS_NONE
     *                                      by 'ahfei_button_register' API.
    */
    uint8_t event               : 4;

    /**
     * @status
     * Used to record the status of the button 
     * Automatically initialized to the default value 0.
    */
    uint8_t status              : 3;
    uint16_t scan_cnt;  /* default 0. Used to record the number of key scans */
    uint16_t click_cnt; /* default 0. Used to record the number of key click */

    uint16_t debounce_tick;          
    uint16_t click_start_tick;
    uint16_t short_press_start_tick;
    uint16_t long_press_start_tick;
    uint16_t long_hold_start_tick;

    uint8_t  (*usr_button_read)(void);
    ahfei_button_response_callback  cb;
    struct ahfei_button* next;
} ahfei_button_t;


#ifdef __cplusplus
extern "C" {
#endif

int8_t ahfei_button_register(ahfei_button_t * button);
ahfei_button_event_t ahfei_button_event_read(ahfei_button_t * button);
void ahfei_button_scan(void);

#ifdef __cplusplus
}
#endif  
#endif /* __ahfeiIBLE_BUTTON_H__ */

demo

#include "button.h"
#include "pin.h"


#define PIN_1 GET_PIN(B, 5)


typedef enum
{
    USER_BUTTON_0 = 0,
    USER_BUTTON_MAX
} user_button_t;

static ahfei_button_t user_button[USER_BUTTON_MAX];

static void btn_0_cb(ahfei_button_t *btn)
{
    rt_kprintf("btn_0_cb\n");
    switch (btn->event)
    {
        case AHFEI_BTN_PRESS_DOWN:
            rt_kprintf("btn_0_cb [AHFEI_BTN_PRESS_DOWN]\n");
            break;
        case AHFEI_BTN_PRESS_CLICK:
            rt_kprintf("btn_0_cb [AHFEI_BTN_PRESS_CLICK]\n");
            break;
        case AHFEI_BTN_PRESS_DOUBLE_CLICK:
            rt_kprintf("btn_0_cb [AHFEI_BTN_PRESS_DOUBLE_CLICK]\n");
            break;
        case AHFEI_BTN_PRESS_SHORT_START:
            rt_kprintf("btn_0_cb [AHFEI_BTN_PRESS_SHORT_START]\n");
            break;
        case AHFEI_BTN_PRESS_SHORT_UP:
            rt_kprintf("btn_0_cb [AHFEI_BTN_PRESS_SHORT_UP]\n");
            break;
        case AHFEI_BTN_PRESS_LONG_START:
            rt_kprintf("btn_0_cb [AHFEI_BTN_PRESS_LONG_START]\n");
            break;
        case AHFEI_BTN_PRESS_LONG_UP:
            rt_kprintf("btn_0_cb [AHFEI_BTN_PRESS_LONG_UP]\n");
            break;
        case AHFEI_BTN_PRESS_LONG_HOLD:
            rt_kprintf("btn_0_cb [AHFEI_BTN_PRESS_LONG_HOLD]\n");
            break;
        case AHFEI_BTN_PRESS_LONG_HOLD_UP:
            rt_kprintf("btn_0_cb [AHFEI_BTN_PRESS_LONG_HOLD_UP]\n");
            break;
    }
}


static uint8_t button_key0_read(void)
{
    return rt_pin_read(PIN_1);
}


static void button_scan(void *arg)
{
    while(1)
    {
        ahfei_button_scan();
        rt_thread_mdelay(20);
    }
}

static void user_button_init(void)
{
    int i;
    
    rt_memset(&user_button[0], 0x0, sizeof(user_button));

    user_button[USER_BUTTON_0].usr_button_read = button_key0_read;
    user_button[USER_BUTTON_0].cb = (ahfei_button_response_callback)btn_0_cb;

    rt_pin_mode(PIN_1, PIN_MODE_INPUT); /* set KEY pin mode to input */

    for (i = 0; i < USER_BUTTON_MAX; i ++)
    {
        user_button[i].pressed_logic_level = 0;
        user_button[i].click_start_tick = 20;
        user_button[i].short_press_start_tick = 100;
        user_button[i].long_press_start_tick = 200;
        user_button[i].long_hold_start_tick = 300;

        ahfei_button_register(&user_button[i]);
    }
}

int ahfei_button_main(void)
{
    rt_thread_t tid = RT_NULL;

    user_button_init();

    /* Create background ticks thread */
    tid = rt_thread_create("ahfei_btn", button_scan, RT_NULL, 1024, 10, 10);
    if(tid != RT_NULL)
    {
        rt_thread_startup(tid);
    }

    return 0;
}










  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在C语言中实现按键按、短按双击功能可以通过多线程的方式来实现。 首先,我们需要一个全局变量来记录按键的状态,比如键盘按下状态(pressed)和释放状态(released)。然后,我们可以使用一个线程来不断地监测键盘的状态,判断按键按、短按双击。 对于按功能,我们可以通过判断按键的按下时间是否超过设定的时间来实现。当按键按下时,我们记录下按下的时间,然后在监测线程中判断按键是否已经释放,如果按键释放时间大于设定的时间,则判定为按。 对于短按功能,我们可以在按键按下时记录下按下的时间,在监测线程中判断按键是否已经释放,如果按键释放时间小于设定的时间,则判定为短按。 对于双击功能,我们可以在监测线程中记录下按键的按下时间和释放时间,如果两次按键的时间间隔小于设定的时间,则判定为双击。 在实现按键功能时,需要注意多线程的同步问题,可以使用锁来确保多个线程对全局变量的操作不会冲突。 以上是一个简单的思路,具体的实现还需要根据具体的应用场景进行调整。同时,还需要考虑到编译器和硬件的差异,以及其他相关的因素,来确保按键功能能够在不同的环境中正常运行。 ### 回答2: 在C语言中,我们可以通过监听键盘的按键事件,来实现检测按键按、短按双击效果。 首先,我们需要定义一些变量来记录按键的状态和时间。我们可以使用一个布尔型变量来表示按键的状态,例如isPressed表示按键是否被按下,以及两个整型变量来记录按键按下和松开的时间。 对于检测按效果,我们可以在每次按键被按下时记录当前的系统时间,并通过不断监听键盘事件来检测按键是否持续按下。如果按键一直被按下,并且按键按下的时间超过一定的阈值(可以自行设定),就可以判断为按。 对于检测短按效果,我们可以判断按键是否在很短的时间内被按下并松开,例如在100毫秒内,即可判断为短按。 对于检测双击效果,我们可以通过记录按键按下的次数来实现。每次按键按下时,我们可以检测两次按键的时间间隔是否在一定的范围内(比如200毫秒内),如果是,则可以判断为双击效果。 总之,通过监听键盘事件,并结合一些时间和状态的记录,我们可以在C语言中实现按键按、短按双击效果。 ### 回答3: C语言中,我们可以利用计时器和状态机来检测按键按、短按双击操作。 首先,我们需要定义一个变量作为计时器,用来记录按键被按下的时间。我们假设按下按键时,将这个变量初始化为0。 当按键被按下后,我们可以在主循环中进行延时操作,然后检测按键是否仍然被按下,如果仍然被按下,就将计时器加1。 接着,我们可以设置一个阈值,用来判断按键按下的时间是短按还是按。如果计时器记录的时间小于阈值,就可以判定为短按;如果计时器记录的时间大于等于阈值,就可以判定为按。 同时,我们还需要维护一个状态机来记录双击操作。当按键短按时,状态机将记录这一次短按操作,并等待一定的时间,如果这段时间内再次检测到按键短按,就可以判定为双击操作。 综上所述,通过利用计时器和状态机,我们可以在C语言中实现按键按、短按双击功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值