Skip to content

Commit

Permalink
accelrometer: add tap event, test by adding tap to set countdown
Browse files Browse the repository at this point in the history
  • Loading branch information
joeycastillo committed Nov 27, 2024
1 parent c2b800b commit 4b5e15c
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 8 deletions.
56 changes: 49 additions & 7 deletions movement.c
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,47 @@ void movement_set_alarm_enabled(bool value) {
movement_state.settings.bit.alarm_enabled = value;
}

void movement_enable_tap_detection_if_available(void) {
#ifdef HAS_ACCELEROMETER
// disable event on INT1/A3 (normally tracks orientation changes)
eic_disable_event(HAL_GPIO_A3_pin());

// configure tap duration threshold and enable Z axis
lis2dw_configure_tap_threshold(0, 0, 12, LIS2DW_REG_TAP_THS_Z_Z_AXIS_ENABLE);
lis2dw_configure_tap_duration(10, 2, 2);

// ramp data rate up to 400 Hz and high performance mode
lis2dw_set_low_noise_mode(true);
lis2dw_set_data_rate(LIS2DW_DATA_RATE_HP_400_HZ);
lis2dw_set_mode(LIS2DW_MODE_HIGH_PERFORMANCE);

// Settling time (1 sample duration, i.e. 1/400Hz)
delay_ms(3);

// enable tap detection on INT1/A3.
lis2dw_configure_int1(LIS2DW_CTRL4_INT1_SINGLE_TAP | LIS2DW_CTRL4_INT1_6D);
// and enable the cb_accelerometer_event interrupt callback, so we can catch tap events.
watch_register_interrupt_callback(HAL_GPIO_A3_pin(), cb_accelerometer_event, INTERRUPT_TRIGGER_RISING);
#endif
}

void movement_disable_tap_detection_if_available(void) {
#ifdef HAS_ACCELEROMETER
// 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_mode(LIS2DW_MODE_LOW_POWER);
// disable the interrupt on INT1/A3...
eic_disable_interrupt(HAL_GPIO_A3_pin());
// ...disable Z axis (not sure if this is needed, does this save power?)...
lis2dw_configure_tap_threshold(0, 0, 0, 0);
// ...re-enable tracking of orientation changes...
lis2dw_configure_int1(LIS2DW_CTRL4_INT1_6D);
// ...and re-enable the event.
eic_enable_event(HAL_GPIO_A3_pin());
#endif
}

void app_init(void) {
_watch_init();

Expand Down Expand Up @@ -958,18 +999,19 @@ 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_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_DOUBLE_TAP) {
event.event_type = EVENT_DOUBLE_TAP;
printf("Double tap!\n");
}
if (int_src & LIS2DW_REG_ALL_INT_SRC_SINGLE_TAP) {
event.event_type = EVENT_SINGLE_TAP;
printf("Single tap!\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;
Expand Down
4 changes: 4 additions & 0 deletions movement.h
Original file line number Diff line number Diff line change
Expand Up @@ -375,3 +375,7 @@ void movement_store_settings(void);
/// Worth considering a better way to handle this.
bool movement_alarm_enabled(void);
void movement_set_alarm_enabled(bool value);

// if the board has an accelerometer, these functions will enable or disable tap detection.
void movement_enable_tap_detection_if_available(void);
void movement_disable_tap_detection_if_available(void);
48 changes: 47 additions & 1 deletion watch-faces/complication/countdown_face.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

#define CD_SELECTIONS 3
#define DEFAULT_MINUTES 3
#define TAP_DETECTION_SECONDS 5

static bool quick_ticks_running;

Expand All @@ -45,6 +46,11 @@ static void abort_quick_ticks(countdown_state_t *state) {
}
}

static void abort_tap_detection(countdown_state_t *state) {
state->tap_detection_ticks = 0;
movement_disable_tap_detection_if_available();
}

static inline void store_countdown(countdown_state_t *state) {
/* Store set countdown time */
state->set_hours = state->hours;
Expand Down Expand Up @@ -132,7 +138,14 @@ static void draw(countdown_state_t *state, uint8_t subsecond) {
}
break;
}

watch_display_text(WATCH_POSITION_BOTTOM, buf);

if (state->tap_detection_ticks) {
watch_set_indicator(WATCH_INDICATOR_SIGNAL);
} else {
watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
}
}

