Add code skeleton

This commit is contained in:
Tilen Majerle
2022-10-25 19:52:34 +02:00
parent d1cd4b89da
commit 342e978316
3 changed files with 293 additions and 16 deletions
+60 -10
View File
@@ -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);
}
+63 -2
View File
@@ -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
View File
@@ -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;
}