按键在嵌入式开发中有非常广泛的用途,比如电子手表侧边的按键,可以用来调节日期和时间,同时也可以切换菜单,实现不同的功能!基本上人们的日常生活中都无法离开按键。

瑞萨RA4M2开发板上具备2个用户按键,分别是P005和P006。有原理图可知,无按键按下时,IO口读入高电平“1”,有按键按下时,IO口读入低电平“0”。可以用来进行多功能操作。
本次测评就使用瑞萨电子的RA4M2开发板来实现按键的单击,多击,长按效果。
板子上有3个LED灯

分别是P405,P404,P404。
本次测评就是用这两个用户按键来实现各种流水灯的操作。
话不多说了,开始上干货!
1、打开瑞萨的RA Smart configurator

熟悉的logo,熟悉的味道。
2。工程取名Button_LED,因为我要用按键操作流水灯。

3。选择RA4M2

4。继续

5。继续

6。继续

生成工程

7。先配置LED引脚

很简单,根据引脚集配



7。记下来设置串口,串口设置就不多说了,我之前发的文章中有详细说明。使用P100和P101


8。 接下来配置按键IO口

很简单


点击生成代码

9。打开KEIL

配置好芯片参数


选择Jlink

编译一遍

出现错误
添加串口函数
void UART0_Init(void)
{
fsp_err_t err = FSP_SUCCESS;
err = R_SCI_UART_Open(&g_uart0_ctrl,&g_uart0_cfg);
assert(err == FSP_SUCCESS);
}
void UART_Agreement(uart_callback_args_t * p_args); //串口通讯协议
void UART0_callback(uart_callback_args_t * p_args)
{
switch (p_args->event)
{
//如果是串口接收中断
case UART_EVENT_RX_CHAR:
{
UART_Agreement(p_args);//串口通讯协议判断
if(USART_RX_STA&0x8000)//如果接收完成
{
USART_RX_BUF[USART_RX_STA&0x3FFF] = 0x0d;//将字符串自动换行
USART_RX_STA++; //因为增加了0x0d,所以字符串数量+1
R_SCI_UART_Write(&g_uart0_ctrl, (uint8_t*)USART_RX_BUF, USART_RX_STA&0x3fff);//将字符串数据输出
USART_RX_STA=First_String_num; //将USART_RX_STA初始化
}
break;
}
//如果是串口发送中断
case UART_EVENT_TX_COMPLETE:
{
uart_send_complete_flag = true;
break;
}
default:
break;
}
}
void UART_Agreement(uart_callback_args_t * p_args)
{
if((USART_RX_STA&0x8000)==0) //bit15没有被置为1,接收未完成
{
if(USART_RX_STA&0x4000) //bit14被置为1,表示接收到了'\n'(0x0d)
{
if((p_args->data)!=0x0a)USART_RX_STA=0; //如果bit14被置1了,但是bit15并不是换行操作,表示接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了
}
else //还没收到'\n'(0x0d)
{
if((p_args->data)==0x0d)USART_RX_STA|=0x4000; //如果接收到了'\n'(0x0d),bit14被置为1
else
{
USART_RX_BUF[USART_RX_STA&0x3FFF]=(uint8_t)(p_args->data);//如果没有接收到结束标志,继续将数据写入USART_RX_BUF[]
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0; //如果接收到的数据大于定义最大接收字节数,表示接收数据错误,重新开始接收
}
}
}
}
/* 重定向 printf 输出 */
#if defined GNUC && !defined clang
int _write(int fd, char *pBuffer, int size); //防止编译警告
int _write(int fd, char *pBuffer, int size)
{
(void)fd;
R_SCI_UART_Write(&g_uartx_ctrl, (uint8_t *)pBuffer, (uint32_t)size);
while(uart_send_complete_flag == false);
uart_send_complete_flag = false;
return size;
}
#else
int fputc(int ch, FILE *f)
{
(void)f;
R_SCI_UART_Write(&g_uartx_ctrl, (uint8_t *)&ch, 1);
while(uart_send_complete_flag == false);
uart_send_complete_flag = false;
return ch;
}
#endif

