]> git.earman.xyz Git - sensor-watch.git/commitdiff
final deep sleep refactor: retain RAM, call it shallow sleep mode
authorJoey Castillo <jose.castillo@gmail.com>
Thu, 23 Sep 2021 18:57:30 +0000 (14:57 -0400)
committerJoey Castillo <jose.castillo@gmail.com>
Thu, 23 Sep 2021 18:57:30 +0000 (14:57 -0400)
apps/Sensor Watch Starter Project/app.c
watch-library/watch/watch_deepsleep.c
watch-library/watch/watch_deepsleep.h

index d2ddd6a614aab64006c14054693077b2133fc3ba..5c3c77af2101d821f6626b7d49972c4f4d0b966b 100644 (file)
@@ -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;
 }
index e5494e795b871ab09f9e92de451899fd47986bb9..89d66ee764226ff19c5e14642a11ecd0643726b6 100644 (file)
@@ -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);
index 3dadd66308da2e3779504f0d501b20debee0ae03..1e1189290fda2e5d66d72f544dc8f6400c1d8b84 100644 (file)
   */
 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();
 /// @}