mirror of
https://github.com/MaJerle/lwbtn.git
synced 2026-09-23 12:53:50 +08:00
improve edge cases for keep alive
This commit is contained in:
+12
-9
@@ -43,8 +43,7 @@
|
||||
((uint16_t)0x0002) /*!< Flag indicates that user wants to manually set button state.
|
||||
Do not call "get_state" function */
|
||||
#define LWBTN_FLAG_FIRST_INACTIVE_RCVD \
|
||||
((uint16_t)0x0004) /*!< We are waiting for first inactive state before we continue further */
|
||||
#define LWBTN_FLAG_RESET ((uint16_t)0x0008) /*!< Reset called on the button */
|
||||
((uint16_t)0x0004) /*!< We are waiting for first inactive state before we continue further */
|
||||
|
||||
#if LWBTN_CFG_TIME_DEBOUNCE_PRESS_DYNAMIC
|
||||
#define LWBTN_TIME_DEBOUNCE_PRESS_GET_MIN(btn) ((lwbtn_time_t)((btn)->time_debounce))
|
||||
@@ -131,9 +130,9 @@ prv_process_btn(lwbtn_t* lwobj, lwbtn_btn_t* btn, lwbtn_time_t mstime) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Reset all states */
|
||||
/* Reset all states, but keep the manual-state selection (if any) intact */
|
||||
btn->last_state = 0;
|
||||
btn->flags = LWBTN_FLAG_FIRST_INACTIVE_RCVD;
|
||||
btn->flags = (btn->flags & LWBTN_FLAG_MANUAL_STATE) | LWBTN_FLAG_FIRST_INACTIVE_RCVD;
|
||||
}
|
||||
|
||||
/* Button state has just changed */
|
||||
@@ -212,10 +211,13 @@ prv_process_btn(lwbtn_t* lwobj, lwbtn_btn_t* btn, lwbtn_time_t mstime) {
|
||||
} else {
|
||||
/*
|
||||
* Handle keep alive, but only if on-press event has been sent
|
||||
* Keep alive is sent when valid press is being detected.
|
||||
*
|
||||
* Keep alive is sent when valid press is being detected
|
||||
* Period is checked to be greater than 0 as part of the loop condition itself,
|
||||
* to prevent infinite loop situation when period is (dynamically, when enabled) set to 0
|
||||
*/
|
||||
while ((lwbtn_time_t)(mstime - btn->keepalive.last_time) >= LWBTN_TIME_KEEPALIVE_PERIOD(btn)) {
|
||||
while (LWBTN_TIME_KEEPALIVE_PERIOD(btn) > 0
|
||||
&& (lwbtn_time_t)(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);
|
||||
@@ -239,7 +241,7 @@ prv_process_btn(lwbtn_t* lwobj, lwbtn_btn_t* btn, lwbtn_time_t mstime) {
|
||||
* - 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))
|
||||
if ((lwbtn_time_t)(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 */
|
||||
@@ -487,7 +489,7 @@ lwbtn_is_btn_active(const lwbtn_btn_t* btn) {
|
||||
*
|
||||
* \note If button is reset during active time, there will be no further events
|
||||
* for this button sent to the application, up until a new valid on-press is detected
|
||||
*
|
||||
*
|
||||
* \param lwobj: Object to reset buttons. Set to non-NULL to reset
|
||||
* all buttons in an object
|
||||
* \param btn: Button object to reset. Optional parameter.
|
||||
@@ -753,7 +755,8 @@ lwbtn_keepalive_get_period(const lwbtn_btn_t* btn) {
|
||||
* \ref LWBTN_CFG_TIME_KEEPALIVE_PERIOD_DYNAMIC are both enabled
|
||||
*
|
||||
* \param[in] btn: Button instance to set keep alive period for
|
||||
* \param[in] period: New keep alive period in `ms`
|
||||
* \param[in] period: New keep alive period in `ms`.
|
||||
* Set to `0` to disable the keep alive for specific button
|
||||
* \return `1` on success, `0` otherwise
|
||||
*/
|
||||
uint8_t
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# CMake include file
|
||||
|
||||
# Add more sources
|
||||
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
|
||||
${CMAKE_CURRENT_LIST_DIR}/test_lwbtn_keepalive_dynamic.c
|
||||
)
|
||||
|
||||
# Options file
|
||||
set(LWBTN_OPTS_FILE ${CMAKE_CURRENT_LIST_DIR}/lwbtn_opts.h)
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef LWBTN_HDR_OPTS_H
|
||||
#define LWBTN_HDR_OPTS_H
|
||||
|
||||
/* Disable debounce completely, to isolate the keep-alive timing under test */
|
||||
#define LWBTN_CFG_TIME_DEBOUNCE_PRESS 0
|
||||
#define LWBTN_CFG_TIME_DEBOUNCE_RELEASE 0
|
||||
|
||||
/* Keep alive period must be runtime-configurable, so the test can set it to 0 */
|
||||
#define LWBTN_CFG_TIME_KEEPALIVE_PERIOD_DYNAMIC 1
|
||||
|
||||
/* Drive input state manually from the test, instead of through a get-state callback */
|
||||
#define LWBTN_CFG_GET_STATE_MODE LWBTN_GET_STATE_MODE_MANUAL
|
||||
|
||||
#endif /* LWBTN_HDR_OPTS_H */
|
||||
@@ -0,0 +1,126 @@
|
||||
#include <stdio.h>
|
||||
#include "lwbtn/lwbtn.h"
|
||||
#include "test.h"
|
||||
|
||||
/* Check the conditions for the test */
|
||||
#if !LWBTN_CFG_TIME_KEEPALIVE_PERIOD_DYNAMIC
|
||||
#error "LWBTN_CFG_TIME_KEEPALIVE_PERIOD_DYNAMIC must be enabled for this test"
|
||||
#endif /* !LWBTN_CFG_TIME_KEEPALIVE_PERIOD_DYNAMIC */
|
||||
#if !LWBTN_CFG_USE_KEEPALIVE
|
||||
#error "LWBTN_CFG_USE_KEEPALIVE must be enabled for this test"
|
||||
#endif /* !LWBTN_CFG_USE_KEEPALIVE */
|
||||
|
||||
/* Single button, driven manually (no get-state callback) */
|
||||
static lwbtn_btn_t btn;
|
||||
|
||||
/* Counters, updated by the event callback and checked by the test */
|
||||
static uint16_t press_cnt, release_cnt, keepalive_cnt;
|
||||
|
||||
/* Event callback -> just counts events by type, no assertions here */
|
||||
static void
|
||||
prv_btn_event(struct lwbtn* lw, struct lwbtn_btn* b, lwbtn_evt_t evt) {
|
||||
(void)lw;
|
||||
(void)b;
|
||||
switch (evt) {
|
||||
case LWBTN_EVT_ONPRESS: ++press_cnt; break;
|
||||
case LWBTN_EVT_ONRELEASE: ++release_cnt; break;
|
||||
case LWBTN_EVT_KEEPALIVE: ++keepalive_cnt; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Test function
|
||||
*/
|
||||
int
|
||||
test_run(void) {
|
||||
int all_ok = 1;
|
||||
lwbtn_time_t mstime = 0;
|
||||
|
||||
/* Init the simple instance */
|
||||
lwbtn_init_ex(NULL, &btn, 1, NULL, prv_btn_event);
|
||||
|
||||
/* Prime the "first inactive state" gate before any transition can be detected */
|
||||
lwbtn_set_btn_state(&btn, 0);
|
||||
lwbtn_process(mstime++);
|
||||
|
||||
/*
|
||||
* Case 1: keep-alive period set to a normal, non-zero value.
|
||||
*
|
||||
* This is a regression check for the keep-alive loop itself: with a normal period,
|
||||
* a single process() call, far in the future, must still produce the exact number
|
||||
* of keep-alive events that elapsed in the meantime.
|
||||
*/
|
||||
{
|
||||
printf("Case 1: keep-alive period = 10ms, expect exactly 5 events after 55ms\r\n-----\r\n");
|
||||
|
||||
lwbtn_keepalive_set_period(&btn, 10);
|
||||
press_cnt = release_cnt = keepalive_cnt = 0;
|
||||
|
||||
/* Press the button -> on-press fires immediately (debounce disabled) */
|
||||
lwbtn_set_btn_state(&btn, 1);
|
||||
lwbtn_process(mstime);
|
||||
mstime += 55;
|
||||
|
||||
/* Single process call, far enough in the future to trigger multiple keep-alive events at once */
|
||||
lwbtn_process(mstime);
|
||||
|
||||
if (press_cnt != 1 || keepalive_cnt != 5 || lwbtn_keepalive_get_count(&btn) != 5) {
|
||||
printf("FAIL: press_cnt=%u, keepalive_cnt=%u, get_count=%u (expected 1, 5, 5)\r\n", (unsigned)press_cnt,
|
||||
(unsigned)keepalive_cnt, (unsigned)lwbtn_keepalive_get_count(&btn));
|
||||
all_ok = 0;
|
||||
} else {
|
||||
printf("OK: got 5 keep-alive events as expected\r\n");
|
||||
}
|
||||
|
||||
/* Release the button to end this case cleanly */
|
||||
lwbtn_set_btn_state(&btn, 0);
|
||||
lwbtn_process(++mstime);
|
||||
}
|
||||
|
||||
/*
|
||||
* Case 2: keep-alive period dynamically set to 0.
|
||||
*
|
||||
* Per `lwbtn_keepalive_set_period` documentation, a period of `0` disables keep-alive
|
||||
* for the button. Before the fix, the process loop never advanced its internal time
|
||||
* reference when the period was 0, so it looped forever instead of returning - this
|
||||
* case reproduces exactly that scenario (a large time jump while pressed).
|
||||
*
|
||||
* Reaching the end of this block at all (rather than hanging) is itself part of what
|
||||
* is being verified here.
|
||||
*/
|
||||
{
|
||||
printf("Case 2: keep-alive period = 0, expect zero events and no hang\r\n-----\r\n");
|
||||
|
||||
lwbtn_keepalive_set_period(&btn, 0);
|
||||
press_cnt = release_cnt = keepalive_cnt = 0;
|
||||
|
||||
/* Press the button -> on-press fires immediately (debounce disabled) */
|
||||
lwbtn_set_btn_state(&btn, 1);
|
||||
lwbtn_process(mstime);
|
||||
mstime += 1000000UL; /* Large jump in time */
|
||||
|
||||
/* If we reach this line at all, the loop above did not hang */
|
||||
lwbtn_process(mstime);
|
||||
|
||||
if (press_cnt != 1 || keepalive_cnt != 0 || lwbtn_keepalive_get_count(&btn) != 0) {
|
||||
printf("FAIL: press_cnt=%u, keepalive_cnt=%u, get_count=%u (expected 1, 0, 0)\r\n", (unsigned)press_cnt,
|
||||
(unsigned)keepalive_cnt, (unsigned)lwbtn_keepalive_get_count(&btn));
|
||||
all_ok = 0;
|
||||
} else {
|
||||
printf("OK: got zero keep-alive events, as expected\r\n");
|
||||
}
|
||||
|
||||
/* Release the button and confirm on-release still fires normally with keep-alive disabled */
|
||||
lwbtn_set_btn_state(&btn, 0);
|
||||
lwbtn_process(++mstime);
|
||||
if (release_cnt != 1) {
|
||||
printf("FAIL: release_cnt=%u (expected 1)\r\n", (unsigned)release_cnt);
|
||||
all_ok = 0;
|
||||
} else {
|
||||
printf("OK: on-release still fired normally\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
return all_ok ? 0 : -1;
|
||||
}
|
||||
Reference in New Issue
Block a user