mirror of
https://github.com/MaJerle/lwbtn.git
synced 2026-08-17 17:44:39 +08:00
Add code skeleton
This commit is contained in:
+60
-10
@@ -3,22 +3,72 @@
|
||||
#include "lwbtn/lwbtn.h"
|
||||
#include "windows.h"
|
||||
|
||||
static LARGE_INTEGER freq, sys_start_time;
|
||||
static uint32_t get_tick(void);
|
||||
|
||||
/* User defined settings */
|
||||
const int keys[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
|
||||
int state[sizeof(keys) / sizeof(keys[0])];
|
||||
int state_prev[sizeof(keys) / sizeof(keys[0])];
|
||||
|
||||
/* List of buttons to process */
|
||||
static lwbtn_btn_t btns[] = {{.arg = (void*)&keys[0]}, {.arg = (void*)&keys[1]}, {.arg = (void*)&keys[2]},
|
||||
{.arg = (void*)&keys[3]}, {.arg = (void*)&keys[4]}, {.arg = (void*)&keys[5]},
|
||||
{.arg = (void*)&keys[6]}, {.arg = (void*)&keys[7]}, {.arg = (void*)&keys[8]},
|
||||
{.arg = (void*)&keys[9]}};
|
||||
|
||||
uint8_t
|
||||
prv_btn_get_state(struct lwbtn* lw, struct lwbtn_btn* btn) {
|
||||
return GetAsyncKeyState(*(int*)btn->arg) < 0;
|
||||
}
|
||||
|
||||
void
|
||||
prv_btn_event(struct lwbtn* lw, struct lwbtn_btn* btn, lwbtn_evt_t evt) {
|
||||
(void)lw;
|
||||
printf("[%7u] State !!!. CH: %c, evt: ", (unsigned)get_tick(), *(int*)btn->arg);
|
||||
switch (evt) {
|
||||
case LWBTN_EVT_KEEPALIVE: {
|
||||
printf("KEEPALIVE");
|
||||
break;
|
||||
}
|
||||
case LWBTN_EVT_ONPRESS: {
|
||||
printf("ONPRESS");
|
||||
break;
|
||||
}
|
||||
case LWBTN_EVT_ONRELEASE: {
|
||||
printf("ONRELEASE");
|
||||
break;
|
||||
}
|
||||
case LWBTN_EVT_ONCLICK: {
|
||||
printf("ONCLICK");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
printf("\r\n");
|
||||
}
|
||||
|
||||
int
|
||||
main(void) {
|
||||
printf("Application running\r\n");
|
||||
QueryPerformanceFrequency(&freq);
|
||||
QueryPerformanceCounter(&sys_start_time);
|
||||
|
||||
lwbtn_init_ex(NULL, btns, sizeof(btns) / sizeof(btns[0]), prv_btn_get_state, prv_btn_event);
|
||||
|
||||
while (1) {
|
||||
/* Simple simulator */
|
||||
for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++i) {
|
||||
state[i] = GetAsyncKeyState(keys[i]) < 0;
|
||||
if (state[i] != state_prev[i]) {
|
||||
printf("State changed for key %c to: %d\r\n", keys[i], state[i]);
|
||||
}
|
||||
state_prev[i] = state[i];
|
||||
}
|
||||
lwbtn_process_ex(NULL, get_tick());
|
||||
Sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
get_tick(void) {
|
||||
LONGLONG ret;
|
||||
LARGE_INTEGER now;
|
||||
|
||||
QueryPerformanceFrequency(&freq);
|
||||
QueryPerformanceCounter(&now);
|
||||
ret = now.QuadPart - sys_start_time.QuadPart;
|
||||
return (uint32_t)((ret * 1000) / freq.QuadPart);
|
||||
}
|
||||
|
||||
@@ -48,8 +48,69 @@ extern "C" {
|
||||
* \{
|
||||
*/
|
||||
|
||||
void lwbtn_init(void);
|
||||
void lwbtn_process(uint32_t mstime);
|
||||
/**
|
||||
* \brief User argument data structure
|
||||
*
|
||||
* This is structure user can use to define
|
||||
* GPIO port, pin and potential active state (high or low)
|
||||
*/
|
||||
typedef struct {
|
||||
void* port; /*!< User defined GPIO port information */
|
||||
void* pin; /*!< User defined GPIO pin information */
|
||||
uint8_t state; /*!< User defined GPIO state level when considered active */
|
||||
} lwbtn_argdata_port_pin_state_t;
|
||||
|
||||
struct lwbtn_btn;
|
||||
struct lwbtn;
|
||||
|
||||
/**
|
||||
* \brief List of button events
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
LWBTN_EVT_ONPRESS = 0x00,
|
||||
LWBTN_EVT_ONRELEASE,
|
||||
LWBTN_EVT_ONCLICK,
|
||||
LWBTN_EVT_KEEPALIVE,
|
||||
} lwbtn_evt_t;
|
||||
|
||||
typedef void (*lwbtn_evt_fn)(struct lwbtn* lw, struct lwbtn_btn* btn, lwbtn_evt_t evt);
|
||||
typedef uint8_t (*lwbtn_get_state_fn)(struct lwbtn* lw, struct lwbtn_btn* btn);
|
||||
|
||||
/**
|
||||
* \brief Button/input structure
|
||||
*/
|
||||
typedef struct lwbtn_btn {
|
||||
uint16_t flags;
|
||||
uint8_t old_state;
|
||||
uint32_t time_change;
|
||||
|
||||
struct {
|
||||
uint32_t last_time;
|
||||
uint16_t cnt;
|
||||
} keepalive;
|
||||
|
||||
struct {
|
||||
uint32_t last_time;
|
||||
uint8_t consecutive_cnt;
|
||||
} click;
|
||||
|
||||
void* arg;
|
||||
} lwbtn_btn_t;
|
||||
|
||||
/**
|
||||
* \brief LwBTN group structure
|
||||
*/
|
||||
typedef struct lwbtn {
|
||||
lwbtn_btn_t* btns;
|
||||
uint16_t btns_cnt;
|
||||
lwbtn_evt_fn evt_fn;
|
||||
lwbtn_get_state_fn get_state_fn;
|
||||
} lwbtn_t;
|
||||
|
||||
uint8_t lwbtn_init_ex(lwbtn_t* lw, lwbtn_btn_t* btns, uint16_t btns_cnt, lwbtn_get_state_fn get_state_fn,
|
||||
lwbtn_evt_fn evt_fn);
|
||||
uint8_t lwbtn_process_ex(lwbtn_t* lw, uint32_t mstime);
|
||||
|
||||
/**
|
||||
* \}
|
||||
|
||||
+170
-4
@@ -34,16 +34,182 @@
|
||||
#include <string.h>
|
||||
#include "lwbtn/lwbtn.h"
|
||||
|
||||
#define LWBTN_FLAG_ONPRESS_SENT ((uint16_t)0x0001)
|
||||
|
||||
#define LWBTN_TIME_DEBOUNCE_GET_MIN(btn) 20 /*!< Minimum debounce time to stabilize input */
|
||||
#define LWBTN_TIME_CLICK_GET_PRESSED_MIN(btn) 20 /*!< Minimum pressed time to consider it as a potential click event */
|
||||
#define LWBTN_TIME_CLICK_GET_PRESSED_MAX(btn) 300 /*!< Maximal pressed time to consider "click" event */
|
||||
#define LWBTN_TIME_CLICK_MAX_MULTI(btn) 500 /*!< Maximum allowed time for user to handle consecutive click events */
|
||||
#define LWBTN_TIME_KEEPALIVE_PERIOD(btn) 100 /*!< Keep alive time period in milliseconds */
|
||||
#define LWBTN_TIME_CLICK_SEND_TIMEOUT(btn) 300 /*!< Minimum time in ms to wait to send click event in timeout */
|
||||
#define LWBTN_CLICK_MAX_CONSECUTIVE(btn) 3 /*!< Max consecutive clicks allowed */
|
||||
|
||||
/* Default button group instance */
|
||||
static lwbtn_t lwbtn_default;
|
||||
#define LWBTN_GET_LW(in_lw) ((in_lw) != NULL ? (in_lw) : (&lwbtn_default))
|
||||
|
||||
/**
|
||||
* \brief Initialize button manager
|
||||
* \return `1` on success, `0` otherwise
|
||||
*/
|
||||
void
|
||||
lwbtn_init(void) {}
|
||||
uint8_t
|
||||
lwbtn_init_ex(lwbtn_t* lw, lwbtn_btn_t* btns, uint16_t btns_cnt, lwbtn_get_state_fn get_state_fn, lwbtn_evt_fn evt_fn) {
|
||||
lw = LWBTN_GET_LW(lw);
|
||||
|
||||
memset(lw, 0x00, sizeof(*lw));
|
||||
lw->btns = btns;
|
||||
lw->btns_cnt = btns_cnt;
|
||||
lw->evt_fn = evt_fn;
|
||||
lw->get_state_fn = get_state_fn;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Button processing function,
|
||||
* that reads the inputs and makes actions accordingly
|
||||
* \param[in] mstime: Current time in milliseconds
|
||||
*/
|
||||
void
|
||||
lwbtn_process(uint32_t mstime) {}
|
||||
uint8_t
|
||||
lwbtn_process_ex(lwbtn_t* lw, uint32_t mstime) {
|
||||
lwbtn_btn_t* b;
|
||||
uint8_t new_state;
|
||||
|
||||
lw = LWBTN_GET_LW(lw);
|
||||
|
||||
/* Process all buttons */
|
||||
for (size_t i = 0; i < lw->btns_cnt; ++i) {
|
||||
b = &lw->btns[i];
|
||||
|
||||
new_state = lw->get_state_fn(lw, b); /* Get button state */
|
||||
|
||||
/*
|
||||
* Button state has changed
|
||||
*/
|
||||
if (new_state != b->old_state) {
|
||||
/*
|
||||
* Button just became inactive
|
||||
*
|
||||
* - Handle on-release event
|
||||
* - Handle on-click event
|
||||
*/
|
||||
if (!new_state) {
|
||||
/*
|
||||
* We only need to react if on-press event has even been started.
|
||||
*
|
||||
* Do nothing if that was not the case
|
||||
*/
|
||||
if (b->flags & LWBTN_FLAG_ONPRESS_SENT) {
|
||||
/* Handle on-release event */
|
||||
b->flags &= ~LWBTN_FLAG_ONPRESS_SENT;
|
||||
lw->evt_fn(lw, b, LWBTN_EVT_ONRELEASE);
|
||||
|
||||
/* Check time validity for click event */
|
||||
if ((mstime - b->time_change) >= LWBTN_TIME_CLICK_GET_PRESSED_MIN(b)
|
||||
&& (mstime - b->time_change) <= LWBTN_TIME_CLICK_GET_PRESSED_MAX(b)) {
|
||||
|
||||
/*
|
||||
* Increase consecutive clicks if max not reached yet
|
||||
* and if time between two clicks is not too long
|
||||
*/
|
||||
if (b->click.consecutive_cnt > 0 && b->click.consecutive_cnt < LWBTN_CLICK_MAX_CONSECUTIVE(b)
|
||||
&& (mstime - b->click.last_time) < LWBTN_TIME_CLICK_MAX_MULTI(b)) {
|
||||
++b->click.consecutive_cnt;
|
||||
} else {
|
||||
/* Start over - set as first click */
|
||||
b->click.consecutive_cnt = 1;
|
||||
}
|
||||
b->click.last_time = mstime;
|
||||
} else {
|
||||
/* TODO: Shall we check previous clicks and send to user (??) */
|
||||
|
||||
/*
|
||||
* Button released with invalid click event
|
||||
*
|
||||
* TODO: Consider sending all data immediately from previous clicks?
|
||||
*
|
||||
* Clear then counter
|
||||
*/
|
||||
b->click.consecutive_cnt = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Button just pressed
|
||||
*/
|
||||
else {
|
||||
/* Do nothing - things are handled after debounce period */
|
||||
}
|
||||
b->time_change = mstime;
|
||||
b->keepalive.last_time = mstime;
|
||||
b->keepalive.cnt = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Button is still pressed
|
||||
*/
|
||||
else if (new_state) {
|
||||
/*
|
||||
* Handle debounce and send on-press event
|
||||
*
|
||||
* This is when we detect valid press
|
||||
*/
|
||||
if (!(b->flags & LWBTN_FLAG_ONPRESS_SENT)) {
|
||||
/* Check minimum stable time */
|
||||
if ((mstime - b->time_change) >= LWBTN_TIME_DEBOUNCE_GET_MIN(b)) {
|
||||
/*
|
||||
* Immediately send click event if number of
|
||||
* previous consecutive clicks reached maximum level.
|
||||
*
|
||||
* Handle this on on-press state
|
||||
*/
|
||||
if (b->click.consecutive_cnt > 0 && b->click.consecutive_cnt == LWBTN_CLICK_MAX_CONSECUTIVE(b)) {
|
||||
lw->evt_fn(lw, b, LWBTN_EVT_ONCLICK);
|
||||
b->click.consecutive_cnt = 0;
|
||||
}
|
||||
|
||||
/* Now start with new on-press */
|
||||
b->flags |= LWBTN_FLAG_ONPRESS_SENT;
|
||||
lw->evt_fn(lw, b, LWBTN_EVT_ONPRESS);
|
||||
|
||||
/* Set keep alive time */
|
||||
b->keepalive.last_time = mstime;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Handle keep alive, but only if on-press event has been sent
|
||||
*
|
||||
* Keep alive is sent when valid press is being detected
|
||||
*/
|
||||
else {
|
||||
if ((mstime - b->keepalive.last_time) >= LWBTN_TIME_KEEPALIVE_PERIOD(b)) {
|
||||
b->keepalive.last_time += LWBTN_TIME_KEEPALIVE_PERIOD(b);
|
||||
++b->keepalive.cnt;
|
||||
lw->evt_fn(lw, b, LWBTN_EVT_KEEPALIVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Button is still released
|
||||
*/
|
||||
else {
|
||||
/*
|
||||
* This is the part to process "timeout" after release event
|
||||
*
|
||||
* - Send click event with delay - to send it once for multi-click setup
|
||||
* This is sent after last valid click has been detected for OnRelease event
|
||||
* - Reset consecutive clicks counter if time after last click is longer than maximum one
|
||||
*/
|
||||
if (b->click.consecutive_cnt > 0) {
|
||||
if ((mstime - b->click.last_time) >= LWBTN_TIME_CLICK_SEND_TIMEOUT(b)) {
|
||||
lw->evt_fn(lw, b, LWBTN_EVT_ONCLICK);
|
||||
b->click.consecutive_cnt = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
b->old_state = new_state;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user