]> git.earman.xyz Git - sensor-watch.git/commitdiff
more accelerometer work
authorjoeycastillo <joeycastillo@utexas.edu>
Sat, 19 Oct 2024 16:35:46 +0000 (12:35 -0400)
committerjoeycastillo <joeycastillo@utexas.edu>
Sat, 19 Oct 2024 16:35:46 +0000 (12:35 -0400)
movement.c
movement.h
watch-faces/demo/accel_interrupt_count_face.c
watch-faces/sensor/activity_logging_face.c
watch-faces/sensor/activity_logging_face.h
watch-library/hardware/watch/watch_deepsleep.c

index 917c2881e92a7388157ea85e9d9da2c44654fd6a..f0d08c2f63b59ce8ca938f1550fb2dbb7dfa1c13 100644 (file)
@@ -77,6 +77,8 @@ void cb_tick(void);
 
 #ifdef HAS_ACCELEROMETER
 void cb_motion_interrupt_1(void);
+void cb_motion_interrupt_2(void);
+uint32_t orientation_changes = 0;
 #endif
 
 #if __EMSCRIPTEN__
@@ -638,25 +640,16 @@ void app_setup(void) {
 
             // set up interrupts:
             // INT1 is on A4 which can wake from deep sleep. Wake on 6D orientation change.
-            lis2dw_configure_int1(LIS2DW_CTRL4_INT1_6D);
-            watch_register_interrupt_callback(HAL_GPIO_A4_pin(), cb_motion_interrupt_1, INTERRUPT_TRIGGER_FALLING);
+            lis2dw_configure_int1(LIS2DW_CTRL4_INT1_6D | LIS2DW_CTRL4_INT1_WU | LIS2DW_CTRL4_INT1_TAP | LIS2DW_CTRL4_INT1_SINGLE_TAP);
+            watch_register_extwake_callback(HAL_GPIO_A4_pin(), cb_motion_interrupt_1, true);
 
-            // configure the accelerometer to fire INT2 when its sleep state changes.
+            // configure the accelerometer to output the sleep state on INT2.
             lis2dw_configure_int2(LIS2DW_CTRL5_INT2_SLEEP_STATE | LIS2DW_CTRL5_INT2_SLEEP_CHG);
             // INT2 is wired to pin A3. set it up on the external interrupt controller.
             HAL_GPIO_A3_in();
             HAL_GPIO_A3_pmuxen(HAL_GPIO_PMUX_EIC);
-            eic_configure_pin(HAL_GPIO_A3_pin(), INTERRUPT_TRIGGER_FALLING);
-            // but rather than firing an interrupt, we'll have it generate an event instead.
-            eic_enable_event(HAL_GPIO_A3_pin());
-            // we can route the EXTINT3 event generator to the TC2 event user...
-            evsys_configure_channel(0, EVSYS_ID_GEN_EIC_EXTINT_3, EVSYS_ID_USER_TC2_EVU, true, true);
-            // and use the TC2 event to count the number of times the sleep state changes.
-            // note that this doesn't actually wake the watch — we can maintain this count even in standby.
-            tc_init(2, GENERIC_CLOCK_3, TC_PRESCALER_DIV1);
-            tc_set_event_action(2, TC_EVENT_ACTION_COUNT);
-            tc_set_counter_mode(2, TC_COUNTER_MODE_16BIT);
-            tc_enable(2);
+            eic_configure_pin(HAL_GPIO_A3_pin(), INTERRUPT_TRIGGER_BOTH);
+            watch_register_interrupt_callback(HAL_GPIO_A3_pin(), cb_motion_interrupt_2, INTERRUPT_TRIGGER_BOTH);
 
             lis2dw_enable_interrupts();
         }
