From 2cf89ed4a0c42a5498b8bcbafa235bead4d3a3b5 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Tue, 4 Feb 2020 14:17:52 +1100 Subject: [PATCH] Fix ChibiOS timer overflow for 16-bit SysTick devices. --- docs/ChangeLog/20200229/PR8078.md | 5 +++++ tmk_core/common/chibios/timer.c | 37 ++++++++++++++++++++++++------- tmk_core/common/timer.h | 5 ++--- 3 files changed, 36 insertions(+), 11 deletions(-) create mode 100644 docs/ChangeLog/20200229/PR8078.md diff --git a/docs/ChangeLog/20200229/PR8078.md b/docs/ChangeLog/20200229/PR8078.md new file mode 100644 index 000000000000..0a8dc5f25eb9 --- /dev/null +++ b/docs/ChangeLog/20200229/PR8078.md @@ -0,0 +1,5 @@ +# Fix ChibiOS timer overflow for 16-bit SysTick devices + +* On 16-bit SysTick devices, the timer subsystem in QMK was incorrectly dealing with overflow. + * When running at a 100000 SysTick frequency (possible on 16-bit devices, but uncommon), this overflow would occur after 0.65 seconds. +* Timers are now correctly handling this overflow case and timing should now be correct on ChibiOS/ARM. \ No newline at end of file diff --git a/tmk_core/common/chibios/timer.c b/tmk_core/common/chibios/timer.c index 777359e6e82a..ac5a5af63822 100644 --- a/tmk_core/common/chibios/timer.c +++ b/tmk_core/common/chibios/timer.c @@ -2,23 +2,44 @@ #include "timer.h" -static uint32_t last_systime_ms = 0; -static uint32_t time_ms = 0; +static uint32_t reset_point = 0; +#if CH_CFG_ST_RESOLUTION < 32 +static uint32_t last_systime = 0; +static uint32_t overflow = 0; +#endif void timer_init(void) { timer_clear(); } void timer_clear(void) { - last_systime_ms = (uint64_t)chVTGetSystemTime(); - time_ms = 0; + reset_point = (uint32_t)chVTGetSystemTime(); +#if CH_CFG_ST_RESOLUTION < 32 + last_systime = reset_point; + overflow = 0; +#endif } uint16_t timer_read(void) { return (uint16_t)timer_read32(); } uint32_t timer_read32(void) { - uint32_t systime_ms = TIME_I2MS((uint32_t)chVTGetSystemTime()); - time_ms += systime_ms - last_systime_ms; - last_systime_ms = systime_ms; - return time_ms; + uint32_t systime = (uint32_t)chVTGetSystemTime(); + +#if CH_CFG_ST_RESOLUTION < 32 + // If/when we need to support 64-bit chips, this may need to be modified to match the native bit-ness of the MCU. + // At this point, the only SysTick resolution allowed other than 32 is 16 bit. + // In the 16-bit case, at: + // - CH_CFG_ST_FREQUENCY = 100000, overflow will occur every ~0.65 seconds + // - CH_CFG_ST_FREQUENCY = 10000, overflow will occur every ~6.5 seconds + // - CH_CFG_ST_FREQUENCY = 1000, overflow will occur every ~65 seconds + // With this implementation, as long as we ensure a timer read happens at least once during the overflow period, timing should be accurate. + if (systime < last_systime) { + overflow += ((uint32_t)1) << CH_CFG_ST_RESOLUTION; + } + + last_systime = systime; + return (uint32_t)TIME_I2MS(systime - reset_point + overflow); +#else + return (uint32_t)TIME_I2MS(systime - reset_point); +#endif } uint16_t timer_elapsed(uint16_t last) { return TIMER_DIFF_16(timer_read(), last); } diff --git a/tmk_core/common/timer.h b/tmk_core/common/timer.h index 378cf7892c1c..bbaae109d0f3 100644 --- a/tmk_core/common/timer.h +++ b/tmk_core/common/timer.h @@ -45,9 +45,8 @@ uint16_t timer_elapsed(uint16_t last); uint32_t timer_elapsed32(uint32_t last); // Utility functions to check if a future time has expired & autmatically handle time wrapping if checked / reset frequently (half of max value) -inline bool timer_expired(uint16_t current, uint16_t future) { return (uint16_t)(current - future) < 0x8000; } - -inline bool timer_expired32(uint32_t current, uint32_t future) { return (uint32_t)(current - future) < 0x80000000; } +#define timer_expired(current, future) (((uint16_t)current - (uint16_t)future) < 0x8000) +#define timer_expired32(current, future) (((uint32_t)current - (uint32_t)future) < 0x80000000) #ifdef __cplusplus }