Fix more corner case that could cause the top of minute alarm to stop firing

This commit is contained in:
Alessandro Genova
2025-08-08 09:19:14 -04:00
parent a616ac6cc4
commit 9770ad4fe9
4 changed files with 98 additions and 28 deletions

View File

@ -220,7 +220,7 @@ static inline void _movement_reset_inactivity_countdown(void) {
rtc_counter_t counter = watch_rtc_get_counter(); rtc_counter_t counter = watch_rtc_get_counter();
uint32_t freq = watch_rtc_get_frequency(); uint32_t freq = watch_rtc_get_frequency();
watch_rtc_register_comp_callback( watch_rtc_register_comp_callback_no_schedule(
cb_resign_timeout_interrupt, cb_resign_timeout_interrupt,
counter + movement_timeout_inactivity_deadlines[movement_state.settings.bit.to_interval] * freq, counter + movement_timeout_inactivity_deadlines[movement_state.settings.bit.to_interval] * freq,
RESIGN_TIMEOUT RESIGN_TIMEOUT
@ -228,16 +228,19 @@ static inline void _movement_reset_inactivity_countdown(void) {
movement_volatile_state.enter_sleep_mode = false; movement_volatile_state.enter_sleep_mode = false;
watch_rtc_register_comp_callback( watch_rtc_register_comp_callback_no_schedule(
cb_sleep_timeout_interrupt, cb_sleep_timeout_interrupt,
counter + movement_le_inactivity_deadlines[movement_state.settings.bit.le_interval] * freq, counter + movement_le_inactivity_deadlines[movement_state.settings.bit.le_interval] * freq,
SLEEP_TIMEOUT SLEEP_TIMEOUT
); );
watch_rtc_schedule_next_comp();
} }
static inline void _movement_disable_inactivity_countdown(void) { static inline void _movement_disable_inactivity_countdown(void) {
watch_rtc_disable_comp_callback(RESIGN_TIMEOUT); watch_rtc_disable_comp_callback_no_schedule(RESIGN_TIMEOUT);
watch_rtc_disable_comp_callback(SLEEP_TIMEOUT); watch_rtc_disable_comp_callback_no_schedule(SLEEP_TIMEOUT);
watch_rtc_schedule_next_comp();
} }
static void _movement_handle_top_of_minute(void) { static void _movement_handle_top_of_minute(void) {
@ -1194,9 +1197,11 @@ bool app_loop(void) {
} }
static movement_event_type_t _process_button_event(bool pin_level, movement_button_t* button) { static movement_event_type_t _process_button_event(bool pin_level, movement_button_t* button) {
movement_event_type_t event_type = EVENT_NONE;
// This shouldn't happen normally // This shouldn't happen normally
if (pin_level == button->is_down) { if (pin_level == button->is_down) {
return EVENT_NONE; return event_type;
} }
uint32_t counter = watch_rtc_get_counter(); uint32_t counter = watch_rtc_get_counter();
@ -1206,43 +1211,42 @@ static movement_event_type_t _process_button_event(bool pin_level, movement_butt
if (pin_level) { if (pin_level) {
// We schedule a timeout to fire the longpress event // We schedule a timeout to fire the longpress event
button->down_timestamp = counter; button->down_timestamp = counter;
watch_rtc_register_comp_callback(button->cb_longpress, counter + MOVEMENT_LONG_PRESS_TICKS, button->timeout_index); watch_rtc_register_comp_callback_no_schedule(button->cb_longpress, counter + MOVEMENT_LONG_PRESS_TICKS, button->timeout_index);
// force alarm off if the user pressed a button. // force alarm off if the user pressed a button.
watch_buzzer_abort_sequence(); watch_buzzer_abort_sequence();
return button->down_event; event_type = button->down_event;
} else { } else {
// We cancel the timeout if it hasn't fired yet // We cancel the timeout if it hasn't fired yet
watch_rtc_disable_comp_callback(button->timeout_index); watch_rtc_disable_comp_callback_no_schedule(button->timeout_index);
if ((counter - button->down_timestamp) >= MOVEMENT_LONG_PRESS_TICKS) { if ((counter - button->down_timestamp) >= MOVEMENT_LONG_PRESS_TICKS) {
return button->down_event + 3; event_type = button->down_event + 3;
} else { } else {
return button->down_event + 1; event_type = button->down_event + 1;
} }
} }
// This will also schedule the comp callbacks above
_movement_reset_inactivity_countdown();
return event_type;
} }
void cb_light_btn_interrupt(void) { void cb_light_btn_interrupt(void) {
bool pin_level = HAL_GPIO_BTN_LIGHT_read(); bool pin_level = HAL_GPIO_BTN_LIGHT_read();
movement_volatile_state.pending_events |= 1 << _process_button_event(pin_level, &movement_volatile_state.light_button); movement_volatile_state.pending_events |= 1 << _process_button_event(pin_level, &movement_volatile_state.light_button);
_movement_reset_inactivity_countdown();
} }
void cb_mode_btn_interrupt(void) { void cb_mode_btn_interrupt(void) {
bool pin_level = HAL_GPIO_BTN_MODE_read(); bool pin_level = HAL_GPIO_BTN_MODE_read();
movement_volatile_state.pending_events |= 1 << _process_button_event(pin_level, &movement_volatile_state.mode_button); movement_volatile_state.pending_events |= 1 << _process_button_event(pin_level, &movement_volatile_state.mode_button);
_movement_reset_inactivity_countdown();
} }
void cb_alarm_btn_interrupt(void) { void cb_alarm_btn_interrupt(void) {
bool pin_level = HAL_GPIO_BTN_ALARM_read(); bool pin_level = HAL_GPIO_BTN_ALARM_read();
movement_volatile_state.pending_events |= 1 << _process_button_event(pin_level, &movement_volatile_state.alarm_button); movement_volatile_state.pending_events |= 1 << _process_button_event(pin_level, &movement_volatile_state.alarm_button);
_movement_reset_inactivity_countdown();
} }
static movement_event_type_t _process_button_longpress_timeout(movement_button_t* button) { static movement_event_type_t _process_button_longpress_timeout(movement_button_t* button) {

View File

@ -198,7 +198,7 @@ void watch_rtc_disable_all_periodic_callbacks(void) {
watch_rtc_disable_matching_periodic_callbacks(0xFF); watch_rtc_disable_matching_periodic_callbacks(0xFF);
} }
static void _watch_rtc_schedule_next_comp(void) { void watch_rtc_schedule_next_comp(void) {
rtc_counter_t curr_counter = watch_rtc_get_counter(); rtc_counter_t curr_counter = watch_rtc_get_counter();
// If there is already a pending comp interrupt for this very tick, let it fire // If there is already a pending comp interrupt for this very tick, let it fire
// And this function will be called again as soon as the interrupt fires. // And this function will be called again as soon as the interrupt fires.
@ -245,7 +245,17 @@ void watch_rtc_register_comp_callback(watch_cb_t callback, rtc_counter_t counter
comp_callbacks[index].callback = callback; comp_callbacks[index].callback = callback;
comp_callbacks[index].enabled = true; comp_callbacks[index].enabled = true;
_watch_rtc_schedule_next_comp(); watch_rtc_schedule_next_comp();
}
void watch_rtc_register_comp_callback_no_schedule(watch_cb_t callback, rtc_counter_t counter, uint8_t index) {
if (index >= WATCH_RTC_N_COMP_CB) {
return;
}
comp_callbacks[index].counter = counter;
comp_callbacks[index].callback = callback;
comp_callbacks[index].enabled = true;
} }
void watch_rtc_disable_comp_callback(uint8_t index) { void watch_rtc_disable_comp_callback(uint8_t index) {
@ -255,13 +265,21 @@ void watch_rtc_disable_comp_callback(uint8_t index) {
comp_callbacks[index].enabled = false; comp_callbacks[index].enabled = false;
_watch_rtc_schedule_next_comp(); watch_rtc_schedule_next_comp();
}
void watch_rtc_disable_comp_callback_no_schedule(uint8_t index) {
if (index >= WATCH_RTC_N_COMP_CB) {
return;
}
comp_callbacks[index].enabled = false;
} }
void watch_rtc_callback(uint16_t interrupt_cause) { void watch_rtc_callback(uint16_t interrupt_cause) {
// First read all relevant registers, to ensure no changes occurr during the callbacks // First read all relevant registers, to ensure no changes occurr during the callbacks
uint16_t interrupt_enabled = RTC->MODE0.INTENSET.reg; uint16_t interrupt_enabled = RTC->MODE0.INTENSET.reg;
rtc_counter_t comp_counter = RTC->MODE0.COMP[0].reg; rtc_counter_t counter = watch_rtc_get_counter();
if ((interrupt_cause & interrupt_enabled) & RTC_MODE0_INTFLAG_PER_Msk) { if ((interrupt_cause & interrupt_enabled) & RTC_MODE0_INTFLAG_PER_Msk) {
@ -291,15 +309,18 @@ void watch_rtc_callback(uint16_t interrupt_cause) {
if ((interrupt_cause & interrupt_enabled) & RTC_MODE0_INTFLAG_CMP0) { if ((interrupt_cause & interrupt_enabled) & RTC_MODE0_INTFLAG_CMP0) {
// The comp interrupt is generated one tick after the matched counter // The comp interrupt is generated one tick after the matched counter
// rtc_counter_t comp_counter = watch_rtc_get_counter() - 1; rtc_counter_t comp_counter = counter - 1;
for (uint8_t index = 0; index < WATCH_RTC_N_COMP_CB; ++index) { for (uint8_t index = 0; index < WATCH_RTC_N_COMP_CB; ++index) {
if (comp_callbacks[index].enabled && comp_counter == comp_callbacks[index].counter) { // Give it a little bit of wiggle room, if a comp callback is enabled and is just passed
if (comp_callbacks[index].enabled &&
(comp_counter - comp_callbacks[index].counter) <= RTC_CNT_HZ
) {
comp_callbacks[index].enabled = false; comp_callbacks[index].enabled = false;
comp_callbacks[index].callback(); comp_callbacks[index].callback();
} }
} }
_watch_rtc_schedule_next_comp(); watch_rtc_schedule_next_comp();
} }
if ((interrupt_cause & interrupt_enabled) & RTC_MODE0_INTFLAG_OVF) { if ((interrupt_cause & interrupt_enabled) & RTC_MODE0_INTFLAG_OVF) {

View File

@ -114,10 +114,38 @@ uint32_t watch_rtc_get_ticks_per_minute(void);
*/ */
void watch_rtc_register_comp_callback(watch_cb_t callback, rtc_counter_t counter, uint8_t index); void watch_rtc_register_comp_callback(watch_cb_t callback, rtc_counter_t counter, uint8_t index);
/** @brief Just like watch_rtc_register_comp_callback but doesn't actually schedule the callback
*
* Useful if you need register multiple callbacks at once, avoids multiple calls to the expensive watch_rtc_schedule_next_comp:
* Usage:
* watch_rtc_register_comp_callback_no_schedule(cb0, counter0, index0);
* watch_rtc_register_comp_callback_no_schedule(cb1, counter1, index1);
* watch_rtc_schedule_next_comp();
*/
void watch_rtc_register_comp_callback_no_schedule(watch_cb_t callback, rtc_counter_t counter, uint8_t index);
/** @brief Disables the specified comp callback. /** @brief Disables the specified comp callback.
*/ */
void watch_rtc_disable_comp_callback(uint8_t index); void watch_rtc_disable_comp_callback(uint8_t index);
/** @brief Just like watch_rtc_disable_comp_callback but doesn't actually schedule the callback
*
* Useful if you need disable multiple callbacks at once, avoids multiple calls to the expensive watch_rtc_schedule_next_comp:
* Usage:
* watch_rtc_disable_comp_callback_no_schedule(index0);
* watch_rtc_disable_comp_callback_no_schedule(index1);
* watch_rtc_schedule_next_comp();
*/
/** @brief Disables the specified comp callback.
*/
void watch_rtc_disable_comp_callback_no_schedule(uint8_t index);
/** @brief Determines the first comp callback that should fire and schedule it with the RTC
*
* You would never need to call this manually, unless you used the 'no_schedule' functions above.
*/
void watch_rtc_schedule_next_comp(void);
/** @brief Disables the alarm callback. /** @brief Disables the alarm callback.
*/ */
// void watch_rtc_disable_alarm_callback(void); // void watch_rtc_disable_alarm_callback(void);

View File

@ -65,7 +65,6 @@ watch_cb_t a4_callback;
static void _watch_increase_counter(void *userData); static void _watch_increase_counter(void *userData);
static void _watch_process_periodic_callbacks(void); static void _watch_process_periodic_callbacks(void);
static void _watch_process_comp_callbacks(void); static void _watch_process_comp_callbacks(void);
static void _watch_rtc_schedule_next_comp(void);
bool _watch_rtc_is_enabled(void) { bool _watch_rtc_is_enabled(void) {
return counter_interval; return counter_interval;
@ -228,7 +227,7 @@ static void _watch_process_comp_callbacks(void) {
} }
} }
_watch_rtc_schedule_next_comp(); watch_rtc_schedule_next_comp();
} }
} }
@ -272,7 +271,17 @@ void watch_rtc_register_comp_callback(watch_cb_t callback, rtc_counter_t counter
comp_callbacks[index].callback = callback; comp_callbacks[index].callback = callback;
comp_callbacks[index].enabled = true; comp_callbacks[index].enabled = true;
_watch_rtc_schedule_next_comp(); watch_rtc_schedule_next_comp();
}
void watch_rtc_register_comp_callback_no_schedule(watch_cb_t callback, rtc_counter_t counter, uint8_t index) {
if (index >= WATCH_RTC_N_COMP_CB) {
return;
}
comp_callbacks[index].counter = counter;
comp_callbacks[index].callback = callback;
comp_callbacks[index].enabled = true;
} }
void watch_rtc_disable_comp_callback(uint8_t index) { void watch_rtc_disable_comp_callback(uint8_t index) {
@ -282,10 +291,18 @@ void watch_rtc_disable_comp_callback(uint8_t index) {
comp_callbacks[index].enabled = false; comp_callbacks[index].enabled = false;
_watch_rtc_schedule_next_comp(); watch_rtc_schedule_next_comp();
} }
static void _watch_rtc_schedule_next_comp(void) { void watch_rtc_disable_comp_callback_no_schedule(uint8_t index) {
if (index >= WATCH_RTC_N_COMP_CB) {
return;
}
comp_callbacks[index].enabled = false;
}
void watch_rtc_schedule_next_comp(void) {
rtc_counter_t curr_counter = watch_rtc_get_counter(); rtc_counter_t curr_counter = watch_rtc_get_counter();
// If there is already a pending comp interrupt for this very tick, let it fire // If there is already a pending comp interrupt for this very tick, let it fire
// And this function will be called again as soon as the interrupt fires. // And this function will be called again as soon as the interrupt fires.