@@ -698,6 +691,17 @@ static void _sleep_mode_app_loop(void) {
 bool app_loop(void) {
     const watch_face_t *wf = &watch_faces[movement_state.current_face_idx];
     bool woke_up_for_buzzer = false;
+
+    // REMOVE THIS before shipping the accelerometer board: test beeps.
+    if (movement_state.settings.bit.button_should_sound && event.event_type == EVENT_ACCELEROMETER_WAKE) {
+        watch_buzzer_play_note_with_volume(BUZZER_NOTE_C6, 20, WATCH_BUZZER_VOLUME_SOFT);
+    }
+    if (movement_state.settings.bit.button_should_sound && event.event_type == EVENT_ACCELEROMETER_SLEEP) {
+        watch_buzzer_play_note_with_volume(BUZZER_NOTE_C5, 15, WATCH_BUZZER_VOLUME_SOFT);
+        watch_buzzer_play_note_with_volume(BUZZER_NOTE_REST, 10, WATCH_BUZZER_VOLUME_SOFT);
+        watch_buzzer_play_note_with_volume(BUZZER_NOTE_C5, 15, WATCH_BUZZER_VOLUME_SOFT);
+    }
+
     if (movement_state.watch_face_changed) {
         if (movement_state.settings.bit.button_should_sound) {
             // low note for nonzero case, high note for return to watch_face 0
@@ -928,7 +932,35 @@ void cb_tick(void) {
 
 #ifdef HAS_ACCELEROMETER
 void cb_motion_interrupt_1(void) {
-    // TODO: Find out what motion event woke us up.
-    printf("INT1\n");
+    uint8_t int_src = lis2dw_get_interrupt_source();
+    if (int_src & LIS2DW_REG_ALL_INT_SRC_6D_IA) {
+        event.event_type = EVENT_ORIENTATION_CHANGE;
+        orientation_changes++;
+    }
+    if (int_src & LIS2DW_REG_ALL_INT_SRC_DOUBLE_TAP) event.event_type = EVENT_DOUBLE_TAP;
+    if (int_src & LIS2DW_REG_ALL_INT_SRC_SINGLE_TAP) event.event_type = EVENT_SINGLE_TAP;
+    if (int_src & LIS2DW_REG_ALL_INT_SRC_FF_IA) event.event_type = EVENT_FREE_FALL;
+
+    // These are handled on INT2, which is not available in low energy mode.
+    // If we want wakeup events on INT1, we could ask for LIS2DW_CTRL4_INT1_WU and get wake events here.
+    // If we want sleep change events on INT1, we'd have to set LIS2DW_CTRL5_INT2_SLEEP_CHG and LIS2DW_CTRL7_VAL_INT2_ON_INT1
+    // That would give us these two cases:
+    // if (int_src & LIS2DW_REG_ALL_INT_SRC_WU_IA) printf(" Wake up");
+    // if (int_src & LIS2DW_REG_ALL_INT_SRC_SLEEP_CHANGE_IA) printf(" Sleep change");
+}
+
+void cb_motion_interrupt_2(void) {
+    if (HAL_GPIO_A3_read()) {
+        event.event_type = EVENT_ACCELEROMETER_SLEEP;
+        printf("Sleep on INT2\n");
+    } else {
+        event.event_type = EVENT_ACCELEROMETER_WAKE;
+        printf("Wake on INT2\n");
+        // Not sure if it's useful to know what axis exceeded the threshold, but here's that:
+        // uint8_t int_src = lis2dw_get_wakeup_source();
+        // if (int_src & LIS2DW_WAKE_UP_SRC_VAL_X_WU) printf("Wake on X");
+        // if (int_src & LIS2DW_WAKE_UP_SRC_VAL_Y_WU) printf("Wake on Y");
+        // if (int_src & LIS2DW_WAKE_UP_SRC_VAL_Z_WU) printf("Wake on Z");
+    }
 }
 #endif
index 879ec3b75ccc31e63bef6fae3cf6f1f4293f9e6f..9e76dc471378a72af2403f1abc3feba285b3ed6b 100644 (file)
@@ -143,6 +143,13 @@ typedef enum {
     EVENT_ALARM_BUTTON_UP,      // The alarm button was pressed for less than half a second, and released.
     EVENT_ALARM_LONG_PRESS,     // The alarm button was held for over half a second, but not yet released.
     EVENT_ALARM_LONG_UP,        // The alarm button was held for over half a second, and released.
+
+    EVENT_ACCELEROMETER_WAKE,   // The accelerometer has detected motion and woken up.
+    EVENT_ACCELEROMETER_SLEEP,  // The accelerometer has returned to sleep.
+    EVENT_ORIENTATION_CHANGE,   // The orientation of the watch has changed. Available in low energy mode.
+    EVENT_SINGLE_TAP,           // Accelerometer detected a single tap. This event is not yet implemented.
+    EVENT_DOUBLE_TAP,           // Accelerometer detected a double tap. This event is not yet implemented.
+    EVENT_FREE_FALL,            // Accelerometer detected the watch in free fall. This event is not yet implemented.
 } movement_event_type_t;
 
 typedef struct {
index 5445a85a14044d1350d4720066930b0333743ebe..3417282c695947c318767ab699ad8391a1771031 100644 (file)
 
 #ifdef HAS_ACCELEROMETER
 
+// hacky: we're just tapping into Movement's orientation changes.
+// we should make better API for this.
+extern uint32_t orientation_changes;
+
 static void _accel_interrupt_count_face_update_display(accel_interrupt_count_state_t *state) {
     (void) state;
     char buf[7];
@@ -38,8 +42,7 @@ static void _accel_interrupt_count_face_update_display(accel_interrupt_count_sta
     // "AC"celerometer "IN"terrupts
     watch_display_text(WATCH_POSITION_TOP_LEFT, "AC");
     watch_display_text(WATCH_POSITION_TOP_RIGHT, "1N");
-    uint16_t count = tc_count16_get_count(2);
-    snprintf(buf, 7, "%6d", count);
+    snprintf(buf, 7, "%6lu", orientation_changes);
     watch_display_text(WATCH_POSITION_BOTTOM, buf);
     printf("%s\n", buf);
 }
@@ -90,10 +93,6 @@ bool accel_interrupt_count_face_loop(movement_event_t event, void *context) {
         }
     } else {
         switch (event.event_type) {
-            case EVENT_ALARM_BUTTON_UP:
-                // reset the counter
-                tc_count16_set_count(2, 0);
-                // fall through
             case EVENT_ACTIVATE:
             case EVENT_TICK:
                 _accel_interrupt_count_face_update_display(state);
index b0411c2ef0879f05de6fe684d9c39557ac9c0e1e..a4fcab7f11156ea915bd02ca5b8bd99e9889212a 100644 (file)
 
 #ifdef HAS_ACCELEROMETER
 
+// hacky: we're just tapping into Movement's orientation changes.
+// we should make better API for this.
+extern uint32_t orientation_changes;
+
 static void _activity_logging_face_log_data(activity_logging_state_t *state) {
     watch_date_time_t date_time = watch_rtc_get_date_time();
     size_t pos = state->data_points % ACTIVITY_LOGGING_NUM_DATA_POINTS;
 
     state->data[pos].timestamp.reg = date_time.reg;
     state->data[pos].active_minutes = state->active_minutes;
+    state->data[pos].orientation_changes = orientation_changes;
     state->active_minutes = 0;    
+    orientation_changes = 0;
 
     state->data_points++;
 }
 
 static void _activity_logging_face_update_display(activity_logging_state_t *state, bool clock_mode_24h) {
     int8_t pos = (state->data_points - 1 - state->display_index) % ACTIVITY_LOGGING_NUM_DATA_POINTS;
-    char buf[7];
+    char buf[16];
 
     watch_clear_indicator(WATCH_INDICATOR_24H);
     watch_clear_indicator(WATCH_INDICATOR_PM);
@@ -71,11 +77,11 @@ static void _activity_logging_face_update_display(activity_logging_state_t *stat
         sprintf(buf, "%2d%02d%02d", date_time.unit.hour, date_time.unit.minute, date_time.unit.second);
         watch_display_text(WATCH_POSITION_BOTTOM, buf);
     } else {
-        // we are displaying the number of accelerometer wakeups
-        watch_display_text_with_fallback(WATCH_POSITION_TOP, "ACT L", "AC");
+        // we are displaying the number of accelerometer wakeups and orientation changes
+        watch_display_text(WATCH_POSITION_TOP, "WO");
         sprintf(buf, "%2d", state->display_index);
         watch_display_text(WATCH_POSITION_TOP_RIGHT, buf);
-        sprintf(buf, "%d", state->data[pos].active_minutes);
+        sprintf(buf, "%2d=%lu", state->data[pos].active_minutes, state->data[pos].orientation_changes);
         watch_display_text(WATCH_POSITION_BOTTOM, buf);
     }
 }
@@ -139,7 +145,7 @@ movement_watch_face_advisory_t activity_logging_face_advise(void *context) {
     activity_logging_state_t *state = (activity_logging_state_t *)context;
     movement_watch_face_advisory_t retval = { 0 };
 
-    // every minute, we want to log whether the accelerometer is alseeep or awake.
+    // every minute, we want to log whether the accelerometer is asleep or awake.
     if (!HAL_GPIO_A3_read()) {
         state->active_minutes++;
     }
index 2d73c4bd662fb28594506042a5943715445fac0c..39747a3188ace5994896979d4351b6ae446f0f93 100644 (file)
@@ -44,6 +44,7 @@
 typedef struct {
     watch_date_time_t timestamp;
     uint8_t active_minutes;
+    uint32_t orientation_changes;
 } activity_logging_data_point_t;
 
 typedef struct {
index 94d6548801d91350094b22c791fee0d2c431e6de..1143850fb641cd3838fa0879f8444cdf623769da 100644 (file)
@@ -133,6 +133,10 @@ static void _watch_disable_all_pins_except_rtc(void) {
     if (config & RTC_TAMPCTRL_IN0ACT_Msk) portb_pins_to_disable &= 0xFFFFFFFE;
     // same with RTC/IN[1] and PB02
     if (config & RTC_TAMPCTRL_IN1ACT_Msk) portb_pins_to_disable &= 0xFFFFFFFB;
+#ifdef HAS_ACCELEROMETER
+    // if we're using the Motion board, keep A3 configured (it tracks the accelerometer sleep state)
+    portb_pins_to_disable &= 0xFFFFFFF7;
+#endif
 
     // port A: that last B is to always keep PA02 configured as-is; that's our ALARM button.
     PORT->Group[0].DIRCLR.reg = 0xFFFFFFFB;