diff options
| -rw-r--r-- | watch-library/watch/watch_deepsleep.c | 4 | ||||
| -rw-r--r-- | watch-library/watch/watch_rtc.c | 71 | ||||
| -rw-r--r-- | watch-library/watch/watch_rtc.h | 39 | 
3 files changed, 94 insertions, 20 deletions
| diff --git a/watch-library/watch/watch_deepsleep.c b/watch-library/watch/watch_deepsleep.c index c83254e4..1f3ae7fb 100644 --- a/watch-library/watch/watch_deepsleep.c +++ b/watch-library/watch/watch_deepsleep.c @@ -164,7 +164,7 @@ void watch_enter_shallow_sleep(char *message) {      _watch_disable_all_peripherals_except_slcd();      // disable tick interrupt -    watch_disable_tick_callback(); +    watch_rtc_disable_all_tick_callbacks();      // disable brownout detector interrupt, which could inadvertently wake us up.      SUPC->INTENCLR.bit.BOD33DET = 1; @@ -190,7 +190,7 @@ void watch_enter_deep_sleep() {      // so let's do it!      watch_register_extwake_callback(BTN_ALARM, NULL, true); -    watch_disable_tick_callback(); +    watch_rtc_disable_all_tick_callbacks();      _watch_disable_all_peripherals_except_slcd();      slcd_sync_deinit(&SEGMENT_LCD_0);      hri_mclk_clear_APBCMASK_SLCD_bit(SLCD); diff --git a/watch-library/watch/watch_rtc.c b/watch-library/watch/watch_rtc.c index 0974c796..519a1f00 100644 --- a/watch-library/watch/watch_rtc.c +++ b/watch-library/watch/watch_rtc.c @@ -24,7 +24,7 @@  #include "watch_rtc.h" -ext_irq_cb_t tick_callback; +ext_irq_cb_t tick_callbacks[8];  ext_irq_cb_t alarm_callback;  ext_irq_cb_t btn_alarm_callback;  ext_irq_cb_t a2_callback; @@ -70,15 +70,40 @@ watch_date_time watch_rtc_get_date_time() {      return retval;  } -void watch_register_tick_callback(ext_irq_cb_t callback) { -    tick_callback = callback; +void watch_rtc_register_1Hz_callback(ext_irq_cb_t callback) { +    watch_rtc_register_tick_callback(callback, 1); +} + +void watch_rtc_disable_1Hz_callback() { +    watch_rtc_disable_tick_callback(1); +} + +void watch_rtc_register_tick_callback(ext_irq_cb_t callback, uint8_t period) { +    // we told them, it has to be a power of 2. +    if (__builtin_popcount(period) != 1) return; + +    // this left-justifies the period in a 32-bit integer. +    uint32_t tmp = period << 24; +    // now we can count the leading zeroes to get the value we need. +    // 0x01 (1 Hz) will have 7 leading zeros for PER7. 0xF0 (128 Hz) will have no leading zeroes for PER0. +    uint8_t per_n = __builtin_clz(tmp); + +    // this also maps nicely to an index for our list of tick callbacks. +    tick_callbacks[per_n] = callback; +      NVIC_ClearPendingIRQ(RTC_IRQn);      NVIC_EnableIRQ(RTC_IRQn); -    RTC->MODE2.INTENSET.reg = RTC_MODE2_INTENSET_PER7; +    RTC->MODE2.INTENSET.reg = 1 << per_n; +} + +void watch_rtc_disable_tick_callback(uint8_t period) { +    if (__builtin_popcount(period) != 1) return; +    uint8_t per_n = __builtin_clz(period << 24); +    RTC->MODE2.INTENCLR.reg = 1 << per_n;  } -void watch_disable_tick_callback() { -    RTC->MODE2.INTENCLR.reg = RTC_MODE2_INTENCLR_PER7; +void watch_rtc_disable_all_tick_callbacks() { +    RTC->MODE2.INTENCLR.reg = 0xFF;  }  void watch_rtc_register_alarm_callback(ext_irq_cb_t callback, watch_date_time alarm_time, watch_rtc_alarm_match mask) { @@ -99,17 +124,20 @@ void RTC_Handler(void) {      uint16_t interrupt_status = RTC->MODE2.INTFLAG.reg;      uint16_t interrupt_enabled = RTC->MODE2.INTENSET.reg; -    if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_ALARM0) { -        if (alarm_callback != NULL) { -            alarm_callback(); +    if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_PER_Msk) { +        // handle the tick callback first, it's what we do the most. +        // start from PER7, the 1 Hz tick. +        for(int8_t i = 7; i >= 0; i--) { +            if ((interrupt_status & interrupt_enabled) & (1 << i)) { +                if (tick_callbacks[i] != NULL) { +                    tick_callbacks[i](); +                } +                RTC->MODE2.INTFLAG.reg = 1 << i; +                break; +            }          } -        RTC->MODE2.INTFLAG.reg = RTC_MODE2_INTFLAG_ALARM0; -    } else if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_PER7) { -        if (tick_callback != NULL) { -            tick_callback(); -        } -        RTC->MODE2.INTFLAG.reg = RTC_MODE2_INTFLAG_PER7;      } else if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_TAMPER) { +        // handle the extwake interrupts next.          uint8_t reason = RTC->MODE2.TAMPID.reg;          if (reason & RTC_TAMPID_TAMPID2) {              if (btn_alarm_callback != NULL) btn_alarm_callback(); @@ -120,6 +148,12 @@ void RTC_Handler(void) {          }          RTC->MODE2.TAMPID.reg = reason;          RTC->MODE2.INTFLAG.reg = RTC_MODE2_INTFLAG_TAMPER; +    } else if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_ALARM0) { +        // finally handle the alarm. +        if (alarm_callback != NULL) { +            alarm_callback(); +        } +        RTC->MODE2.INTFLAG.reg = RTC_MODE2_INTFLAG_ALARM0;      }  } @@ -152,3 +186,10 @@ void watch_get_date_time(struct calendar_date_time *date_time) {      date_time->date.month = val.bit.MONTH;      date_time->date.year = val.bit.YEAR + WATCH_RTC_REFERENCE_YEAR;  } + +void watch_register_tick_callback(ext_irq_cb_t callback) { +    tick_callbacks[7] = callback; +    NVIC_ClearPendingIRQ(RTC_IRQn); +    NVIC_EnableIRQ(RTC_IRQn); +    RTC->MODE2.INTENSET.reg = RTC_MODE2_INTENSET_PER7; +} diff --git a/watch-library/watch/watch_rtc.h b/watch-library/watch/watch_rtc.h index 71f555ab..f6b6329e 100644 --- a/watch-library/watch/watch_rtc.h +++ b/watch-library/watch/watch_rtc.h @@ -106,11 +106,37 @@ void watch_rtc_disable_alarm_callback();    * @param callback The function you wish to have called when the clock ticks. If you pass in NULL, the tick    *                 interrupt will still be enabled, but no callback function will be called.    */ -void watch_register_tick_callback(ext_irq_cb_t callback); +void watch_rtc_register_1Hz_callback(ext_irq_cb_t callback); + +/** @brief Disables the tick callback for the given period. +  */ +void watch_rtc_disable_1Hz_callback(); + +/** @brief Registers a "tick" callback that will be called at a configurable period. +  * @param callback The function you wish to have called when the clock ticks. If you pass in NULL, the tick +  *                 interrupt will still be enabled, but no callback function will be called. +  * @param period The frequency of the tick in Hz. **Must be a power of 2**, from 1 to 128 inclusive. +  * @note A 1 Hz tick (@see watch_rtc_register_1Hz_callback) is suitable for most applications, in that it gives you a +  *       chance to update the display once a second — an ideal update rate for a watch! If however you are displaying +  *       a value (such as an accelerometer output) that updates more frequently than once per second, you may want to +  *       tick at 16 or 32 Hz to update the screen more quickly. Just remember that the more frequent the tick, the more +  *       power your app will consume. Ideally you should enable the fast tick only when the user requires it (i.e. in +  *       response to an input event), and move back to the slow tick after some time. +  * +  *       Also note that the RTC peripheral does not have sub-second resolution, so even if you set a 2 or 4 Hz interval, +  *       the system will not have any way of telling you where you are within a given second; watch_rtc_get_date_time +  *       will return the exact same timestamp until the second ticks over. +  */ +void watch_rtc_register_tick_callback(ext_irq_cb_t callback, uint8_t period); -/** @brief Disables the tick callback. +/** @brief Disables the tick callback for the given period. +  * @param period The frequency of the tick you wish to disable, in Hz. **Must be a power of 2**, from 1 to 128.    */ -void watch_disable_tick_callback(); +void watch_rtc_disable_tick_callback(uint8_t period); + +/** @brief Disables all tick callbacks. +  */ +void watch_rtc_disable_all_tick_callbacks();  /** @brief Sets the system date and time.    * @param date_time A struct representing the date and time you wish to set. @@ -124,5 +150,12 @@ void watch_set_date_time(struct calendar_date_time date_time);  __attribute__((deprecated("Use the watch_rtc_get_date_time function instead")))  void watch_get_date_time(struct calendar_date_time *date_time); +/** @brief Registers a "tick" callback that will be called once per second. +  * @param callback The function you wish to have called when the clock ticks. If you pass in NULL, the tick +  *                 interrupt will still be enabled, but no callback function will be called. +  */ +__attribute__((deprecated("Use the watch_rtc_register_1Hz_callback function instead"))) +void watch_register_tick_callback(ext_irq_cb_t callback); +  /// @}  #endif | 