static void pause(countdown_state_t *state) {
Expand Down Expand Up @@ -206,6 +219,13 @@ void countdown_face_activate(void *context) {

movement_request_tick_frequency(1);
quick_ticks_running = false;
#if HAS_ACCELEROMETER
if (state->mode != cd_running) {
state->tap_detection_ticks = TAP_DETECTION_SECONDS;
state->has_tapped_once = false;
movement_enable_tap_detection_if_available();
}
#endif
}

bool countdown_face_loop(movement_event_t event, void *context) {
Expand All @@ -227,6 +247,12 @@ bool countdown_face_loop(movement_event_t event, void *context) {
if (state->mode == cd_running) {
state->now_ts++;
}

if (state->tap_detection_ticks > 0) {
state->tap_detection_ticks--;
if (state->tap_detection_ticks == 0) movement_disable_tap_detection_if_available();
}

draw(state, event.subsecond);
break;
case EVENT_MODE_BUTTON_UP:
Expand Down Expand Up @@ -264,8 +290,9 @@ bool countdown_face_loop(movement_event_t event, void *context) {
break;
case cd_reset:
case cd_paused:
// Only start the timer if we have a valid time.
if (!(state->hours == 0 && state->minutes == 0 && state->seconds == 0)) {
// Only start the timer if we have a valid time.
abort_tap_detection(state);
start(state);
button_beep();
watch_set_indicator(WATCH_INDICATOR_SIGNAL);
Expand All @@ -281,6 +308,7 @@ bool countdown_face_loop(movement_event_t event, void *context) {
switch(state->mode) {
case cd_reset:
// long press in reset mode enters settings
abort_tap_detection(state);
state->mode = cd_setting;
movement_request_tick_frequency(4);
button_beep();
Expand Down Expand Up @@ -338,6 +366,21 @@ bool countdown_face_loop(movement_event_t event, void *context) {
case EVENT_LIGHT_BUTTON_DOWN:
// intentionally squelch the light default event; we only show the light when cd is running or reset
break;
case EVENT_SINGLE_TAP:
if (state->has_tapped_once == false) {
// on first tap, set the countdown to 1 minute
state->has_tapped_once = true;
state->hours = 0;
state->minutes = 1;
state->seconds = 0;
} else {
// on subsequent taps, increment the countdown by 1 minute, up to 59 taps
state->minutes = state->minutes < 59 ? state->minutes + 1 : state->minutes;
}
// reset the tap detection timer
state->tap_detection_ticks = TAP_DETECTION_SECONDS;
draw(state, event.subsecond);
break;
default:
movement_default_loop_handler(event);
break;
Expand All @@ -353,4 +396,7 @@ void countdown_face_resign(void *context) {
state->mode = cd_reset;
store_countdown(state);
}

// return accelerometer to the state it was in before
abort_tap_detection(state);
}
2 changes: 2 additions & 0 deletions watch-faces/complication/countdown_face.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ typedef struct {
uint8_t set_minutes;
uint8_t set_seconds;
uint8_t selection;
uint8_t tap_detection_ticks;
bool has_tapped_once;
countdown_mode_t mode;
bool repeat;
uint8_t watch_face_index;
Expand Down
18 changes: 18 additions & 0 deletions watch-library/shared/driver/lis2dw.c
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,24 @@ void lis2dw_configure_6d_threshold(uint8_t threshold) {
watch_i2c_write8(LIS2DW_ADDRESS, LIS2DW_REG_TAP_THS_X, configuration | ((threshold & 0b11) << 5));
}

void lis2dw_configure_tap_threshold(uint8_t threshold_x, uint8_t threshold_y, uint8_t threshold_z, uint8_t axes_to_enable) {
uint8_t configuration;
// if (axes_to_enable & LIS2DW_REG_TAP_THS_Z_X_AXIS_ENABLE); // X axis tap not implemented
// if (axes_to_enable & LIS2DW_REG_TAP_THS_Z_Y_AXIS_ENABLE); // Y axis tap not implemented
// tap enable bitmask is the high bits of LIS2DW_REG_TAP_THS_Z
configuration = axes_to_enable & 0b00100000; // NOTE: should be 0b11100000 to allow use of all three axes, but we're not using X or Y.
if (axes_to_enable & LIS2DW_REG_TAP_THS_Z_Z_AXIS_ENABLE) {
// mask out high bits if set
configuration |= (threshold_z & 0b00011111);
}
watch_i2c_write8(LIS2DW_ADDRESS, LIS2DW_REG_TAP_THS_Z, configuration);
}

void lis2dw_configure_tap_duration(uint8_t latency, uint8_t quiet, uint8_t shock) {
uint8_t configuration = (latency << 4) | ((quiet & 0b11) << 2) | (shock & 0b11);
watch_i2c_write8(LIS2DW_ADDRESS, LIS2DW_REG_INT1_DUR, configuration);
}

void lis2dw_configure_int1(uint8_t sources) {
watch_i2c_write8(LIS2DW_ADDRESS, LIS2DW_REG_CTRL4_INT1, sources);
}
Expand Down
10 changes: 10 additions & 0 deletions watch-library/shared/driver/lis2dw.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,14 @@ typedef enum {
#define LIS2DW_FIFO_SAMPLE_COUNT (0b00111111)

#define LIS2DW_REG_TAP_THS_X 0x30

#define LIS2DW_REG_TAP_THS_Y 0x31

#define LIS2DW_REG_TAP_THS_Z 0x32
#define LIS2DW_REG_TAP_THS_Z_X_AXIS_ENABLE 0b10000000
#define LIS2DW_REG_TAP_THS_Z_Y_AXIS_ENABLE 0b01000000
#define LIS2DW_REG_TAP_THS_Z_Z_AXIS_ENABLE 0b00100000

#define LIS2DW_REG_INT1_DUR 0x33

#define LIS2DW_REG_WAKE_UP_THS 0x34
Expand Down Expand Up @@ -353,6 +359,10 @@ void lis2dw_configure_wakeup_threshold(uint8_t threshold);

void lis2dw_configure_6d_threshold(uint8_t threshold);

void lis2dw_configure_tap_threshold(uint8_t threshold_x, uint8_t threshold_y, uint8_t threshold_z, uint8_t axes_to_enable);

void lis2dw_configure_tap_duration(uint8_t latency, uint8_t quiet, uint8_t shock);

void lis2dw_configure_int1(uint8_t sources);

void lis2dw_configure_int2(uint8_t sources);
Expand Down

0 comments on commit 4b5e15c

Please sign in to comment.