From c6af677b79a2c8872d16fd38d092cee91382a88a Mon Sep 17 00:00:00 2001 From: joeycastillo Date: Mon, 18 Nov 2024 23:50:21 -0500 Subject: [PATCH] accel: count orientation changes with TC2 instead of waking up --- movement.c | 67 +++++++++++-------- movement.h | 3 - watch-faces/demo/accel_interrupt_count_face.c | 4 +- watch-faces/sensor/activity_logging_face.c | 7 +- 4 files changed, 46 insertions(+), 35 deletions(-) diff --git a/movement.c b/movement.c index f81105cd..aa08cadc 100644 --- a/movement.c +++ b/movement.c @@ -77,8 +77,7 @@ void cb_tick(void); #ifdef HAS_ACCELEROMETER void cb_accelerometer_event(void); -void cb_accelerometer_sleep_change(void); -uint32_t orientation_changes = 0; +void cb_accelerometer_wake(void); uint8_t stationary_minutes = 0; #endif @@ -657,14 +656,36 @@ void app_setup(void) { // set up interrupts: // INT1 is wired to pin A3. We'll configure the accelerometer to output an interrupt on INT1 when it detects an orientation change. lis2dw_configure_int1(LIS2DW_CTRL4_INT1_6D); - watch_register_interrupt_callback(HAL_GPIO_A3_pin(), cb_accelerometer_event, INTERRUPT_TRIGGER_RISING); + HAL_GPIO_A3_in(); + HAL_GPIO_A3_pmuxen(HAL_GPIO_PMUX_EIC); + eic_configure_pin(HAL_GPIO_A3_pin(), INTERRUPT_TRIGGER_RISING); + // but rather than hooking it up to an interrupt callback, we'll have it trigger an event. + eic_enable_event(HAL_GPIO_A3_pin()); - // INT2 is wired to pin A4. We'll configure the accelerometer to output the sleep state on INT2. - lis2dw_configure_int2(LIS2DW_CTRL5_INT2_SLEEP_STATE | LIS2DW_CTRL5_INT2_SLEEP_CHG); + // that event will increment a counter on TC2. So let's set that up: + if (!tc_is_enabled(2)) { + // TC2 clocked from GCLK3, the 1024 Hz clock. No further division. + tc_init(2, GENERIC_CLOCK_3, TC_PRESCALER_DIV1); + } + // COUNT16 mode, count up to 65535 orientation changes (we will reset before it overflows) + tc_set_counter_mode(2, TC_COUNTER_MODE_16BIT); + // run in standby (we spend most of our time in standby) + tc_set_run_in_standby(2, true); + // finally set the event action to count up when an event is received. + tc_set_event_action(2, TC_EVENT_ACTION_COUNT); + // enable TC2! + tc_enable(2); + + // now configure the event system: + // on channel 0, route the INT3 event generator to the TC2 event user. Run in standby, on the asynchronous path. + evsys_configure_channel(0, EVSYS_ID_GEN_EIC_EXTINT_3, EVSYS_ID_USER_TC2_EVU, true, true); + // this concludes the setup for orientation tracking. + + // next: INT2 is wired to pin A4. We'll configure the accelerometer to output the sleep state on INT2. // a falling edge on INT2 indicates the accelerometer has woken up. - // when configured via the extwake callback, we can only detect rising or falling edges, not both (as we could using the EIC). - // this line configures an extwake calling `cb_accelerometer_sleep_change` when the accelerometer wakes up. - watch_register_extwake_callback(HAL_GPIO_A4_pin(), cb_accelerometer_sleep_change, false); + lis2dw_configure_int2(LIS2DW_CTRL5_INT2_SLEEP_STATE | LIS2DW_CTRL5_INT2_SLEEP_CHG); + HAL_GPIO_A4_in(); + watch_register_extwake_callback(HAL_GPIO_A4_pin(), cb_accelerometer_wake, false); lis2dw_enable_interrupts(); } @@ -937,31 +958,23 @@ void cb_tick(void) { #ifdef HAS_ACCELEROMETER void cb_accelerometer_event(void) { + // This callback is not currently in use! Tap tracking will require using an interrupt on A3 instead of an event. + // I imagine watch faces will request tap tracking in a specific context, and we'll expose a Movement function + // that swaps out the mode for as long as the watch face needs taps. (the sampling rate required for tap tracking + // will to consume a lot more power, so we can't leave it on all the time.) 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++; - printf("Orientation change\n"); - } - // not currently listening for these: 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; } -void cb_accelerometer_sleep_change(void) { - if (HAL_GPIO_A4_read()) { - event.event_type = EVENT_ACCELEROMETER_SLEEP; - printf("Sleep on INT2\n"); - } else { - event.event_type = EVENT_ACCELEROMETER_WAKE; - // reset the stationary minutes counter; we're counting consecutive stationary minutes. - stationary_minutes = 0; - // also: wake up! - _movement_reset_inactivity_countdown(); - printf("Wake on INT2\n"); - } +void cb_accelerometer_wake(void) { + printf("Woke up from accelerometer!\n"); + event.event_type = EVENT_ACCELEROMETER_WAKE; + // reset the stationary minutes counter; we're counting consecutive stationary minutes. + stationary_minutes = 0; + // also: wake up! + _movement_reset_inactivity_countdown(); } #endif diff --git a/movement.h b/movement.h index 9e76dc47..bc957427 100644 --- a/movement.h +++ b/movement.h @@ -145,11 +145,8 @@ typedef enum { 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 { diff --git a/watch-faces/demo/accel_interrupt_count_face.c b/watch-faces/demo/accel_interrupt_count_face.c index d9662457..c82f67c7 100644 --- a/watch-faces/demo/accel_interrupt_count_face.c +++ b/watch-faces/demo/accel_interrupt_count_face.c @@ -33,7 +33,6 @@ // hacky: we're just tapping into Movement's global state. // we should make better API for this. -extern uint32_t orientation_changes; extern uint8_t stationary_minutes; static void _accel_interrupt_count_face_update_display(accel_interrupt_count_state_t *state) { @@ -48,7 +47,8 @@ static void _accel_interrupt_count_face_update_display(accel_interrupt_count_sta else watch_display_text(WATCH_POSITION_TOP_RIGHT, " A"); // Orientation changes / active minutes - sprintf(buf, "%-3lu/%2d", orientation_changes > 999 ? 999 : orientation_changes, stationary_minutes); + uint16_t orientation_changes = tc_count16_get_count(2); + sprintf(buf, "%-3u/%2d", orientation_changes > 999 ? 999 : orientation_changes, stationary_minutes); watch_display_text(WATCH_POSITION_BOTTOM, buf); } diff --git a/watch-faces/sensor/activity_logging_face.c b/watch-faces/sensor/activity_logging_face.c index b0a75a6c..370ba356 100644 --- a/watch-faces/sensor/activity_logging_face.c +++ b/watch-faces/sensor/activity_logging_face.c @@ -27,12 +27,12 @@ #include "activity_logging_face.h" #include "filesystem.h" #include "watch.h" +#include "tc.h" #ifdef HAS_ACCELEROMETER // hacky: we're just tapping into Movement's global state. // we should make better API for this. -extern uint32_t orientation_changes; extern uint8_t stationary_minutes; static void _activity_logging_face_log_data(activity_logging_state_t *state) { @@ -45,7 +45,7 @@ static void _activity_logging_face_log_data(activity_logging_state_t *state) { data_point.bit.hour = date_time.unit.hour; data_point.bit.minute = date_time.unit.minute; data_point.bit.stationary_minutes = stationary_minutes; - data_point.bit.orientation_changes = orientation_changes; + data_point.bit.orientation_changes = tc_count16_get_count(2); // orientation changes are counted in TC2 // print size of thing printf("Size of data point: %d\n", sizeof(activity_logging_data_point_t)); if (filesystem_append_file("activity.dat", (char *)&data_point, sizeof(activity_logging_data_point_t))) { @@ -57,7 +57,8 @@ static void _activity_logging_face_log_data(activity_logging_state_t *state) { state->data[pos].reg = data_point.reg; state->data_points++; - orientation_changes = 0; + // reset the number of orientation changes. + tc_count16_set_count(2, 0); } static void _activity_logging_face_update_display(activity_logging_state_t *state, bool clock_mode_24h) {