diff --git a/apps/Sensor Watch Starter Project/app.c b/apps/Sensor Watch Starter Project/app.c index d2ddd6a..5c3c77a 100644 --- a/apps/Sensor Watch Starter Project/app.c +++ b/apps/Sensor Watch Starter Project/app.c @@ -21,7 +21,7 @@ typedef struct ApplicationState { LightColor color; bool light_on; uint8_t wake_count; - bool enter_deep_sleep; + bool enter_sleep_mode; } ApplicationState; ApplicationState application_state; @@ -57,13 +57,7 @@ void app_init() { * @see watch_enter_deep_sleep() */ void app_wake_from_deep_sleep() { - // retrieve our application state from the backup registers - application_state.mode = (ApplicationMode)watch_get_backup_data(0); - application_state.color = (LightColor)watch_get_backup_data(1); - application_state.wake_count = (uint8_t)watch_get_backup_data(2) + 1; - - // wait a moment for the user's finger to be off the button - delay_ms(250); + // this app only supports shallow sleep mode. } /** @@ -151,22 +145,21 @@ bool app_loop() { break; } - if (application_state.enter_deep_sleep) { - application_state.enter_deep_sleep = false; - - // stash our application state in the backup registers - watch_store_backup_data((uint32_t)application_state.mode, 0); - watch_store_backup_data((uint32_t)application_state.color, 1); - watch_store_backup_data((uint32_t)application_state.wake_count, 2); - - // turn off the LED - watch_set_led_off(); - + if (application_state.enter_sleep_mode) { // wait a moment for the user's finger to be off the button delay_ms(250); // nap time :) - watch_enter_deep_sleep(NULL); + watch_enter_shallow_sleep(NULL); + + // we just woke up; wait a moment again for the user's finger to be off the button... + delay_ms(250); + + // and prevent ourselves from going right back to sleep. + application_state.enter_sleep_mode = false; + + // finally, after sleep, return false so that our app loop runs again and updates the display. + return false; } return true; @@ -191,5 +184,5 @@ void cb_mode_pressed() { } void cb_alarm_pressed() { - application_state.enter_deep_sleep = true; + application_state.enter_sleep_mode = true; } diff --git a/watch-library/watch/watch_deepsleep.c b/watch-library/watch/watch_deepsleep.c index e5494e7..89d66ee 100644 --- a/watch-library/watch/watch_deepsleep.c +++ b/watch-library/watch/watch_deepsleep.c @@ -89,6 +89,34 @@ void watch_register_extwake_callback(uint8_t pin, ext_irq_cb_t callback, bool le _extwake_register_callback(&CALENDAR_0.device, extwake_callback); } +void watch_disable_extwake_interrupt(uint8_t pin) { + hri_rtc_tampctrl_reg_t config = hri_rtc_get_TAMPCTRL_reg(RTC, 0xFFFFFFFF); + + switch (pin) { + case A4: + a4_callback = NULL; + config &= ~(3 << RTC_TAMPCTRL_IN0ACT_Pos); + break; + case A2: + a2_callback = NULL; + config &= ~(3 << RTC_TAMPCTRL_IN1ACT_Pos); + break; + case BTN_ALARM: + btn_alarm_callback = NULL; + config &= ~(3 << RTC_TAMPCTRL_IN2ACT_Pos); + break; + default: + return; + } + + if (hri_rtcmode0_get_CTRLA_ENABLE_bit(RTC)) { + hri_rtcmode0_clear_CTRLA_ENABLE_bit(RTC); + hri_rtcmode0_wait_for_sync(RTC, RTC_MODE0_SYNCBUSY_ENABLE); + } + hri_rtc_write_TAMPCTRL_reg(RTC, config); + hri_rtcmode0_set_CTRLA_ENABLE_bit(RTC); +} + void watch_store_backup_data(uint32_t data, uint8_t reg) { if (reg < 8) { RTC->MODE0.BKUP[reg].reg = data; @@ -128,10 +156,7 @@ void _watch_disable_all_peripherals_except_slcd() { MCLK->APBCMASK.reg &= ~MCLK_APBCMASK_SERCOM3; } -void watch_enter_deep_sleep(char *message) { - // configure the ALARM interrupt (the callback doesn't matter) - watch_register_extwake_callback(BTN_ALARM, NULL, true); - +void watch_enter_shallow_sleep(char *message) { if (message != NULL) { watch_display_string(" ", 0); watch_display_string(message, 0); @@ -152,16 +177,20 @@ void watch_enter_deep_sleep(char *message) { // disable all pins _watch_disable_all_pins_except_rtc(); - // turn off RAM completely. - PM->STDBYCFG.bit.BBIASHS = 3; - - // enter standby (4); we basically hang out here until an interrupt forces us to reset. + // enter standby (4); we basically hang out here until an interrupt wakes us. sleep(4); - NVIC_SystemReset(); + // and we awake! re-enable the brownout detector + SUPC->INTENSET.bit.BOD33DET = 1; + + // call app_setup so the app can re-enable everything we disabled. + app_setup(); + + // and call app_wake_from_sleep (since main won't have a chance to do it) + app_wake_from_sleep(); } -void watch_enter_backup_mode() { +void watch_enter_deep_sleep() { // this will not work on the current silicon revision, but I said in the documentation that we do it. // so let's do it! watch_register_extwake_callback(BTN_ALARM, NULL, true); diff --git a/watch-library/watch/watch_deepsleep.h b/watch-library/watch/watch_deepsleep.h index 3dadd66..1e11892 100644 --- a/watch-library/watch/watch_deepsleep.h +++ b/watch-library/watch/watch_deepsleep.h @@ -49,15 +49,14 @@ */ void watch_register_extwake_callback(uint8_t pin, ext_irq_cb_t callback, bool level); -/** @brief Unregisters the interrupt on one of the EXTWAKE pins. This will prevent a value change on - * one of these pins from waking the device from deep sleep or BACKUP modes. +/** @brief Unregisters the RTC interrupt on one of the EXTWAKE pins. This will prevent a value change on + * one of these pins from waking the device from shallow and deep sleep modes. * @param pin Either pin BTN_ALARM, A2, or A4. If the pin is BTN_ALARM, this function DOES NOT disable * the internal pull down on that pin. */ -void watch_disable_extwake_interrupt(uint8_t pin, ext_irq_cb_t callback, bool level); +void watch_disable_extwake_interrupt(uint8_t pin); -/** @brief Stores data in one of the RTC's backup registers, which retain their data in the deep sleep - and backup modes. +/** @brief Stores data in one of the RTC's backup registers, which retain their data in deep sleep mode. * @param data An unsigned 32 bit integer with the data you wish to store. * @param reg A register from 0-7. */ @@ -69,33 +68,29 @@ void watch_store_backup_data(uint32_t data, uint8_t reg); */ uint32_t watch_get_backup_data(uint8_t reg); -/** @brief Enters a deep sleep mode by disabling RAM retention and all peripherals except the RTC and - * (optionally) the LCD. You can wake from this mode by pressing the ALARM button. - * @param message Either NULL, or a string representing a message to display while in deep sleep mode. The - * message will be displayed at position 0, so you should pad out the beginning of the string - * with spaces if you wish for the message to appear on line 2, i.e. " SLEEP". If this - * parameter is NULL, the screen will be blanked, and this function will disable the SLCD - * peripheral for additional power savings. (also note that while the message will replace any - * text on the display, this function will not clear any indicators you have set. This is by - * design, in case you wish to leave an indicator lit in sleep mode.) - * @details This deep sleep mode is not the lowest power mode available (see watch_enter_backup_mode), but - * it has the benefit of being able to wake with a press of the ALARM button, and provides an option - * for displaying a message to the user when asleep. The only way to wake from this mode is by - * pressing the ALARM button, or receiving an interrupt on pin A2 or A4 of the nine-pin connector. - * (An alarm interrupt would also work, but this has not yet been implemented.) This function enables - * the ALARM button interrupt for you, but if you wish to wake from the A2 or A4 RTC interrupt, you - * must configure them by calling watch_register_extwake_callback. Note however that your callback - * will not be called in this case. - * Power consumption in deep sleep mode varies a bit with the battery voltage and the temperature, - * but at 3 V and ~25° C you can eoughly estimate: - * * ~12µA current draw with the LCD controller on (message != NULL) - * * ~6.5µA current draw with the LCD controller off (message == NULL) - * @note With RAM powered off, your application state will be cleared as soon as you call this function, and - * when the user wakes up the watch, your app will effectively be waking from reset. Your app's @ref - * app_wake_from_deep_sleep function will be called to give your app a chance to restore any state that - * you stored using @ref watch_store_backup_data. +/** @brief Enters a shallow sleep mode by disabling all pins and peripherals except the RTC and (optionally) + * the LCD. You can wake from this mode by pressing the ALARM button, if you have an registered an + * external wake callback on the ALARM button. When your app wakes from this shallow sleep mode, your + * app_setup method will be called, since this function will have disabled things you set up. + * @param message Either NULL, or a string representing a message to display while in shallow sleep mode. If + * this parameter is NULL, the screen will be blanked out, and this function will disable the + * SLCD peripheral for additional power savings. If the message is non-NULL, it will replace + * any text on the screen, and will be displayed at position 0 (so you should pad out the beginning + * of the string with spaces if you wish for the message to appear on line 2, i.e. " SLEEP"). + * Also note that this function will NOT clear any indicator segments that you have set. This is + * by design, in case you wish to leave an indicator lit in sleep mode. + * @details This shallow sleep mode is not the lowest power mode available (see watch_enter_deep_sleep), but + * it has the benefit of retaining your application state and being able to wake from the ALARM button. + * It also provides an option for displaying a message to the user when asleep. Note that whether you + * want to wake from the ALARM button, the A2 RTC interrupt or the A4 interrupt, you must configure + * this by calling watch_register_extwake_callback first. + * + * Power consumption in shallow sleep mode varies a bit with the battery voltage and the temperature, + * but at 3 V and 25~30° C you can roughly estimate: + * * < 12µA current draw with the LCD controller on (message != NULL) + * * < 6µA current draw with the LCD controller off (message == NULL) */ -void watch_enter_deep_sleep(char *message); +void watch_enter_shallow_sleep(char *message); /** @brief Enters the SAM L22's lowest-power mode, BACKUP. * @details This function does some housekeeping before entering BACKUP mode. It first disables all @@ -113,5 +108,5 @@ void watch_enter_deep_sleep(char *message); * this function unless you have a device on the nine-pin connector with an external interrupt * on pin A2 or A4 (i.e. an accelerometer with an interrupt pin). */ -void watch_enter_backup_mode(); +void watch_enter_deep_sleep(); /// @}