open source----Flexible button按键

一个挺好用的开源的代码…按键
按键对应的开发应该大家都有用过…
这里就先不大概讲解原理,估计很多人都用过。我最近也在用,功能得话包括:单击、双击、多击、组合键,长按、长按hold住、长按松开等,可以调按键时序。直接看代码吧

这两个就是主要的open source,按键灵魂,.c文件这里给到得是确定几个按键、扫描按键状态等信息。

Flexible_button.c

#include "flexible_button.h"

#ifndef NULL
#define NULL 0
#endif

#define EVENT_SET_AND_EXEC_CB(btn, evt)                                        \
    do                                                                         \
    {                                                                          \
        btn->event = evt;                                                      \
        if(btn->cb)                                                            \
            btn->cb((flex_button_t*)btn);                                      \
    } while(0)

/**
 * BTN_IS_PRESSED
 * 
 * 1: is pressed
 * 0: is not pressed
*/
#define BTN_IS_PRESSED(i) (g_btn_status_reg & (1 << i))

enum FLEX_BTN_STAGE
{
    FLEX_BTN_STAGE_DEFAULT = 0,
    FLEX_BTN_STAGE_DOWN    = 1,
    FLEX_BTN_STAGE_MULTIPLE_CLICK = 2
};

typedef uint32_t btn_type_t;

static flex_button_t *btn_head = NULL;

/**
 * g_logic_level
 * 
 * The logic level of the button pressed, 
 * Each bit represents a button.
 * 
 * First registered button, the logic level of the button pressed is 
 * at the low bit of g_logic_level.
*/
btn_type_t g_logic_level = (btn_type_t)0;

/**
 * g_btn_status_reg
 * 
 * The status register of all button, each bit records the pressing state of a button.
 * 
 * First registered button, the pressing state of the button is 
 * at the low bit of g_btn_status_reg.
*/
btn_type_t g_btn_status_reg = (btn_type_t)0;

static uint8_t button_cnt = 0;

/**
 * @brief Register a user button
 * 
 * @param button: button structure instance
 * @return Number of keys that have been registered, or -1 when error
*/
int32_t flex_button_register(flex_button_t *button)
{
    flex_button_t *curr = btn_head;
    
    if (!button || (button_cnt > sizeof(btn_type_t) * 8))
    {
        return -1;
    }

    while (curr)
    {
        if(curr == button)
        {
            return -1;  /* already exist. */
        }
        curr = curr->next;
    }

    /**
     * First registered button is at the end of the 'linked list'.
     * btn_head points to the head of the 'linked list'.
    */
    button->next = btn_head;
    button->status = FLEX_BTN_STAGE_DEFAULT;
    button->event = FLEX_BTN_PRESS_NONE;
    button->scan_cnt = 0;
    button->click_cnt = 0;
    button->max_multiple_clicks_interval = MAX_MULTIPLE_CLICKS_INTERVAL;
    btn_head = button;

    /**
     * First registered button, the logic level of the button pressed is 
     * at the low bit of g_logic_level.
    */
    g_logic_level |= (button->pressed_logic_level << button_cnt);
    button_cnt ++;

    return button_cnt;
}

/**
 * @brief Read all key values in one scan cycle
 * 
 * @param void
 * @return none
*/
static void flex_button_read(void)
{
    uint8_t i;
    flex_button_t* target;

    /* The button that was registered first, the button value is in the low position of raw_data */
    btn_type_t raw_data = 0;

    for(target = btn_head, i = button_cnt - 1;
        (target != NULL) && (target->usr_button_read != NULL);
        target = target->next, i--)
    {
        raw_data = raw_data | ((target->usr_button_read)(target) << i);
    }

    g_btn_status_reg = (~raw_data) ^ g_logic_level;
}

