diff --git a/movement.c b/movement.c index 4f49fb8a..7e4a23bf 100644 --- a/movement.c +++ b/movement.c @@ -220,7 +220,7 @@ static inline void _movement_reset_inactivity_countdown(void) { rtc_counter_t counter = watch_rtc_get_counter(); uint32_t freq = watch_rtc_get_frequency(); - watch_rtc_register_comp_callback( + watch_rtc_register_comp_callback_no_schedule( cb_resign_timeout_interrupt, counter + movement_timeout_inactivity_deadlines[movement_state.settings.bit.to_interval] * freq, RESIGN_TIMEOUT @@ -228,16 +228,19 @@ static inline void _movement_reset_inactivity_countdown(void) { movement_volatile_state.enter_sleep_mode = false; - watch_rtc_register_comp_callback( + watch_rtc_register_comp_callback_no_schedule( cb_sleep_timeout_interrupt, counter + movement_le_inactivity_deadlines[movement_state.settings.bit.le_interval] * freq, SLEEP_TIMEOUT ); + + watch_rtc_schedule_next_comp(); } static inline void _movement_disable_inactivity_countdown(void) { - watch_rtc_disable_comp_callback(RESIGN_TIMEOUT); - watch_rtc_disable_comp_callback(SLEEP_TIMEOUT); + watch_rtc_disable_comp_callback_no_schedule(RESIGN_TIMEOUT); + watch_rtc_disable_comp_callback_no_schedule(SLEEP_TIMEOUT); + watch_rtc_schedule_next_comp(); } 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) { + movement_event_type_t event_type = EVENT_NONE; + // This shouldn't happen normally if (pin_level == button->is_down) { - return EVENT_NONE; + return event_type; } 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) { // We schedule a timeout to fire the longpress event 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. watch_buzzer_abort_sequence(); - return button->down_event; + event_type = button->down_event; } else { // 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) { - return button->down_event + 3; + event_type = button->down_event + 3; } 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) { 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_reset_inactivity_countdown(); } void cb_mode_btn_interrupt(void) { 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_reset_inactivity_countdown(); } void cb_alarm_btn_interrupt(void) { 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_reset_inactivity_countdown(); } static movement_event_type_t _process_button_longpress_timeout(movement_button_t* button) { diff --git a/watch-library/hardware/watch/watch_rtc.c b/watch-library/hardware/watch/watch_rtc.c index 1fee9917..8d5255ad 100644 --- a/watch-library/hardware/watch/watch_rtc.c +++ b/watch-library/hardware/watch/watch_rtc.c @@ -198,7 +198,7 @@ void watch_rtc_disable_all_periodic_callbacks(void) { 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(); // 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. @@ -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].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) { @@ -255,13 +265,21 @@ void watch_rtc_disable_comp_callback(uint8_t index) { 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) { // First read all relevant registers, to ensure no changes occurr during the callbacks 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) { @@ -291,15 +309,18 @@ void watch_rtc_callback(uint16_t interrupt_cause) { if ((interrupt_cause & interrupt_enabled) & RTC_MODE0_INTFLAG_CMP0) { // 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) { - 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].callback(); } } - _watch_rtc_schedule_next_comp(); + watch_rtc_schedule_next_comp(); } if ((interrupt_cause & interrupt_enabled) & RTC_MODE0_INTFLAG_OVF) { diff --git a/watch-library/shared/watch/watch_rtc.h b/watch-library/shared/watch/watch_rtc.h index e268ad28..504c37f3 100644 --- a/watch-library/shared/watch/watch_rtc.h +++ b/watch-library/shared/watch/watch_rtc.h @@ -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); +/** @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. */ 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. */ // void watch_rtc_disable_alarm_callback(void); diff --git a/watch-library/simulator/watch/watch_rtc.c b/watch-library/simulator/watch/watch_rtc.c index d9cfb9f2..1347d797 100644 --- a/watch-library/simulator/watch/watch_rtc.c +++ b/watch-library/simulator/watch/watch_rtc.c @@ -65,7 +65,6 @@ watch_cb_t a4_callback; static void _watch_increase_counter(void *userData); static void _watch_process_periodic_callbacks(void); static void _watch_process_comp_callbacks(void); -static void _watch_rtc_schedule_next_comp(void); bool _watch_rtc_is_enabled(void) { 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].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) { @@ -282,10 +291,18 @@ void watch_rtc_disable_comp_callback(uint8_t index) { 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(); // 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.