From e1b5395e10325d68b1a8d3a5681933c165ffa80e Mon Sep 17 00:00:00 2001 From: joeycastillo Date: Wed, 9 Oct 2024 07:46:28 -0400 Subject: [PATCH] preliminary accelerometer support in Movement --- Makefile | 9 +- movement.c | 52 +++++++++++ movement_config.h | 8 +- watch-faces/demo/accel_interrupt_count_face.c | 86 +++---------------- watch-faces/demo/accel_interrupt_count_face.h | 2 - watch-library/shared/driver/lis2dw.c | 5 ++ watch-library/shared/driver/lis2dw.h | 2 + 7 files changed, 83 insertions(+), 81 deletions(-) diff --git a/Makefile b/Makefile index ec57af06..02be7a12 100644 --- a/Makefile +++ b/Makefile @@ -2,17 +2,24 @@ GOSSAMER_PATH=gossamer # Which board are we building for? -BOARD=sensorwatch_red +BOARD=sensorwatch_pro # Which screen are we building for? DISPLAY=CLASSIC +# Which sensor board? +SENSOR=MOTION + # Support USB features? TINYUSB_CDC=1 # Leave this line here. include $(GOSSAMER_PATH)/make.mk +ifeq ($(SENSOR), MOTION) + DEFINES += -DMOTION_BOARD_INSTALLED +endif + ifdef EMSCRIPTEN all: $(BUILD)/$(BIN).elf $(BUILD)/$(BIN).html $(BUILD)/$(BIN).html: $(OBJS) diff --git a/movement.c b/movement.c index 28aec82f..c1bb9c23 100644 --- a/movement.c +++ b/movement.c @@ -40,6 +40,8 @@ #include "shell.h" #include "utz.h" #include "zones.h" +#include "lis2dw.h" +#include "tc.h" #include "movement_config.h" @@ -72,6 +74,10 @@ void cb_alarm_fired(void); void cb_fast_tick(void); void cb_tick(void); +#ifdef MOTION_BOARD_INSTALLED +void cb_motion_interrupt_1(void); +#endif + #if __EMSCRIPTEN__ void yield(void) { } @@ -600,6 +606,7 @@ void app_setup(void) { alarm_time.unit.second = 59; // after a match, the alarm fires at the next rising edge of CLK_RTC_CNT, so 59 seconds lets us update at :00 watch_rtc_register_alarm_callback(cb_alarm_fired, alarm_time, ALARM_MATCH_SS); } + if (movement_state.le_mode_ticks != -1) { watch_disable_extwake_interrupt(HAL_GPIO_BTN_ALARM_pin()); @@ -608,6 +615,44 @@ void app_setup(void) { watch_register_interrupt_callback(HAL_GPIO_BTN_LIGHT_pin(), cb_light_btn_interrupt, INTERRUPT_TRIGGER_BOTH); watch_register_interrupt_callback(HAL_GPIO_BTN_ALARM_pin(), cb_alarm_btn_interrupt, INTERRUPT_TRIGGER_BOTH); +#ifdef MOTION_BOARD_INSTALLED + watch_enable_i2c(); + if (lis2dw_begin()) { + lis2dw_set_mode(LIS2DW_MODE_LOW_POWER); // select low power (not high performance) + lis2dw_set_low_power_mode(LIS2DW_LP_MODE_1); // lowest power mode, 12-bit, up this if needed + lis2dw_set_low_noise_mode(true); // only marginally raises power consumption + lis2dw_enable_sleep(); // sleep at 1.6Hz, wake to 12.5Hz? + lis2dw_set_range(LIS2DW_CTRL6_VAL_RANGE_2G); // data sheet recommends 2G range + lis2dw_set_data_rate(LIS2DW_DATA_RATE_LOWEST); // 1.6Hz in low power mode + lis2dw_enable_sleep(); // allow acceleromter to sleep and wake on activity + lis2dw_configure_wakeup_threshold(24); // threshold is 1/64th of full scale, so for a FS of ±2G this is 1.5G + lis2dw_configure_6d_threshold(3); // 0-3 is 80, 70, 60, or 50 degrees. 50 is least precise, hopefully most sensitive? + + // 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_RISING); + + // INT2 is on A3 which can increment TC2. + lis2dw_configure_int2(LIS2DW_CTRL5_INT2_SLEEP_CHG); + HAL_GPIO_A3_in(); + HAL_GPIO_A3_pmuxen(HAL_GPIO_PMUX_EIC); + eic_configure_pin(HAL_GPIO_A3_pin(), INTERRUPT_TRIGGER_FALLING); + eic_enable_event(HAL_GPIO_A3_pin()); + EVSYS->USER[EVSYS_ID_USER_TC2_EVU].reg = EVSYS_USER_CHANNEL(0 + 1); + // Set up channel 0: + EVSYS->CHANNEL[0].reg = EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_3) | // External interrupt 3 generates events on channel 0 + EVSYS_CHANNEL_RUNSTDBY | // The channel should run in standby sleep mode... + EVSYS_CHANNEL_PATH_ASYNCHRONOUS; // on the asynchronous path (event drives action directly) + tc_init(2, GENERIC_CLOCK_3, TC_PRESCALER_DIV1); + TC2->COUNT16.EVCTRL.reg = TC_EVCTRL_TCEI | TC_EVCTRL_EVACT_COUNT; // Event 0 should increment the count + tc_set_counter_mode(2, TC_COUNTER_MODE_16BIT); + tc_enable(2); + + lis2dw_enable_interrupts(); + } +#endif + watch_enable_buzzer(); watch_enable_leds(); watch_enable_display(); @@ -871,3 +916,10 @@ void cb_tick(void) { movement_state.subsecond++; } } + +#ifdef MOTION_BOARD_INSTALLED +void cb_motion_interrupt_1(void) { + // TODO: Find out what motion event woke us up. + printf("INT1\n"); +} +#endif diff --git a/movement_config.h b/movement_config.h index 349de1ea..f2abc311 100644 --- a/movement_config.h +++ b/movement_config.h @@ -31,11 +31,11 @@ const watch_face_t watch_faces[] = { clock_face, world_clock_face, sunrise_sunset_face, - beats_face, - advanced_alarm_face, countdown_face, - fast_stopwatch_face, + beats_face, + accel_interrupt_count_face, voltage_face, + temperature_display_face, preferences_face, set_time_face, }; @@ -48,7 +48,7 @@ const watch_face_t watch_faces[] = { * Some folks also like to use this to hide the preferences and time set faces from the normal rotation. * If you don't want any faces to be excluded, set this to 0 and a long Mode press will have no effect. */ -#define MOVEMENT_SECONDARY_FACE_INDEX (0) // (MOVEMENT_NUM_FACES - 2) +#define MOVEMENT_SECONDARY_FACE_INDEX (MOVEMENT_NUM_FACES - 4) /* Custom hourly chime tune. Check movement_custom_signal_tunes.h for options. */ #define SIGNAL_TUNE_DEFAULT diff --git a/watch-faces/demo/accel_interrupt_count_face.c b/watch-faces/demo/accel_interrupt_count_face.c index 420980cf..5714f0eb 100644 --- a/watch-faces/demo/accel_interrupt_count_face.c +++ b/watch-faces/demo/accel_interrupt_count_face.c @@ -26,68 +26,30 @@ #include #include "accel_interrupt_count_face.h" #include "lis2dw.h" +#include "tc.h" #include "watch.h" -// hacky hacky! -uint32_t *ptr_to_count = 0; - -void accel_interrupt_handler(void); -void accel_interrupt_handler(void) { - (*ptr_to_count)++; -} - -void sleep_interrupt_handler(void); -void sleep_interrupt_handler(void) { - if (HAL_GPIO_A3_read()) { - movement_force_led_on(255, 0, 0); - } else { - movement_force_led_on(0, 255, 0); - } -} - static void _accel_interrupt_count_face_update_display(accel_interrupt_count_state_t *state) { + (void) state; char buf[7]; - if (state->running) { - watch_set_indicator(WATCH_INDICATOR_SIGNAL); - } else { - watch_clear_indicator(WATCH_INDICATOR_SIGNAL); - } - // "AC"celerometer "IN"terrupts watch_display_text(WATCH_POSITION_TOP_LEFT, "AC"); watch_display_text(WATCH_POSITION_TOP_RIGHT, "1N"); - snprintf(buf, 7, "%6ld", state->count); + uint16_t count = tc_count16_get_count(2); + snprintf(buf, 7, "%6d", count); watch_display_text(WATCH_POSITION_BOTTOM, buf); printf("%s\n", buf); } -static void _accel_interrupt_count_face_configure_threshold(uint8_t threshold) { - lis2dw_enable_sleep(); - lis2dw_configure_wakeup_threshold(threshold); - // lis2dw_configure_int1(LIS2DW_CTRL4_INT1_WU); - lis2dw_configure_int2(LIS2DW_CTRL5_INT2_SLEEP_CHG); - lis2dw_enable_interrupts(); -} - void accel_interrupt_count_face_setup(uint8_t watch_face_index, void ** context_ptr) { (void) watch_face_index; if (*context_ptr == NULL) { *context_ptr = malloc(sizeof(accel_interrupt_count_state_t)); memset(*context_ptr, 0, sizeof(accel_interrupt_count_state_t)); - ptr_to_count = &((accel_interrupt_count_state_t *)*context_ptr)->count; - watch_enable_i2c(); - lis2dw_begin(); - lis2dw_set_mode(LIS2DW_MODE_LOW_POWER); - lis2dw_set_low_power_mode(LIS2DW_LP_MODE_1); // lowest power mode - lis2dw_set_low_noise_mode(true); // only marginally raises power consumption - lis2dw_enable_sleep(); // sleep at 1.6Hz, wake to 12.5Hz? - lis2dw_set_range(LIS2DW_CTRL6_VAL_RANGE_2G); // data sheet recommends 2G range - lis2dw_set_data_rate(LIS2DW_DATA_RATE_LOWEST); // 1.6Hz in low power mode - - // threshold is 1/64th of full scale, so for a FS of ±2G this is 1.5G - ((accel_interrupt_count_state_t *)*context_ptr)->threshold = 24; - _accel_interrupt_count_face_configure_threshold(((accel_interrupt_count_state_t *)*context_ptr)->threshold); + accel_interrupt_count_state_t *state = (accel_interrupt_count_state_t *)*context_ptr; + /// TODO: hook up to movement methods for tracking threshold + state->threshold = 24; } } @@ -96,13 +58,6 @@ void accel_interrupt_count_face_activate(void *context) { // never in settings mode at the start state->is_setting = false; - - // force LE interval to never sleep - movement_set_low_energy_timeout(0); - - state->running = true; - watch_register_interrupt_callback(HAL_GPIO_A4_pin(), accel_interrupt_handler, INTERRUPT_TRIGGER_RISING); - watch_register_interrupt_callback(HAL_GPIO_A3_pin(), sleep_interrupt_handler, INTERRUPT_TRIGGER_BOTH); } bool accel_interrupt_count_face_loop(movement_event_t event, void *context) { @@ -133,34 +88,17 @@ bool accel_interrupt_count_face_loop(movement_event_t event, void *context) { } } else { switch (event.event_type) { - case EVENT_LIGHT_BUTTON_DOWN: - movement_illuminate_led(); - - // if stopped, reset the count - if (!state->running) { - state->count = 0; - } - _accel_interrupt_count_face_update_display(state); - break; case EVENT_ALARM_BUTTON_UP: - if (state->running) { - state->running = false; - watch_register_interrupt_callback(HAL_GPIO_A4_pin(), NULL, INTERRUPT_TRIGGER_RISING); - } else { - state->running = true; - watch_register_interrupt_callback(HAL_GPIO_A4_pin(), accel_interrupt_handler, INTERRUPT_TRIGGER_RISING); - } - _accel_interrupt_count_face_update_display(state); - break; + // reset the counter + tc_count16_set_count(2, 0); + // fall through case EVENT_ACTIVATE: case EVENT_TICK: _accel_interrupt_count_face_update_display(state); break; case EVENT_ALARM_LONG_PRESS: - if (!state->running) { - state->new_threshold = state->threshold; - state->is_setting = true; - } + state->new_threshold = state->threshold; + state->is_setting = true; return false; default: movement_default_loop_handler(event); diff --git a/watch-faces/demo/accel_interrupt_count_face.h b/watch-faces/demo/accel_interrupt_count_face.h index 29d5df13..3361976c 100644 --- a/watch-faces/demo/accel_interrupt_count_face.h +++ b/watch-faces/demo/accel_interrupt_count_face.h @@ -36,10 +36,8 @@ #include "watch.h" typedef struct { - uint32_t count; uint8_t new_threshold; uint8_t threshold; - bool running; bool is_setting; } accel_interrupt_count_state_t; diff --git a/watch-library/shared/driver/lis2dw.c b/watch-library/shared/driver/lis2dw.c index c5b45793..8376d8ce 100644 --- a/watch-library/shared/driver/lis2dw.c +++ b/watch-library/shared/driver/lis2dw.c @@ -233,6 +233,11 @@ void lis2dw_configure_wakeup_threshold(uint8_t threshold) { watch_i2c_write8(LIS2DW_ADDRESS, LIS2DW_REG_WAKE_UP_THS, configuration | threshold); } +void lis2dw_configure_6d_threshold(uint8_t threshold) { + uint8_t configuration = watch_i2c_read8(LIS2DW_ADDRESS, LIS2DW_REG_TAP_THS_X) & 0b01100000; + watch_i2c_write8(LIS2DW_ADDRESS, LIS2DW_REG_TAP_THS_X, configuration | ((threshold & 0b11) << 5)); +} + void lis2dw_configure_int1(uint8_t sources) { watch_i2c_write8(LIS2DW_ADDRESS, LIS2DW_REG_CTRL4_INT1, sources); } diff --git a/watch-library/shared/driver/lis2dw.h b/watch-library/shared/driver/lis2dw.h index e80236de..6185150f 100644 --- a/watch-library/shared/driver/lis2dw.h +++ b/watch-library/shared/driver/lis2dw.h @@ -345,6 +345,8 @@ void lis2dw_disable_tap_detection(void); void lis2dw_configure_wakeup_threshold(uint8_t threshold); +void lis2dw_configure_6d_threshold(uint8_t threshold); + void lis2dw_configure_int1(uint8_t sources); void lis2dw_configure_int2(uint8_t sources);