]> git.earman.xyz Git - sensor-watch.git/commitdiff
thinking through deep sleep stuff
authorJoey Castillo <jose.castillo@gmail.com>
Mon, 2 Aug 2021 20:14:47 +0000 (16:14 -0400)
committerJoey Castillo <jose.castillo@gmail.com>
Mon, 2 Aug 2021 20:14:47 +0000 (16:14 -0400)
Sensor Watch Starter Project/app.c
Sensor Watch Starter Project/app.h
watch-library/main.c
watch-library/watch/watch.c
watch-library/watch/watch.h

index eb862767cc64493462be12fd9c9ba94ba1ddb235..30c1b8ea51d6ab5413cfe3e967ec2e95dc2487d0 100644 (file)
@@ -1,11 +1,11 @@
 #include <stdio.h>
 #include <string.h>
+#include "watch.h"
 #include "app.h"
 
-// these are implemented in main.c, just want to have access to them here.
-void uart_putc(char c);
-void uart_puts(char *s);
-
+//////////////////////////////////////////////////////////////////////////////////////////
+// This section sets up types and storage for our application state.
+// You can tear this out and replace it with whatever you want.
 typedef enum ApplicationMode {
     MODE_HELLO = 0,
     MODE_THERE
@@ -21,35 +21,64 @@ typedef enum LightColor {
 typedef struct ApplicationState {
     ApplicationMode mode;
     LightColor color;
+    uint8_t wake_count;
 } ApplicationState;
 
 ApplicationState applicationState;
 
-void cb_light_pressed() {
-    applicationState.color = (applicationState.color + 1) % 4;
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// This section defines the callbacks for our button press events (implemented at bottom).
+// Add any other callbacks you may need either here or in another file.
+void cb_light_pressed();
+void cb_mode_pressed();
+void cb_alarm_pressed();
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// This section contains the required functions for any watch app. You should tear out
+// all the code in these functions when writing your app, but you must implement all
+// of the functions, even if they are empty stubs. You can also replace the documentation
+// lines with documentation that describes what your functions do!
+
+/**
+ * @brief the app_init function is called before anything else. Use it to set up any
+ * internal data structures or application state required by your app.
+ */
+void app_init() {
+    memset(&applicationState, 0, sizeof(applicationState));
 }
 
-void cb_mode_pressed() {
-    applicationState.mode = (applicationState.mode + 1) % 2;
+/**
+ * @brief the app_wake_from_deep_sleep function is only called if your app is waking from
+ * the ultra-low power BACKUP sleep mode. You may have chosen to store some state in the
+ * RTC's backup registers prior to entering this mode. You may restore that state here.
+ *
+ * @see watch_enter_deep_sleep()
+ */
+void app_wake_from_deep_sleep() {
+    // TODO: deep sleep demo
 }
 
 /**
- * @brief the app_init function is like setup() in Arduino. It is called once when the
+ * @brief the app_setup function is like setup() in Arduino. It is called once when the
  * program begins. You should set pin modes and enable any peripherals you want to
- * set up (real-time clock, I2C, etc.)
- * 
- * @note If your app enters the ultra-low power BACKUP sleep mode, this function will 
+ * set up (real-time clock, I2C, etc.) Depending on your application, you may or may not
+ * want to configure sensors on your sensor board here. For example, a low-power
+ * accelerometer that will run at all times should be configured here, whereas you may
+ * want to enable a more power-hungry environmental sensor only when you need it.
+ *
+ * @note If your app enters the ultra-low power BACKUP sleep mode, this function will
  * be called again when it wakes from that deep sleep state. In this state, the RTC will
  * still be configured with the correct date and time.
  */
-void app_init() {
-    memset(&applicationState, 0, sizeof(applicationState));
-
-    watch_enable_led(false);
+void app_setup() {
+    watch_enable_led(false); // enable LED with plain digital IO, not PWM
 
     watch_enable_buttons();
     watch_register_button_callback(BTN_LIGHT, cb_light_pressed);
     watch_register_button_callback(BTN_MODE, cb_mode_pressed);
+    watch_register_button_callback(BTN_ALARM, cb_alarm_pressed);
 
     watch_enable_display();
 }
@@ -68,13 +97,15 @@ void app_prepare_for_sleep() {
  * STANDBY sleep mode.
  */
 void app_wake_from_sleep() {
+    applicationState.wake_count++;
 }
 
 /**
  * @brief the app_loop function is called once on app startup and then again each time
  * the watch STANDBY sleep mode.
  */
-void app_loop() {
+bool app_loop() {
+    // set the LED to a color
     switch (applicationState.color) {
         case COLOR_RED:
             watch_set_led_red();
@@ -89,6 +120,13 @@ void app_loop() {
             applicationState.color = COLOR_OFF;
             watch_set_led_off();
     }
+
+    // Display the number of times we've woken up (modulo 32 to fit in 2 digits at top right)
+    char buf[3] = {0};
+    sprintf(buf, "%2d", applicationState.wake_count % 32);
+    watch_display_string(buf, 2);
+
+    // display "Hello there" text
     switch (applicationState.mode) {
         case MODE_HELLO:
             watch_display_string("Hello", 5);
@@ -97,4 +135,25 @@ void app_loop() {
             watch_display_string("there", 5);
             break;
     }
+
+    // Wait a moment to debounce button input
+    delay_ms(250);
+
+    return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Implementations for our callback functions. Replace these with whatever functionality
+// your app requires.
+void cb_light_pressed() {
+    applicationState.color = (applicationState.color + 1) % 4;
+}
+
+void cb_mode_pressed() {
+    applicationState.mode = (applicationState.mode + 1) % 2;
 }
+
+void cb_alarm_pressed() {
+    // TODO: deep sleep demo
+}
\ No newline at end of file
index b025725c8af7723bfa95ca720cfa8a5831c0fa19..df40a30fcca000900fdefc64dc09af3d3efbfcb4 100644 (file)
@@ -1,29 +1,33 @@
 /**
   * Header file for Sensor Watch application
-  * 
-  * Ideally you should implement your app entirely within these functions, as well as any
-  * interrupt callbacks you register with the watch API. The general flow is as follows:
-  * 
-  * 1. main.c configures the watch
-  * 2. main.c calls your app_init() function. 
+  *
+  * You should be able to write a watch app by simply implementing these functions
+  * and declaring callbacks for various GPIO and peripheral interrupts. The main.c
+  * file takes care of calling these functions for you. The general flow:
+  *
+  * 1. Your app_init() function is called.
+  *      - This method should only be used to set your initial application state.
+  * 2. If your app is waking from BACKUP, app_wake_from_deep_sleep()  is called.
+  *      - If you saved state in the RTC's backup registers, you can restore it here.
+  * 3. Your app_setup() method is called.
   *      - You may wish to enable some functionality and peripherals here.
-  *      - You should definitely set up some wake-up sources here.
-  * 3. main.c calls your app_loop() function. 
+  *      - You should definitely set up some interrupts here.
+  * 4. The main run loop begins: your app_loop() function is called.
   *      - Run code and update your UI here.
-  * 4. main.c calls your app_prepare_for_sleep() function.
-  *      - Consider resetting any state that was set in your wakeup callback here.
-  *      - You may also want to disable / depower external sensors or peripherals here.
-  * 5. main.c enters the STANDBY sleep mode.
+  *      - Return true if your app is prepared to enter STANDBY mode.
+  * 5. This step differs depending on the value returned by app_loop:
+  *      - If you returned false, execution resumes at (4).
+  *      - If you returned true, app_prepare_for_sleep() is called; execution moves on to (6).
+  * 6. The microcontroller enters the STANDBY sleep mode.
   *      - No user code will run, and the watch will enter a low power mode.
-  *      - The watch will remain in this state until something from (2) wakes it.
-  * 6. main.c calls your app_wake_from_sleep() function.
-  *      - You may wish to re-enable any peripherals you disabled.
-  *      - After this, execution resumes at step (3).
+  *      - The watch will remain in this state until an interrupt wakes it.
+  * 7. Once woken from STANDBY, your app_wake_from_sleep() function is called.
+  *      - After this, execution resumes at (4).
   */
 
-#include "watch.h"
-
 void app_init();
-void app_loop();
+void app_wake_from_deep_sleep();
+void app_setup();
+bool app_loop();
 void app_prepare_for_sleep();
 void app_wake_from_sleep();
index 0d457b5ff36ebd8cf6803ed4e250e6548fb093bc..67e47896cc6e574a8d5844504dd47e4fcc7281ec 100755 (executable)
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2021, Joey Castillo
- * SAML22 starter project is Copyright (c) 2014-2017, Alex Taradov <alex@taradov.com>
+ * UART methods are Copyright (c) 2014-2017, Alex Taradov <alex@taradov.com>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -82,26 +82,35 @@ void uart_puts(char *s) {
     while (*s) uart_putc(*s++);
 }
 
-//-----------------------------------------------------------------------------
-static void sys_init(void) {
-    uart_puts("init_mcu\n");
+int main(void) {
+    // Temporary, for debugging.
+    uart_init(115200);
+
+    // ASF code. Initialize the MCU with configuration options from Atmel Studio.
     init_mcu();
-    uart_puts("watch_init\n");
-    watch_init();
-    uart_puts("app_init\n");
+
+    // User code. Give the app a chance to initialize its data structures and state.
     app_init();
-}
 
-//-----------------------------------------------------------------------------
-int main(void) {
-    uart_init(115200);
-    sys_init();
+    // At this point, if the RTC peripheral is enabled, we are waking from BACKUP.
+    if (watch_rtc_is_enabled()) {
+        // User code. Give the application a chance to restore state from backup registers.
+        app_wake_from_deep_sleep();
+    }
+
+    // Watch library code. Set initial parameters for the device and enable the RTC.
+    watch_init();
+
+    // User code. Give the app a chance to enable and set up peripherals.
+    app_setup();
 
     while (1) {
-        app_loop();
-        app_prepare_for_sleep();
-        sleep(4);
-        app_wake_from_sleep();
+        bool can_sleep = app_loop();
+        if (can_sleep) {
+            app_prepare_for_sleep();
+            sleep(4);
+            app_wake_from_sleep();
+        }
     }
 
     return 0;
index 6ab093bd7ba7a8e3aefd9fd47112279af5ebbd36..82b7467c256c00903048eb3b27021ffd1a5d96e7 100644 (file)
@@ -1,15 +1,8 @@
-/*
- * watch.c
- *
- * Created: 4/25/2021 10:22:10 AM
- *  Author: joeycastillo
- */
-
 #include "watch.h"
 #include <stdlib.h>
 
 void watch_init() {
-    // use switching regulator
+    // Use switching regulator for lower power consumption.
     SUPC->VREG.bit.SEL = 1;
     while(!SUPC->STATUS.bit.VREGRDY);
 
@@ -17,9 +10,8 @@ void watch_init() {
     CALENDAR_0_init();
     calendar_enable(&CALENDAR_0);
 
-    // TODO: use performance level 0?
-//    _set_performance_level(0);
-//    hri_pm_write_PLCFG_PLDIS_bit(PM, true);
+    // Not sure if this belongs in every app -- is there a power impact?
+    delay_driver_init();
 }
 
 static const uint8_t Character_Set[] =
@@ -258,6 +250,10 @@ void watch_set_led_off() {
     }
 }
 
+bool watch_rtc_is_enabled() {
+    return RTC->MODE0.CTRLA.bit.ENABLE;
+}
+
 void watch_set_date_time(struct calendar_date_time date_time) {
     calendar_set_date(&CALENDAR_0, &date_time.date);
     calendar_set_time(&CALENDAR_0, &date_time.time);
@@ -314,7 +310,7 @@ void watch_enable_pull_down(const uint8_t pin) {
     gpio_set_pin_pull_mode(pin, GPIO_PULL_DOWN);
 }
 
-bool watch_get_pin_level(const uint8_t pin, const bool level) {
+bool watch_get_pin_level(const uint8_t pin) {
     return gpio_get_pin_level(pin);
 }
 
@@ -348,3 +344,23 @@ void watch_i2c_receive(int16_t addr, uint8_t *buf, uint16_t length) {
     i2c_m_sync_set_slaveaddr(&I2C_0, addr, I2C_M_SEVEN);
     io_read(I2C_0_io, buf, length);
 }
+
+void watch_store_backup_data(uint32_t data, uint8_t reg) {
+    if (reg < 8) {
+        RTC->MODE0.BKUP[reg].reg = data;
+    }
+}
+
+uint32_t watch_get_backup_data(uint8_t reg) {
+    if (reg < 8) {
+        return RTC->MODE0.BKUP[reg].reg;
+    }
+
+    return 0;
+}
+
+void watch_enter_deep_sleep(){
+    // Not yet implemented.
+    // TODO: enable tamper interrupt on ALARM pin.
+    // sleep(5);
+}
index 0a8f8a38ddeb79d03d5ab358e6360b16cddc7c4c..ece87eedcf4183943d43be72cf186f63b5a7a719 100644 (file)
@@ -1,55 +1,52 @@
-/*\r
- * Watch.h\r
- *\r
- * Created: 4/25/2021 8:29:16 AM\r
- *  Author: joeycastillo\r
- */ \r
-\r
-\r
-#ifndef WATCH_H_\r
-#define WATCH_H_\r
-#include <stdint.h>\r
-#include "driver_init.h"\r
-#include "hpl_calendar.h"\r
-#include "hal_ext_irq.h"\r
-\r
-void watch_init();\r
-\r
-void watch_enable_display();\r
-void watch_display_pixel(uint8_t com, uint8_t seg);\r
-void watch_display_string(char *string, uint8_t position);\r
-\r
-void watch_enable_led(bool pwm);\r
-void watch_disable_led(bool pwm);\r
-void watch_set_led_color(uint16_t red, uint16_t green);\r
-void watch_set_led_red();\r
-void watch_set_led_green();\r
-void watch_set_led_yellow();\r
-void watch_set_led_off();\r
-\r
-void watch_set_date_time(struct calendar_date_time date_time);\r
-void watch_get_date_time(struct calendar_date_time *date_time);\r
-\r
-void watch_enable_tick_callback(ext_irq_cb_t callback);\r
-\r
-void watch_enable_analog(const uint8_t pin);\r
-\r
-void watch_enable_buttons();\r
-void watch_register_button_callback(const uint32_t pin, ext_irq_cb_t callback);\r
-\r
-void watch_enable_digital_input(const uint8_t pin);\r
-void watch_enable_pull_up(const uint8_t pin);\r
-void watch_enable_pull_down(const uint8_t pin);\r
-bool watch_get_pin_level(const uint8_t pin, const bool level);\r
-\r
-void watch_enable_digital_output(const uint8_t pin);\r
-void watch_disable_digital_output(const uint8_t pin);\r
-void watch_set_pin_level(const uint8_t pin, const bool level);\r
-\r
-struct io_descriptor *I2C_0_io;\r
-\r
-void watch_enable_i2c();\r
-void watch_i2c_send(int16_t addr, uint8_t *buf, uint16_t length);\r
-void watch_i2c_receive(int16_t addr, uint8_t *buf, uint16_t length);\r
-\r
+#ifndef WATCH_H_
+#define WATCH_H_
+#include <stdint.h>
+#include "driver_init.h"
+#include "hpl_calendar.h"
+#include "hal_ext_irq.h"
+
+void watch_init();
+
+void watch_enable_display();
+void watch_display_pixel(uint8_t com, uint8_t seg);
+void watch_display_string(char *string, uint8_t position);
+
+void watch_enable_led(bool pwm);
+void watch_disable_led(bool pwm);
+void watch_set_led_color(uint16_t red, uint16_t green);
+void watch_set_led_red();
+void watch_set_led_green();
+void watch_set_led_yellow();
+void watch_set_led_off();
+
+bool watch_rtc_is_enabled();
+void watch_set_date_time(struct calendar_date_time date_time);
+void watch_get_date_time(struct calendar_date_time *date_time);
+
+void watch_enable_tick_callback(ext_irq_cb_t callback);
+
+void watch_enable_analog(const uint8_t pin);
+
+void watch_enable_buttons();
+void watch_register_button_callback(const uint32_t pin, ext_irq_cb_t callback);
+
+void watch_enable_digital_input(const uint8_t pin);
+void watch_enable_pull_up(const uint8_t pin);
+void watch_enable_pull_down(const uint8_t pin);
+bool watch_get_pin_level(const uint8_t pin);
+
+void watch_enable_digital_output(const uint8_t pin);
+void watch_disable_digital_output(const uint8_t pin);
+void watch_set_pin_level(const uint8_t pin, const bool level);
+
+struct io_descriptor *I2C_0_io;
+
+void watch_enable_i2c();
+void watch_i2c_send(int16_t addr, uint8_t *buf, uint16_t length);
+void watch_i2c_receive(int16_t addr, uint8_t *buf, uint16_t length);
+
+void watch_store_backup_data(uint32_t data, uint8_t reg);
+uint32_t watch_get_backup_data(uint8_t reg);
+void watch_enter_deep_sleep();
+
 #endif /* WATCH_H_ */
\ No newline at end of file