/**
 * @brief Handle all key events in one scan cycle.
 *        Must be used after 'flex_button_read' API
 * 
 * @param void
 * @return Activated button count
*/
static uint8_t flex_button_process(void)
{
    uint8_t i;
    uint8_t active_btn_cnt = 0;
    flex_button_t* target;
    
    for (target = btn_head, i = button_cnt - 1; target != NULL; target = target->next, i--)
    {
        if (target->status > FLEX_BTN_STAGE_DEFAULT)
        {
            target->scan_cnt ++;
            if (target->scan_cnt >= ((1 << (sizeof(target->scan_cnt) * 8)) - 1))
            {
                target->scan_cnt = target->long_hold_start_tick;
            }
        }

        switch (target->status)
        {
        case FLEX_BTN_STAGE_DEFAULT: /* stage: default(button up) */
            if (BTN_IS_PRESSED(i)) /* is pressed */
            {
                target->scan_cnt = 0;
                target->click_cnt = 0;

                EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_DOWN);

                /* swtich to button down stage */
                target->status = FLEX_BTN_STAGE_DOWN;
            }
            else
            {
                target->event = FLEX_BTN_PRESS_NONE;
            }
            break;

        case FLEX_BTN_STAGE_DOWN: /* stage: button down */
            if (BTN_IS_PRESSED(i)) /* is pressed */
            {
                if (target->click_cnt > 0) /* multiple click */
                {
                    if (target->scan_cnt > target->max_multiple_clicks_interval)
                    {
                        EVENT_SET_AND_EXEC_CB(target, 
                            target->click_cnt < FLEX_BTN_PRESS_REPEAT_CLICK ? 
                                target->click_cnt :
                                FLEX_BTN_PRESS_REPEAT_CLICK);

                        /* swtich to button down stage */
                        target->status = FLEX_BTN_STAGE_DOWN;
                        target->scan_cnt = 0;
                        target->click_cnt = 0;
                    }
                }
                else if (target->scan_cnt >= target->long_hold_start_tick)
                {
                    if (target->event != FLEX_BTN_PRESS_LONG_HOLD)
                    {
                        EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_LONG_HOLD);
                    }
                }
                else if (target->scan_cnt >= target->long_press_start_tick)
                {
                    if (target->event != FLEX_BTN_PRESS_LONG_START)
                    {
                        EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_LONG_START);
                    }
                }
                else if (target->scan_cnt >= target->short_press_start_tick)
                {
                    if (target->event != FLEX_BTN_PRESS_SHORT_START)
                    {
                        EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_SHORT_START);
                    }
                }
            }
            else /* button up */
            {
                if (target->scan_cnt >= target->long_hold_start_tick)
                {
                    EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_LONG_HOLD_UP);
                    target->status = FLEX_BTN_STAGE_DEFAULT;
                }
                else if (target->scan_cnt >= target->long_press_start_tick)
                {
                    EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_LONG_UP);
                    target->status = FLEX_BTN_STAGE_DEFAULT;
                }
                else if (target->scan_cnt >= target->short_press_start_tick)
                {
                    EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_SHORT_UP);
                    target->status = FLEX_BTN_STAGE_DEFAULT;
                }
                else
                {
                    /* swtich to multiple click stage */
                    target->status = FLEX_BTN_STAGE_MULTIPLE_CLICK;
                    target->click_cnt ++;
                }
            }
            break;

        case FLEX_BTN_STAGE_MULTIPLE_CLICK: /* stage: multiple click */
            if (BTN_IS_PRESSED(i)) /* is pressed */
            {
                /* swtich to button down stage */
                target->status = FLEX_BTN_STAGE_DOWN;
                target->scan_cnt = 0;
            }
            else
            {
                if (target->scan_cnt > target->max_multiple_clicks_interval)
                {
                    EVENT_SET_AND_EXEC_CB(target, 
                        target->click_cnt < FLEX_BTN_PRESS_REPEAT_CLICK ? 
                            target->click_cnt :
                            FLEX_BTN_PRESS_REPEAT_CLICK);

                    /* swtich to default stage */
                    target->status = FLEX_BTN_STAGE_DEFAULT;
                }
            }
            break;
        }
        
        if (target->status > FLEX_BTN_STAGE_DEFAULT)
        {
            active_btn_cnt ++;
        }
    }
    
    return active_btn_cnt;
}

/**
 * flex_button_event_read
 * 
 * @brief Get the button event of the specified button.
 * 
 * @param button: button structure instance
 * @return button event
*/
flex_button_event_t flex_button_event_read(flex_button_t* button)
{
    return (flex_button_event_t)(button->event);
}

/**
 * flex_button_scan
 * 
 * @brief Start key scan.
 *        Need to be called cyclically within the specified period.
 *        Sample cycle: 5 - 20ms
 * 
 * @param void
 * @return Activated button count
*/
uint8_t flex_button_scan(void)
{
    flex_button_read();
    return flex_button_process();
}

Flexible_button.h

#ifndef __FLEXIBLE_BUTTON_H__
#define __FLEXIBLE_BUTTON_H__

#include "stdint.h"

#define FLEX_BTN_SCAN_FREQ_HZ 50 // How often flex_button_scan () is called
#define FLEX_MS_TO_SCAN_CNT(ms) (ms / (1000 / FLEX_BTN_SCAN_FREQ_HZ))

