power down accelerometer unless a watch face requests it

This commit is contained in:
Joey Castillo 2025-05-26 10:51:11 -04:00
parent 6ac2c13098
commit 69b8048790
5 changed files with 48 additions and 4 deletions

View File

@ -40,7 +40,6 @@
#include "shell.h"
#include "utz.h"
#include "zones.h"
#include "lis2dw.h"
#include "tc.h"
#include "evsys.h"
#include "delay.h"
@ -539,7 +538,7 @@ bool movement_disable_tap_detection_if_available(void) {
if (movement_state.has_lis2dw) {
// Ramp data rate back down to the usual lowest rate to save power.
lis2dw_set_low_noise_mode(false);
lis2dw_set_data_rate(LIS2DW_DATA_RATE_LOWEST);
lis2dw_set_data_rate(movement_state.accelerometer_background_rate);
lis2dw_set_mode(LIS2DW_MODE_LOW_POWER);
// ...disable Z axis (not sure if this is needed, does this save power?)...
lis2dw_configure_tap_threshold(0, 0, 0, 0);
@ -550,6 +549,24 @@ bool movement_disable_tap_detection_if_available(void) {
return false;
}
lis2dw_data_rate_t movement_get_accelerometer_background_rate(void) {
if (movement_state.has_lis2dw) return movement_state.accelerometer_background_rate;
else return LIS2DW_DATA_RATE_POWERDOWN;
}
bool movement_set_accelerometer_background_rate(lis2dw_data_rate_t new_rate) {
if (movement_state.has_lis2dw) {
if (movement_state.accelerometer_background_rate != new_rate) {
lis2dw_set_data_rate(new_rate);
movement_state.accelerometer_background_rate = new_rate;
return true;
}
}
return false;
}
float movement_get_temperature(void) {
float temperature_c = (float)0xFFFFFFFF;
@ -702,7 +719,6 @@ void app_setup(void) {
lis2dw_set_mode(LIS2DW_MODE_LOW_POWER); // select low power (not high performance) mode
lis2dw_set_low_power_mode(LIS2DW_LP_MODE_1); // lowest power mode, 12-bit
lis2dw_set_low_noise_mode(false); // low noise mode raises power consumption slightly; we don't need it
lis2dw_set_data_rate(LIS2DW_DATA_RATE_LOWEST); // sample at 1.6 Hz, lowest rate available
lis2dw_enable_stationary_motion_detection(); // stationary/motion detection mode keeps the data rate at 1.6 Hz even in sleep
lis2dw_set_range(LIS2DW_RANGE_2_G); // Application note AN5038 recommends 2g range
lis2dw_enable_sleep(); // allow acceleromter to sleep and wake on activity
@ -730,7 +746,14 @@ void app_setup(void) {
// but it will only fire once tap recognition is enabled.
watch_register_interrupt_callback(HAL_GPIO_A3_pin(), cb_accelerometer_event, INTERRUPT_TRIGGER_RISING);
// Enable the interrupts...
lis2dw_enable_interrupts();
// ...and power down the accelerometer to save energy. This means the interrupts we just configured won't fire.
// Tap detection will ramp up sesing and make use of the A3 interrupt.
// If a watch face wants to check in on the A4 pin, it can call movement_set_accelerometer_background_rate
lis2dw_set_data_rate(LIS2DW_DATA_RATE_POWERDOWN);
movement_state.accelerometer_background_rate = LIS2DW_DATA_RATE_POWERDOWN;
} else {
watch_disable_i2c();
}

View File

@ -28,6 +28,7 @@
#include <stdbool.h>
#include "watch.h"
#include "utz.h"
#include "lis2dw.h"
/// @brief A struct that allows a watch face to report its state back to Movement.
typedef struct {
@ -291,6 +292,8 @@ typedef struct {
// boolean set if accelerometer is detected
bool has_lis2dw;
// data rate for background accelerometer sensing
lis2dw_data_rate_t accelerometer_background_rate;
} movement_state_t;
void movement_move_to_face(uint8_t watch_face_index);
@ -373,6 +376,10 @@ void movement_set_alarm_enabled(bool value);
bool movement_enable_tap_detection_if_available(void);
bool movement_disable_tap_detection_if_available(void);
// gets and sets the accelerometer data rate in the background
lis2dw_data_rate_t movement_get_accelerometer_background_rate(void);
bool movement_set_accelerometer_background_rate(lis2dw_data_rate_t new_rate);
// If the board has a temperature sensor, this function will give you the temperature in degrees celsius.
// If the board has multiple temperature sensors, it will use the most accurate one available.
// If the board has no temperature sensors, it will return 0xFFFFFFFF.

View File

@ -54,6 +54,13 @@ void accelerometer_status_face_setup(uint8_t watch_face_index, void ** context_p
void accelerometer_status_face_activate(void *context) {
accel_interrupt_count_state_t *state = (accel_interrupt_count_state_t *)context;
// get the current data rate from Movement
state->old_rate = movement_get_accelerometer_background_rate();
if (state->old_rate == LIS2DW_DATA_RATE_POWERDOWN) {
// if accelerometer was powered down, power it up at the lowest possible rate
movement_set_accelerometer_background_rate(LIS2DW_DATA_RATE_LOWEST);
}
// never in settings mode at the start
state->is_setting = false;
@ -126,5 +133,9 @@ bool accelerometer_status_face_loop(movement_event_t event, void *context) {
}
void accelerometer_status_face_resign(void *context) {
(void) context;
accel_interrupt_count_state_t *state = (accel_interrupt_count_state_t *)context;
// restore old data rate. This only does something if old_rate was POWERDOWN.
// If the old rate was anything other than POWERDOWN, we didn't change anything and this will be a no-op.
movement_set_accelerometer_background_rate(state->old_rate);
}

View File

@ -38,6 +38,7 @@
typedef struct {
uint8_t new_threshold;
uint8_t threshold;
lis2dw_data_rate_t old_rate;
bool is_setting;
} accel_interrupt_count_state_t;

View File

@ -73,6 +73,8 @@ void activity_logging_face_setup(uint8_t watch_face_index, void ** context_ptr)
if (*context_ptr == NULL) {
*context_ptr = malloc(sizeof(activity_logging_state_t));
memset(*context_ptr, 0, sizeof(activity_logging_state_t));
// At first run, tell Movement to run the accelerometer in the background. It will now run at this rate forever.
movement_set_accelerometer_background_rate(LIS2DW_DATA_RATE_LOWEST);
}
}