一个挺好用的开源的代码…按键
按键对应的开发应该大家都有用过…
这里就先不大概讲解原理,估计很多人都用过。我最近也在用,功能得话包括:单击、双击、多击、组合键,长按、长按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);
}