/* Multiple clicks interval, default 300ms */
#define MAX_MULTIPLE_CLICKS_INTERVAL (FLEX_MS_TO_SCAN_CNT(300))

typedef void (*flex_button_response_callback)(void*);

typedef enum
{
    FLEX_BTN_PRESS_DOWN = 0,
    FLEX_BTN_PRESS_CLICK,
    FLEX_BTN_PRESS_DOUBLE_CLICK,
    FLEX_BTN_PRESS_REPEAT_CLICK,
    FLEX_BTN_PRESS_SHORT_START,
    FLEX_BTN_PRESS_SHORT_UP,
    FLEX_BTN_PRESS_LONG_START,
    FLEX_BTN_PRESS_LONG_UP,
    FLEX_BTN_PRESS_LONG_HOLD,
    FLEX_BTN_PRESS_LONG_HOLD_UP,
    FLEX_BTN_PRESS_MAX,
    FLEX_BTN_PRESS_NONE,
} flex_button_event_t;

/**
 * flex_button_t
 * 
 * @brief Button data structure
 *        Below are members that need to user init before scan.
 * 
 * @member next
 *         Internal use.
 *         One-way linked list, pointing to the next button.
 * 
 * @member usr_button_read
 *         User function is used to read button vaule.
 * 
 * @member cb
 *         Button event callback function.
 * 
 * @member scan_cnt
 *         Internal use, user read-only.
 *         Number of scans, counted when the button is pressed, plus one per scan cycle.
 * 
 * @member click_cnt
 *         Internal use, user read-only.
 *         Number of button clicks
 * 
 * @member max_multiple_clicks_interval
 *         Multiple click interval. Default 'MAX_MULTIPLE_CLICKS_INTERVAL'.
 *         Need to use FLEX_MS_TO_SCAN_CNT to convert milliseconds into scan cnts.
 * 
 * @member debounce_tick
 *         Debounce. Not used yet.
 *         Need to use FLEX_MS_TO_SCAN_CNT to convert milliseconds into scan cnts.
 * 
 * @member short_press_start_tick
 *         Short press start time. Requires user configuration.
 *         Need to use FLEX_MS_TO_SCAN_CNT to convert milliseconds into scan cnts.
 * 
 * @member long_press_start_tick
 *         Long press start time. Requires user configuration.
 *         Need to use FLEX_MS_TO_SCAN_CNT to convert milliseconds into scan cnts.
 * 
 * @member long_hold_start_tick
 *         Long hold press start time. Requires user configuration.
 * 
 * @member id
 *         Button id. Requires user configuration.
 *         When multiple buttons use the same button callback function, 
 *         they are used to distinguish the buttons. 
 *         Each button id must be unique.
 * 
 * @member pressed_logic_level
 *         Requires user configuration.
 *         The logic level of the button pressed, each bit represents a button.
 * 
 * @member event
 *         Internal use, users can call 'flex_button_event_read' to get current button event.
 *         Used to record the current button event.
 * 
 * @member status
 *         Internal use, user unavailable.
 *         Used to record the current state of buttons.
 * 
*/
typedef struct flex_button
{
    struct flex_button* next;

    uint8_t  (*usr_button_read)(void *);
    flex_button_response_callback  cb;

    uint16_t scan_cnt;
    uint16_t click_cnt;
    uint16_t max_multiple_clicks_interval;

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

    uint8_t id;
    uint8_t pressed_logic_level : 1;
    uint8_t event               : 4;
    uint8_t status              : 3;
} flex_button_t;

#ifdef __cplusplus
extern "C" {
#endif

int32_t flex_button_register(flex_button_t *button);
flex_button_event_t flex_button_event_read(flex_button_t* button);
uint8_t flex_button_scan(void);

#ifdef __cplusplus
}
#endif  
#endif /* __FLEXIBLE_BUTTON_H__ */

具体看如何使用

#define BUTTON_DEBUG
#define ENUM_TO_STR(e) (#e)
typedef enum
{
    USER_POWER_BUTTON = 0,
    USER_BT_PAIR_BUTTON,
    USER_ENCODER_BUTTON,
    USER_LOOPER1_BUTTON,
    USER_LOOPER2_BUTTON,
    USER_LOOPER3_BUTTON,
    USER_LOOPER4_BUTTON,
    USER_BUTTON_MAX
} user_button_t;