/* 重定向 printf 输出 */



串口调试正常
10。 添加按键库函数

#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)
/**
- [url=home.php?mod=space&uid=2666770]@Brief[/url] 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.
- [url=home.php?mod=space&uid=1141835]@Return[/url] 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
- [url=home.php?mod=space&uid=3142012]@param[/url] 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 */
#ifndef BIT_ARRAY_H
#define BIT_ARRAY_H
#include <stdint.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
// #define BIT_ARRAY_CONFIG_64
// here can change to uint64_t, if you system is 64bit.
#ifdef BIT_ARRAY_CONFIG_64
typedef uint64_t bit_array_t;
#define BIT_ARRAY_BIT(n) (1ULL << (n))
#else
typedef uint32_t bit_array_t;
#define BIT_ARRAY_BIT(n) (1UL << (n))
#endif
typedef bit_array_t bit_array_val_t;
#define BIT_ARRAY_BITS (sizeof(bit_array_val_t) * 8)
#define BIT_ARRAY_BIT_WORD(bit) ((bit) / BIT_ARRAY_BITS)
#define BIT_ARRAY_BIT_INDEX(bit) ((bit_array_val_t)(bit) & (BIT_ARRAY_BITS - 1U))
#define BIT_ARRAY_MASK(bit) BIT_ARRAY_BIT(BIT_ARRAY_BIT_INDEX(bit))
#define BIT_ARRAY_ELEM(addr, bit) ((addr)[BIT_ARRAY_BIT_WORD(bit)])
// word of all 1s
#define BIT_ARRAY_WORD_MAX (~(bit_array_val_t)0)
#define BIT_ARRAY_SUB_MASK(nbits) ((nbits) ? BIT_ARRAY_WORD_MAX >> (BIT_ARRAY_BITS - (nbits)) : (bit_array_val_t)0)
// A possibly faster way to combine two words with a mask
// #define bitmask_merge(a,b,abits) ((a & abits) | (b & ~abits))
#define bitmask_merge(a, b, abits) (b ^ ((a ^ b) & abits))
/**
- @brief This macro computes the number of bit array variables necessary to
- represent a bitmap with [url=home.php?mod=space&uid=16293]@a[/url] num_bits.
- @param num_bits Number of bits.
*/
#define BIT_ARRAY_BITMAP_SIZE(num_bits) (1 + ((num_bits)-1) / BIT_ARRAY_BITS)
/**
- @brief Define an array of bit array variables.
- This macro defines an array of bit array variables containing at least
- @a num_bits bits.
- @note
- If used from file scope, the bits of the array are initialized to zero;
- if used from within a function, the bits are left uninitialized.
- @cond INTERNAL_HIDDEN
- @note
- This macro should be replicated in the PREDEFINED field of the documentation
- Doxyfile.
- @endcond
- @param name Name of array of bit array variables.
- @param num_bits Number of bits needed.
*/
#define BIT_ARRAY_DEFINE(name, num_bits) bit_array_t name[BIT_ARRAY_BITMAP_SIZE(num_bits)]
#if 1
// See http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
static inline bit_array_val_t _windows_popcount(bit_array_val_t w)
{
w = w - ((w >> 1) & (bit_array_val_t) ~(bit_array_val_t)0 / 3);
w = (w & (bit_array_val_t) ~(bit_array_val_t)0 / 15 * 3) + ((w >> 2) & (bit_array_val_t) ~(bit_array_val_t)0 / 15 * 3);
w = (w + (w >> 4)) & (bit_array_val_t) ~(bit_array_val_t)0 / 255 * 15;
return (bit_array_val_t)(w * ((bit_array_val_t) ~(bit_array_val_t)0 / 255)) >> (sizeof(bit_array_val_t) - 1) * 8;
}
#define POPCOUNT(x) _windows_popcount(x)
#else
#define POPCOUNT(x) (unsigned)__builtin_popcountll(x)
#endif
#define bits_in_top_word(nbits) ((nbits) ? BIT_ARRAY_BIT_INDEX((nbits)-1) + 1 : 0)
static inline void _bit_array_mask_top_word(bit_array_t *target, int num_bits)
{
// Mask top word
int num_of_words = BIT_ARRAY_BITMAP_SIZE(num_bits);
int bits_active = bits_in_top_word(num_bits);
target[num_of_words - 1] &= BIT_ARRAY_SUB_MASK(bits_active);
}
/**
-
@brief Bit Array test a bit.
-
This routine tests whether bit number @a bit of @a target is set or not.
-
@param target Address of bit array variable or array.
-
@param bit Bit number (starting from 0).
-
@return true if the bit was set, false if it wasn't.
*/
static inline int bit_array_get(const bit_array_t *target, int bit)
{
bit_array_val_t val = BIT_ARRAY_ELEM(target, bit);
return (1 & (val >> (bit & (BIT_ARRAY_BITS - 1)))) != 0;
}
/**
-
@brief Bit Array clear a bit.
-
Bit Array clear bit number @a bit of @a target.
-
@param target Address of bit array variable or array.
-
@param bit Bit number (starting from 0).
*/
static inline void bit_array_clear(bit_array_t *target, int bit)
{
bit_array_val_t mask = BIT_ARRAY_MASK(bit);
BIT_ARRAY_ELEM(target, bit) &= ~mask;
}
/**
-
@brief Bit Array set a bit.
-
Bit Array set bit number @a bit of @a target.
-
@param target Address of bit array variable or array.
-
@param bit Bit number (starting from 0).
*/
static inline void bit_array_set(bit_array_t *target, int bit)
{
bit_array_val_t mask = BIT_ARRAY_MASK(bit);
BIT_ARRAY_ELEM(target, bit) |= mask;
}
/**
-
@brief Bit Array toggle a bit.
-
Bit Array toggle bit number @a bit of @a target.
-
@param target Address of bit array variable or array.
-
@param bit Bit number (starting from 0).
*/
static inline void bit_array_toggle(bit_array_t *target, int bit)
{
bit_array_val_t mask = BIT_ARRAY_MASK(bit);
BIT_ARRAY_ELEM(target, bit) ^= mask;
}
/**
-
@brief Bit Array set a bit to a given value.
-
Bit Array set bit number @a bit of @a target to value @a val.
-
@param target Address of bit array variable or array.
-
@param bit Bit number (starting from 0).
-
@param val true for 1, false for 0.
*/
static inline void bit_array_assign(bit_array_t *target, int bit, int val)
{
bit_array_val_t mask = BIT_ARRAY_MASK(bit);
if (val)
{
BIT_ARRAY_ELEM(target, bit) |= mask;
}
else
{
BIT_ARRAY_ELEM(target, bit) &= ~mask;
}
}
static inline void bit_array_clear_all(bit_array_t *target, int num_bits)
{
memset((void *)target, 0, BIT_ARRAY_BITMAP_SIZE(num_bits) * sizeof(bit_array_val_t));
}
static inline void bit_array_set_all(bit_array_t *target, int num_bits)
{
memset((void *)target, 0xff, BIT_ARRAY_BITMAP_SIZE(num_bits) * sizeof(bit_array_val_t));
_bit_array_mask_top_word(target, num_bits);
}
static inline void bit_array_toggle_all(bit_array_t *target, int num_bits)
{
for (int i = 0; i < BIT_ARRAY_BITMAP_SIZE(num_bits); i++)
{
target[i] ^= BIT_ARRAY_WORD_MAX;
}
_bit_array_mask_top_word(target, num_bits);
}
//
// Strings and printing
//
// Construct a BIT_ARRAY from a substring with given on and off characters.
// From string method
static inline void bit_array_from_str(bit_array_t *bitarr, const char *str)
{
int i, index;
int space = 0;
int len = strlen(str);
for (i = 0; i < len; i++)
{
index = i - space;
if (strchr("1", str[i]) != NULL)
{
bit_array_set(bitarr, index);
}
else if (strchr("0", str[i]) != NULL)
{
bit_array_clear(bitarr, index);
}
else
{
// error.
space++;
}
}
}
// Takes a char array to write to. str must be bitarr->num_of_bits+1 in length
// Terminates string with '\0'
static inline char *bit_array_to_str(const bit_array_t *bitarr, int num_bits, char *str)
{
int i;
for (i = 0; i < num_bits; i++)
{
str[i] = bit_array_get(bitarr, i) ? '1' : '0';
}
str[num_bits] = '\0';
return str;
}
// Takes a char array to write to. str must be bitarr->num_of_bits+1 in length
// Terminates string with '\0'
static inline char *bit_array_to_str_8(const bit_array_t *bitarr, int num_bits, char *str)
{
int i;
int space = 0;
for (i = 0; i < num_bits; i++)
{
str[i + space] = bit_array_get(bitarr, i) ? '1' : '0';
if ((i + 1) % 8 == 0)
{
space++;
str[i + space] = ' ';
}
}
str[num_bits + space] = '\0';
return str;
}
//
// Get and set words (internal use only -- no bounds checking)
//
static inline bit_array_val_t _bit_array_get_word(const bit_array_t *target, int num_bits, int start)
{
int word_index = BIT_ARRAY_BIT_WORD(start);
int word_offset = BIT_ARRAY_BIT_INDEX(start);
bit_array_val_t result = target[word_index] >> word_offset;
int bits_taken = BIT_ARRAY_BITS - word_offset;
if (word_offset > 0 && start + bits_taken < num_bits)
{
result |= target[word_index + 1] << (BIT_ARRAY_BITS - word_offset);
}
return result;
}
// Set 64 bits from a particular start position
// Doesn't extend bit array
static inline void _bit_array_set_word(bit_array_t *target, int num_bits, int start, bit_array_val_t word)
{
int word_index = BIT_ARRAY_BIT_WORD(start);
int word_offset = BIT_ARRAY_BIT_INDEX(start);
if (word_offset == 0)
{
target[word_index] = word;
}
else
{
target[word_index] = (word << word_offset) | (target[word_index] & BIT_ARRAY_SUB_MASK(word_offset));
if (word_index + 1 < BIT_ARRAY_BITMAP_SIZE(num_bits))
{
target[word_index + 1] = (word >> (BIT_ARRAY_BITS - word_offset)) | (target[word_index + 1] & (BIT_ARRAY_WORD_MAX << word_offset));
}
}
// Mask top word
_bit_array_mask_top_word(target, num_bits);
}
//
// Fill a region (internal use only)
//
// FillAction is fill with 0 or 1 or toggle
typedef enum
{
ZERO_REGION,
FILL_REGION,
SWAP_REGION
} FillAction;
static inline void _bit_array_set_region(bit_array_t *target, int start, int length, FillAction action)
{
if (length == 0)
return;
int first_word = BIT_ARRAY_BIT_WORD(start);
int last_word = BIT_ARRAY_BIT_WORD(start + length - 1);
int foffset = BIT_ARRAY_BIT_INDEX(start);
int loffset = BIT_ARRAY_BIT_INDEX(start + length - 1);
if (first_word == last_word)
{
bit_array_val_t mask = BIT_ARRAY_SUB_MASK(length) << foffset;
switch (action)
{
case ZERO_REGION:
target[first_word] &= ~mask;
break;
case FILL_REGION:
target[first_word] |= mask;
break;
case SWAP_REGION:
target[first_word] ^= mask;
break;
}
}
else
{
switch (action)
{
case ZERO_REGION:
target[first_word] &= BIT_ARRAY_SUB_MASK(foffset);
break;
case FILL_REGION:
target[first_word] |= ~BIT_ARRAY_SUB_MASK(foffset);
break;
case SWAP_REGION:
target[first_word] ^= ~BIT_ARRAY_SUB_MASK(foffset);
break;
}
int i;
switch (action)
{
case ZERO_REGION:
for (i = first_word + 1; i < last_word; i++)
target[i] = (bit_array_val_t)0;
break;
case FILL_REGION:
for (i = first_word + 1; i < last_word; i++)
target[i] = BIT_ARRAY_WORD_MAX;
break;
case SWAP_REGION:
for (i = first_word + 1; i < last_word; i++)
target[i] ^= BIT_ARRAY_WORD_MAX;
break;
}
switch (action)
{
case ZERO_REGION:
target[last_word] &= ~BIT_ARRAY_SUB_MASK(loffset + 1);
break;
case FILL_REGION:
target[last_word] |= BIT_ARRAY_SUB_MASK(loffset + 1);
break;
case SWAP_REGION:
target[last_word] ^= BIT_ARRAY_SUB_MASK(loffset + 1);
break;
}
}
}
// Get the number of bits set (hamming weight)
static inline int bit_array_num_bits_set(bit_array_t *target, int num_bits)
{
int i;
int num_of_bits_set = 0;
for (i = 0; i < BIT_ARRAY_BITMAP_SIZE(num_bits); i++)
{
if (target[i] > 0)
{
num_of_bits_set += POPCOUNT(target[i]);
}
}
return num_of_bits_set;
}
// Get the number of bits not set (1 - hamming weight)
static inline int bit_array_num_bits_cleared(bit_array_t *target, int num_bits)
{
return num_bits - bit_array_num_bits_set(target, num_bits);
}
// Copy bits from one array to another
// Note: use MACRO bit_array_copy
// Destination and source can be the same bit_array and
// src/dst regions can overlap
static inline void bit_array_copy(bit_array_t *dst, int dstindx, const bit_array_t *src, int srcindx, int length, int src_num_bits, int dst_num_bits)
{
// Num of full words to copy
int num_of_full_words = length / BIT_ARRAY_BITS;
int i;
int bits_in_last_word = bits_in_top_word(length);
if (dst == src && srcindx > dstindx)
{
for (i = 0; i < num_of_full_words; i++)
{
bit_array_val_t word = _bit_array_get_word(src, src_num_bits, srcindx + i * BIT_ARRAY_BITS);
_bit_array_set_word(dst, dst_num_bits, dstindx + i * BIT_ARRAY_BITS, word);
}
if (bits_in_last_word > 0)
{
bit_array_val_t src_word = _bit_array_get_word(src, src_num_bits, srcindx + i * BIT_ARRAY_BITS);
bit_array_val_t dst_word = _bit_array_get_word(dst, dst_num_bits, dstindx + i * BIT_ARRAY_BITS);
bit_array_val_t mask = BIT_ARRAY_SUB_MASK(bits_in_last_word);
bit_array_val_t word = bitmask_merge(src_word, dst_word, mask);
_bit_array_set_word(dst, dst_num_bits, dstindx + num_of_full_words * BIT_ARRAY_BITS, word);
}
}
else
{
for (i = 0; i < num_of_full_words; i++)
{
bit_array_val_t word = _bit_array_get_word(src, src_num_bits, srcindx + length - (i + 1) * BIT_ARRAY_BITS);
_bit_array_set_word(dst, dst_num_bits, dstindx + length - (i + 1) * BIT_ARRAY_BITS, word);
}
if (bits_in_last_word > 0)
{
bit_array_val_t src_word = _bit_array_get_word(src, src_num_bits, srcindx);
bit_array_val_t dst_word = _bit_array_get_word(dst, dst_num_bits, dstindx);
bit_array_val_t mask = BIT_ARRAY_SUB_MASK(bits_in_last_word);
bit_array_val_t word = bitmask_merge(src_word, dst_word, mask);
_bit_array_set_word(dst, dst_num_bits, dstindx, word);
}
}
_bit_array_mask_top_word(dst, dst_num_bits);
}
// copy all of src to dst. dst is resized to match src.
static inline void bit_array_copy_all(bit_array_t *dst, const bit_array_t *src, int num_bits)
{
for (int i = 0; i < BIT_ARRAY_BITMAP_SIZE(num_bits); i++)
{
dst[i] = src[i];
}
}
//
// Logic operators
//
// Destination can be the same as one or both of the sources
static inline void bit_array_and(bit_array_t *dest, const bit_array_t *src1, const bit_array_t *src2, int num_bits)
{
for (int i = 0; i < BIT_ARRAY_BITMAP_SIZE(num_bits); i++)
{
dest[i] = src1[i] & src2[i];
}
}
static inline void bit_array_or(bit_array_t *dest, const bit_array_t *src1, const bit_array_t *src2, int num_bits)
{
for (int i = 0; i < BIT_ARRAY_BITMAP_SIZE(num_bits); i++)
{
dest[i] = src1[i] | src2[i];
}
}
static inline void bit_array_xor(bit_array_t *dest, const bit_array_t *src1, const bit_array_t *src2, int num_bits)
{
for (int i = 0; i < BIT_ARRAY_BITMAP_SIZE(num_bits); i++)
{
dest[i] = src1[i] ^ src2[i];
}
}
static inline void bit_array_not(bit_array_t *dest, const bit_array_t *src, int num_bits)
{
for (int i = 0; i < BIT_ARRAY_BITMAP_SIZE(num_bits); i++)
{
dest[i] = ~src[i];
}
}
//
// Shift array left/right. If fill is zero, filled with 0, otherwise 1
//
// Shift towards LSB / lower index
static inline void bit_array_shift_right(bit_array_t *target, int num_bits, int shift_dist, int fill)
{
if (shift_dist >= num_bits)
{
fill ? bit_array_set_all(target, num_bits) : bit_array_clear_all(target, num_bits);
return;
}
else if (shift_dist == 0)
{
return;
}
FillAction action = fill ? FILL_REGION : ZERO_REGION;
int cpy_length = num_bits - shift_dist;
bit_array_copy(target, 0, target, shift_dist, cpy_length, num_bits, num_bits);
_bit_array_set_region(target, cpy_length, shift_dist, action);
}
// Shift towards MSB / higher index
static inline void bit_array_shift_left(bit_array_t *target, int num_bits, int shift_dist, int fill)
{
if (shift_dist >= num_bits)
{
fill ? bit_array_set_all(target, num_bits) : bit_array_clear_all(target, num_bits);
return;
}
else if (shift_dist == 0)
{
return;
}
FillAction action = fill ? FILL_REGION : ZERO_REGION;
int cpy_length = num_bits - shift_dist;
bit_array_copy(target, shift_dist, target, 0, cpy_length, num_bits, num_bits);
_bit_array_set_region(target, 0, shift_dist, action);
}
//
// Comparisons
//
// Compare two bit arrays by value stored, with index 0 being the Least
// Significant Bit (LSB). Arrays must have the same length.
// returns:
// >0 iff bitarr1 > bitarr2
// 0 iff bitarr1 == bitarr2
// <0 iff bitarr1 < bitarr2
static inline int bit_array_cmp(const bit_array_t *bitarr1, const bit_array_t *bitarr2, int num_bits)
{
return memcmp(bitarr1, bitarr2, BIT_ARRAY_BITMAP_SIZE(num_bits) * sizeof(bit_array_val_t));
}
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* BIT_ARRAY_H */
bsp_io_level_t p_port_value_port_005;
bsp_io_level_t p_port_value_port_006;
#define TICKS_PER_SECONDS 1000
volatile uint32_t g_tick_count = 0;
void hal_systick_init()
{
SysTick_Config(SystemCoreClock / TICKS_PER_SECONDS);
}
void SysTick_Handler(void)
{
g_tick_count += 1;
}
uint32_t hal_systick_get()
{
return g_tick_count;
}
static uint32_t get_tick(void);
static uint32_t get_tick(void)
{
return hal_systick_get();
}
typedef enum
{
USER_BUTTON_0 = 0,
USER_BUTTON_1,
USER_BUTTON_2,
USER_BUTTON_3,
USER_BUTTON_4,
USER_BUTTON_5,
USER_BUTTON_6,
USER_BUTTON_7,
USER_BUTTON_8,
USER_BUTTON_9,
USER_BUTTON_INVALID,
USER_BUTTON_MAX,
USER_BUTTON_COMBO_0 = 0x100,
USER_BUTTON_COMBO_1,
USER_BUTTON_COMBO_2,
USER_BUTTON_COMBO_3,
USER_BUTTON_COMBO_MAX,
} user_button_t;
/* User defined settings */
static const ebtn_btn_param_t defaul_ebtn_param = EBTN_PARAMS_INIT(20, 0, 20, 300, 200, 500, 10);
static ebtn_btn_t btns[] = {
EBTN_BUTTON_INIT(USER_BUTTON_0, &defaul_ebtn_param),
EBTN_BUTTON_INIT(USER_BUTTON_1, &defaul_ebtn_param),
};
uint32_t last_time_keys[USER_BUTTON_MAX - USER_BUTTON_0] = {0};
static ebtn_btn_combo_t btns_combo[] = {
EBTN_BUTTON_COMBO_INIT(USER_BUTTON_COMBO_0, &defaul_ebtn_param),
EBTN_BUTTON_COMBO_INIT(USER_BUTTON_COMBO_1, &defaul_ebtn_param),
};
uint32_t last_time_keys_combo[USER_BUTTON_COMBO_MAX - USER_BUTTON_COMBO_0] = {0};
/**
- \brief Get input state callback
- \param btn: Button instance
- \return
1 if button active, 0 otherwise
*/
uint8_t prv_btn_get_state(struct ebtn_btn btn)
{
/
- Function will return negative number if button is pressed,
- or zero if button is releases
*/
uint8_t key = 0;
switch (btn->key_id)
{
case USER_BUTTON_0:
R_IOPORT_PinRead(&g_ioport_ctrl,BSP_IO_PORT_00_PIN_05,&p_port_value_port_005);
key = p_port_value_port_005 == 0;
break;
case USER_BUTTON_1:
R_IOPORT_PinRead(&g_ioport_ctrl,BSP_IO_PORT_00_PIN_06,&p_port_value_port_006);
key = p_port_value_port_006 == 0;
break;
}
return key;
}
void LED_1(void);
void LED_ALLON(void);
void LED_ALLOFF(void);
volatile uint8_t ledcount = 0;
void prv_btn_event(struct ebtn_btn *btn, ebtn_evt_t evt)
{
const char *s;
uint32_t color, keepalive_cnt = 0;
//HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
uint32_t diff_time = 0;
uint32_t *diff_time_ptr = NULL;
if (btn->key_id < USER_BUTTON_MAX)
{
diff_time_ptr = &last_time_keys[btn->key_id - USER_BUTTON_0];
}
else
{
diff_time_ptr = &last_time_keys[btn->key_id - USER_BUTTON_COMBO_0];
}
diff_time = get_tick() - *diff_time_ptr;
if (diff_time > 2000)
{
diff_time = 0;
}
*diff_time_ptr = get_tick();
if (evt == EBTN_EVT_KEEPALIVE)
{
s = "KEEPALIVE";
if(btn->key_id == USER_BUTTON_0 )
LED_1();
}
else if (evt == EBTN_EVT_ONPRESS)
{
s = "ONPRESS";
}
else if (evt == EBTN_EVT_ONRELEASE)
{
s = "ONRELEASE";
}
else if (evt == EBTN_EVT_ONCLICK)
{
s = "ONCLICK";
if(btn->key_id == USER_BUTTON_0 )
LED_ALLON();
if(btn->key_id == USER_BUTTON_1 )
LED_ALLOFF();
}
else
{
s = "UNKNOWN";
}
printf("[%7u][%6u] ID(hex):%4x, evt: %10s, keep-alive cnt: %3u, click cnt: %3u\r\n", (unsigned)get_tick(), (unsigned)diff_time, btn->key_id, s,
(unsigned)ebtn_keepalive_get_count(btn), (unsigned)ebtn_click_get_count(btn));
}
void button(void);
/*******************************************************************************************************************//**
- 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 */
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
UART0_Init();
hal_systick_init();
ebtn_init(btns, EBTN_ARRAY_SIZE(btns), btns_combo, EBTN_ARRAY_SIZE(btns_combo), prv_btn_get_state, prv_btn_event);
ebtn_combo_btn_add_btn(&btns_combo[0], USER_BUTTON_0);
ebtn_combo_btn_add_btn(&btns_combo[0], USER_BUTTON_1);
ebtn_combo_btn_add_btn(&btns_combo[1], USER_BUTTON_2);
ebtn_combo_btn_add_btn(&btns_combo[1], USER_BUTTON_3);
printf("欢迎来到瑞萨电子RA4M2开发板\r\n");
while (1)
{
ebtn_process(get_tick());
}
}
void LED_1(void)
{
while(1)
{
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_00_PIN_02, BSP_IO_LEVEL_LOW);
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_04, BSP_IO_LEVEL_LOW);
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_05, BSP_IO_LEVEL_HIGH);
R_BSP_SoftwareDelay(500, BSP_DELAY_UNITS_MILLISECONDS);
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_04, BSP_IO_LEVEL_HIGH);
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_05, BSP_IO_LEVEL_LOW);
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_00_PIN_02, BSP_IO_LEVEL_LOW);
R_BSP_SoftwareDelay(500, BSP_DELAY_UNITS_MILLISECONDS);
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_04, BSP_IO_LEVEL_LOW);
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_05, BSP_IO_LEVEL_LOW);
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_00_PIN_02, BSP_IO_LEVEL_HIGH);
R_BSP_SoftwareDelay(500, BSP_DELAY_UNITS_MILLISECONDS);
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_04, BSP_IO_LEVEL_HIGH);
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_05, BSP_IO_LEVEL_HIGH);
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_00_PIN_02, BSP_IO_LEVEL_HIGH);
R_BSP_SoftwareDelay(500, BSP_DELAY_UNITS_MILLISECONDS);
}
}
void LED_ALLON(void)
{
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_00_PIN_02, BSP_IO_LEVEL_HIGH);
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_04, BSP_IO_LEVEL_HIGH);
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_05, BSP_IO_LEVEL_HIGH);
//R_BSP_SoftwareDelay(500, BSP_DELAY_UNITS_MILLISECONDS);
}
void LED_ALLOFF(void)
{
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_00_PIN_02, BSP_IO_LEVEL_LOW);
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_04, BSP_IO_LEVEL_LOW);
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_05, BSP_IO_LEVEL_LOW);
//R_BSP_SoftwareDelay(500, BSP_DELAY_UNITS_MILLISECONDS);
}
void button(void)
{
R_IOPORT_PinRead(&g_ioport_ctrl,BSP_IO_PORT_00_PIN_05,&p_port_value_port_005);
if(p_port_value_port_005)
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_05, BSP_IO_LEVEL_LOW);
else
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_05, BSP_IO_LEVEL_HIGH);
R_IOPORT_PinRead(&g_ioport_ctrl,BSP_IO_PORT_00_PIN_06,&p_port_value_port_006);
if(p_port_value_port_006)
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_04, BSP_IO_LEVEL_LOW);
else
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_04, BSP_IO_LEVEL_HIGH);
}
添加如上函数

我现在实现的是按键1和按键2控制流水灯
1,当按下按键1时,3个LED全亮
2,当按下按键2时,3个LED全灭
3,当长按按键1时,3个LED流水灯循环

详情请看视频
1 和2 实现按键单击
3实现按键长按
满足实验效果