From 38a569e0bd2432d4745a9d87a3ab9ab70dd891cd Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 30 Oct 2022 18:53:01 +0100 Subject: [PATCH 01/16] Put example code to separate file --- CMakeLists.txt | 1 + dev/main.c | 101 +----------------------- docs/user-manual/index.rst | 7 ++ examples/example_win32.c | 116 ++++++++++++++++++++++++++++ lwbtn/src/include/lwbtn/lwbtn_opt.h | 2 +- 5 files changed, 128 insertions(+), 99 deletions(-) create mode 100644 examples/example_win32.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 269701d..2585989 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,7 @@ else() # Add key executable block target_sources(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/dev/main.c + ${CMAKE_CURRENT_LIST_DIR}/examples/example_win32.c ) # Add key include paths diff --git a/dev/main.c b/dev/main.c index 17d4c89..81d091f 100644 --- a/dev/main.c +++ b/dev/main.c @@ -3,105 +3,10 @@ #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'}; -uint32_t last_time_keys[sizeof(keys) / sizeof(keys[0])] = {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]}}; - -/** - * \brief Get input state callback - * \param lw: LwBTN instance - * \param btn: Button instance - * \return `1` if button active, `0` otherwise - */ -uint8_t -prv_btn_get_state(struct lwbtn* lw, struct lwbtn_btn* btn) { - (void)lw; - return GetAsyncKeyState(*(int*)btn->arg) < 0; -} - -/** - * \brief Button event - * - * \param lw: LwBTN instance - * \param btn: Button instance - * \param evt: Button event - */ -void -prv_btn_event(struct lwbtn* lw, struct lwbtn_btn* btn, lwbtn_evt_t evt) { - const char* s; - uint32_t color; - HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); - uint32_t* diff_time_ptr = &last_time_keys[(*(int*)btn->arg) - '0']; - uint32_t diff_time = get_tick() - *diff_time_ptr; - - /* This is for purpose of test and timing validation */ - if (diff_time > 2000) { - diff_time = 0; - } - *diff_time_ptr = get_tick(); /* Set current date as last one */ - - /* Get event string */ - if (evt == LWBTN_EVT_KEEPALIVE) { - s = "KEEPALIVE"; - color = FOREGROUND_RED; - } else if (evt == LWBTN_EVT_ONPRESS) { - s = " ONPRESS"; - color = FOREGROUND_GREEN; - } else if (evt == LWBTN_EVT_ONRELEASE) { - s = "ONRELEASE"; - color = FOREGROUND_BLUE; - } else if (evt == LWBTN_EVT_ONCLICK) { - s = " ONCLICK"; - color = FOREGROUND_RED | FOREGROUND_GREEN; - } else { - s = " UNKNOWN"; - color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; - } - SetConsoleTextAttribute(hConsole, color); - printf("[%7u][%6u] CH: %c, evt: %s, keep-alive cnt: %3u, click cnt: %3u\r\n", (unsigned)get_tick(), - (unsigned)diff_time, *(int*)btn->arg, s, (unsigned)btn->keepalive.cnt, (unsigned)btn->click.cnt); - SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); - (void)lw; -} +extern int example_win32(void); int main(void) { - printf("Application running\r\n"); - QueryPerformanceFrequency(&freq); - QueryPerformanceCounter(&sys_start_time); - - /* Define buttons */ - lwbtn_init_ex(NULL, btns, sizeof(btns) / sizeof(btns[0]), prv_btn_get_state, prv_btn_event); - - while (1) { - /* Process forever */ - lwbtn_process_ex(NULL, get_tick()); - - /* Artificial sleep to offload win process */ - Sleep(5); - } -} - -/** - * \brief Get current tick in ms from start of program - * \return uint32_t: Tick in ms - */ -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); + example_win32(); + return 0; } diff --git a/docs/user-manual/index.rst b/docs/user-manual/index.rst index 10c053c..cc1dc72 100644 --- a/docs/user-manual/index.rst +++ b/docs/user-manual/index.rst @@ -19,6 +19,13 @@ User must define buttons array and pass it to the library. Next to that, ``2`` m User shall later periodically call processing function with current system time as simple parameter and get ready to receive various events. +A simple example for win32 is below: + +.. literalinclude:: ../../examples/example_win32.c + :language: c + :linenos: + :caption: Win32 example code + Input events ^^^^^^^^^^^^ diff --git a/examples/example_win32.c b/examples/example_win32.c new file mode 100644 index 0000000..5d6c341 --- /dev/null +++ b/examples/example_win32.c @@ -0,0 +1,116 @@ +#include "lwbtn/lwbtn.h" +#include "windows.h" +#include +#include + +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'}; +uint32_t last_time_keys[sizeof(keys) / sizeof(keys[0])] = {0}; + +/* List of buttons to process with assigned custom arguments for callback functions */ +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]}}; + +/** + * \brief Get input state callback + * \param lw: LwBTN instance + * \param btn: Button instance + * \return `1` if button active, `0` otherwise + */ +uint8_t +prv_btn_get_state(struct lwbtn* lw, struct lwbtn_btn* btn) { + (void)lw; + + /* + * Function will return negative number if button is pressed, + * or zero if button is releases + */ + return GetAsyncKeyState(*(int*)btn->arg) < 0; +} + +/** + * \brief Button event + * + * \param lw: LwBTN instance + * \param btn: Button instance + * \param evt: Button event + */ +void +prv_btn_event(struct lwbtn* lw, struct lwbtn_btn* btn, lwbtn_evt_t evt) { + const char* s; + uint32_t color; + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + uint32_t* diff_time_ptr = &last_time_keys[(*(int*)btn->arg) - '0']; + uint32_t diff_time = get_tick() - *diff_time_ptr; + + /* This is for purpose of test and timing validation */ + if (diff_time > 2000) { + diff_time = 0; + } + *diff_time_ptr = get_tick(); /* Set current date as last one */ + + /* Get event string */ + if (evt == LWBTN_EVT_KEEPALIVE) { + s = "KEEPALIVE"; + color = FOREGROUND_RED; + } else if (evt == LWBTN_EVT_ONPRESS) { + s = " ONPRESS"; + color = FOREGROUND_GREEN; + } else if (evt == LWBTN_EVT_ONRELEASE) { + s = "ONRELEASE"; + color = FOREGROUND_BLUE; + } else if (evt == LWBTN_EVT_ONCLICK) { + s = " ONCLICK"; + color = FOREGROUND_RED | FOREGROUND_GREEN; + } else { + s = " UNKNOWN"; + color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + } + SetConsoleTextAttribute(hConsole, color); + printf("[%7u][%6u] CH: %c, evt: %s, keep-alive cnt: %3u, click cnt: %3u\r\n", (unsigned)get_tick(), + (unsigned)diff_time, *(int*)btn->arg, s, (unsigned)btn->keepalive.cnt, (unsigned)btn->click.cnt); + SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + (void)lw; +} + +/** + * \brief Example function + */ +int +example_win32(void) { + printf("Application running\r\n"); + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(&sys_start_time); + + /* Define buttons */ + lwbtn_init_ex(NULL, btns, sizeof(btns) / sizeof(btns[0]), prv_btn_get_state, prv_btn_event); + + while (1) { + /* Process forever */ + lwbtn_process_ex(NULL, get_tick()); + + /* Artificial sleep to offload win process */ + Sleep(5); + } + return 0; +} + +/** + * \brief Get current tick in ms from start of program + * \return uint32_t: Tick in ms + */ +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); +} diff --git a/lwbtn/src/include/lwbtn/lwbtn_opt.h b/lwbtn/src/include/lwbtn/lwbtn_opt.h index 7d9275b..3d00da1 100644 --- a/lwbtn/src/include/lwbtn/lwbtn_opt.h +++ b/lwbtn/src/include/lwbtn/lwbtn_opt.h @@ -97,7 +97,7 @@ extern "C" { * before structure gets reset to default value */ #ifndef LWBTN_CFG_CLICK_MAX_CONSECUTIVE -#define LWBTN_CFG_CLICK_MAX_CONSECUTIVE 1 +#define LWBTN_CFG_CLICK_MAX_CONSECUTIVE 3 #endif /** From bb06b782019cc96cbde5f4e365a1e38784190386 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Mon, 31 Oct 2022 10:24:55 +0100 Subject: [PATCH 02/16] Update vscode --- .vscode/launch.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index a53089a..c76ad22 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,15 +1,12 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { + /* GDB must in be in the PATH environment */ "name": "(Windows) Launch", "type": "cppdbg", "request": "launch", "program": "${command:cmake.launchTargetPath}", - "miDebuggerPath": "c:\\msys64\\mingw64\\bin\\gdb.exe", "args": [], "stopAtEntry": false, "cwd": "${fileDirname}", From 0d66e8cf5fee7ed00806793f7625d2a12e3fb998 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 18 Dec 2022 12:30:03 +0100 Subject: [PATCH 03/16] fix(hdr): Apply header guard syntax LWxxx_module_HDR_H --- lwbtn/src/include/lwbtn/lwbtn_opt.h | 6 +++--- lwbtn/src/include/lwbtn/lwbtn_opts_template.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lwbtn/src/include/lwbtn/lwbtn_opt.h b/lwbtn/src/include/lwbtn/lwbtn_opt.h index 3d00da1..71212e4 100644 --- a/lwbtn/src/include/lwbtn/lwbtn_opt.h +++ b/lwbtn/src/include/lwbtn/lwbtn_opt.h @@ -31,8 +31,8 @@ * Author: Tilen MAJERLE * Version: v0.0.1 */ -#ifndef LWBTN_HDR_OPT_H -#define LWBTN_HDR_OPT_H +#ifndef LWBTN_OPT_HDR_H +#define LWBTN_OPT_HDR_H /* Uncomment to ignore user options (or set macro in compiler flags) */ /* #define LWBTN_IGNORE_USER_OPTS */ @@ -130,4 +130,4 @@ extern "C" { } #endif /* __cplusplus */ -#endif /* LWBTN_HDR_OPT_H */ +#endif /* LWBTN_OPT_HDR_H */ diff --git a/lwbtn/src/include/lwbtn/lwbtn_opts_template.h b/lwbtn/src/include/lwbtn/lwbtn_opts_template.h index c8ca728..0b09a2c 100644 --- a/lwbtn/src/include/lwbtn/lwbtn_opts_template.h +++ b/lwbtn/src/include/lwbtn/lwbtn_opts_template.h @@ -31,8 +31,8 @@ * Author: Tilen MAJERLE * Version: v0.0.1 */ -#ifndef LWBTN_HDR_OPTS_H -#define LWBTN_HDR_OPTS_H +#ifndef LWBTN_OPTS_HDR_H +#define LWBTN_OPTS_HDR_H /* Rename this file to "lwbtn_opts.h" for your application */ @@ -41,4 +41,4 @@ * copy & replace here settings you want to change values */ -#endif /* LWBTN_HDR_OPTS_H */ +#endif /* LWBTN_OPTS_HDR_H */ From 1ea33dc93c20beb9c9faa9aab29feb97852842f2 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Tue, 20 Dec 2022 17:45:17 +0100 Subject: [PATCH 04/16] Change lw to lwobj to better fit with clang-tidy --- lwbtn/src/include/lwbtn/lwbtn.h | 12 +++++----- lwbtn/src/lwbtn/lwbtn.c | 42 +++++++++++++++++---------------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/lwbtn/src/include/lwbtn/lwbtn.h b/lwbtn/src/include/lwbtn/lwbtn.h index d209750..f5a6e10 100644 --- a/lwbtn/src/include/lwbtn/lwbtn.h +++ b/lwbtn/src/include/lwbtn/lwbtn.h @@ -80,19 +80,19 @@ typedef enum { /** * \brief Button event function callback prototype - * \param[in] lw: LwBTN instance + * \param[in] lwobj: LwBTN instance * \param[in] btn: Button instance from array for which event occured * \param[in] evt: Event type */ -typedef void (*lwbtn_evt_fn)(struct lwbtn* lw, struct lwbtn_btn* btn, lwbtn_evt_t evt); +typedef void (*lwbtn_evt_fn)(struct lwbtn* lwobj, struct lwbtn_btn* btn, lwbtn_evt_t evt); /** * \brief Get button/input state callback function - * \param[in] lw: LwBTN instance + * \param[in] lwobj: LwBTN instance * \param[in] btn: Button instance from array to read state * \return `1` when button is considered `active`, `0` otherwise */ -typedef uint8_t (*lwbtn_get_state_fn)(struct lwbtn* lw, struct lwbtn_btn* btn); +typedef uint8_t (*lwbtn_get_state_fn)(struct lwbtn* lwobj, struct lwbtn_btn* btn); /** * \brief Button/input structure @@ -126,9 +126,9 @@ typedef struct lwbtn { lwbtn_get_state_fn get_state_fn; /*!< Pointer to get state function */ } 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, +uint8_t lwbtn_init_ex(lwbtn_t* lwobj, 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); +uint8_t lwbtn_process_ex(lwbtn_t* lwobj, uint32_t mstime); /** * \brief Initialize LwBTN library with buttons on default button group diff --git a/lwbtn/src/lwbtn/lwbtn.c b/lwbtn/src/lwbtn/lwbtn.c index b68b444..9087e52 100644 --- a/lwbtn/src/lwbtn/lwbtn.c +++ b/lwbtn/src/lwbtn/lwbtn.c @@ -45,11 +45,11 @@ /* Default button group instance */ static lwbtn_t lwbtn_default; -#define LWBTN_GET_LW(in_lw) ((in_lw) != NULL ? (in_lw) : (&lwbtn_default)) +#define LWBTN_GET_LWOBJ(in_lwobj) ((in_lwobj) != NULL ? (in_lwobj) : (&lwbtn_default)) /** * \brief Initialize button manager - * \param[in] lw: LwBTN instance. Set to `NULL` to use default one + * \param[in] lwobj: LwBTN instance. Set to `NULL` to use default one * \param[in] btns: Array of buttons to process * \param[in] btns_cnt: Number of buttons to process * \param[in] get_state_fn: Pointer to function providing button state on demand @@ -57,38 +57,40 @@ static lwbtn_t lwbtn_default; * \return `1` on success, `0` otherwise */ 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); +lwbtn_init_ex(lwbtn_t* lwobj, lwbtn_btn_t* btns, uint16_t btns_cnt, lwbtn_get_state_fn get_state_fn, + lwbtn_evt_fn evt_fn) { + lwobj = LWBTN_GET_LWOBJ(lwobj); if (btns == NULL || btns_cnt == 0 || get_state_fn == NULL || evt_fn == NULL) { return 0; } - 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; + memset(lwobj, 0x00, sizeof(*lwobj)); + lwobj->btns = btns; + lwobj->btns_cnt = btns_cnt; + lwobj->evt_fn = evt_fn; + lwobj->get_state_fn = get_state_fn; return 1; } /** * \brief Button processing function, * that reads the inputs and makes actions accordingly. + * \param[in] lwobj: LwBTN instance. Set to `NULL` to use default one * \param[in] mstime: Current time in milliseconds * \return `1` on success, `0` otherwise */ uint8_t -lwbtn_process_ex(lwbtn_t* lw, uint32_t mstime) { +lwbtn_process_ex(lwbtn_t* lwobj, uint32_t mstime) { lwbtn_btn_t* btn = NULL; uint8_t new_state = 0; - lw = LWBTN_GET_LW(lw); + lwobj = LWBTN_GET_LWOBJ(lwobj); /* Process all buttons */ - for (size_t index = 0; index < lw->btns_cnt; ++index) { - btn = &lw->btns[index]; + for (size_t index = 0; index < lwobj->btns_cnt; ++index) { + btn = &lwobj->btns[index]; - new_state = lw->get_state_fn(lw, btn); /* Get button state */ + new_state = lwobj->get_state_fn(lwobj, btn); /* Get button state */ /* * Button state has changed @@ -109,7 +111,7 @@ lwbtn_process_ex(lwbtn_t* lw, uint32_t mstime) { if (btn->flags & LWBTN_FLAG_ONPRESS_SENT) { /* Handle on-release event */ btn->flags &= ~LWBTN_FLAG_ONPRESS_SENT; - lw->evt_fn(lw, btn, LWBTN_EVT_ONRELEASE); + lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONRELEASE); /* Check time validity for click event */ if ((mstime - btn->time_change) >= LWBTN_TIME_CLICK_GET_PRESSED_MIN(btn) @@ -145,7 +147,7 @@ lwbtn_process_ex(lwbtn_t* lw, uint32_t mstime) { * if maximum number of consecutive clicks has been reached. */ if (btn->click.cnt > 0 && btn->click.cnt == LWBTN_CLICK_MAX_CONSECUTIVE(btn)) { - lw->evt_fn(lw, btn, LWBTN_EVT_ONCLICK); + lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONCLICK); btn->click.cnt = 0; } #endif /* LWBTN_CFG_CLICK_MAX_CONSECUTIVE_SEND_IMMEDIATELY */ @@ -182,14 +184,14 @@ lwbtn_process_ex(lwbtn_t* lw, uint32_t mstime) { * if maximum number of consecutive clicks has been reached. */ if (btn->click.cnt > 0 && btn->click.cnt == LWBTN_CLICK_MAX_CONSECUTIVE(btn)) { - lw->evt_fn(lw, btn, LWBTN_EVT_ONCLICK); + lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONCLICK); btn->click.cnt = 0; } #endif /* !LWBTN_CFG_CLICK_MAX_CONSECUTIVE_SEND_IMMEDIATELY */ /* Start with new on-press */ btn->flags |= LWBTN_FLAG_ONPRESS_SENT; - lw->evt_fn(lw, btn, LWBTN_EVT_ONPRESS); + lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONPRESS); /* Set keep alive time */ btn->keepalive.last_time = mstime; @@ -205,7 +207,7 @@ lwbtn_process_ex(lwbtn_t* lw, uint32_t mstime) { if ((mstime - btn->keepalive.last_time) >= LWBTN_TIME_KEEPALIVE_PERIOD(btn)) { btn->keepalive.last_time += LWBTN_TIME_KEEPALIVE_PERIOD(btn); ++btn->keepalive.cnt; - lw->evt_fn(lw, btn, LWBTN_EVT_KEEPALIVE); + lwobj->evt_fn(lwobj, btn, LWBTN_EVT_KEEPALIVE); } } } @@ -224,7 +226,7 @@ lwbtn_process_ex(lwbtn_t* lw, uint32_t mstime) { */ if (btn->click.cnt > 0) { if ((mstime - btn->click.last_time) >= LWBTN_TIME_CLICK_MAX_MULTI(btn)) { - lw->evt_fn(lw, btn, LWBTN_EVT_ONCLICK); + lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONCLICK); btn->click.cnt = 0; } } From a803a736f7a0673166657661ded7870d6183d37e Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Fri, 30 Dec 2022 14:36:35 +0100 Subject: [PATCH 05/16] feat: Add option to manually set btn state --- lwbtn/src/include/lwbtn/lwbtn_opt.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/lwbtn/src/include/lwbtn/lwbtn_opt.h b/lwbtn/src/include/lwbtn/lwbtn_opt.h index 71212e4..7574ead 100644 --- a/lwbtn/src/include/lwbtn/lwbtn_opt.h +++ b/lwbtn/src/include/lwbtn/lwbtn_opt.h @@ -122,6 +122,31 @@ extern "C" { #define LWBTN_CFG_CLICK_MAX_CONSECUTIVE_SEND_IMMEDIATELY 1 #endif +/** + * \brief Enables `1` or disables `0` optional manual state set for buttons. + * + * When this feature is enabled, user has an option to use a callback for state check, + * or can manually set the button state with API functions. + * + * After user has once manually set state for the button, it can not longer be set back + * to callback state check. + */ +#ifndef LWBTN_CFG_ALLOW_MANUAL_STATE_SET +#define LWBTN_CFG_ALLOW_MANUAL_STATE_SET 0 +#endif + +/** + * \brief Enables `1` or disables `0` force manual button state set. + * + * When enabled, it forces the user to manually set the state of the button. + * API functions are modified and no longer support callback parameter for new state check. + * + * \note This can only be used when \ref LWBTN_CFG_ALLOW_MANUAL_STATE_SET is enabled. + */ +#ifndef LWBTN_CFG_FORCE_MANUAL_STATE_SET +#define LWBTN_CFG_FORCE_MANUAL_STATE_SET 1 +#endif + /** * \} */ From 99c8fb70cc89d1b82a120c124b4a3fad4517b045 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Fri, 30 Dec 2022 14:46:04 +0100 Subject: [PATCH 06/16] feat: Add option to set button state manually. --- lwbtn/src/include/lwbtn/lwbtn.h | 30 ++- lwbtn/src/include/lwbtn/lwbtn_opt.h | 25 +- lwbtn/src/lwbtn/lwbtn.c | 379 +++++++++++++++++----------- 3 files changed, 276 insertions(+), 158 deletions(-) diff --git a/lwbtn/src/include/lwbtn/lwbtn.h b/lwbtn/src/include/lwbtn/lwbtn.h index f5a6e10..5b2b0af 100644 --- a/lwbtn/src/include/lwbtn/lwbtn.h +++ b/lwbtn/src/include/lwbtn/lwbtn.h @@ -98,7 +98,11 @@ typedef uint8_t (*lwbtn_get_state_fn)(struct lwbtn* lwobj, struct lwbtn_btn* btn * \brief Button/input structure */ typedef struct lwbtn_btn { - uint16_t flags; /*!< Private button flags management */ + uint16_t flags; /*!< Private button flags management */ +#if LWBTN_CFG_ALLOW_MANUAL_STATE_SET + uint8_t curr_state; /*!< Current button state to be processed. It is used + to keep track when application manually sets the button state */ +#endif /* LWBTN_CFG_ALLOW_MANUAL_STATE_SET */ uint8_t old_state; /*!< Old button state - `1` means active, `0` means inactive */ uint32_t time_change; /*!< Time in ms when button state got changed last time */ @@ -120,33 +124,47 @@ typedef struct lwbtn_btn { * \brief LwBTN group structure */ typedef struct lwbtn { - lwbtn_btn_t* btns; /*!< Pointer to buttons array */ - uint16_t btns_cnt; /*!< Number of buttons in array */ - lwbtn_evt_fn evt_fn; /*!< Pointer to event function */ + lwbtn_btn_t* btns; /*!< Pointer to buttons array */ + uint16_t btns_cnt; /*!< Number of buttons in array */ + lwbtn_evt_fn evt_fn; /*!< Pointer to event function */ +#if !LWBTN_CFG_FORCE_MANUAL_STATE_SET lwbtn_get_state_fn get_state_fn; /*!< Pointer to get state function */ +#endif /* !LWBTN_CFG_FORCE_MANUAL_STATE_SET */ } lwbtn_t; uint8_t lwbtn_init_ex(lwbtn_t* lwobj, 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* lwobj, uint32_t mstime); +uint8_t lwbtn_process_btn_ex(lwbtn_t* lwobj, lwbtn_btn_t* btn, uint32_t mstime); +uint8_t lwbtn_set_btn_state(lwbtn_btn_t* btn, uint8_t state); /** * \brief Initialize LwBTN library with buttons on default button group * \param[in] btns: Array of buttons to process * \param[in] btns_cnt: Number of buttons to process - * \param[in] get_state_fn: Pointer to function providing button state on demand + * \param[in] get_state_fn: Pointer to function providing button state on demand. + * Can be set to `NULL` if \ref LWBTN_CFG_FORCE_MANUAL_STATE_SET is enabled * \param[in] evt_fn: Button event function callback * \sa lwbtn_init_ex */ #define lwbtn_init(btns, btns_cnt, get_state_fn, evt_fn) lwbtn_init_ex(NULL, btns, btns_cnt, get_state_fn, evt_fn) /** - * \brief Periodically read button states and take appropriate actions + * \brief Periodically read button states and take appropriate actions. + * It processes the default buttons instance group. * \param[in] mstime: Current system time in milliseconds * \sa lwbtn_process_ex */ #define lwbtn_process(mstime) lwbtn_process_ex(NULL, mstime) +/** + * \brief Process specific button in a default LwBTN instance + * + * \param[in] btn: Button instance to process + * \param[in] mstime: Current system time in milliseconds + */ +#define lwbtn_process_btn(btn, mstime) lwbtn_process_btn_ex(NULL, (btn), (mstime)) + /** * \} */ diff --git a/lwbtn/src/include/lwbtn/lwbtn_opt.h b/lwbtn/src/include/lwbtn/lwbtn_opt.h index 7574ead..e2e3f90 100644 --- a/lwbtn/src/include/lwbtn/lwbtn_opt.h +++ b/lwbtn/src/include/lwbtn/lwbtn_opt.h @@ -142,9 +142,32 @@ extern "C" { * API functions are modified and no longer support callback parameter for new state check. * * \note This can only be used when \ref LWBTN_CFG_ALLOW_MANUAL_STATE_SET is enabled. + * + * Combination of both, \ref LWBTN_CFG_ALLOW_MANUAL_STATE_SET and \ref LWBTN_CFG_FORCE_MANUAL_STATE_SET, provides + * + * | ALLOW_MANUAL_STATE | FORCE_MANUAL_STATE | Comment + * +--------------------+--------------------+------------------------------------------------------------------+ + * | 0 | 0 | A callback is used to get new button state + * +--------------------+--------------------+------------------------------------------------------------------+ + * | 0 | 1 | Compilation error -> invalid configuration + * +--------------------+--------------------+------------------------------------------------------------------+ + * | 1 | 0 | API to manually set the state is enabled. + * | | | Default setting to get new state remains a callback check. + * | | | If user calls API to manually set the state for specific button, + * | | | callback is no more used for that specific button and only + * | | | manual API can set new state from now on -> use with caution. + * | | | Buttons where manual API was not called on, are still checked + * | | | through callback. + * | | | + * | | | You would normally enable this feature when library is used + * | | | with various button types and you may want this flexibility. + * +--------------------+--------------------+------------------------------------------------------------------+ + * | 1 | 1 | Callback API for new state is completely disabled. + * | | | user must manually set the state for all buttons. + * +--------------------+--------------------+------------------------------------------------------------------+ */ #ifndef LWBTN_CFG_FORCE_MANUAL_STATE_SET -#define LWBTN_CFG_FORCE_MANUAL_STATE_SET 1 +#define LWBTN_CFG_FORCE_MANUAL_STATE_SET 0 #endif /** diff --git a/lwbtn/src/lwbtn/lwbtn.c b/lwbtn/src/lwbtn/lwbtn.c index 9087e52..c7194d8 100644 --- a/lwbtn/src/lwbtn/lwbtn.c +++ b/lwbtn/src/lwbtn/lwbtn.c @@ -34,7 +34,14 @@ #include #include "lwbtn/lwbtn.h" -#define LWBTN_FLAG_ONPRESS_SENT ((uint16_t)0x0001) +#if LWBTN_CFG_FORCE_MANUAL_STATE_SET && !LWBTN_CFG_ALLOW_MANUAL_STATE_SET +#error "LWBTN_CFG_FORCE_MANUAL_STATE_SET && !LWBTN_CFG_ALLOW_MANUAL_STATE_SET is not a valid combination" +#endif + +#define LWBTN_FLAG_ONPRESS_SENT ((uint16_t)0x0001) /*!< Flag indicates that on-press event has been sent */ +#define LWBTN_FLAG_MANUAL_STATE \ + ((uint16_t)0x0002) /*!< Flag indicates that user wants to manually set button state. + Do not call "get_state" function */ #define LWBTN_TIME_DEBOUNCE_GET_MIN(btn) LWBTN_CFG_TIME_DEBOUNCE #define LWBTN_TIME_CLICK_GET_PRESSED_MIN(btn) LWBTN_CFG_TIME_CLICK_MIN @@ -43,16 +50,187 @@ #define LWBTN_TIME_KEEPALIVE_PERIOD(btn) LWBTN_CFG_TIME_KEEPALIVE_PERIOD #define LWBTN_CLICK_MAX_CONSECUTIVE(btn) LWBTN_CFG_CLICK_MAX_CONSECUTIVE +#if LWBTN_CFG_ALLOW_MANUAL_STATE_SET +#if LWBTN_CFG_FORCE_MANUAL_STATE_SET +/* Set the direct state */ +#define LWBTN_BTN_GET_STATE(lwobj, btn) (btn)->curr_state +#else +/* Check flag and decide how to get state */ +#define LWBTN_BTN_GET_STATE(lwobj, btn) \ + (((btn)->flags & LWBTN_FLAG_MANUAL_STATE) ? ((btn)->curr_state) : ((lwobj)->get_state_fn((lwobj), (btn)))) +#endif +#else /* !LWBTN_CFG_ALLOW_MANUAL_STATE_SET */ +/* Get state from callback */ +#define LWBTN_BTN_GET_STATE(lwobj, btn) (lwobj)->get_state_fn((lwobj), (btn)) +#endif /* LWBTN_CFG_ALLOW_MANUAL_STATE_SET */ + /* Default button group instance */ static lwbtn_t lwbtn_default; #define LWBTN_GET_LWOBJ(in_lwobj) ((in_lwobj) != NULL ? (in_lwobj) : (&lwbtn_default)) +/** + * \brief Process the button information and state + * + * \param[in] lwobj: LwBTN instance. Set to `NULL` to use default one + * \param[in] btn: Button instance to process + * \param[in] mstime: Current milliseconds system time + */ +void +prv_process_btn(lwbtn_t* lwobj, lwbtn_btn_t* btn, uint32_t mstime) { + uint8_t new_state; + + /* Get button state */ + new_state = LWBTN_BTN_GET_STATE(lwobj, btn); + + /* + * Button state has changed + */ + if (new_state != btn->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 (btn->flags & LWBTN_FLAG_ONPRESS_SENT) { + /* Handle on-release event */ + btn->flags &= ~LWBTN_FLAG_ONPRESS_SENT; + lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONRELEASE); + + /* Check time validity for click event */ + if ((mstime - btn->time_change) >= LWBTN_TIME_CLICK_GET_PRESSED_MIN(btn) + && (mstime - btn->time_change) <= LWBTN_TIME_CLICK_GET_PRESSED_MAX(btn)) { + + /* + * Increase consecutive clicks if max not reached yet + * and if time between two clicks is not too long + * + * Otherwise we consider click as fresh one + */ + if (btn->click.cnt > 0 && btn->click.cnt < LWBTN_CLICK_MAX_CONSECUTIVE(btn) + && (mstime - btn->click.last_time) < LWBTN_TIME_CLICK_MAX_MULTI(btn)) { + ++btn->click.cnt; + } else { + btn->click.cnt = 1; + } + btn->click.last_time = mstime; + } else { + /* + * There was an on-release event, but timing + * for click event detection is outside allowed window. + * + * If user has some consecutive clicks from previous clicks, + * these will be sent after the timeout window (and after the on-release event) + */ + } + +#if LWBTN_CFG_CLICK_MAX_CONSECUTIVE_SEND_IMMEDIATELY + /* + * Depending on the configuration, + * this part will send on-click event immediately after release event, + * if maximum number of consecutive clicks has been reached. + */ + if (btn->click.cnt > 0 && btn->click.cnt == LWBTN_CLICK_MAX_CONSECUTIVE(btn)) { + lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONCLICK); + btn->click.cnt = 0; + } +#endif /* LWBTN_CFG_CLICK_MAX_CONSECUTIVE_SEND_IMMEDIATELY */ + } + } + + /* + * Button just pressed + */ + else { + /* Do nothing - things are handled after debounce period */ + } + btn->time_change = mstime; + btn->keepalive.last_time = mstime; + btn->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 (!(btn->flags & LWBTN_FLAG_ONPRESS_SENT)) { + /* Check minimum stable time */ + if ((mstime - btn->time_change) >= LWBTN_TIME_DEBOUNCE_GET_MIN(btn)) { +#if !LWBTN_CFG_CLICK_MAX_CONSECUTIVE_SEND_IMMEDIATELY + /* + * Depending on the configuration, + * this part will send on-click event just before the next on-press release, + * if maximum number of consecutive clicks has been reached. + */ + if (btn->click.cnt > 0 && btn->click.cnt == LWBTN_CLICK_MAX_CONSECUTIVE(btn)) { + lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONCLICK); + btn->click.cnt = 0; + } +#endif /* !LWBTN_CFG_CLICK_MAX_CONSECUTIVE_SEND_IMMEDIATELY */ + + /* Start with new on-press */ + btn->flags |= LWBTN_FLAG_ONPRESS_SENT; + lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONPRESS); + + /* Set keep alive time */ + btn->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 - btn->keepalive.last_time) >= LWBTN_TIME_KEEPALIVE_PERIOD(btn)) { + btn->keepalive.last_time += LWBTN_TIME_KEEPALIVE_PERIOD(btn); + ++btn->keepalive.cnt; + lwobj->evt_fn(lwobj, btn, LWBTN_EVT_KEEPALIVE); + } + } + } + + /* + * Button is still released + */ + else { + /* + * Based on te configuration, this part of the code + * will send on-click event after certain timeout. + * + * This feature is useful if users prefers multi-click feature + * that is reported only after last click event happened, + * including number of clicks made by user + */ + if (btn->click.cnt > 0) { + if ((mstime - btn->click.last_time) >= LWBTN_TIME_CLICK_MAX_MULTI(btn)) { + lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONCLICK); + btn->click.cnt = 0; + } + } + } + btn->old_state = new_state; +} + /** * \brief Initialize button manager * \param[in] lwobj: LwBTN instance. Set to `NULL` to use default one * \param[in] btns: Array of buttons to process * \param[in] btns_cnt: Number of buttons to process - * \param[in] get_state_fn: Pointer to function providing button state on demand + * \param[in] get_state_fn: Pointer to function providing button state on demand. + * Can be set to `NULL` if \ref LWBTN_CFG_FORCE_MANUAL_STATE_SET is enabled * \param[in] evt_fn: Button event function callback * \return `1` on success, `0` otherwise */ @@ -68,170 +246,69 @@ lwbtn_init_ex(lwbtn_t* lwobj, lwbtn_btn_t* btns, uint16_t btns_cnt, lwbtn_get_st lwobj->btns = btns; lwobj->btns_cnt = btns_cnt; lwobj->evt_fn = evt_fn; +#if !LWBTN_CFG_FORCE_MANUAL_STATE_SET lwobj->get_state_fn = get_state_fn; +#endif /* !LWBTN_CFG_FORCE_MANUAL_STATE_SET */ + (void)get_state_fn; /* May be unused */ return 1; } /** - * \brief Button processing function, - * that reads the inputs and makes actions accordingly. + * \brief Button processing function, that reads the inputs and makes actions accordingly. + * + * It checks state of all the buttons, linked to the specific LwBTN instance (group). + * * \param[in] lwobj: LwBTN instance. Set to `NULL` to use default one * \param[in] mstime: Current time in milliseconds * \return `1` on success, `0` otherwise */ uint8_t lwbtn_process_ex(lwbtn_t* lwobj, uint32_t mstime) { - lwbtn_btn_t* btn = NULL; - uint8_t new_state = 0; - lwobj = LWBTN_GET_LWOBJ(lwobj); /* Process all buttons */ for (size_t index = 0; index < lwobj->btns_cnt; ++index) { - btn = &lwobj->btns[index]; - - new_state = lwobj->get_state_fn(lwobj, btn); /* Get button state */ - - /* - * Button state has changed - */ - if (new_state != btn->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 (btn->flags & LWBTN_FLAG_ONPRESS_SENT) { - /* Handle on-release event */ - btn->flags &= ~LWBTN_FLAG_ONPRESS_SENT; - lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONRELEASE); - - /* Check time validity for click event */ - if ((mstime - btn->time_change) >= LWBTN_TIME_CLICK_GET_PRESSED_MIN(btn) - && (mstime - btn->time_change) <= LWBTN_TIME_CLICK_GET_PRESSED_MAX(btn)) { - - /* - * Increase consecutive clicks if max not reached yet - * and if time between two clicks is not too long - * - * Otherwise we consider click as fresh one - */ - if (btn->click.cnt > 0 && btn->click.cnt < LWBTN_CLICK_MAX_CONSECUTIVE(btn) - && (mstime - btn->click.last_time) < LWBTN_TIME_CLICK_MAX_MULTI(btn)) { - ++btn->click.cnt; - } else { - btn->click.cnt = 1; - } - btn->click.last_time = mstime; - } else { - /* - * There was an on-release event, but timing - * for click event detection is outside allowed window. - * - * If user has some consecutive clicks from previous clicks, - * these will be sent after the timeout window (and after the on-release event) - */ - } - -#if LWBTN_CFG_CLICK_MAX_CONSECUTIVE_SEND_IMMEDIATELY - /* - * Depending on the configuration, - * this part will send on-click event immediately after release event, - * if maximum number of consecutive clicks has been reached. - */ - if (btn->click.cnt > 0 && btn->click.cnt == LWBTN_CLICK_MAX_CONSECUTIVE(btn)) { - lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONCLICK); - btn->click.cnt = 0; - } -#endif /* LWBTN_CFG_CLICK_MAX_CONSECUTIVE_SEND_IMMEDIATELY */ - } - } - - /* - * Button just pressed - */ - else { - /* Do nothing - things are handled after debounce period */ - } - btn->time_change = mstime; - btn->keepalive.last_time = mstime; - btn->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 (!(btn->flags & LWBTN_FLAG_ONPRESS_SENT)) { - /* Check minimum stable time */ - if ((mstime - btn->time_change) >= LWBTN_TIME_DEBOUNCE_GET_MIN(btn)) { -#if !LWBTN_CFG_CLICK_MAX_CONSECUTIVE_SEND_IMMEDIATELY - /* - * Depending on the configuration, - * this part will send on-click event just before the next on-press release, - * if maximum number of consecutive clicks has been reached. - */ - if (btn->click.cnt > 0 && btn->click.cnt == LWBTN_CLICK_MAX_CONSECUTIVE(btn)) { - lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONCLICK); - btn->click.cnt = 0; - } -#endif /* !LWBTN_CFG_CLICK_MAX_CONSECUTIVE_SEND_IMMEDIATELY */ - - /* Start with new on-press */ - btn->flags |= LWBTN_FLAG_ONPRESS_SENT; - lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONPRESS); - - /* Set keep alive time */ - btn->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 - btn->keepalive.last_time) >= LWBTN_TIME_KEEPALIVE_PERIOD(btn)) { - btn->keepalive.last_time += LWBTN_TIME_KEEPALIVE_PERIOD(btn); - ++btn->keepalive.cnt; - lwobj->evt_fn(lwobj, btn, LWBTN_EVT_KEEPALIVE); - } - } - } - - /* - * Button is still released - */ - else { - /* - * Based on te configuration, this part of the code - * will send on-click event after certain timeout. - * - * This feature is useful if users prefers multi-click feature - * that is reported only after last click event happened, - * including number of clicks made by user - */ - if (btn->click.cnt > 0) { - if ((mstime - btn->click.last_time) >= LWBTN_TIME_CLICK_MAX_MULTI(btn)) { - lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONCLICK); - btn->click.cnt = 0; - } - } - } - btn->old_state = new_state; + prv_process_btn(lwobj, &lwobj->btns[index], mstime); } return 1; } + +/** + * \brief Process single button instance from the specific LwOBJ instance (group). + * + * This feature can be used if application wants to process the button events only + * when interrupt hits (as a trigger). It gives user higher autonomy to decide which + * and when it will call specific button processing. + * + * \param[in] lwobj: LwBTN instance. Set to `NULL` to use default one + * \param[in] btn: Button object. Must not be set to `NULL`. + * \param[in] mstime: Current time in milliseconds + * \return `1` on success, `0` otherwise + */ +uint8_t +lwbtn_process_btn_ex(lwbtn_t* lwobj, lwbtn_btn_t* btn, uint32_t mstime) { + if (btn != NULL) { + prv_process_btn(LWBTN_GET_LWOBJ(lwobj), btn, mstime); + return 1; + } + return 0; +} + +/** + * \brief Set button state to either "active" or "inactive". + * \param[in] btn: Button instance + * \param[in] state: New button state. `1` is for active (pressed), `0` is for inactive (released). + * \return `1` on success, `0` otherwise + */ +uint8_t +lwbtn_set_btn_state(lwbtn_btn_t* btn, uint8_t state) { +#if LWBTN_CFG_MANUAL_STATE_SET + btn->curr_state = state; + btn->flags |= LWBTN_FLAG_MANUAL_STATE; + return 1; +#else + (void)btn; + (void)state; + return 0; +#endif +} From e37ddfc3c16725720478cd5a7d70a3ad85c6418b Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 1 Jan 2023 20:09:31 +0100 Subject: [PATCH 07/16] feat: Add LWBTN_CFG_GET_STATE_MODE config --- .vscode/settings.json | 3 +- CHANGELOG.md | 4 +++ examples/example_win32.c | 16 ++++++--- lwbtn/src/include/lwbtn/lwbtn.h | 8 ++--- lwbtn/src/include/lwbtn/lwbtn_opt.h | 54 ++++++++--------------------- lwbtn/src/lwbtn/lwbtn.c | 33 ++++++++---------- 6 files changed, 51 insertions(+), 67 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 50e7c03..0221941 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,7 +6,8 @@ "string.h": "c", "lwevt_opt.h": "c", "lwbtn.h": "c", - "lwbtn_opt.h": "c" + "lwbtn_opt.h": "c", + "lwbtn_opts.h": "c" }, "esbonio.sphinx.confDir": "" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 248fe70..442ef05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,4 +2,8 @@ ## Develop +- Added `LWBTN_CFG_GET_STATE_MODE` to control *get state* mode + +## v0.0.1 + - First commit \ No newline at end of file diff --git a/examples/example_win32.c b/examples/example_win32.c index 5d6c341..de753b2 100644 --- a/examples/example_win32.c +++ b/examples/example_win32.c @@ -11,10 +11,11 @@ const int keys[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; uint32_t last_time_keys[sizeof(keys) / sizeof(keys[0])] = {0}; /* List of buttons to process with assigned custom arguments for callback functions */ -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]}}; +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]}, +}; /** * \brief Get input state callback @@ -94,6 +95,13 @@ example_win32(void) { /* Process forever */ lwbtn_process_ex(NULL, get_tick()); + /* Manually read button state */ +#if LWBTN_CFG_GET_STATE_MODE == LWBTN_GET_STATE_MODE_MANUAL + for (size_t i = 0; i < sizeof(btns) / sizeof(btns[0]); ++i) { + lwbtn_set_btn_state(&btns[i], prv_btn_get_state(NULL, &btns[i])); + } +#endif /* LWBTN_CFG_GET_STATE_MODE == LWBTN_GET_STATE_MODE_MANUAL */ + /* Artificial sleep to offload win process */ Sleep(5); } diff --git a/lwbtn/src/include/lwbtn/lwbtn.h b/lwbtn/src/include/lwbtn/lwbtn.h index 5b2b0af..b1fca68 100644 --- a/lwbtn/src/include/lwbtn/lwbtn.h +++ b/lwbtn/src/include/lwbtn/lwbtn.h @@ -99,10 +99,10 @@ typedef uint8_t (*lwbtn_get_state_fn)(struct lwbtn* lwobj, struct lwbtn_btn* btn */ typedef struct lwbtn_btn { uint16_t flags; /*!< Private button flags management */ -#if LWBTN_CFG_ALLOW_MANUAL_STATE_SET +#if LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_CALLBACK uint8_t curr_state; /*!< Current button state to be processed. It is used to keep track when application manually sets the button state */ -#endif /* LWBTN_CFG_ALLOW_MANUAL_STATE_SET */ +#endif /* LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_CALLBACK */ uint8_t old_state; /*!< Old button state - `1` means active, `0` means inactive */ uint32_t time_change; /*!< Time in ms when button state got changed last time */ @@ -127,9 +127,9 @@ typedef struct lwbtn { lwbtn_btn_t* btns; /*!< Pointer to buttons array */ uint16_t btns_cnt; /*!< Number of buttons in array */ lwbtn_evt_fn evt_fn; /*!< Pointer to event function */ -#if !LWBTN_CFG_FORCE_MANUAL_STATE_SET +#if LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_MANUAL lwbtn_get_state_fn get_state_fn; /*!< Pointer to get state function */ -#endif /* !LWBTN_CFG_FORCE_MANUAL_STATE_SET */ +#endif /* LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_MANUAL */ } lwbtn_t; uint8_t lwbtn_init_ex(lwbtn_t* lwobj, lwbtn_btn_t* btns, uint16_t btns_cnt, lwbtn_get_state_fn get_state_fn, diff --git a/lwbtn/src/include/lwbtn/lwbtn_opt.h b/lwbtn/src/include/lwbtn/lwbtn_opt.h index e2e3f90..939c656 100644 --- a/lwbtn/src/include/lwbtn/lwbtn_opt.h +++ b/lwbtn/src/include/lwbtn/lwbtn_opt.h @@ -122,52 +122,26 @@ extern "C" { #define LWBTN_CFG_CLICK_MAX_CONSECUTIVE_SEND_IMMEDIATELY 1 #endif -/** - * \brief Enables `1` or disables `0` optional manual state set for buttons. - * - * When this feature is enabled, user has an option to use a callback for state check, - * or can manually set the button state with API functions. - * - * After user has once manually set state for the button, it can not longer be set back - * to callback state check. - */ -#ifndef LWBTN_CFG_ALLOW_MANUAL_STATE_SET -#define LWBTN_CFG_ALLOW_MANUAL_STATE_SET 0 -#endif +#define LWBTN_GET_STATE_MODE_CALLBACK 0 /*!< Callback-only state mode */ +#define LWBTN_GET_STATE_MODE_MANUAL 1 /*!< Manual-only state mode */ +#define LWBTN_GET_STATE_MODE_CALLBACK_OR_MANUAL 2 /*!< Callback or manual state mode */ /** - * \brief Enables `1` or disables `0` force manual button state set. + * \brief Sets the mode how new button state is acquired. * - * When enabled, it forces the user to manually set the state of the button. - * API functions are modified and no longer support callback parameter for new state check. + * Different modes are availale, set with the level number: * - * \note This can only be used when \ref LWBTN_CFG_ALLOW_MANUAL_STATE_SET is enabled. + * - `LWBTN_GET_STATE_MODE_CALLBACK`: State of the button is checked through *get state* callback function (default mode, legacy) + * - `LWBTN_GET_STATE_MODE_MANUAL`: Only manual state set is enabled. Application must set the button state with API functions. + * Callback API is not used. + * - `LWBTN_GET_STATE_MODE_CALLBACK_OR_MANUAL`: State of the button is checked through *get state* callback function (by default). + * It enables API to manually set the state with approapriate function call. + * Button state is checked with the callback at least until manual state API function is called. * - * Combination of both, \ref LWBTN_CFG_ALLOW_MANUAL_STATE_SET and \ref LWBTN_CFG_FORCE_MANUAL_STATE_SET, provides - * - * | ALLOW_MANUAL_STATE | FORCE_MANUAL_STATE | Comment - * +--------------------+--------------------+------------------------------------------------------------------+ - * | 0 | 0 | A callback is used to get new button state - * +--------------------+--------------------+------------------------------------------------------------------+ - * | 0 | 1 | Compilation error -> invalid configuration - * +--------------------+--------------------+------------------------------------------------------------------+ - * | 1 | 0 | API to manually set the state is enabled. - * | | | Default setting to get new state remains a callback check. - * | | | If user calls API to manually set the state for specific button, - * | | | callback is no more used for that specific button and only - * | | | manual API can set new state from now on -> use with caution. - * | | | Buttons where manual API was not called on, are still checked - * | | | through callback. - * | | | - * | | | You would normally enable this feature when library is used - * | | | with various button types and you may want this flexibility. - * +--------------------+--------------------+------------------------------------------------------------------+ - * | 1 | 1 | Callback API for new state is completely disabled. - * | | | user must manually set the state for all buttons. - * +--------------------+--------------------+------------------------------------------------------------------+ + * This allows multiple build configurations for various button types */ -#ifndef LWBTN_CFG_FORCE_MANUAL_STATE_SET -#define LWBTN_CFG_FORCE_MANUAL_STATE_SET 0 +#ifndef LWBTN_CFG_GET_STATE_MODE +#define LWBTN_CFG_GET_STATE_MODE LWBTN_GET_STATE_MODE_CALLBACK #endif /** diff --git a/lwbtn/src/lwbtn/lwbtn.c b/lwbtn/src/lwbtn/lwbtn.c index c7194d8..9e9599e 100644 --- a/lwbtn/src/lwbtn/lwbtn.c +++ b/lwbtn/src/lwbtn/lwbtn.c @@ -34,8 +34,8 @@ #include #include "lwbtn/lwbtn.h" -#if LWBTN_CFG_FORCE_MANUAL_STATE_SET && !LWBTN_CFG_ALLOW_MANUAL_STATE_SET -#error "LWBTN_CFG_FORCE_MANUAL_STATE_SET && !LWBTN_CFG_ALLOW_MANUAL_STATE_SET is not a valid combination" +#if LWBTN_CFG_GET_STATE_MODE > 2 +#error "Invalid LWBTN_GET_STATE_MODE_CALLBACK configuration" #endif #define LWBTN_FLAG_ONPRESS_SENT ((uint16_t)0x0001) /*!< Flag indicates that on-press event has been sent */ @@ -50,19 +50,15 @@ #define LWBTN_TIME_KEEPALIVE_PERIOD(btn) LWBTN_CFG_TIME_KEEPALIVE_PERIOD #define LWBTN_CLICK_MAX_CONSECUTIVE(btn) LWBTN_CFG_CLICK_MAX_CONSECUTIVE -#if LWBTN_CFG_ALLOW_MANUAL_STATE_SET -#if LWBTN_CFG_FORCE_MANUAL_STATE_SET -/* Set the direct state */ -#define LWBTN_BTN_GET_STATE(lwobj, btn) (btn)->curr_state -#else -/* Check flag and decide how to get state */ +/* Get button state */ +#if LWBTN_CFG_GET_STATE_MODE == LWBTN_GET_STATE_MODE_CALLBACK +#define LWBTN_BTN_GET_STATE(lwobj, btn) ((lwobj)->get_state_fn((lwobj), (btn))) +#elif LWBTN_CFG_GET_STATE_MODE == LWBTN_GET_STATE_MODE_MANUAL +#define LWBTN_BTN_GET_STATE(lwobj, btn) ((btn)->curr_state) +#elif LWBTN_CFG_GET_STATE_MODE == LWBTN_GET_STATE_MODE_CALLBACK_OR_MANUAL #define LWBTN_BTN_GET_STATE(lwobj, btn) \ (((btn)->flags & LWBTN_FLAG_MANUAL_STATE) ? ((btn)->curr_state) : ((lwobj)->get_state_fn((lwobj), (btn)))) #endif -#else /* !LWBTN_CFG_ALLOW_MANUAL_STATE_SET */ -/* Get state from callback */ -#define LWBTN_BTN_GET_STATE(lwobj, btn) (lwobj)->get_state_fn((lwobj), (btn)) -#endif /* LWBTN_CFG_ALLOW_MANUAL_STATE_SET */ /* Default button group instance */ static lwbtn_t lwbtn_default; @@ -230,7 +226,7 @@ prv_process_btn(lwbtn_t* lwobj, lwbtn_btn_t* btn, uint32_t mstime) { * \param[in] btns: Array of buttons to process * \param[in] btns_cnt: Number of buttons to process * \param[in] get_state_fn: Pointer to function providing button state on demand. - * Can be set to `NULL` if \ref LWBTN_CFG_FORCE_MANUAL_STATE_SET is enabled + * May be set to `NULL` when \ref LWBTN_CFG_GET_STATE_MODE is set to manual. * \param[in] evt_fn: Button event function callback * \return `1` on success, `0` otherwise */ @@ -246,10 +242,11 @@ lwbtn_init_ex(lwbtn_t* lwobj, lwbtn_btn_t* btns, uint16_t btns_cnt, lwbtn_get_st lwobj->btns = btns; lwobj->btns_cnt = btns_cnt; lwobj->evt_fn = evt_fn; -#if !LWBTN_CFG_FORCE_MANUAL_STATE_SET +#if LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_MANUAL lwobj->get_state_fn = get_state_fn; -#endif /* !LWBTN_CFG_FORCE_MANUAL_STATE_SET */ +#else (void)get_state_fn; /* May be unused */ +#endif /* LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_MANUAL */ return 1; } @@ -302,13 +299,13 @@ lwbtn_process_btn_ex(lwbtn_t* lwobj, lwbtn_btn_t* btn, uint32_t mstime) { */ uint8_t lwbtn_set_btn_state(lwbtn_btn_t* btn, uint8_t state) { -#if LWBTN_CFG_MANUAL_STATE_SET +#if LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_CALLBACK btn->curr_state = state; btn->flags |= LWBTN_FLAG_MANUAL_STATE; return 1; -#else +#else /* LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_CALLBACK */ (void)btn; (void)state; return 0; -#endif +#endif /* LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_CALLBACK */ } From 9a213bc95a95a08f51b338b76e92208b87469c77 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 1 Jan 2023 23:19:58 +0100 Subject: [PATCH 08/16] fix: call get state only if set, check parameter only if callback-only --- lwbtn/src/lwbtn/lwbtn.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lwbtn/src/lwbtn/lwbtn.c b/lwbtn/src/lwbtn/lwbtn.c index 9e9599e..084583f 100644 --- a/lwbtn/src/lwbtn/lwbtn.c +++ b/lwbtn/src/lwbtn/lwbtn.c @@ -57,7 +57,9 @@ #define LWBTN_BTN_GET_STATE(lwobj, btn) ((btn)->curr_state) #elif LWBTN_CFG_GET_STATE_MODE == LWBTN_GET_STATE_MODE_CALLBACK_OR_MANUAL #define LWBTN_BTN_GET_STATE(lwobj, btn) \ - (((btn)->flags & LWBTN_FLAG_MANUAL_STATE) ? ((btn)->curr_state) : ((lwobj)->get_state_fn((lwobj), (btn)))) + (((btn)->flags & LWBTN_FLAG_MANUAL_STATE) \ + ? ((btn)->curr_state) \ + : (((lwobj)->get_state_fn != NULL) ? ((lwobj)->get_state_fn((lwobj), (btn))) : 0)) #endif /* Default button group instance */ @@ -235,9 +237,14 @@ lwbtn_init_ex(lwbtn_t* lwobj, lwbtn_btn_t* btns, uint16_t btns_cnt, lwbtn_get_st lwbtn_evt_fn evt_fn) { lwobj = LWBTN_GET_LWOBJ(lwobj); - if (btns == NULL || btns_cnt == 0 || get_state_fn == NULL || evt_fn == NULL) { + if (btns == NULL || btns_cnt == 0 || get_state_fn == NULL +#if LWBTN_CFG_GET_STATE_MODE == LWBTN_GET_STATE_MODE_CALLBACK + || evt_fn == NULL /* Parameter is a must only in callback-only mode */ +#endif /* LWBTN_CFG_GET_STATE_MODE == LWBTN_GET_STATE_MODE_CALLBACK */ + ) { return 0; } + memset(lwobj, 0x00, sizeof(*lwobj)); lwobj->btns = btns; lwobj->btns_cnt = btns_cnt; From f912d33ecd65fd1415d99be7514a1daae9c46a6f Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Mon, 2 Jan 2023 00:10:56 +0100 Subject: [PATCH 09/16] fix: Fix wrong parameter check --- lwbtn/src/lwbtn/lwbtn.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lwbtn/src/lwbtn/lwbtn.c b/lwbtn/src/lwbtn/lwbtn.c index 084583f..15c9487 100644 --- a/lwbtn/src/lwbtn/lwbtn.c +++ b/lwbtn/src/lwbtn/lwbtn.c @@ -237,10 +237,10 @@ lwbtn_init_ex(lwbtn_t* lwobj, lwbtn_btn_t* btns, uint16_t btns_cnt, lwbtn_get_st lwbtn_evt_fn evt_fn) { lwobj = LWBTN_GET_LWOBJ(lwobj); - if (btns == NULL || btns_cnt == 0 || get_state_fn == NULL + if (btns == NULL || btns_cnt == 0 || evt_fn == NULL #if LWBTN_CFG_GET_STATE_MODE == LWBTN_GET_STATE_MODE_CALLBACK - || evt_fn == NULL /* Parameter is a must only in callback-only mode */ -#endif /* LWBTN_CFG_GET_STATE_MODE == LWBTN_GET_STATE_MODE_CALLBACK */ + || get_state_fn == NULL /* Parameter is a must only in callback-only mode */ +#endif /* LWBTN_CFG_GET_STATE_MODE == LWBTN_GET_STATE_MODE_CALLBACK */ ) { return 0; } From 284098a8caa1605ec1365ec124bfa4a438466096 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Mon, 2 Jan 2023 00:32:08 +0100 Subject: [PATCH 10/16] feat: Add active check --- CHANGELOG.md | 3 ++- examples/example_win32.c | 10 ++++++++++ lwbtn/src/include/lwbtn/lwbtn.h | 1 + lwbtn/src/lwbtn/lwbtn.c | 13 +++++++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 442ef05..cd851c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ ## Develop -- Added `LWBTN_CFG_GET_STATE_MODE` to control *get state* mode +- Add `LWBTN_CFG_GET_STATE_MODE` to control *get state* mode +- Add option to check if button is currently active (after debounce period has elapsed) ## v0.0.1 diff --git a/examples/example_win32.c b/examples/example_win32.c index de753b2..af5536e 100644 --- a/examples/example_win32.c +++ b/examples/example_win32.c @@ -84,6 +84,7 @@ prv_btn_event(struct lwbtn* lw, struct lwbtn_btn* btn, lwbtn_evt_t evt) { */ int example_win32(void) { + uint32_t time_last; printf("Application running\r\n"); QueryPerformanceFrequency(&freq); QueryPerformanceCounter(&sys_start_time); @@ -91,6 +92,7 @@ example_win32(void) { /* Define buttons */ lwbtn_init_ex(NULL, btns, sizeof(btns) / sizeof(btns[0]), prv_btn_get_state, prv_btn_event); + time_last = get_tick(); while (1) { /* Process forever */ lwbtn_process_ex(NULL, get_tick()); @@ -102,6 +104,14 @@ example_win32(void) { } #endif /* LWBTN_CFG_GET_STATE_MODE == LWBTN_GET_STATE_MODE_MANUAL */ + /* Check if specific button is active and do some action */ + if (lwbtn_is_btn_active(&btns[0])) { + if ((get_tick() - time_last) > 200) { + time_last = get_tick(); + printf("Button is active\r\n"); + } + } + /* Artificial sleep to offload win process */ Sleep(5); } diff --git a/lwbtn/src/include/lwbtn/lwbtn.h b/lwbtn/src/include/lwbtn/lwbtn.h index b1fca68..657f51c 100644 --- a/lwbtn/src/include/lwbtn/lwbtn.h +++ b/lwbtn/src/include/lwbtn/lwbtn.h @@ -137,6 +137,7 @@ uint8_t lwbtn_init_ex(lwbtn_t* lwobj, lwbtn_btn_t* btns, uint16_t btns_cnt, lwbt uint8_t lwbtn_process_ex(lwbtn_t* lwobj, uint32_t mstime); uint8_t lwbtn_process_btn_ex(lwbtn_t* lwobj, lwbtn_btn_t* btn, uint32_t mstime); uint8_t lwbtn_set_btn_state(lwbtn_btn_t* btn, uint8_t state); +uint8_t lwbtn_is_btn_active(const lwbtn_btn_t* btn); /** * \brief Initialize LwBTN library with buttons on default button group diff --git a/lwbtn/src/lwbtn/lwbtn.c b/lwbtn/src/lwbtn/lwbtn.c index 15c9487..6ade4e1 100644 --- a/lwbtn/src/lwbtn/lwbtn.c +++ b/lwbtn/src/lwbtn/lwbtn.c @@ -316,3 +316,16 @@ lwbtn_set_btn_state(lwbtn_btn_t* btn, uint8_t state) { return 0; #endif /* LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_CALLBACK */ } + +/** + * \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 + */ +uint8_t +lwbtn_is_btn_active(const lwbtn_btn_t* btn) { + return btn != NULL && (btn->flags & LWBTN_FLAG_ONPRESS_SENT); +} From a338e0a2e30be47fb16c594a9ef010f0cc32b4f0 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Mon, 2 Jan 2023 10:45:10 +0100 Subject: [PATCH 11/16] feat: add optional keep-alive (default enabled for legacy) --- examples/example_win32.c | 12 +++++++++--- lwbtn/src/include/lwbtn/lwbtn.h | 12 ++++++++---- lwbtn/src/include/lwbtn/lwbtn_opt.h | 9 +++++++++ lwbtn/src/lwbtn/lwbtn.c | 6 ++++++ 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/examples/example_win32.c b/examples/example_win32.c index af5536e..5ed771f 100644 --- a/examples/example_win32.c +++ b/examples/example_win32.c @@ -44,7 +44,7 @@ prv_btn_get_state(struct lwbtn* lw, struct lwbtn_btn* btn) { void prv_btn_event(struct lwbtn* lw, struct lwbtn_btn* btn, lwbtn_evt_t evt) { const char* s; - uint32_t color; + uint32_t color, keepalive_cnt = 0; HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); uint32_t* diff_time_ptr = &last_time_keys[(*(int*)btn->arg) - '0']; uint32_t diff_time = get_tick() - *diff_time_ptr; @@ -56,9 +56,12 @@ prv_btn_event(struct lwbtn* lw, struct lwbtn_btn* btn, lwbtn_evt_t evt) { *diff_time_ptr = get_tick(); /* Set current date as last one */ /* Get event string */ - if (evt == LWBTN_EVT_KEEPALIVE) { + if (0) { +#if LWBTN_CFG_USE_KEEPALIVE + } else if (evt == LWBTN_EVT_KEEPALIVE) { s = "KEEPALIVE"; color = FOREGROUND_RED; +#endif /* LWBTN_CFG_USE_KEEPALIVE */ } else if (evt == LWBTN_EVT_ONPRESS) { s = " ONPRESS"; color = FOREGROUND_GREEN; @@ -72,9 +75,12 @@ prv_btn_event(struct lwbtn* lw, struct lwbtn_btn* btn, lwbtn_evt_t evt) { s = " UNKNOWN"; color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; } +#if LWBTN_CFG_USE_KEEPALIVE + keepalive_cnt = btn->keepalive.cnt; +#endif SetConsoleTextAttribute(hConsole, color); printf("[%7u][%6u] CH: %c, evt: %s, keep-alive cnt: %3u, click cnt: %3u\r\n", (unsigned)get_tick(), - (unsigned)diff_time, *(int*)btn->arg, s, (unsigned)btn->keepalive.cnt, (unsigned)btn->click.cnt); + (unsigned)diff_time, *(int*)btn->arg, s, (unsigned)keepalive_cnt, (unsigned)btn->click.cnt); SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); (void)lw; } diff --git a/lwbtn/src/include/lwbtn/lwbtn.h b/lwbtn/src/include/lwbtn/lwbtn.h index 657f51c..bedd398 100644 --- a/lwbtn/src/include/lwbtn/lwbtn.h +++ b/lwbtn/src/include/lwbtn/lwbtn.h @@ -75,7 +75,9 @@ typedef enum { LWBTN_EVT_ONPRESS = 0x00, /*!< On press event - sent when valid press is detected (after debounce if enabled) */ LWBTN_EVT_ONRELEASE, /*!< On release event - sent when valid release event is detected (from active to inactive) */ LWBTN_EVT_ONCLICK, /*!< On Click event - sent when valid sequence of on-press and on-release events occurs */ +#if LWBTN_CFG_USE_KEEPALIVE || __DOXYGEN__ LWBTN_EVT_KEEPALIVE, /*!< Keep alive event - sent periodically when button is active */ +#endif /* LWBTN_CFG_USE_KEEPALIVE || __DOXYGEN__ */ } lwbtn_evt_t; /** @@ -99,18 +101,20 @@ typedef uint8_t (*lwbtn_get_state_fn)(struct lwbtn* lwobj, struct lwbtn_btn* btn */ typedef struct lwbtn_btn { uint16_t flags; /*!< Private button flags management */ -#if LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_CALLBACK +#if LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_CALLBACK || __DOXYGEN__ uint8_t curr_state; /*!< Current button state to be processed. It is used to keep track when application manually sets the button state */ -#endif /* LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_CALLBACK */ +#endif /* LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_CALLBACK || __DOXYGEN__ */ uint8_t old_state; /*!< Old button state - `1` means active, `0` means inactive */ uint32_t time_change; /*!< Time in ms when button state got changed last time */ +#if LWBTN_CFG_USE_KEEPALIVE || __DOXYGEN__ struct { uint32_t last_time; /*!< Time in ms of last send keep alive event */ uint16_t cnt; /*!< Number of keep alive events sent after successful on-press detection. Value is reset after on-release */ } keepalive; /*!< Keep alive structure */ +#endif /* LWBTN_CFG_USE_KEEPALIVE || __DOXYGEN__ */ struct { uint32_t last_time; /*!< Time in ms of last successfully detected (not sent!) click event */ @@ -127,9 +131,9 @@ typedef struct lwbtn { lwbtn_btn_t* btns; /*!< Pointer to buttons array */ uint16_t btns_cnt; /*!< Number of buttons in array */ lwbtn_evt_fn evt_fn; /*!< Pointer to event function */ -#if LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_MANUAL +#if LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_MANUAL || __DOXYGEN__ lwbtn_get_state_fn get_state_fn; /*!< Pointer to get state function */ -#endif /* LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_MANUAL */ +#endif /* LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_MANUAL || __DOXYGEN__ */ } lwbtn_t; uint8_t lwbtn_init_ex(lwbtn_t* lwobj, lwbtn_btn_t* btns, uint16_t btns_cnt, lwbtn_get_state_fn get_state_fn, diff --git a/lwbtn/src/include/lwbtn/lwbtn_opt.h b/lwbtn/src/include/lwbtn/lwbtn_opt.h index 939c656..ae87e10 100644 --- a/lwbtn/src/include/lwbtn/lwbtn_opt.h +++ b/lwbtn/src/include/lwbtn/lwbtn_opt.h @@ -52,6 +52,15 @@ extern "C" { * \{ */ +/** + * \brief Enables `1` or disables `0` periodic keep alive events. + * + * Default keep alive period is set with \ref LWBTN_CFG_TIME_KEEPALIVE_PERIOD macro + */ +#ifndef LWBTN_CFG_USE_KEEPALIVE +#define LWBTN_CFG_USE_KEEPALIVE 1 +#endif + /** * \brief Minimum debounce time in units of milliseconds * diff --git a/lwbtn/src/lwbtn/lwbtn.c b/lwbtn/src/lwbtn/lwbtn.c index 6ade4e1..cd347d5 100644 --- a/lwbtn/src/lwbtn/lwbtn.c +++ b/lwbtn/src/lwbtn/lwbtn.c @@ -149,8 +149,10 @@ prv_process_btn(lwbtn_t* lwobj, lwbtn_btn_t* btn, uint32_t mstime) { /* Do nothing - things are handled after debounce period */ } btn->time_change = mstime; +#if LWBTN_CFG_USE_KEEPALIVE btn->keepalive.last_time = mstime; btn->keepalive.cnt = 0; +#endif /* LWBTN_CFG_USE_KEEPALIVE */ } /* @@ -181,8 +183,10 @@ prv_process_btn(lwbtn_t* lwobj, lwbtn_btn_t* btn, uint32_t mstime) { btn->flags |= LWBTN_FLAG_ONPRESS_SENT; lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONPRESS); +#if LWBTN_CFG_USE_KEEPALIVE /* Set keep alive time */ btn->keepalive.last_time = mstime; +#endif /* LWBTN_CFG_USE_KEEPALIVE */ } } @@ -192,11 +196,13 @@ prv_process_btn(lwbtn_t* lwobj, lwbtn_btn_t* btn, uint32_t mstime) { * Keep alive is sent when valid press is being detected */ else { +#if LWBTN_CFG_USE_KEEPALIVE if ((mstime - btn->keepalive.last_time) >= LWBTN_TIME_KEEPALIVE_PERIOD(btn)) { btn->keepalive.last_time += LWBTN_TIME_KEEPALIVE_PERIOD(btn); ++btn->keepalive.cnt; lwobj->evt_fn(lwobj, btn, LWBTN_EVT_KEEPALIVE); } +#endif /* LWBTN_CFG_USE_KEEPALIVE */ } } From 9a51e1d4e44b9e96034585d537e9db7a6ddcf370 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Wed, 4 Jan 2023 21:14:08 +0100 Subject: [PATCH 12/16] Update license to 2023 --- LICENSE | 2 +- dev/lwbtn_opts.h | 2 +- lwbtn/src/include/lwbtn/lwbtn.h | 2 +- lwbtn/src/include/lwbtn/lwbtn_opt.h | 2 +- lwbtn/src/include/lwbtn/lwbtn_opts_template.h | 2 +- lwbtn/src/lwbtn/lwbtn.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/LICENSE b/LICENSE index 5625f63..3702ca4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Tilen MAJERLE +Copyright (c) 2023 Tilen MAJERLE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/dev/lwbtn_opts.h b/dev/lwbtn_opts.h index 13322e5..6842ac3 100644 --- a/dev/lwbtn_opts.h +++ b/dev/lwbtn_opts.h @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2022 Tilen MAJERLE + * Copyright (c) 2023 Tilen MAJERLE * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/lwbtn/src/include/lwbtn/lwbtn.h b/lwbtn/src/include/lwbtn/lwbtn.h index bedd398..61bdfcb 100644 --- a/lwbtn/src/include/lwbtn/lwbtn.h +++ b/lwbtn/src/include/lwbtn/lwbtn.h @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2022 Tilen MAJERLE + * Copyright (c) 2023 Tilen MAJERLE * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/lwbtn/src/include/lwbtn/lwbtn_opt.h b/lwbtn/src/include/lwbtn/lwbtn_opt.h index ae87e10..0fc0256 100644 --- a/lwbtn/src/include/lwbtn/lwbtn_opt.h +++ b/lwbtn/src/include/lwbtn/lwbtn_opt.h @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2022 Tilen MAJERLE + * Copyright (c) 2023 Tilen MAJERLE * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/lwbtn/src/include/lwbtn/lwbtn_opts_template.h b/lwbtn/src/include/lwbtn/lwbtn_opts_template.h index 0b09a2c..a659d25 100644 --- a/lwbtn/src/include/lwbtn/lwbtn_opts_template.h +++ b/lwbtn/src/include/lwbtn/lwbtn_opts_template.h @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2022 Tilen MAJERLE + * Copyright (c) 2023 Tilen MAJERLE * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/lwbtn/src/lwbtn/lwbtn.c b/lwbtn/src/lwbtn/lwbtn.c index cd347d5..c03559b 100644 --- a/lwbtn/src/lwbtn/lwbtn.c +++ b/lwbtn/src/lwbtn/lwbtn.c @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2022 Tilen MAJERLE + * Copyright (c) 2023 Tilen MAJERLE * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation From 7932fcc817166c56c1182077126434199d0fdb72 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sat, 7 Jan 2023 16:46:33 +0100 Subject: [PATCH 13/16] feat: Add optionto set time parameters manually for each btn --- .vscode/settings.json | 3 +- CHANGELOG.md | 1 + lwbtn/src/include/lwbtn/lwbtn.h | 19 ++++++++++ lwbtn/src/include/lwbtn/lwbtn_opt.h | 54 +++++++++++++++++++++++++++++ lwbtn/src/lwbtn/lwbtn.c | 54 ++++++++++++++++++++++++++--- 5 files changed, 126 insertions(+), 5 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 0221941..b7a4aa5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,7 +7,8 @@ "lwevt_opt.h": "c", "lwbtn.h": "c", "lwbtn_opt.h": "c", - "lwbtn_opts.h": "c" + "lwbtn_opts.h": "c", + "compare": "c" }, "esbonio.sphinx.confDir": "" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index cd851c2..5da27ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Add `LWBTN_CFG_GET_STATE_MODE` to control *get state* mode - Add option to check if button is currently active (after debounce period has elapsed) +- Add option to set time/click parameters at run time for each button specifically ## v0.0.1 diff --git a/lwbtn/src/include/lwbtn/lwbtn.h b/lwbtn/src/include/lwbtn/lwbtn.h index 61bdfcb..ef1a945 100644 --- a/lwbtn/src/include/lwbtn/lwbtn.h +++ b/lwbtn/src/include/lwbtn/lwbtn.h @@ -122,6 +122,25 @@ typedef struct lwbtn_btn { } click; /*!< Click event structure */ void* arg; /*!< User defined custom argument for callback function purpose */ + +#if LWBTN_CFG_TIME_DEBOUNCE_RUNTIME || __DOXYGEN__ + uint16_t time_debounce; /*!< Debounce time in milliseconds */ +#endif +#if LWBTN_CFG_TIME_CLICK_MIN_RUNTIME || __DOXYGEN__ + uint16_t time_click_pressed_min; /*!< Minimum pressed time for valid click event */ +#endif /* LWBTN_CFG_TIME_CLICK_MIN_RUNTIME || __DOXYGEN__ */ +#if LWBTN_CFG_TIME_CLICK_MAX_RUNTIME || __DOXYGEN__ + uint16_t time_click_pressed_max; /*!< Maximum pressed time for valid click event*/ +#endif /* LWBTN_CFG_TIME_CLICK_MAX_RUNTIME || __DOXYGEN__ */ +#if LWBTN_CFG_TIME_CLICK_MULTI_MAX_RUNTIME || __DOXYGEN__ + uint16_t time_click_multi_max; /*!< Maximum time between 2 clicks to be considered consecutive click */ +#endif /* LWBTN_CFG_TIME_CLICK_MULTI_MAX_RUNTIME || __DOXYGEN__ */ +#if LWBTN_CFG_TIME_KEEPALIVE_PERIOD_RUNTIME || __DOXYGEN__ + uint16_t time_keepalive_period; /*!< Time in ms for periodic keep alive event */ +#endif /* LWBTN_CFG_TIME_KEEPALIVE_PERIOD_RUNTIME || __DOXYGEN__ */ +#if LWBTN_CFG_CLICK_MAX_CONSECUTIVE_RUNTIME || __DOXYGEN__ + uint16_t max_consecutive; /*!< Max number of consecutive clicks */ +#endif /* LWBTN_CFG_CLICK_MAX_CONSECUTIVE_RUNTIME || __DOXYGEN__ */ } lwbtn_btn_t; /** diff --git a/lwbtn/src/include/lwbtn/lwbtn_opt.h b/lwbtn/src/include/lwbtn/lwbtn_opt.h index 0fc0256..2dbd99a 100644 --- a/lwbtn/src/include/lwbtn/lwbtn_opt.h +++ b/lwbtn/src/include/lwbtn/lwbtn_opt.h @@ -70,6 +70,15 @@ extern "C" { #define LWBTN_CFG_TIME_DEBOUNCE 20 #endif +/** + * \brief Enables `1` or disables `0` runtime settable time debounce + * + * When enabled, additional field is added to button structure + */ +#ifndef LWBTN_CFG_TIME_DEBOUNCE_RUNTIME +#define LWBTN_CFG_TIME_DEBOUNCE_RUNTIME 0 +#endif + /** * \brief Minimum active input time for valid click event, in milliseconds * @@ -80,6 +89,15 @@ extern "C" { #define LWBTN_CFG_TIME_CLICK_MIN 20 #endif +/** + * \brief Enables `1` or disables `0` runtime settable min time for click + * + * When enabled, additional field is added to button structure + */ +#ifndef LWBTN_CFG_TIME_CLICK_MIN_RUNTIME +#define LWBTN_CFG_TIME_CLICK_MIN_RUNTIME 0 +#endif + /** * \brief Maximum active input time for valid click event, in milliseconds * @@ -90,6 +108,15 @@ extern "C" { #define LWBTN_CFG_TIME_CLICK_MAX 300 #endif +/** + * \brief Enables `1` or disables `0` runtime settable max time for click + * + * When enabled, additional field is added to button structure + */ +#ifndef LWBTN_CFG_TIME_CLICK_MAX_RUNTIME +#define LWBTN_CFG_TIME_CLICK_MAX_RUNTIME 0 +#endif + /** * \brief Maximum allowed time between last on-release and next valid on-press, * to still allow multi-click events, in milliseconds @@ -101,6 +128,15 @@ extern "C" { #define LWBTN_CFG_TIME_CLICK_MULTI_MAX 400 #endif +/** + * \brief Enables `1` or disables `0` runtime settable max time for multi click + * + * When enabled, additional field is added to button structure + */ +#ifndef LWBTN_CFG_TIME_CLICK_MULTI_MAX_RUNTIME +#define LWBTN_CFG_TIME_CLICK_MULTI_MAX_RUNTIME 0 +#endif + /** * \brief Maximum number of allowed consecutive click events, * before structure gets reset to default value @@ -109,6 +145,15 @@ extern "C" { #define LWBTN_CFG_CLICK_MAX_CONSECUTIVE 3 #endif +/** + * \brief Enables `1` or disables `0` runtime settable max consecutive clicks + * + * When enabled, additional field is added to button structure + */ +#ifndef LWBTN_CFG_CLICK_MAX_CONSECUTIVE_RUNTIME +#define LWBTN_CFG_CLICK_MAX_CONSECUTIVE_RUNTIME 0 +#endif + /** * \brief Keep-alive event period, in milliseconds * @@ -117,6 +162,15 @@ extern "C" { #define LWBTN_CFG_TIME_KEEPALIVE_PERIOD 100 #endif +/** + * \brief Enables `1` or disables `0` runtime settable keep alive period + * + * When enabled, additional field is added to button structure + */ +#ifndef LWBTN_CFG_TIME_KEEPALIVE_PERIOD_RUNTIME +#define LWBTN_CFG_TIME_KEEPALIVE_PERIOD_RUNTIME 0 +#endif + /** * \brief Enables `1` or disables `0` immediate onclick event * after on-release event, if number of consecutive diff --git a/lwbtn/src/lwbtn/lwbtn.c b/lwbtn/src/lwbtn/lwbtn.c index c03559b..c6a5e2e 100644 --- a/lwbtn/src/lwbtn/lwbtn.c +++ b/lwbtn/src/lwbtn/lwbtn.c @@ -43,12 +43,36 @@ ((uint16_t)0x0002) /*!< Flag indicates that user wants to manually set button state. Do not call "get_state" function */ -#define LWBTN_TIME_DEBOUNCE_GET_MIN(btn) LWBTN_CFG_TIME_DEBOUNCE +#if LWBTN_CFG_TIME_DEBOUNCE_RUNTIME +#define LWBTN_TIME_DEBOUNCE_GET_MIN(btn) ((btn)->time_debounce) +#else +#define LWBTN_TIME_DEBOUNCE_GET_MIN(btn) LWBTN_CFG_TIME_DEBOUNCE +#endif /* LWBTN_CFG_TIME_DEBOUNCE_RUNTIME */ +#if LWBTN_CFG_TIME_CLICK_MIN_RUNTIME +#define LWBTN_TIME_CLICK_GET_PRESSED_MIN(btn) ((btn)->time_click_pressed_min) +#else #define LWBTN_TIME_CLICK_GET_PRESSED_MIN(btn) LWBTN_CFG_TIME_CLICK_MIN +#endif /* LWBTN_CFG_TIME_CLICK_MIN_RUNTIME */ +#if LWBTN_CFG_TIME_CLICK_MAX_RUNTIME +#define LWBTN_TIME_CLICK_GET_PRESSED_MAX(btn) ((btn)->time_click_pressed_max) +#else #define LWBTN_TIME_CLICK_GET_PRESSED_MAX(btn) LWBTN_CFG_TIME_CLICK_MAX -#define LWBTN_TIME_CLICK_MAX_MULTI(btn) LWBTN_CFG_TIME_CLICK_MULTI_MAX -#define LWBTN_TIME_KEEPALIVE_PERIOD(btn) LWBTN_CFG_TIME_KEEPALIVE_PERIOD -#define LWBTN_CLICK_MAX_CONSECUTIVE(btn) LWBTN_CFG_CLICK_MAX_CONSECUTIVE +#endif /* LWBTN_CFG_TIME_CLICK_MAX_RUNTIME */ +#if LWBTN_CFG_TIME_CLICK_MULTI_MAX_RUNTIME +#define LWBTN_TIME_CLICK_MAX_MULTI(btn) ((btn)->time_click_multi_max) +#else +#define LWBTN_TIME_CLICK_MAX_MULTI(btn) LWBTN_CFG_TIME_CLICK_MULTI_MAX +#endif /* LWBTN_CFG_TIME_CLICK_MULTI_MAX_RUNTIME */ +#if LWBTN_CFG_TIME_KEEPALIVE_PERIOD_RUNTIME +#define LWBTN_TIME_KEEPALIVE_PERIOD(btn) ((btn)->time_keepalive_period) +#else +#define LWBTN_TIME_KEEPALIVE_PERIOD(btn) LWBTN_CFG_TIME_KEEPALIVE_PERIOD +#endif /* LWBTN_CFG_TIME_KEEPALIVE_PERIOD_RUNTIME */ +#if LWBTN_CFG_CLICK_MAX_CONSECUTIVE_RUNTIME +#define LWBTN_CLICK_MAX_CONSECUTIVE(btn) ((btn)->max_consecutive) +#else +#define LWBTN_CLICK_MAX_CONSECUTIVE(btn) LWBTN_CFG_CLICK_MAX_CONSECUTIVE +#endif /* LWBTN_CFG_CLICK_MAX_CONSECUTIVE_RUNTIME */ /* Get button state */ #if LWBTN_CFG_GET_STATE_MODE == LWBTN_GET_STATE_MODE_CALLBACK @@ -260,6 +284,28 @@ lwbtn_init_ex(lwbtn_t* lwobj, lwbtn_btn_t* btns, uint16_t btns_cnt, lwbtn_get_st #else (void)get_state_fn; /* May be unused */ #endif /* LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_MANUAL */ + + for (size_t i = 0; i < btns_cnt; ++i) { +#if LWBTN_CFG_TIME_DEBOUNCE_RUNTIME + btns[i].time_debounce = LWBTN_CFG_TIME_DEBOUNCE; +#endif /* LWBTN_CFG_TIME_DEBOUNCE_RUNTIME */ +#if LWBTN_CFG_TIME_CLICK_MIN_RUNTIME + btns[i].time_click_pressed_min = LWBTN_CFG_TIME_CLICK_MIN; +#endif /* LWBTN_CFG_TIME_CLICK_MIN_RUNTIME */ +#if LWBTN_CFG_TIME_CLICK_MAX_RUNTIME + btns[i].time_click_pressed_max = LWBTN_CFG_TIME_CLICK_MAX; +#endif /* LWBTN_CFG_TIME_CLICK_MAX_RUNTIME */ +#if LWBTN_CFG_TIME_CLICK_MULTI_MAX_RUNTIME + btns[i].time_click_multi_max = LWBTN_CFG_TIME_CLICK_MULTI_MAX; +#endif /* LWBTN_CFG_TIME_CLICK_MULTI_MAX_RUNTIME */ +#if LWBTN_CFG_TIME_KEEPALIVE_PERIOD_RUNTIME + btns[i].time_keepalive_period = LWBTN_CFG_TIME_KEEPALIVE_PERIOD; +#endif /* LWBTN_CFG_TIME_KEEPALIVE_PERIOD_RUNTIME */ +#if LWBTN_CFG_CLICK_MAX_CONSECUTIVE_RUNTIME + btns[i].max_consecutive = LWBTN_CFG_CLICK_MAX_CONSECUTIVE; +#endif /* LWBTN_CFG_CLICK_MAX_CONSECUTIVE_RUNTIME */ + } + return 1; } From 6e5550be5b4e03cfefc095cf6b207d77632bd107 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 19 Feb 2023 12:42:43 +0100 Subject: [PATCH 14/16] Fix documentation --- docs/conf.py | 2 +- docs/get-started/index.rst | 2 +- docs/index.rst | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 3d2351f..718c57c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -23,7 +23,7 @@ subprocess.call('doxygen doxyfile.doxy', shell=True) # -- Project information ----------------------------------------------------- project = 'LwBTN' -copyright = '2022, Tilen MAJERLE' +copyright = '2023, Tilen MAJERLE' author = 'Tilen MAJERLE' # Try to get branch at which this is running diff --git a/docs/get-started/index.rst b/docs/get-started/index.rst index f372066..5740046 100644 --- a/docs/get-started/index.rst +++ b/docs/get-started/index.rst @@ -62,7 +62,7 @@ Next step is to add the library to the project, by means of source files to comp * Copy ``lwbtn`` folder to your project, it contains library files * Add ``lwbtn/src/include`` folder to `include path` of your toolchain. This is where `C/C++` compiler can find the files during compilation process. Usually using ``-I`` flag -* Add source files from ``lwbtn/src/`` folder to toolchain build. These files are built by `C/C++` compiler +* Add source files from ``lwbtn/src/`` folder to toolchain build. These files are built by `C/C++` compiler. CMake configuration comes with the library, allows users to include library in the project as **subdirectory** and **library**. * Copy ``lwbtn/src/include/lwbtn/lwbtn_opts_template.h`` to project folder and rename it to ``lwbtn_opts.h`` * Copy ``lwbtn/src/include/lwbtn/lwbtn_types_template.h`` to project folder and rename it to ``lwbtn_types.h`` * Build the project diff --git a/docs/index.rst b/docs/index.rst index 7d55164..9697b2a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -78,3 +78,4 @@ Table of contents LwRB - Ring buffer LwSHELL - Shell LwUTIL - Utility functions + LwWDG - RTOS task watchdog From 76ae58288367205b75d897967db5a7f71f050dde Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Mon, 27 Mar 2023 19:13:33 +0200 Subject: [PATCH 15/16] Add option fot release debounce + change config settings --- CHANGELOG.md | 3 + dev/lwbtn_opts.h | 10 ++ lwbtn/src/include/lwbtn/lwbtn.h | 36 ++-- lwbtn/src/include/lwbtn/lwbtn_opt.h | 83 +++++++--- lwbtn/src/lwbtn/lwbtn.c | 245 ++++++++++++++-------------- 5 files changed, 220 insertions(+), 157 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5da27ab..7facbbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ - Add `LWBTN_CFG_GET_STATE_MODE` to control *get state* mode - Add option to check if button is currently active (after debounce period has elapsed) - Add option to set time/click parameters at run time for each button specifically +- Rename `_RUNTIME` configuration with `_DYNAMIC` +- Change `LWBTC_CFG_TIME_DEBOUNCE` to `LWBTC_CFG_TIME_DEBOUNCE_PRESS` and `LWBTC_CFG_TIME_DEBOUNCE_RUNTIME` to `LWBTC_CFG_TIME_DEBOUNCE_PRESS_DYNAMIC` respectively +- Add option release debounce with `LWBTC_CFG_TIME_DEBOUNCE_RELEASE` and `LWBTC_CFG_TIME_DEBOUNCE_RELEASE_DYNAMIC` options ## v0.0.1 diff --git a/dev/lwbtn_opts.h b/dev/lwbtn_opts.h index 6842ac3..4a74405 100644 --- a/dev/lwbtn_opts.h +++ b/dev/lwbtn_opts.h @@ -41,4 +41,14 @@ * copy & replace here settings you want to change values */ +/* Press config */ +#define LWBTN_CFG_TIME_DEBOUNCE_PRESS 20 /* No debounce for press event */ +#define LWBTN_CFG_TIME_DEBOUNCE_PRESS_DYNAMIC \ + 1 /* Debounce for press event is statically set with macro -> no dynamic config */ + +/* Release config */ +#define LWBTN_CFG_TIME_DEBOUNCE_RELEASE 20 /* No debounce for release event */ +#define LWBTN_CFG_TIME_DEBOUNCE_RELEASE_DYNAMIC \ + 1 /* Debounce for release event is statically set with macro -> no dynamic config */ + #endif /* LWBTN_HDR_OPTS_H */ diff --git a/lwbtn/src/include/lwbtn/lwbtn.h b/lwbtn/src/include/lwbtn/lwbtn.h index ef1a945..8452673 100644 --- a/lwbtn/src/include/lwbtn/lwbtn.h +++ b/lwbtn/src/include/lwbtn/lwbtn.h @@ -102,11 +102,12 @@ typedef uint8_t (*lwbtn_get_state_fn)(struct lwbtn* lwobj, struct lwbtn_btn* btn typedef struct lwbtn_btn { uint16_t flags; /*!< Private button flags management */ #if LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_CALLBACK || __DOXYGEN__ - uint8_t curr_state; /*!< Current button state to be processed. It is used + uint8_t curr_state; /*!< Current button state to be processed. It is used to keep track when application manually sets the button state */ -#endif /* LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_CALLBACK || __DOXYGEN__ */ - uint8_t old_state; /*!< Old button state - `1` means active, `0` means inactive */ - uint32_t time_change; /*!< Time in ms when button state got changed last time */ +#endif /* LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_CALLBACK || __DOXYGEN__ */ + uint8_t old_state; /*!< Old button state - `1` means active, `0` means inactive */ + uint32_t time_change; /*!< Time in ms when button state got changed last time after valid debounce */ + uint32_t time_state_change; /*!< Time in ms when button state got changed last time */ #if LWBTN_CFG_USE_KEEPALIVE || __DOXYGEN__ struct { @@ -123,24 +124,27 @@ typedef struct lwbtn_btn { void* arg; /*!< User defined custom argument for callback function purpose */ -#if LWBTN_CFG_TIME_DEBOUNCE_RUNTIME || __DOXYGEN__ +#if LWBTN_CFG_TIME_DEBOUNCE_PRESS_DYNAMIC || __DOXYGEN__ uint16_t time_debounce; /*!< Debounce time in milliseconds */ -#endif -#if LWBTN_CFG_TIME_CLICK_MIN_RUNTIME || __DOXYGEN__ +#endif /* LWBTN_CFG_TIME_DEBOUNCE_PRESS_DYNAMIC || __DOXYGEN__ */ +#if LWBTN_CFG_TIME_DEBOUNCE_RELEASE_DYNAMIC || __DOXYGEN__ + uint16_t time_debounce_release; /*!< Debounce time in milliseconds for release event */ +#endif /* LWBTN_CFG_TIME_DEBOUNCE_RELEASE */ +#if LWBTN_CFG_TIME_CLICK_MIN_DYNAMIC || __DOXYGEN__ uint16_t time_click_pressed_min; /*!< Minimum pressed time for valid click event */ -#endif /* LWBTN_CFG_TIME_CLICK_MIN_RUNTIME || __DOXYGEN__ */ -#if LWBTN_CFG_TIME_CLICK_MAX_RUNTIME || __DOXYGEN__ +#endif /* LWBTN_CFG_TIME_CLICK_MIN_DYNAMIC || __DOXYGEN__ */ +#if LWBTN_CFG_TIME_CLICK_MAX_DYNAMIC || __DOXYGEN__ uint16_t time_click_pressed_max; /*!< Maximum pressed time for valid click event*/ -#endif /* LWBTN_CFG_TIME_CLICK_MAX_RUNTIME || __DOXYGEN__ */ -#if LWBTN_CFG_TIME_CLICK_MULTI_MAX_RUNTIME || __DOXYGEN__ +#endif /* LWBTN_CFG_TIME_CLICK_MAX_DYNAMIC || __DOXYGEN__ */ +#if LWBTN_CFG_TIME_CLICK_MULTI_MAX_DYNAMIC || __DOXYGEN__ uint16_t time_click_multi_max; /*!< Maximum time between 2 clicks to be considered consecutive click */ -#endif /* LWBTN_CFG_TIME_CLICK_MULTI_MAX_RUNTIME || __DOXYGEN__ */ -#if LWBTN_CFG_TIME_KEEPALIVE_PERIOD_RUNTIME || __DOXYGEN__ +#endif /* LWBTN_CFG_TIME_CLICK_MULTI_MAX_DYNAMIC || __DOXYGEN__ */ +#if LWBTN_CFG_TIME_KEEPALIVE_PERIOD_DYNAMIC || __DOXYGEN__ uint16_t time_keepalive_period; /*!< Time in ms for periodic keep alive event */ -#endif /* LWBTN_CFG_TIME_KEEPALIVE_PERIOD_RUNTIME || __DOXYGEN__ */ -#if LWBTN_CFG_CLICK_MAX_CONSECUTIVE_RUNTIME || __DOXYGEN__ +#endif /* LWBTN_CFG_TIME_KEEPALIVE_PERIOD_DYNAMIC || __DOXYGEN__ */ +#if LWBTN_CFG_CLICK_MAX_CONSECUTIVE_DYNAMIC || __DOXYGEN__ uint16_t max_consecutive; /*!< Max number of consecutive clicks */ -#endif /* LWBTN_CFG_CLICK_MAX_CONSECUTIVE_RUNTIME || __DOXYGEN__ */ +#endif /* LWBTN_CFG_CLICK_MAX_CONSECUTIVE_DYNAMIC || __DOXYGEN__ */ } lwbtn_btn_t; /** diff --git a/lwbtn/src/include/lwbtn/lwbtn_opt.h b/lwbtn/src/include/lwbtn/lwbtn_opt.h index 2dbd99a..4eb29ba 100644 --- a/lwbtn/src/include/lwbtn/lwbtn_opt.h +++ b/lwbtn/src/include/lwbtn/lwbtn_opt.h @@ -62,21 +62,58 @@ extern "C" { #endif /** - * \brief Minimum debounce time in units of milliseconds + * \brief Minimum debounce time for press event in units of milliseconds * - * This is the time input shall have stable level to detect valid *onpress* event + * This is the time when the input shall have stable active level to detect valid *onpress* event. + * + * This is used to detect initial debounce when button/input is being pressed by the user. + * + * When value is set to `> 0`, input must be in active state for at least + * minimum milliseconds time, before valid *onpress* event is detected */ -#ifndef LWBTN_CFG_TIME_DEBOUNCE -#define LWBTN_CFG_TIME_DEBOUNCE 20 +#ifndef LWBTN_CFG_TIME_DEBOUNCE_PRESS +#define LWBTN_CFG_TIME_DEBOUNCE_PRESS 20 #endif /** - * \brief Enables `1` or disables `0` runtime settable time debounce + * \brief Enables `1` or disables `0` dynamic settable time debounce * - * When enabled, additional field is added to button structure + * When enabled, additional field is added to button structure to allow + * each button setting its very own debounce time for press event. + * + * If not used, \ref LWBTN_CFG_TIME_DEBOUNCE_PRESS is used as default + * debouncing configuration */ -#ifndef LWBTN_CFG_TIME_DEBOUNCE_RUNTIME -#define LWBTN_CFG_TIME_DEBOUNCE_RUNTIME 0 +#ifndef LWBTN_CFG_TIME_DEBOUNCE_PRESS_DYNAMIC +#define LWBTN_CFG_TIME_DEBOUNCE_PRESS_DYNAMIC 0 +#endif + +/** + * \brief Minimum debounce time for release event in units of milliseconds + * + * This is the time when the input shall have 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 + */ +#ifndef LWBTN_CFG_TIME_DEBOUNCE_RELEASE +#define LWBTN_CFG_TIME_DEBOUNCE_RELEASE 0 +#endif + +/** + * \brief Enables `1` or disables `0` dynamic settable time debounce for release event + * + * When enabled, additional field is added to button structure to allow + * each button setting its very own debounce time for release event. + * + * If not used, \ref LWBTN_CFG_TIME_DEBOUNCE_RELEASE is used as default + * debouncing configuration + */ +#ifndef LWBTN_CFG_TIME_DEBOUNCE_RELEASE_DYNAMIC +#define LWBTN_CFG_TIME_DEBOUNCE_RELEASE_DYNAMIC 0 #endif /** @@ -90,12 +127,12 @@ extern "C" { #endif /** - * \brief Enables `1` or disables `0` runtime settable min time for click + * \brief Enables `1` or disables `0` dynamic settable min time for click * * When enabled, additional field is added to button structure */ -#ifndef LWBTN_CFG_TIME_CLICK_MIN_RUNTIME -#define LWBTN_CFG_TIME_CLICK_MIN_RUNTIME 0 +#ifndef LWBTN_CFG_TIME_CLICK_MIN_DYNAMIC +#define LWBTN_CFG_TIME_CLICK_MIN_DYNAMIC 0 #endif /** @@ -109,12 +146,12 @@ extern "C" { #endif /** - * \brief Enables `1` or disables `0` runtime settable max time for click + * \brief Enables `1` or disables `0` dynamic settable max time for click * * When enabled, additional field is added to button structure */ -#ifndef LWBTN_CFG_TIME_CLICK_MAX_RUNTIME -#define LWBTN_CFG_TIME_CLICK_MAX_RUNTIME 0 +#ifndef LWBTN_CFG_TIME_CLICK_MAX_DYNAMIC +#define LWBTN_CFG_TIME_CLICK_MAX_DYNAMIC 0 #endif /** @@ -129,12 +166,12 @@ extern "C" { #endif /** - * \brief Enables `1` or disables `0` runtime settable max time for multi click + * \brief Enables `1` or disables `0` dynamic settable max time for multi click * * When enabled, additional field is added to button structure */ -#ifndef LWBTN_CFG_TIME_CLICK_MULTI_MAX_RUNTIME -#define LWBTN_CFG_TIME_CLICK_MULTI_MAX_RUNTIME 0 +#ifndef LWBTN_CFG_TIME_CLICK_MULTI_MAX_DYNAMIC +#define LWBTN_CFG_TIME_CLICK_MULTI_MAX_DYNAMIC 0 #endif /** @@ -146,12 +183,12 @@ extern "C" { #endif /** - * \brief Enables `1` or disables `0` runtime settable max consecutive clicks + * \brief Enables `1` or disables `0` dynamic settable max consecutive clicks * * When enabled, additional field is added to button structure */ -#ifndef LWBTN_CFG_CLICK_MAX_CONSECUTIVE_RUNTIME -#define LWBTN_CFG_CLICK_MAX_CONSECUTIVE_RUNTIME 0 +#ifndef LWBTN_CFG_CLICK_MAX_CONSECUTIVE_DYNAMIC +#define LWBTN_CFG_CLICK_MAX_CONSECUTIVE_DYNAMIC 0 #endif /** @@ -163,12 +200,12 @@ extern "C" { #endif /** - * \brief Enables `1` or disables `0` runtime settable keep alive period + * \brief Enables `1` or disables `0` dynamic settable keep alive period * * When enabled, additional field is added to button structure */ -#ifndef LWBTN_CFG_TIME_KEEPALIVE_PERIOD_RUNTIME -#define LWBTN_CFG_TIME_KEEPALIVE_PERIOD_RUNTIME 0 +#ifndef LWBTN_CFG_TIME_KEEPALIVE_PERIOD_DYNAMIC +#define LWBTN_CFG_TIME_KEEPALIVE_PERIOD_DYNAMIC 0 #endif /** diff --git a/lwbtn/src/lwbtn/lwbtn.c b/lwbtn/src/lwbtn/lwbtn.c index c6a5e2e..9e33625 100644 --- a/lwbtn/src/lwbtn/lwbtn.c +++ b/lwbtn/src/lwbtn/lwbtn.c @@ -43,36 +43,43 @@ ((uint16_t)0x0002) /*!< Flag indicates that user wants to manually set button state. Do not call "get_state" function */ -#if LWBTN_CFG_TIME_DEBOUNCE_RUNTIME -#define LWBTN_TIME_DEBOUNCE_GET_MIN(btn) ((btn)->time_debounce) +#if LWBTN_CFG_TIME_DEBOUNCE_PRESS_DYNAMIC +#define LWBTN_TIME_DEBOUNCE_GET_MIN(btn) (uint32_t)((btn)->time_debounce) #else -#define LWBTN_TIME_DEBOUNCE_GET_MIN(btn) LWBTN_CFG_TIME_DEBOUNCE -#endif /* LWBTN_CFG_TIME_DEBOUNCE_RUNTIME */ -#if LWBTN_CFG_TIME_CLICK_MIN_RUNTIME +#define LWBTN_TIME_DEBOUNCE_GET_MIN(btn) (uint32_t) LWBTN_CFG_TIME_DEBOUNCE_PRESS +#endif /* LWBTN_CFG_TIME_DEBOUNCE_PRESS_DYNAMIC */ + +#if LWBTN_CFG_TIME_DEBOUNCE_RELEASE_DYNAMIC +#define LWBTN_TIME_DEBOUNCE_RELEASE_GET_MIN(btn) (uint32_t)((btn)->time_debounce_release) +#else +#define LWBTN_TIME_DEBOUNCE_RELEASE_GET_MIN(btn) (uint32_t) LWBTN_CFG_TIME_DEBOUNCE_RELEASE +#endif /* LWBTN_CFG_TIME_DEBOUNCE_RELEASE_DYNAMIC */ + +#if LWBTN_CFG_TIME_CLICK_MIN_DYNAMIC #define LWBTN_TIME_CLICK_GET_PRESSED_MIN(btn) ((btn)->time_click_pressed_min) #else #define LWBTN_TIME_CLICK_GET_PRESSED_MIN(btn) LWBTN_CFG_TIME_CLICK_MIN -#endif /* LWBTN_CFG_TIME_CLICK_MIN_RUNTIME */ -#if LWBTN_CFG_TIME_CLICK_MAX_RUNTIME +#endif /* LWBTN_CFG_TIME_CLICK_MIN_DYNAMIC */ +#if LWBTN_CFG_TIME_CLICK_MAX_DYNAMIC #define LWBTN_TIME_CLICK_GET_PRESSED_MAX(btn) ((btn)->time_click_pressed_max) #else #define LWBTN_TIME_CLICK_GET_PRESSED_MAX(btn) LWBTN_CFG_TIME_CLICK_MAX -#endif /* LWBTN_CFG_TIME_CLICK_MAX_RUNTIME */ -#if LWBTN_CFG_TIME_CLICK_MULTI_MAX_RUNTIME +#endif /* LWBTN_CFG_TIME_CLICK_MAX_DYNAMIC */ +#if LWBTN_CFG_TIME_CLICK_MULTI_MAX_DYNAMIC #define LWBTN_TIME_CLICK_MAX_MULTI(btn) ((btn)->time_click_multi_max) #else #define LWBTN_TIME_CLICK_MAX_MULTI(btn) LWBTN_CFG_TIME_CLICK_MULTI_MAX -#endif /* LWBTN_CFG_TIME_CLICK_MULTI_MAX_RUNTIME */ -#if LWBTN_CFG_TIME_KEEPALIVE_PERIOD_RUNTIME +#endif /* LWBTN_CFG_TIME_CLICK_MULTI_MAX_DYNAMIC */ +#if LWBTN_CFG_TIME_KEEPALIVE_PERIOD_DYNAMIC #define LWBTN_TIME_KEEPALIVE_PERIOD(btn) ((btn)->time_keepalive_period) #else #define LWBTN_TIME_KEEPALIVE_PERIOD(btn) LWBTN_CFG_TIME_KEEPALIVE_PERIOD -#endif /* LWBTN_CFG_TIME_KEEPALIVE_PERIOD_RUNTIME */ -#if LWBTN_CFG_CLICK_MAX_CONSECUTIVE_RUNTIME +#endif /* LWBTN_CFG_TIME_KEEPALIVE_PERIOD_DYNAMIC */ +#if LWBTN_CFG_CLICK_MAX_CONSECUTIVE_DYNAMIC #define LWBTN_CLICK_MAX_CONSECUTIVE(btn) ((btn)->max_consecutive) #else #define LWBTN_CLICK_MAX_CONSECUTIVE(btn) LWBTN_CFG_CLICK_MAX_CONSECUTIVE -#endif /* LWBTN_CFG_CLICK_MAX_CONSECUTIVE_RUNTIME */ +#endif /* LWBTN_CFG_CLICK_MAX_CONSECUTIVE_DYNAMIC */ /* Get button state */ #if LWBTN_CFG_GET_STATE_MODE == LWBTN_GET_STATE_MODE_CALLBACK @@ -104,23 +111,89 @@ prv_process_btn(lwbtn_t* lwobj, lwbtn_btn_t* btn, uint32_t mstime) { /* Get button state */ new_state = LWBTN_BTN_GET_STATE(lwobj, btn); - /* - * Button state has changed - */ + /* Button state has just changed */ if (new_state != btn->old_state) { - /* - * Button just became inactive + btn->time_state_change = mstime; + } + + /* Button is still pressed */ + else if (new_state) { + /* + * Handle debounce and send on-press event * - * - Handle on-release event - * - Handle on-click event + * This is when we detect valid press */ - if (!new_state) { + if (!(btn->flags & LWBTN_FLAG_ONPRESS_SENT)) { /* - * We only need to react if on-press event has even been started. + * Run if statement when: * - * Do nothing if that was not the case + * - Runtime mode is enabled -> user sets its own config for debounce + * - Config debounce time for press is more than `0` */ - if (btn->flags & LWBTN_FLAG_ONPRESS_SENT) { +#if LWBTN_CFG_TIME_DEBOUNCE_PRESS_DYNAMIC || LWBTN_CFG_TIME_DEBOUNCE_PRESS > 0 + if ((mstime - btn->time_state_change) >= LWBTN_TIME_DEBOUNCE_GET_MIN(btn)) +#endif /* LWBTN_CFG_TIME_DEBOUNCE_PRESS_DYNAMIC || LWBTN_CFG_TIME_DEBOUNCE_PRESS> 0 */ + { +#if !LWBTN_CFG_CLICK_MAX_CONSECUTIVE_SEND_IMMEDIATELY + /* + * Depending on the configuration, + * this part will send on-click event just before the next on-press release, + * if maximum number of consecutive clicks has been reached. + */ + if (btn->click.cnt > 0 && btn->click.cnt == LWBTN_CLICK_MAX_CONSECUTIVE(btn)) { + lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONCLICK); + btn->click.cnt = 0; + } +#endif /* !LWBTN_CFG_CLICK_MAX_CONSECUTIVE_SEND_IMMEDIATELY */ + + /* Start with new on-press */ + btn->flags |= LWBTN_FLAG_ONPRESS_SENT; + lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONPRESS); + + btn->time_change = mstime; /* Button state has now changed */ +#if LWBTN_CFG_USE_KEEPALIVE + /* Set keep alive time */ + btn->keepalive.last_time = mstime; + btn->keepalive.last_time = mstime; + btn->keepalive.cnt = 0; +#endif /* LWBTN_CFG_USE_KEEPALIVE */ + } + } + + /* + * Handle keep alive, but only if on-press event has been sent + * + * Keep alive is sent when valid press is being detected + */ + else { +#if LWBTN_CFG_USE_KEEPALIVE + if ((mstime - btn->keepalive.last_time) >= LWBTN_TIME_KEEPALIVE_PERIOD(btn)) { + btn->keepalive.last_time += LWBTN_TIME_KEEPALIVE_PERIOD(btn); + ++btn->keepalive.cnt; + lwobj->evt_fn(lwobj, btn, LWBTN_EVT_KEEPALIVE); + } +#endif /* LWBTN_CFG_USE_KEEPALIVE */ + } + } + + /* 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 & LWBTN_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 LWBTN_CFG_TIME_DEBOUNCE_RELEASE_DYNAMIC || LWBTN_CFG_TIME_DEBOUNCE_RELEASE > 0 + if ((mstime - btn->time_state_change) >= LWBTN_TIME_DEBOUNCE_RELEASE_GET_MIN(btn)) +#endif /* LWBTN_CFG_TIME_DEBOUNCE_RELEASE_DYNAMIC || LWBTN_CFG_TIME_DEBOUNCE_RELEASE > 0 */ + { /* Handle on-release event */ btn->flags &= ~LWBTN_FLAG_ONPRESS_SENT; lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONRELEASE); @@ -162,90 +235,23 @@ prv_process_btn(lwbtn_t* lwobj, lwbtn_btn_t* btn, uint32_t mstime) { lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONCLICK); btn->click.cnt = 0; } -#endif /* LWBTN_CFG_CLICK_MAX_CONSECUTIVE_SEND_IMMEDIATELY */ +#endif /* LWBTN_CFG_CLICK_MAX_CONSECUTIVE_SEND_IMMEDIATELY */ + btn->time_change = mstime; /* Button state has now changed */ } - } - - /* - * Button just pressed - */ - else { - /* Do nothing - things are handled after debounce period */ - } - btn->time_change = mstime; -#if LWBTN_CFG_USE_KEEPALIVE - btn->keepalive.last_time = mstime; - btn->keepalive.cnt = 0; -#endif /* LWBTN_CFG_USE_KEEPALIVE */ - } - - /* - * Button is still pressed - */ - else if (new_state) { - /* - * Handle debounce and send on-press event - * - * This is when we detect valid press - */ - if (!(btn->flags & LWBTN_FLAG_ONPRESS_SENT)) { - /* Check minimum stable time */ - if ((mstime - btn->time_change) >= LWBTN_TIME_DEBOUNCE_GET_MIN(btn)) { -#if !LWBTN_CFG_CLICK_MAX_CONSECUTIVE_SEND_IMMEDIATELY - /* - * Depending on the configuration, - * this part will send on-click event just before the next on-press release, - * if maximum number of consecutive clicks has been reached. - */ - if (btn->click.cnt > 0 && btn->click.cnt == LWBTN_CLICK_MAX_CONSECUTIVE(btn)) { + } else { + /* + * Based on te configuration, this part of the code + * will send on-click event after certain timeout. + * + * This feature is useful if users prefers multi-click feature + * that is reported only after last click event happened, + * including number of clicks made by user + */ + if (btn->click.cnt > 0) { + if ((mstime - btn->click.last_time) >= LWBTN_TIME_CLICK_MAX_MULTI(btn)) { lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONCLICK); btn->click.cnt = 0; } -#endif /* !LWBTN_CFG_CLICK_MAX_CONSECUTIVE_SEND_IMMEDIATELY */ - - /* Start with new on-press */ - btn->flags |= LWBTN_FLAG_ONPRESS_SENT; - lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONPRESS); - -#if LWBTN_CFG_USE_KEEPALIVE - /* Set keep alive time */ - btn->keepalive.last_time = mstime; -#endif /* LWBTN_CFG_USE_KEEPALIVE */ - } - } - - /* - * Handle keep alive, but only if on-press event has been sent - * - * Keep alive is sent when valid press is being detected - */ - else { -#if LWBTN_CFG_USE_KEEPALIVE - if ((mstime - btn->keepalive.last_time) >= LWBTN_TIME_KEEPALIVE_PERIOD(btn)) { - btn->keepalive.last_time += LWBTN_TIME_KEEPALIVE_PERIOD(btn); - ++btn->keepalive.cnt; - lwobj->evt_fn(lwobj, btn, LWBTN_EVT_KEEPALIVE); - } -#endif /* LWBTN_CFG_USE_KEEPALIVE */ - } - } - - /* - * Button is still released - */ - else { - /* - * Based on te configuration, this part of the code - * will send on-click event after certain timeout. - * - * This feature is useful if users prefers multi-click feature - * that is reported only after last click event happened, - * including number of clicks made by user - */ - if (btn->click.cnt > 0) { - if ((mstime - btn->click.last_time) >= LWBTN_TIME_CLICK_MAX_MULTI(btn)) { - lwobj->evt_fn(lwobj, btn, LWBTN_EVT_ONCLICK); - btn->click.cnt = 0; } } } @@ -286,24 +292,27 @@ lwbtn_init_ex(lwbtn_t* lwobj, lwbtn_btn_t* btns, uint16_t btns_cnt, lwbtn_get_st #endif /* LWBTN_CFG_GET_STATE_MODE != LWBTN_GET_STATE_MODE_MANUAL */ for (size_t i = 0; i < btns_cnt; ++i) { -#if LWBTN_CFG_TIME_DEBOUNCE_RUNTIME - btns[i].time_debounce = LWBTN_CFG_TIME_DEBOUNCE; -#endif /* LWBTN_CFG_TIME_DEBOUNCE_RUNTIME */ -#if LWBTN_CFG_TIME_CLICK_MIN_RUNTIME +#if LWBTN_CFG_TIME_DEBOUNCE_PRESS_DYNAMIC + btns[i].time_debounce = LWBTN_CFG_TIME_DEBOUNCE_PRESS; +#endif /* LWBTN_CFG_TIME_DEBOUNCE_PRESS_DYNAMIC */ +#if LWBTN_CFG_TIME_DEBOUNCE_RELEASE_DYNAMIC + btns[i].time_debounce_release = LWBTN_CFG_TIME_DEBOUNCE_RELEASE; +#endif /* LWBTN_CFG_TIME_DEBOUNCE_RELEASE_DYNAMIC */ +#if LWBTN_CFG_TIME_CLICK_MIN_DYNAMIC btns[i].time_click_pressed_min = LWBTN_CFG_TIME_CLICK_MIN; -#endif /* LWBTN_CFG_TIME_CLICK_MIN_RUNTIME */ -#if LWBTN_CFG_TIME_CLICK_MAX_RUNTIME +#endif /* LWBTN_CFG_TIME_CLICK_MIN_DYNAMIC */ +#if LWBTN_CFG_TIME_CLICK_MAX_DYNAMIC btns[i].time_click_pressed_max = LWBTN_CFG_TIME_CLICK_MAX; -#endif /* LWBTN_CFG_TIME_CLICK_MAX_RUNTIME */ -#if LWBTN_CFG_TIME_CLICK_MULTI_MAX_RUNTIME +#endif /* LWBTN_CFG_TIME_CLICK_MAX_DYNAMIC */ +#if LWBTN_CFG_TIME_CLICK_MULTI_MAX_DYNAMIC btns[i].time_click_multi_max = LWBTN_CFG_TIME_CLICK_MULTI_MAX; -#endif /* LWBTN_CFG_TIME_CLICK_MULTI_MAX_RUNTIME */ -#if LWBTN_CFG_TIME_KEEPALIVE_PERIOD_RUNTIME +#endif /* LWBTN_CFG_TIME_CLICK_MULTI_MAX_DYNAMIC */ +#if LWBTN_CFG_TIME_KEEPALIVE_PERIOD_DYNAMIC btns[i].time_keepalive_period = LWBTN_CFG_TIME_KEEPALIVE_PERIOD; -#endif /* LWBTN_CFG_TIME_KEEPALIVE_PERIOD_RUNTIME */ -#if LWBTN_CFG_CLICK_MAX_CONSECUTIVE_RUNTIME +#endif /* LWBTN_CFG_TIME_KEEPALIVE_PERIOD_DYNAMIC */ +#if LWBTN_CFG_CLICK_MAX_CONSECUTIVE_DYNAMIC btns[i].max_consecutive = LWBTN_CFG_CLICK_MAX_CONSECUTIVE; -#endif /* LWBTN_CFG_CLICK_MAX_CONSECUTIVE_RUNTIME */ +#endif /* LWBTN_CFG_CLICK_MAX_CONSECUTIVE_DYNAMIC */ } return 1; From f833825e2df31664a1afd713178a20051ad1fad2 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Mon, 27 Mar 2023 19:14:53 +0200 Subject: [PATCH 16/16] Version 0.0.2 --- CHANGELOG.md | 2 ++ dev/lwbtn_opts.h | 2 +- library.json | 2 +- lwbtn/src/include/lwbtn/lwbtn.h | 2 +- lwbtn/src/include/lwbtn/lwbtn_opt.h | 2 +- lwbtn/src/include/lwbtn/lwbtn_opts_template.h | 2 +- lwbtn/src/lwbtn/lwbtn.c | 2 +- 7 files changed, 8 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7facbbe..5184e3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Develop +## v0.0.2 + - Add `LWBTN_CFG_GET_STATE_MODE` to control *get state* mode - Add option to check if button is currently active (after debounce period has elapsed) - Add option to set time/click parameters at run time for each button specifically diff --git a/dev/lwbtn_opts.h b/dev/lwbtn_opts.h index 4a74405..e7e1ac9 100644 --- a/dev/lwbtn_opts.h +++ b/dev/lwbtn_opts.h @@ -29,7 +29,7 @@ * This file is part of LwBTN - Lightweight button manager. * * Author: Tilen MAJERLE - * Version: v0.0.1 + * Version: v0.0.2 */ #ifndef LWBTN_HDR_OPTS_H #define LWBTN_HDR_OPTS_H diff --git a/library.json b/library.json index edba3fa..6e8cf76 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "LwBTN", - "version": "0.0.1", + "version": "0.0.2", "description": "Lightweight button handler for embedded systems", "keywords": "lwbtn, button, input, manager, btn, lightweight, embedded", "repository": { diff --git a/lwbtn/src/include/lwbtn/lwbtn.h b/lwbtn/src/include/lwbtn/lwbtn.h index 8452673..fac2734 100644 --- a/lwbtn/src/include/lwbtn/lwbtn.h +++ b/lwbtn/src/include/lwbtn/lwbtn.h @@ -29,7 +29,7 @@ * This file is part of LwBTN - Lightweight button manager. * * Author: Tilen MAJERLE - * Version: v0.0.1 + * Version: v0.0.2 */ #ifndef LWBTN_HDR_H #define LWBTN_HDR_H diff --git a/lwbtn/src/include/lwbtn/lwbtn_opt.h b/lwbtn/src/include/lwbtn/lwbtn_opt.h index 4eb29ba..dfb0853 100644 --- a/lwbtn/src/include/lwbtn/lwbtn_opt.h +++ b/lwbtn/src/include/lwbtn/lwbtn_opt.h @@ -29,7 +29,7 @@ * This file is part of LwBTN - Lightweight button manager. * * Author: Tilen MAJERLE - * Version: v0.0.1 + * Version: v0.0.2 */ #ifndef LWBTN_OPT_HDR_H #define LWBTN_OPT_HDR_H diff --git a/lwbtn/src/include/lwbtn/lwbtn_opts_template.h b/lwbtn/src/include/lwbtn/lwbtn_opts_template.h index a659d25..81c23d0 100644 --- a/lwbtn/src/include/lwbtn/lwbtn_opts_template.h +++ b/lwbtn/src/include/lwbtn/lwbtn_opts_template.h @@ -29,7 +29,7 @@ * This file is part of LwBTN - Lightweight button manager. * * Author: Tilen MAJERLE - * Version: v0.0.1 + * Version: v0.0.2 */ #ifndef LWBTN_OPTS_HDR_H #define LWBTN_OPTS_HDR_H diff --git a/lwbtn/src/lwbtn/lwbtn.c b/lwbtn/src/lwbtn/lwbtn.c index 9e33625..eaad815 100644 --- a/lwbtn/src/lwbtn/lwbtn.c +++ b/lwbtn/src/lwbtn/lwbtn.c @@ -29,7 +29,7 @@ * This file is part of LwBTN - Lightweight button manager. * * Author: Tilen MAJERLE - * Version: v0.0.1 + * Version: v0.0.2 */ #include #include "lwbtn/lwbtn.h"