static char *enum_event_string[] = {
    ENUM_TO_STR(FLEX_BTN_PRESS_DOWN),
    ENUM_TO_STR(FLEX_BTN_PRESS_CLICK),
    ENUM_TO_STR(FLEX_BTN_PRESS_DOUBLE_CLICK),
    ENUM_TO_STR(FLEX_BTN_PRESS_REPEAT_CLICK),
    ENUM_TO_STR(FLEX_BTN_PRESS_SHORT_START),
    ENUM_TO_STR(FLEX_BTN_PRESS_SHORT_UP),
    ENUM_TO_STR(FLEX_BTN_PRESS_LONG_START),
    ENUM_TO_STR(FLEX_BTN_PRESS_LONG_UP),
    ENUM_TO_STR(FLEX_BTN_PRESS_LONG_HOLD),
    ENUM_TO_STR(FLEX_BTN_PRESS_LONG_HOLD_UP),
    ENUM_TO_STR(FLEX_BTN_PRESS_MAX),
    ENUM_TO_STR(FLEX_BTN_PRESS_NONE),
};

static char *enum_btn_id_string[] = {
    ENUM_TO_STR(USER_POWER_BUTTON),
    ENUM_TO_STR(USER_BT_PAIR_BUTTON),
    ENUM_TO_STR(USER_LOOPER1_BUTTON),
    ENUM_TO_STR(USER_LOOPER2_BUTTON),
    ENUM_TO_STR(USER_LOOPER3_BUTTON),
    ENUM_TO_STR(USER_LOOPER4_BUTTON),
    ENUM_TO_STR(USER_BUTTON_MAX),
};

int main()
{
	user_button_init();
	while(1)
	{
		button_scan();
		Hal_Delay(10);
}
}


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


    for (i = 0; i < USER_BUTTON_MAX; i ++)
    {
        user_button[i].id = i;
        user_button[i].usr_button_read = button_read;
        user_button[i].cb = botton_evt_cb;
        user_button[i].pressed_logic_level = 0;
        user_button[i].max_multiple_clicks_interval = FLEX_MS_TO_SCAN_CNT(300);
        user_button[i].short_press_start_tick = FLEX_MS_TO_SCAN_CNT(600);
        user_button[i].long_press_start_tick = FLEX_MS_TO_SCAN_CNT(3000);
        user_button[i].long_hold_start_tick = FLEX_MS_TO_SCAN_CNT(5000);

        flex_button_register(&user_button[i]);
    }
}


uint8_t flex_button_scan(void)
{
    flex_button_read();
    return flex_button_process();
}

void button_scan(void)
{
    flex_button_scan();
}

static uint8_t button_read(void *arg)
{
    uint8_t value = 0;

    flex_button_t *btn = (flex_button_t *)arg;

    switch (btn->id)
    {
    case USER_POWER_BUTTON:
        value = HAL_GPIO_ReadPin(POWER_KEY_GPIO_Port,POWER_KEY_Pin);
        break;

    case USER_BT_PAIR_BUTTON:

        value = HAL_GPIO_ReadPin(BT_PAIRK_KEY_GPIO_Port, BT_PAIRK_KEY_Pin);

        break;
#if 0
    case USER_ENCODER_BUTTON:
        if((POWER_ON == Get_Power_State())&&(_power_transition_idle()))
        {
            //value =  HAL_GPIO_ReadPin(TACT_SW_GPIO_Port, TACT_SW_Pin);
        }
#endif
        break;

    case USER_LOOPER1_BUTTON:
        if((POWER_ON == Get_Power_State())&&(_power_transition_idle()))
        value = HAL_GPIO_ReadPin(LOOPER1_GPIO_Port, LOOPER1_Pin);

        break;
    case USER_LOOPER2_BUTTON:
        if((POWER_ON == Get_Power_State())&&(_power_transition_idle()))
        value = HAL_GPIO_ReadPin(LOOPER2_GPIO_Port, LOOPER2_Pin);

        break;
    case USER_LOOPER3_BUTTON:
        if((POWER_ON == Get_Power_State())&&(_power_transition_idle()))
        value = HAL_GPIO_ReadPin(LOOPER3_GPIO_Port, LOOPER3_Pin);

        break;
    case USER_LOOPER4_BUTTON:
        if((POWER_ON == Get_Power_State())&&(_power_transition_idle()))
        value = HAL_GPIO_ReadPin(LOOPER4_GPIO_Port, LOOPER4_Pin);

        break;

    default:break;
    }

    return value;
}




