上篇帖子中我已经实现了串口通信,本帖子就来实现按键操作,根据原理图,板子上有3个用户按键,可以实现3个按键的单击,双击,三击,长短按,组合按键。非常好的按键板子。完美适配我的按键软件库。

可以看到
KEY1对应P111
KEY2对应P112
KEY3对应P013
1。打开瑞萨的RA Smart配置软件


3。实现按键的所有功能
按键核心代码
#include <string.h>
#include "ebtn.h"
#define EBTN_FLAG_ONPRESS_SENT ((uint8_t)0x01) /*!< Flag indicates that on-press event has been sent /
#define EBTN_FLAG_IN_PROCESS ((uint8_t)0x02) /!< Flag indicates that button in process */
/* Default button group instance */
static ebtn_t ebtn_default;
/**
-
\brief Process the button information and state
-
\param[in] btn: Button instance to process
-
\param[in] old_state: old state
-
\param[in] new_state: new state
-
\param[in] mstime: Current milliseconds system time
*/
static void prv_process_btn(ebtn_btn_t *btn, uint8_t old_state, uint8_t new_state, ebtn_time_t mstime)
{
ebtn_t *ebtobj = &ebtn_default;
/* Check params set or not. */
if (btn->param == NULL)
{
return;
}
/* Button state has just changed */
if (new_state != old_state)
{
btn->time_state_change = mstime;
if (new_state)
{
btn->flags |= EBTN_FLAG_IN_PROCESS;
}
}
/* Button is still pressed /
if (new_state)
{
/
- Handle debounce and send on-press event
- This is when we detect valid press
/
if (!(btn->flags & EBTN_FLAG_ONPRESS_SENT))
{
/
- Run if statement when:
-
- Runtime mode is enabled -> user sets its own config for debounce
-
- Config debounce time for press is more than
0
/
if (ebtn_timer_sub(mstime, btn->time_state_change) >= btn->param->time_debounce)
{
/
- Check mutlti click limit reach or not.
*/
if ((btn->click_cnt > 0) && (ebtn_timer_sub(mstime, btn->click_last_time) >= btn->param->time_click_multi_max))
{
if (btn->event_mask & EBTN_EVT_MASK_ONCLICK)
{
ebtobj->evt_fn(btn, EBTN_EVT_ONCLICK);
}
btn->click_cnt = 0;
}
btn->keepalive_last_time = mstime;
btn->keepalive_cnt = 0;
btn->flags |= EBTN_FLAG_ONPRESS_SENT;
if (btn->event_mask & EBTN_EVT_MASK_ONPRESS)
{
ebtobj->evt_fn(btn, EBTN_EVT_ONPRESS);
}
btn->time_change = mstime;
}
}
else
{
while ((btn->param->time_keepalive_period > 0) && (ebtn_timer_sub(mstime, btn->keepalive_last_time) >= btn->param->time_keepalive_period))
{
btn->keepalive_last_time += btn->param->time_keepalive_period;
++btn->keepalive_cnt;
if (btn->event_mask & EBTN_EVT_MASK_KEEPALIVE)
{
ebtobj->evt_fn(btn, EBTN_EVT_KEEPALIVE);
}
}
if ((btn->click_cnt > 0) && (ebtn_timer_sub(mstime, btn->time_change) > btn->param->time_click_pressed_max))
{
if (btn->event_mask & EBTN_EVT_MASK_ONCLICK)
{
ebtobj->evt_fn(btn, EBTN_EVT_ONCLICK);
}
btn->click_cnt = 0;
}
}
}
/* Button is still released /
else
{
/
- We only need to react if on-press event has even been started.
- Do nothing if that was not the case
/
if (btn->flags & EBTN_FLAG_ONPRESS_SENT)
{
/
- Run if statement when:
-
- Runtime mode is enabled -> user sets its own config for debounce
-
- Config debounce time for release is more than
0
/
if (ebtn_timer_sub(mstime, btn->time_state_change) >= btn->param->time_debounce_release)
{
/ Handle on-release event */
btn->flags &= ~EBTN_FLAG_ONPRESS_SENT;
if (btn->event_mask & EBTN_EVT_MASK_ONRELEASE)
{
ebtobj->evt_fn(btn, EBTN_EVT_ONRELEASE);
}
if (ebtn_timer_sub(mstime, btn->time_change) >= btn->param->time_click_pressed_min &&
ebtn_timer_sub(mstime, btn->time_change) <= btn->param->time_click_pressed_max)
{
++btn->click_cnt;
btn->click_last_time = mstime;
}
else
{
if ((btn->click_cnt > 0) && (ebtn_timer_sub(mstime, btn->time_change) < btn->param->time_click_pressed_min))
{
if (btn->event_mask & EBTN_EVT_MASK_ONCLICK)
{
ebtobj->evt_fn(btn, EBTN_EVT_ONCLICK);
}
}
btn->click_cnt = 0;
}
if ((btn->click_cnt > 0) && (btn->click_cnt == btn->param->max_consecutive))
{
if (btn->event_mask & EBTN_EVT_MASK_ONCLICK)
{
ebtobj->evt_fn(btn, EBTN_EVT_ONCLICK);
}
btn->click_cnt = 0;
}
btn->time_change = mstime;
}
}
else
{
if (btn->click_cnt > 0)
{
if (ebtn_timer_sub(mstime, btn->click_last_time) >= btn->param->time_click_multi_max)
{
if (btn->event_mask & EBTN_EVT_MASK_ONCLICK)
{
ebtobj->evt_fn(btn, EBTN_EVT_ONCLICK);
}
btn->click_cnt = 0;
}
}
else
{
if (btn->flags & EBTN_FLAG_IN_PROCESS)
{
btn->flags &= ~EBTN_FLAG_IN_PROCESS;
}
}
}
}
}
int ebtn_init(ebtn_btn_t *btns, uint16_t btns_cnt, ebtn_btn_combo_t *btns_combo, uint16_t btns_combo_cnt, ebtn_get_state_fn get_state_fn, ebtn_evt_fn evt_fn)
{
ebtn_t *ebtobj = &ebtn_default;
if (evt_fn == NULL || get_state_fn == NULL
)
{
return 0;
}
memset(ebtobj, 0x00, sizeof(*ebtobj));
ebtobj->btns = btns;
ebtobj->btns_cnt = btns_cnt;
ebtobj->btns_combo = btns_combo;
ebtobj->btns_combo_cnt = btns_combo_cnt;
ebtobj->evt_fn = evt_fn;
ebtobj->get_state_fn = get_state_fn;
return 1;
}
/**
-
\brief Get all button state with get_state_fn.
-
\param[out] state_array: store the button state
*/
static void ebtn_get_current_state(bit_array_t *state_array)
{
ebtn_t *ebtobj = &ebtn_default;
ebtn_btn_dyn_t *target;
int i;
/* Process all buttons /
for (i = 0; i < ebtobj->btns_cnt; ++i)
{
/ Get button state */
uint8_t new_state = ebtobj->get_state_fn(&ebtobj->btns[i]);
// save state
bit_array_assign(state_array, i, new_state);
}
for (target = ebtobj->btn_dyn_head, i = ebtobj->btns_cnt; target; target = target->next, i++)
{
/* Get button state */
uint8_t new_state = ebtobj->get_state_fn(&target->btn);
bit_array_assign(state_array, i, new_state);
}
}
/**
- \brief Process the button state
- \param[in] btn: Button instance to process
- \param[in] old_state: all button old state
- \param[in] curr_state: all button current state
- \param[in] idx: Button internal key_idx
- \param[in] mstime: Current milliseconds system time
*/
static void ebtn_process_btn(ebtn_btn_t *btn, bit_array_t *old_state, bit_array_t *curr_state, int idx, ebtn_time_t mstime)
{
prv_process_btn(btn, bit_array_get(old_state, idx), bit_array_get(curr_state, idx), mstime);
}
/**
-
\brief Process the combo-button state
-
\param[in] btn: Button instance to process
-
\param[in] old_state: all button old state
-
\param[in] curr_state: all button current state
-
\param[in] comb_key: Combo key
-
\param[in] mstime: Current milliseconds system time
*/
static void ebtn_process_btn_combo(ebtn_btn_t *btn, bit_array_t *old_state, bit_array_t *curr_state, bit_array_t *comb_key, ebtn_time_t mstime)
{
BIT_ARRAY_DEFINE(tmp_data, EBTN_MAX_KEYNUM) = {0};
if (bit_array_num_bits_set(comb_key, EBTN_MAX_KEYNUM) == 0)
{
return;
}
bit_array_and(tmp_data, curr_state, comb_key, EBTN_MAX_KEYNUM);
uint8_t curr = bit_array_cmp(tmp_data, comb_key, EBTN_MAX_KEYNUM) == 0;
bit_array_and(tmp_data, old_state, comb_key, EBTN_MAX_KEYNUM);
uint8_t old = bit_array_cmp(tmp_data, comb_key, EBTN_MAX_KEYNUM) == 0;
prv_process_btn(btn, old, curr, mstime);
}
void ebtn_process_with_curr_state(bit_array_t *curr_state, ebtn_time_t mstime)
{
ebtn_t *ebtobj = &ebtn_default;
ebtn_btn_dyn_t *target;
ebtn_btn_combo_dyn_t *target_combo;
int i;
for (i = 0; i < ebtobj->btns_cnt; ++i)
{
ebtn_process_btn(&ebtobj->btns[i], ebtobj->old_state, curr_state, i, mstime);
}
for (target = ebtobj->btn_dyn_head, i = ebtobj->btns_cnt; target; target = target->next, i++)
{
ebtn_process_btn(&target->btn, ebtobj->old_state, curr_state, i, mstime);
}
for (i = 0; i < ebtobj->btns_combo_cnt; ++i)
{
ebtn_process_btn_combo(&ebtobj->btns_combo[i].btn, ebtobj->old_state, curr_state, ebtobj->btns_combo[i].comb_key, mstime);
}
for (target_combo = ebtobj->btn_combo_dyn_head; target_combo; target_combo = target_combo->next)
{
ebtn_process_btn_combo(&target_combo->btn.btn, ebtobj->old_state, curr_state, target_combo->btn.comb_key, mstime);
}
bit_array_copy_all(ebtobj->old_state, curr_state, EBTN_MAX_KEYNUM);
}
void ebtn_process(ebtn_time_t mstime)
{
BIT_ARRAY_DEFINE(curr_state, EBTN_MAX_KEYNUM) = {0};
ebtn_get_current_state(curr_state);
ebtn_process_with_curr_state(curr_state, mstime);
}
int ebtn_get_total_btn_cnt(void)
{
ebtn_t *ebtobj = &ebtn_default;
int total_cnt = 0;
ebtn_btn_dyn_t *curr = ebtobj->btn_dyn_head;
total_cnt += ebtobj->btns_cnt;
while (curr)
{
total_cnt++;
curr = curr->next;
}
return total_cnt;
}
int ebtn_get_btn_index_by_key_id(uint16_t key_id)
{
ebtn_t *ebtobj = &ebtn_default;
int i = 0;
ebtn_btn_dyn_t *target;
for (i = 0; i < ebtobj->btns_cnt; ++i)
{
if (ebtobj->btns[i].key_id == key_id)
{
return i;
}
}
for (target = ebtobj->btn_dyn_head, i = ebtobj->btns_cnt; target; target = target->next, i++)
{
if (target->btn.key_id == key_id)
{
return i;
}
}
return -1;
}
ebtn_btn_t *ebtn_get_btn_by_key_id(uint16_t key_id)
{
ebtn_t *ebtobj = &ebtn_default;
int i = 0;
ebtn_btn_dyn_t *target;
for (i = 0; i < ebtobj->btns_cnt; ++i)
{
if (ebtobj->btns[i].key_id == key_id)
{
return &ebtobj->btns[i];
}
}
for (target = ebtobj->btn_dyn_head, i = ebtobj->btns_cnt; target; target = target->next, i++)
{
if (target->btn.key_id == key_id)
{
return &target->btn;
}
}
return NULL;
}
int ebtn_get_btn_index_by_btn(ebtn_btn_t *btn)
{
return ebtn_get_btn_index_by_key_id(btn->key_id);
}
int ebtn_get_btn_index_by_btn_dyn(ebtn_btn_dyn_t *btn)
{
return ebtn_get_btn_index_by_key_id(btn->btn.key_id);
}
void ebtn_combo_btn_add_btn_by_idx(ebtn_btn_combo_t *btn, int idx)
{
bit_array_set(btn->comb_key, idx);
}
void ebtn_combo_btn_remove_btn_by_idx(ebtn_btn_combo_t *btn, int idx)
{
bit_array_clear(btn->comb_key, idx);
}
void ebtn_combo_btn_add_btn(ebtn_btn_combo_t *btn, uint16_t key_id)
{
int idx = ebtn_get_btn_index_by_key_id(key_id);
if (idx < 0)
{
return;
}
ebtn_combo_btn_add_btn_by_idx(btn, idx);
}
void ebtn_combo_btn_remove_btn(ebtn_btn_combo_t *btn, uint16_t key_id)
{
int idx = ebtn_get_btn_index_by_key_id(key_id);
if (idx < 0)
{
return;
}
ebtn_combo_btn_remove_btn_by_idx(btn, idx);
}
int ebtn_is_btn_active(const ebtn_btn_t *btn)
{
return btn != NULL && (btn->flags & EBTN_FLAG_ONPRESS_SENT);
}
int ebtn_is_btn_in_process(const ebtn_btn_t *btn)
{
return btn != NULL && (btn->flags & EBTN_FLAG_IN_PROCESS);
}
int ebtn_is_in_process(void)
{
ebtn_t *ebtobj = &ebtn_default;
ebtn_btn_dyn_t *target;
ebtn_btn_combo_dyn_t *target_combo;
int i;
for (i = 0; i < ebtobj->btns_cnt; ++i)
{
if (ebtn_is_btn_in_process(&ebtobj->btns[i]))
{
return 1;
}
}
for (target = ebtobj->btn_dyn_head, i = ebtobj->btns_cnt; target; target = target->next, i++)
{
if (ebtn_is_btn_in_process(&target->btn))
{
return 1;
}
}
for (i = 0; i < ebtobj->btns_combo_cnt; ++i)
{
if (ebtn_is_btn_in_process(&ebtobj->btns_combo[i].btn))
{
return 1;
}
}
for (target_combo = ebtobj->btn_combo_dyn_head; target_combo; target_combo = target_combo->next)
{
if (ebtn_is_btn_in_process(&target_combo->btn.btn))
{
return 1;
}
}
return 0;
}
int ebtn_register(ebtn_btn_dyn_t *button)
{
ebtn_t *ebtobj = &ebtn_default;
ebtn_btn_dyn_t *curr = ebtobj->btn_dyn_head;
ebtn_btn_dyn_t *last = NULL;
if (!button)
{
return 0;
}
if (ebtn_get_total_btn_cnt() >= EBTN_MAX_KEYNUM)
{
return 0;
}
if (curr == NULL)
{
ebtobj->btn_dyn_head = button;
return 1;
}
while (curr)
{
if (curr == button)
{
return 0;
}
last = curr;
curr = curr->next;
}
last->next = button;
return 1;
}
int ebtn_combo_register(ebtn_btn_combo_dyn_t *button)
{
ebtn_t *ebtobj = &ebtn_default;
ebtn_btn_combo_dyn_t *curr = ebtobj->btn_combo_dyn_head;
ebtn_btn_combo_dyn_t *last = NULL;
if (!button)
{
return 0;
}
if (curr == NULL)
{
ebtobj->btn_combo_dyn_head = button;
return 1;
}
while (curr)
{
if (curr == button)
{
return 0;
}
last = curr;
curr = curr->next;
}
last->next = button;
return 1;
}
#ifndef _EBTN_H
#define _EBTN_H
#include <stdint.h>
#include <string.h>
#include "bit_array.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
// #define EBTN_CONFIG_TIMER_16
// here can change to uint16_t, if you want reduce RAM size.
#ifdef EBTN_CONFIG_TIMER_16
typedef uint16_t ebtn_time_t;
typedef int16_t ebtn_time_sign_t;
#else
typedef uint32_t ebtn_time_t;
typedef int32_t ebtn_time_sign_t;
#endif
/* Forward declarations */
struct ebtn_btn;
struct ebtn;
#define EBTN_MAX_KEYNUM (64)
/**
- \brief List of button events
/
typedef enum
{
EBTN_EVT_ONPRESS = 0x00, /!< On press event - sent when valid press is detected /
EBTN_EVT_ONRELEASE, /!< On release event - sent when valid release event is detected (from
active to inactive) /
EBTN_EVT_ONCLICK, /!< On Click event - sent when valid sequence of on-press and on-release
events occurs /
EBTN_EVT_KEEPALIVE, /!< Keep alive event - sent periodically when button is active */
} ebtn_evt_t;
#define EBTN_EVT_MASK_ONPRESS (1 << EBTN_EVT_ONPRESS)
#define EBTN_EVT_MASK_ONRELEASE (1 << EBTN_EVT_ONRELEASE)
#define EBTN_EVT_MASK_ONCLICK (1 << EBTN_EVT_ONCLICK)
#define EBTN_EVT_MASK_KEEPALIVE (1 << EBTN_EVT_KEEPALIVE)
#define EBTN_EVT_MASK_ALL (EBTN_EVT_MASK_ONPRESS | EBTN_EVT_MASK_ONRELEASE | EBTN_EVT_MASK_ONCLICK | EBTN_EVT_MASK_KEEPALIVE)
/**
- @brief Returns the difference between two absolute times: time1-time2.
- @param[in] time1: Absolute time expressed in internal time units.
- @param[in] time2: Absolute time expressed in internal time units.
- @return resulting signed relative time expressed in internal time units.
*/
static inline ebtn_time_sign_t ebtn_timer_sub(ebtn_time_t time1, ebtn_time_t time2)
{
return time1 - time2;
}
// test time overflow error
// #define ebtn_timer_sub(time1, time2) (time1 - time2)
/**
- \brief Button event function callback prototype
- \param[in] btn: Button instance from array for which event occured
- \param[in] evt: Event type
*/
typedef void (*ebtn_evt_fn)(struct ebtn_btn *btn, ebtn_evt_t evt);
/**
- \brief Get button/input state callback function
- \param[in] btn: Button instance from array to read state
- \return
1 when button is considered active, 0 otherwise
*/
typedef uint8_t (*ebtn_get_state_fn)(struct ebtn_btn *btn);
/**
-
\brief Button Params structure
/
typedef struct ebtn_btn_param
{
/*
/
uint16_t time_debounce; /!< Debounce time in milliseconds */
/**
- \brief Minimum debounce time for release event in units of milliseconds
- This is the time when the input shall have minimum stable released level to detect valid
- onrelease event.
- This setting can be useful if application wants to protect against
- unwanted glitches on the line when input is considered "active".
- When value is set to
> 0, input must be in inactive low for at least
- minimum milliseconds time, before valid onrelease event is detected
- \note If value is set to
0, debounce is not used and release event will be
- triggered immediately when input states goes to inactive state
/
uint16_t time_debounce_release; /!< Debounce time in milliseconds for release event */
/**
- \brief Minimum active input time for valid click event, in milliseconds
- Input shall be in active state (after debounce) at least this amount of time to even consider
- the potential valid click event. Set the value to
0 to disable this feature
/
uint16_t time_click_pressed_min; /!< Minimum pressed time for valid click event */
/**
- \brief Maximum active input time for valid click event, in milliseconds
- Input shall be pressed at most this amount of time to still trigger valid click.
- Set to
-1 to allow any time triggering click event.
- When input is active for more than the configured time, click even is not detected and is
- ignored.
/
uint16_t time_click_pressed_max; /!< Maximum pressed time for valid click event*/
/**
/
uint16_t time_click_multi_max; /!< Maximum time between 2 clicks to be considered consecutive
click */
/**
- \brief Keep-alive event period, in milliseconds
- When input is active, keep alive events will be sent through this period of time.
- First keep alive will be sent after input being considered
- active.
/
uint16_t time_keepalive_period; /!< Time in ms for periodic keep alive event */
/**
/
uint16_t max_consecutive; /!< Max number of consecutive clicks */
} ebtn_btn_param_t;
#define EBTN_PARAMS_INIT(_time_debounce, _time_debounce_release, _time_click_pressed_min, _time_click_pressed_max, _time_click_multi_max,
_time_keepalive_period, _max_consecutive)
{
.time_debounce = _time_debounce, .time_debounce_release = _time_debounce_release, .time_click_pressed_min = _time_click_pressed_min,
.time_click_pressed_max = _time_click_pressed_max, .time_click_multi_max = _time_click_multi_max, .time_keepalive_period = _time_keepalive_period,
.max_consecutive = _max_consecutive
}
#define EBTN_BUTTON_INIT_RAW(_key_id, _param, _mask)
{
.key_id = _key_id, .param = _param, .event_mask = _mask,
}
#define EBTN_BUTTON_INIT(_key_id, _param) EBTN_BUTTON_INIT_RAW(_key_id, _param, EBTN_EVT_MASK_ALL)
#define EBTN_BUTTON_DYN_INIT(_key_id, _param)
{
.next = NULL, .btn = EBTN_BUTTON_INIT(_key_id, _param),
}
#define EBTN_BUTTON_COMBO_INIT_RAW(_key_id, _param, _mask)
{
.comb_key = {0}, .btn = EBTN_BUTTON_INIT_RAW(_key_id, _param, _mask),
}
#define EBTN_BUTTON_COMBO_INIT(_key_id, _param)
{
.comb_key = {0}, .btn = EBTN_BUTTON_INIT(_key_id, _param),
}
#define EBTN_BUTTON_COMBO_DYN_INIT(_key_id, _param)
{
.next = NULL, .btn = EBTN_BUTTON_COMBO_INIT(_key_id, _param),
}
#define EBTN_ARRAY_SIZE(_arr) sizeof(_arr) / sizeof((_arr)[0])
/**
-
\brief Button structure
/
typedef struct ebtn_btn
{
uint16_t key_id; /!< User defined custom argument for callback function purpose /
uint8_t flags; /!< Private button flags management /
uint8_t event_mask; /!< Private button event mask management */
ebtn_time_t time_change; /*!< Time in ms when button state got changed last time after valid
debounce /
ebtn_time_t time_state_change; /!< Time in ms when button state got changed last time */
ebtn_time_t keepalive_last_time; /*!< Time in ms of last send keep alive event /
ebtn_time_t click_last_time; /!< Time in ms of last successfully detected (not sent!) click event
*/
uint16_t keepalive_cnt; /*!< Number of keep alive events sent after successful on-press
detection. Value is reset after on-release /
uint16_t click_cnt; /!< Number of consecutive clicks detected, respecting maximum timeout
between clicks */
const ebtn_btn_param_t *param;
} ebtn_btn_t;
/**
-
\brief ComboButton structure
/
typedef struct ebtn_btn_combo
{
BIT_ARRAY_DEFINE(comb_key, EBTN_MAX_KEYNUM); /!< select key index - 1 means active, 0 means inactive */
ebtn_btn_t btn;
} ebtn_btn_combo_t;
/**
/**
/**
-
\brief easy_button group structure
*/
typedef struct ebtn
{
ebtn_btn_t btns; /!< Pointer to buttons array /
uint16_t btns_cnt; /!< Number of buttons in array */
ebtn_btn_combo_t btns_combo; /!< Pointer to comb-buttons array /
uint16_t btns_combo_cnt; /!< Number of comb-buttons in array */
ebtn_btn_dyn_t btn_dyn_head; /!< Pointer to btn-dynamic list */
ebtn_btn_combo_dyn_t btn_combo_dyn_head; /!< Pointer to btn-combo-dynamic list */
ebtn_evt_fn evt_fn; /*!< Pointer to event function /
ebtn_get_state_fn get_state_fn; /!< Pointer to get state function */
BIT_ARRAY_DEFINE(old_state, EBTN_MAX_KEYNUM); /*!< Old button state - 1 means active, 0 means inactive */
} ebtn_t;
/**
- \brief Button processing function, that reads the inputs and makes actions accordingly.
- \param[in] mstime: Current system time in milliseconds
*/
void ebtn_process(ebtn_time_t mstime);
/**
- \brief Button processing function, with all button input state.
- \param[in] curr_state: Current all button input state
- \param[in] mstime: Current system time in milliseconds
*/
void ebtn_process_with_curr_state(bit_array_t *curr_state, ebtn_time_t mstime);
/**
- \brief Check if button is active.
- Active is considered when initial debounce period has been a pass.
- This is the period between on-press and on-release events.
- \param[in] btn: Button handle to check
- \return
1 if active, 0 otherwise
*/
int ebtn_is_btn_active(const ebtn_btn_t *btn);
/**
- \brief Check if button is in process.
- Used for low-power processing, indicating that the buttons are temporarily idle, and embedded systems can consider entering deep sleep.
- \param[in] btn: Button handle to check
- \return
1 if in process, 0 otherwise
*/
int ebtn_is_btn_in_process(const ebtn_btn_t *btn);
/**
- \brief Check if some button is in process.
- Used for low-power processing, indicating that the buttons are temporarily idle, and embedded systems can consider entering deep sleep.
- \return
1 if in process, 0 otherwise
*/
int ebtn_is_in_process(void);
/**
- \brief Initialize button manager
- \param[in] btns: Array of buttons to process
- \param[in] btns_cnt: Number of buttons to process
- \param[in] btns_combo: Array of combo-buttons to process
- \param[in] btns_combo_cnt: Number of combo-buttons to process
- \param[in] get_state_fn: Pointer to function providing button state on demand.
- \param[in] evt_fn: Button event function callback
- \return
1 on success, 0 otherwise
*/
int ebtn_init(ebtn_btn_t *btns, uint16_t btns_cnt, ebtn_btn_combo_t *btns_combo, uint16_t btns_combo_cnt, ebtn_get_state_fn get_state_fn, ebtn_evt_fn evt_fn);
/**
- @brief Register a dynamic button
- @param button: Dynamic button structure instance
- \return
1 on success, 0 otherwise
*/
int ebtn_register(ebtn_btn_dyn_t *button);
/**
- \brief Register a dynamic combo-button
- \param[in] button: Dynamic combo-button structure instance
- \return
1 on success, 0 otherwise
*/
int ebtn_combo_register(ebtn_btn_combo_dyn_t *button);
/**
- \brief Get the current total button cnt
- \return size of button.
*/
int ebtn_get_total_btn_cnt(void);
/**
- \brief Get the internal key_idx of the key_id
- \param[in] key_id: key_id
- \return '-1' on error, other is key_idx
*/
int ebtn_get_btn_index_by_key_id(uint16_t key_id);
/**
- \brief Get the internal btn instance of the key_id, here is the button instance, and what is dynamically registered is also to obtain its button
- instance
- \param[in] key_id: key_id
- \return 'NULL' on error, other is button instance
*/
ebtn_btn_t *ebtn_get_btn_by_key_id(uint16_t key_id);
/**
- \brief Get the internal key_idx of the button
- \param[in] btn: Button
- \return '-1' on error, other is key_idx
*/
int ebtn_get_btn_index_by_btn(ebtn_btn_t *btn);
/**
- \brief Get the internal key_idx of the dynamic button
- \param[in] btn: Button
- \return '-1' on error, other is key_idx
*/
int ebtn_get_btn_index_by_btn_dyn(ebtn_btn_dyn_t *btn);
/**
- \brief Bind combo-button key with key_idx
- \param[in] btn: Combo Button
- \param[in] idx: key_idx
*/
void ebtn_combo_btn_add_btn_by_idx(ebtn_btn_combo_t *btn, int idx);
/**
- \brief Remove combo-button key with key_idx
- \param[in] btn: Combo Button
- \param[in] idx: key_idx
*/
void ebtn_combo_btn_remove_btn_by_idx(ebtn_btn_combo_t *btn, int idx);
/**
- \brief Bind combo-button key with key_id, make sure key_id(button) is already register.
- \param[in] btn: Combo Button
- \param[in] key_id: key_id
*/
void ebtn_combo_btn_add_btn(ebtn_btn_combo_t *btn, uint16_t key_id);
/**
- \brief Remove combo-button key with key_id, make sure key_id(button) is already
- register. \param[in] btn: Combo Button \param[in] key_id: key_id
*/
void ebtn_combo_btn_remove_btn(ebtn_btn_combo_t *btn, uint16_t key_id);
/**
- \brief Get keep alive period for specific button
- \param[in] btn: Button instance to get keep alive period for
- \return Keep alive period in
ms
*/
#define ebtn_keepalive_get_period(btn) ((btn)->time_keepalive_period)
/**
/**
- \brief Get number of keep alive counts for specific required time in milliseconds.
-
It will calculate number of keepalive ticks specific button shall make,
-
before requested time is reached.
- Result of the function can be used with \ref ebtn_keepalive_get_count which returns
- actual number of keep alive counts since last on-press event of the button.
- \note Value is always integer aligned, with granularity of one keepalive time period
- \note Implemented as macro, as it may be optimized by compiler when static keep alive
- is used
- \param[in] btn: Button to use for check
- \param[in] ms_time: Time in ms to calculate number of keep alive counts
- \return Number of keep alive counts
*/
#define ebtn_keepalive_get_count_for_time(btn, ms_time) ((ms_time) / ebtn_keepalive_get_period(btn))
/**
- \brief Get number of consecutive click events on a button
- \param[in] btn: Button instance to get number of clicks
- \return Number of consecutive clicks on a button
*/
#define ebtn_click_get_count(btn) ((btn)->click_cnt)
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _EBTN_H */
#include "ebtn_app.h"
/* -------------------------------- 初始化和处理函数 -------------------------------- */
/** ***************************************************************************
-
@brief easy_button初始化,在主函数中调用
-
@note 如果使用组合键,则需要在ebtn_init之后调用ebtn_combo_btn_add_btn添加组合键
*/
void ebtn_APP_Key_INIT(void)
{
// 初始化easy_button库
ebtn_init(btn_array, btn_array_size,
btn_combo_array, btn_combo_array_size,
ebtn_Get_State, ebtn_Event_Handler);
/* ---------------------------- 此处向组合键结构体数组静态添加按键 --------------------------- */
// 结构体数组索引值与ebtn_custom_config.c中组合键结构体数组btn_combo_array中的索引值一致
// 示例:四个组合键
// 为组合键1添加按键KEY_1和KEY_2
ebtn_combo_btn_add_btn(&btn_combo_array[0], KEY_1);
ebtn_combo_btn_add_btn(&btn_combo_array[0], KEY_2);
// // 为组合键2添加按键KEY_3和KEY_4
ebtn_combo_btn_add_btn(&btn_combo_array[1], KEY_2);
ebtn_combo_btn_add_btn(&btn_combo_array[1], KEY_3);
// // 为组合键3添加按键KEY_1和KEY_3
ebtn_combo_btn_add_btn(&btn_combo_array[2], KEY_1);
ebtn_combo_btn_add_btn(&btn_combo_array[2], KEY_3);
// // 为组合键4添加按键KEY_2和KEY_4
//ebtn_combo_btn_add_btn(&btn_combo_array[3], KEY_2);
//ebtn_combo_btn_add_btn(&btn_combo_array[3], KEY_4);
}
/* --------------------------------- 自定义配置部分结束 -------------------------------- */
/** ***************************************************************************
- @brief 处理按键事件,需要定期调用,建议以20ms为周期执行一次
- @note Tick时基为1ms
*/
void ebtn_APP_Key_Process(void)
{
ebtn_process(ebtn_custom_hal.Get_Tick()); // 获取时间处理按键事件
}
/* -------------------------------- 辅助部分 ------------------------------- */
/** ***************************************************************************
- @brief 检查按键是否激活
- @param key_id: 按键ID
- @return uint8_t: 1-激活,0-未激活
*/
uint8_t ebtn_APP_Is_Key_Active(uint16_t key_id)
{
ebtn_btn_t *btn = ebtn_get_btn_by_key_id(key_id);
return ebtn_is_btn_active(btn);
}
/** ***************************************************************************
- @brief 检查是否有按键处于处理中
- @return uint8_t: 1-有按键处理中,0-无按键处理中
*/
uint8_t ebtn_APP_Is_Any_Key_In_Process(void)
{
return ebtn_is_in_process();
}
/** ***************************************************************************
- @brief 获取指定按键的按键状态
- @param key_id: 按键ID
- @return uint8_t: 1-按下,0-松开
*/
uint8_t ebtn_APP_Get_Key_State(uint16_t key_id)
{
ebtn_btn_t *btn = ebtn_get_btn_by_key_id(key_id);
if (btn == NULL)
{
return 0;
}
return ebtn_Get_State(btn);
}
#ifndef EBTN_APP_H
#define EBTN_APP_H
#include "ebtn.h"
#include "ebtn_app.h"
#include "ebtn_custom_callback.h"
#include "ebtn_custom_hal.h"
#include "ebtn_custom_config.h"
void ebtn_APP_Key_INIT(void);
void ebtn_APP_Key_Process(void);
uint8_t ebtn_APP_Is_Key_Active(uint16_t key_id);
uint8_t ebtn_APP_Is_Any_Key_In_Process(void);
uint8_t ebtn_APP_Get_Key_State(uint16_t key_id);
#endif /* EBTN_APP_H */
#include "ebtn_custom_callback.h"
volatile uint8_t key_count = 0;
/* ---------------------------- 此函数中可自定义按键状态检测方式 ---------------------------- */
/** ***************************************************************************
-
@brief 获取按键状态回调函数
-
此函数默认采用了查表检测,免去需要手动为每个按键添加检测方式
-
也可为特殊按键自定义检测方法
-
@note 查表检测需要配合ebtn_custom_config.c中的按键配置结构体数组使用
-
@param btn: easy_button按键结构体指针
-
@return 按键状态,0表示未按下,1表示按下
*/
uint8_t ebtn_Get_State(struct ebtn_btn *btn)
{
// 查表法检测
for (int i = 0; i < key_list_size; i++)
{
if (key_list[i].key_id == btn->key_id)
{
uint8_t pin_state = ebtn_custom_hal.Read_Pin(key_list[i].gpio_port, key_list[i].gpio_pin);
// 根据有效电平转换
if (key_list[i].active_level == pin_state)
{
pin_state = 1;
}
else
{
pin_state = 0;
}
return pin_state;
}
}
/* ----------------------------- 此处可自定义按键状态获取方式 ----------------------------- */
// 可自定义特殊按键的检测方式,如矩阵按键的检测
return 0; // 未找到按键ID,返回0
}
/* ------------------------------- 此函数可修改按键触发事件 ------------------------------- */
/** ***************************************************************************
-
@brief 按键事件处理回调函数,在此定义按键触发事件
-
推荐将不同按键或事件的处理逻辑拆分为独立的函数,并参考原有的 switch(ebtn->key) 结构,
-
在对应按键编号的 case 分支中调用相应的处理函数,以提升代码的可读性和可维护性
-
@param btn: easy_button按键结构体指针
-
@param evt: 事件类型
*/
void ebtn_Event_Handler(struct ebtn_btn btn, ebtn_evt_t evt)
{
switch (btn->key_id) // 按键ID
{
/ ---------------------------------- KEY1 ---------------------------------- /
case KEY_1:
/ ---------------------------------- 按下按键时 --------------------------------- /
if (evt == EBTN_EVT_ONPRESS)
{
}
/ ---------------------------------- 松开按键时 --------------------------------- /
else if (evt == EBTN_EVT_ONRELEASE)
{
}
/ ----------------------------- 短按按键时(可获取连击次数) ----------------------------- /
else if (evt == EBTN_EVT_ONCLICK)
{
/ ----------------------------------- 单击时 ---------------------------------- */
if (btn->click_cnt == 1)
{
printf("KEY_1 单击\r\n");
key_count++;
if(key_count>=7)key_count=0;
}
else if (btn->click_cnt == 2)
{
printf("KEY_1 双击\r\n");
key_count += 2;
if(key_count>=7)key_count=0;
}
else if (btn->click_cnt == 3)
{
printf("KEY_1 三击\r\n");
key_count += 3;
if(key_count>=7)key_count=0;
}
}
else if (evt == EBTN_EVT_KEEPALIVE)
{
if (btn->keepalive_cnt == 1)
{
key_count++;if(key_count>=7)key_count=0;
printf("KEY_1 长按\r\n");
}
}
break;
case KEY_2:
/* ---------------------------------- 按下按键时 --------------------------------- */
if (evt == EBTN_EVT_ONPRESS)
{
}
else if (evt == EBTN_EVT_ONRELEASE)
{
}
else if (evt == EBTN_EVT_ONCLICK)
{
if (btn->click_cnt == 1)
{
printf("KEY_2 单击\r\n");
key_count--;
if(key_count==0)key_count=0;
}
else if (btn->click_cnt == 2)
{
printf("KEY_2 双击\r\n");
key_count -= 2;
if(key_count==0)key_count=0;
}
else if (btn->click_cnt == 3)
{
printf("KEY_2 三击\r\n");
key_count -= 3;
if(key_count==0)key_count=0;
}
}
else if (evt == EBTN_EVT_KEEPALIVE)
{
if (btn->keepalive_cnt == 1)
{
printf("KEY_2 长按\r\n");
key_count--;
if(key_count==0)key_count=0;
}
}
break;
case KEY_3:
/* ---------------------------------- 按下按键时 --------------------------------- */
if (evt == EBTN_EVT_ONPRESS)
{
}
else if (evt == EBTN_EVT_ONRELEASE)
{
}
else if (evt == EBTN_EVT_ONCLICK)
{
if (btn->click_cnt == 1)
{
printf("KEY_3 单击\r\n");
key_count--;
if(key_count==0)key_count=0;
}
else if (btn->click_cnt == 2)
{
printf("KEY_3 双击\r\n");
key_count -= 2;
if(key_count==0)key_count=0;
}
else if (btn->click_cnt == 3)
{
printf("KEY_3 三击\r\n");
key_count -= 3;
if(key_count==0)key_count=0;
}
}
else if (evt == EBTN_EVT_KEEPALIVE)
{
if (btn->keepalive_cnt == 1)
{
printf("KEY_3 长按\r\n");
key_count--;
if(key_count==0)key_count=0;
}
}
break;
case COMBO_KEY_1:
/* ---------------------------------- 按下按键时 --------------------------------- /
if (evt == EBTN_EVT_ONPRESS)
{
printf("按组合按键KEY1 KEY2 \r\n");
}
/ ---------------------------------- 松开按键时 --------------------------------- */
else if (evt == EBTN_EVT_ONRELEASE)
{
//printf("松开KEY_1 和 KEY_2 组合按键\r\n");
}
break;
case COMBO_KEY_2:
/* ---------------------------------- 按下按键时 --------------------------------- /
if (evt == EBTN_EVT_ONPRESS)
{
printf("按组合按键KEY2 KEY3 \r\n");
}
/ ---------------------------------- 松开按键时 --------------------------------- */
else if (evt == EBTN_EVT_ONRELEASE)
{
}
break;
case COMBO_KEY_3:
/* ---------------------------------- 按下按键时 --------------------------------- /
if (evt == EBTN_EVT_ONPRESS)
{
printf("按组合按键KEY1 KEY3 \r\n");
}
/ ---------------------------------- 松开按键时 --------------------------------- */
else if (evt == EBTN_EVT_ONRELEASE)
{
}
break;
case COMBO_KEY_4:
/* ---------------------------------- 按下按键时 --------------------------------- /
if (evt == EBTN_EVT_ONPRESS)
{
}
/ ---------------------------------- 松开按键时 --------------------------------- */
else if (evt == EBTN_EVT_ONRELEASE)
{
}
break;
}
}#ifndef EBTN_CUSTOM_CALLBACK_H
#define EBTN_CUSTOM_CALLBACK_H
#include "ebtn_app.h"
#include "ebtn_custom_hal.h"
#include "ebtn_custom_config.h"
/* -------------------------- 此处添加所需的按键触发事件的函数声明的头文件 ------------------------- */
extern volatile uint8_t key_count;
/* -------------------------------- 自定义配置部分结束 ------------------------------- */
uint8_t ebtn_Get_State(struct ebtn_btn *btn);
void ebtn_Event_Handler(struct ebtn_btn *btn, ebtn_evt_t evt);
#endif
2。主函数中添加功能码
#include "hal_data.h"
#include "LED.h"
#include "Systick.h"
#include "usart0.h"
#include "usart9.h"
#include "shell.h"
#include "ebtn_app.h"
FSP_CPP_HEADER
void R_BSP_WarmStart(bsp_warm_start_event_t event);
FSP_CPP_FOOTER
/*******************************************************************************************************************//**
-
main() is generated by the RA Configuration editor and is used to generate threads if an RTOS is used. This function
-
is called by main() when no RTOS is used.
*********************************************************************************************************************/
void hal_entry(void)
{
/ TODO: add your own code here */
hal_systick_init();
UART0_Init();UART9_Init();
ebtn_APP_Key_INIT();
//shell_init();
while(1)
{
//shell_process();
}
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}
/*******************************************************************************************************************//**
-
This function is called at various points during the startup process. This implementation uses the event that is
-
called right before main() to set up the pins.
-
@param[in] event Where at in the start up process the code is currently at
**********************************************************************************************************************/
void R_BSP_WarmStart (bsp_warm_start_event_t event)
{
if (BSP_WARM_START_RESET == event)
{
#if BSP_FEATURE_FLASH_LP_VERSION != 0
R_FACI_LP->DFLCTL = 1U;
#endif
}
if (BSP_WARM_START_POST_C == event)
{
R_IOPORT_Open(&IOPORT_CFG_CTRL, &IOPORT_CFG_NAME);
#if BSP_CFG_SDRAM_ENABLED
R_BSP_SdramInit(true);
#endif
}
}
#if BSP_TZ_SECURE_BUILD
FSP_CPP_HEADER
BSP_CMSE_NONSECURE_ENTRY void template_nonsecure_callable ();
/* Trustzone Secure Projects require at least one nonsecure callable function in order to build (Remove this if it is not required to build). */
BSP_CMSE_NONSECURE_ENTRY void template_nonsecure_callable ()
{
}
FSP_CPP_FOOTER
#endif

主函数中其实每调用按键函数,因为按键函数在20ms中断里执行
void SysTick_Handler(void)
{
g_tick_count++;
g_20ms++;
if(g_20ms == 20)
{
g_20ms = 0;
ebtn_APP_Key_Process();
}
}


3。编译代码,烧录到板子


4。打开串口助手,调试

完美的实现了3个按键的单击,双击,三击,长短安,组合按键,史上最完美的按键库!!!!!
附件是按键hex,你们自己烧录,用板载USB串口测试
*附件:RA4M2_Button.zip