static void non_combination_btn_event(flex_button_t *btn)
{
    switch (btn->id)
    {
        case USER_POWER_BUTTON:
        {
            switch (btn->event)
            {

                case FLEX_BTN_PRESS_CLICK:
                    DBG_Printf("system power on \n\r");
                    power_key();
                    break;
                case FLEX_BTN_PRESS_LONG_UP:

                    //printf("%s : %s\n",  enum_btn_id_string[btn->id], enum_event_string[btn->event]);
                    break;
                case FLEX_BTN_PRESS_LONG_HOLD:

                    break;
                case FLEX_BTN_PRESS_DOUBLE_CLICK:
                    printf("%s : %s\n",  enum_btn_id_string[btn->id], enum_event_string[btn->event]);

                    break;

                default:
                    break;
            }
            break;
        }


        case USER_BT_PAIR_BUTTON:
        {
            switch (btn->event)
            {

                case FLEX_BTN_PRESS_CLICK:

                    printf("%s : %s\n",  enum_btn_id_string[btn->id], enum_event_string[btn->event]);

                    break;
                case FLEX_BTN_PRESS_LONG_HOLD:
                    DBG_Printf("<<<<<<<<<<<<<<<<<<<<<<<<bt long press\n\r");

                    break;

                case FLEX_BTN_PRESS_DOUBLE_CLICK:
                    printf("%s : %s\n",  enum_btn_id_string[btn->id], enum_event_string[btn->event]);

                    break;


                default:
                    break;
            }
            break;
        }

        case USER_ENCODER_BUTTON:
        {
            switch (btn->event)
            {
                case FLEX_BTN_PRESS_CLICK:
                    DBG_Printf("USER_ENCODER_BUTTON press\n\r");
                    break;
                case FLEX_BTN_PRESS_LONG_HOLD:

                    break;
                default:
                    break;
                }
                break;
        }

        case USER_LOOPER1_BUTTON:
        {
            switch (btn->event)
            {
                case FLEX_BTN_PRESS_CLICK:
                    DBG_Printf("USER_LOOPER1_BUTTON press\n\r");
                    break;
                case FLEX_BTN_PRESS_LONG_HOLD:


                    break;
                default:
                    break;
                }
                break;
        }

        case USER_LOOPER2_BUTTON:
        {
            switch (btn->event)
            {
                case FLEX_BTN_PRESS_CLICK:
                    DBG_Printf("USER_LOOPER2_BUTTON press\n\r");

                    break;
                case FLEX_BTN_PRESS_LONG_HOLD:

                    break;
                default:
                    break;
            }
            break;
        }

        case USER_LOOPER3_BUTTON:
        {
            switch (btn->event)
            {
                case FLEX_BTN_PRESS_CLICK:
                    DBG_Printf("USER_LOOPER3_BUTTON press\n\r");

                    break;
                case FLEX_BTN_PRESS_LONG_HOLD:


                    break;
                default:
                    break;
            }
            break;
        }
        case USER_LOOPER4_BUTTON:
        {
            switch (btn->event)
            {
                case FLEX_BTN_PRESS_CLICK:
                    DBG_Printf("USER_LOOPER4_BUTTON press\n\r");
   
                    break;
                case FLEX_BTN_PRESS_LONG_HOLD:


                    break;
                default:
                    break;
            }
            break;
        }

        default:
            break;
    }
}


static void  combination_btn_event(void)
{

    if ((flex_button_event_read(&user_button[USER_POWER_BUTTON]) == FLEX_BTN_PRESS_CLICK) &&\
        (flex_button_event_read(&user_button[USER_BT_PAIR_BUTTON]) == FLEX_BTN_PRESS_CLICK))
    {
        DBG_Printf(">>>>>>>>[combination]: button power and button bt\n\r");
    }
    if((POWER_ON == Get_Power_State())&&(is_power_transition_idle())) {
        if ((flex_button_event_read(&user_button[USER_LOOPER1_BUTTON]) == FLEX_BTN_PRESS_LONG_HOLD) &&\
            (flex_button_event_read(&user_button[USER_LOOPER4_BUTTON]) == FLEX_BTN_PRESS_LONG_HOLD))
        {
            switch_ak7755_5805m_control_method();
            DBG_Printf(">>>>>>>>[combination]: button looper 1 and looper 4\n\r");
        }
    }

}


static void botton_evt_cb(void *arg)
{
    flex_button_t *btn = (flex_button_t *)arg;

    #ifndef BUTTON_DEBUG
    DBG_Printf("id: [%d - %s]  event: [%d - %30s]  repeat: %d\n\r", 
        btn->id, enum_btn_id_string[btn->id],
        btn->event, enum_event_string[btn->event],
        btn->click_cnt);
    #endif

    combination_btn_event();
    non_combination_btn_event(btn);

}


  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yank_k

点个关注加分享,一起探讨学习!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值