From 4cd86bff5356fa5fcb1f87a6310c23f02b6b2f30 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Sat, 19 Feb 2022 20:53:35 -0500 Subject: working data acquisition face! --- .../sensor/accelerometer_data_acquisition_face.c | 221 +++++++++++++-------- .../sensor/accelerometer_data_acquisition_face.h | 34 ++-- 2 files changed, 157 insertions(+), 98 deletions(-) (limited to 'movement/watch_faces') diff --git a/movement/watch_faces/sensor/accelerometer_data_acquisition_face.c b/movement/watch_faces/sensor/accelerometer_data_acquisition_face.c index b6199029..4fb75aa9 100644 --- a/movement/watch_faces/sensor/accelerometer_data_acquisition_face.c +++ b/movement/watch_faces/sensor/accelerometer_data_acquisition_face.c @@ -27,14 +27,16 @@ #include "accelerometer_data_acquisition_face.h" #include "watch_utility.h" #include "lis2dw.h" +#include "spiflash.h" #define ACCELEROMETER_RANGE LIS2DW_RANGE_4_G -#define ACCELEROMETER_LPMODE LIS2DW_LP_MODE_1 +#define ACCELEROMETER_LPMODE LIS2DW_LP_MODE_2 #define ACCELEROMETER_FILTER LIS2DW_BANDWIDTH_FILTER_DIV2 #define ACCELEROMETER_LOW_NOISE true #define SECONDS_TO_RECORD 15 static const char activity_types[][3] = { + "TE", // Testing "ID", // Idle "OF", // Off-wrist "SL", // Sleeping @@ -49,7 +51,6 @@ static const char activity_types[][3] = { "SU", // Stairs Up "SD", // Stairs Down "WL", // Weight Lifting - "TE", // Testing }; static void update(accelerometer_data_acquisition_state_t *state); @@ -58,23 +59,43 @@ static void advance_current_setting(accelerometer_data_acquisition_state_t *stat static void start_reading(accelerometer_data_acquisition_state_t *state, movement_settings_t *settings); static void continue_reading(accelerometer_data_acquisition_state_t *state); static void finish_reading(accelerometer_data_acquisition_state_t *state); +static bool wait_for_flash_ready(void); +static int16_t get_next_available_page(void); +static void write_buffer_to_page(uint8_t *buf, uint16_t page); +static void write_page(accelerometer_data_acquisition_state_t *state); +static void log_data_point(accelerometer_data_acquisition_state_t *state, lis2dw_reading_t reading, uint8_t centiseconds); void accelerometer_data_acquisition_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { (void) settings; (void) watch_face_index; + accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)*context_ptr; if (*context_ptr == NULL) { *context_ptr = malloc(sizeof(accelerometer_data_acquisition_state_t)); memset(*context_ptr, 0, sizeof(accelerometer_data_acquisition_state_t)); - accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)*context_ptr; + state = (accelerometer_data_acquisition_state_t *)*context_ptr; state->beep_with_countdown = true; state->countdown_length = 3; } + spi_flash_init(); + wait_for_flash_ready(); + uint8_t buf[256] = {0xFF}; + spi_flash_read_data(0, buf, 256); + if (buf[0] & 0xF0) { + // mark first four pages as used + buf[0] = 0x0F; + wait_for_flash_ready(); + watch_set_pin_level(A3, false); + spi_flash_command(CMD_ENABLE_WRITE); + wait_for_flash_ready(); + spi_flash_write_data(0, buf, 256); + } + } void accelerometer_data_acquisition_face_activate(movement_settings_t *settings, void *context) { (void) settings; accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)context; - state->next_available_page = 123; + state->next_available_page = get_next_available_page(); } bool accelerometer_data_acquisition_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { @@ -91,6 +112,7 @@ bool accelerometer_data_acquisition_face_loop(movement_event_t event, movement_s case ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN: if (state->countdown_ticks > 0) { state->countdown_ticks--; + printf("countdown: %d\n", state->countdown_ticks); if (state->countdown_ticks == 0) { // at zero, begin reading state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_SENSING; @@ -102,6 +124,16 @@ bool accelerometer_data_acquisition_face_loop(movement_event_t event, movement_s // beep for last two ticks before reading if (state->beep_with_countdown) watch_buzzer_play_note(BUZZER_NOTE_C5, 75); } + if (state->countdown_ticks == 1) { + watch_enable_i2c(); + lis2dw_begin(); + lis2dw_set_data_rate(LIS2DW_DATA_RATE_25_HZ); + lis2dw_set_range(ACCELEROMETER_RANGE); + lis2dw_set_low_power_mode(ACCELEROMETER_LPMODE); + lis2dw_set_bandwidth_filtering(ACCELEROMETER_FILTER); + if (ACCELEROMETER_LOW_NOISE) lis2dw_set_low_noise_mode(true); + lis2dw_enable_fifo(); + } } update(state); break; @@ -187,8 +219,6 @@ bool accelerometer_data_acquisition_face_loop(movement_event_t event, movement_s break; } - // return true if the watch can enter standby mode. If you are PWM'ing an LED or buzzing the buzzer here, - // you should return false since the PWM driver does not operate in standby mode. return true; } @@ -231,8 +261,9 @@ static void update(accelerometer_data_acquisition_state_t *state) { watch_set_colon(); - // special case: display full if full + // special case: display full if full, <1% if nearly full if (state->next_available_page < 0) watch_display_string(" FUL", 6); + else if (state->next_available_page > 8110) watch_display_string("<1", 6); // Bell if beep enabled if (state->beep_with_countdown) watch_set_indicator(WATCH_INDICATOR_BELL); @@ -249,7 +280,6 @@ static void update(accelerometer_data_acquisition_state_t *state) { } static void update_settings(accelerometer_data_acquisition_state_t *state) { - printf("TODO: Settings screen\n"); char buf[12]; watch_clear_colon(); if (state->beep_with_countdown) watch_set_indicator(WATCH_INDICATOR_BELL); @@ -287,112 +317,141 @@ static void advance_current_setting(accelerometer_data_acquisition_state_t *stat } } -bool deleteme = false; +static int16_t get_next_available_page(void) { + uint8_t buf[256] = {0}; + + uint16_t page = 0; + for(int16_t i = 0; i < 4; i++) { + wait_for_flash_ready(); + spi_flash_read_data(i * 256, buf, 256); + for(int16_t j = 0; j < 256; j++) { + if(buf[j] == 0) { + page += 8; + } else { + page += __builtin_clz(((uint32_t)buf[j]) << 24); + break; + } + } + } -static void start_reading(accelerometer_data_acquisition_state_t *state, movement_settings_t *settings) { - (void) state; - watch_enable_i2c(); - lis2dw_begin(); - lis2dw_set_data_rate(LIS2DW_DATA_RATE_25_HZ); - lis2dw_set_range(ACCELEROMETER_RANGE); - lis2dw_set_low_power_mode(ACCELEROMETER_LPMODE); - lis2dw_set_bandwidth_filtering(ACCELEROMETER_FILTER); - if (ACCELEROMETER_LOW_NOISE) lis2dw_set_low_noise_mode(true); - lis2dw_enable_fifo(); + if (page >= 8192) return -1; - accelerometer_data_acquisition_record_t record; - watch_date_time date_time = watch_rtc_get_date_time(); - state->starting_timestamp = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60); - record.header.info.record_type = ACCELEROMETER_DATA_ACQUISITION_HEADER; - record.header.info.range = ACCELEROMETER_RANGE; - record.header.info.temperature = lis2dw_get_temperature(); - record.header.char1 = activity_types[state->activity_type_index][0]; - record.header.char2 = activity_types[state->activity_type_index][1]; - record.header.timestamp = state->starting_timestamp; - - uint8_t range = 0; + return page; +} - switch (record.header.info.range) { - case LIS2DW_RANGE_16_G: - range = 16; - break; - case LIS2DW_RANGE_8_G: - range = 8; - break; - case LIS2DW_RANGE_4_G: - range = 4; - break; - case LIS2DW_RANGE_2_G: - range = 2; - break; +static void write_buffer_to_page(uint8_t *buf, uint16_t page) { + uint32_t address = 256 * page; + + wait_for_flash_ready(); + watch_set_pin_level(A3, false); + spi_flash_command(CMD_ENABLE_WRITE); + wait_for_flash_ready(); + watch_set_pin_level(A3, false); + spi_flash_write_data(address, buf, 256); + wait_for_flash_ready(); + + uint8_t buf2[256]; + watch_set_pin_level(A3, false); + spi_flash_read_data(address, buf2, 256); + wait_for_flash_ready(); + + uint8_t used_pages[256] = {0xFF}; + uint16_t address_to_mark_used = page / 8; + uint8_t header_page = address_to_mark_used / 256; + uint8_t used_byte = 0x7F >> (page % 8); + uint8_t offset_in_buf = address_to_mark_used % 256; + + printf("\twrite 256 bytes to address %ld, page %d.\n", address, page); + for(int i = 0; i < 256; i++) { + if (buf[i] != buf2[i]) { + printf("\tData mismatch detected at offset %d: %d != %d.\n", i, buf[i], buf2[i]); + } } - state->records[state->pos++] = record; - - printf("TRAINING_%c%c_%d_RANGE%d_", record.header.char1, record.header.char2, record.header.timestamp, range); + watch_set_pin_level(A3, false); + spi_flash_read_data(header_page * 256, used_pages, 256); + used_pages[offset_in_buf] = used_byte; + watch_set_pin_level(A3, false); + spi_flash_command(CMD_ENABLE_WRITE); + wait_for_flash_ready(); + watch_set_pin_level(A3, false); + spi_flash_write_data(header_page * 256, used_pages, 256); + wait_for_flash_ready(); +} - deleteme = true; +static bool wait_for_flash_ready(void) { + watch_set_pin_level(A3, false); + bool ok = true; + uint8_t read_status_response[1] = {0x00}; + do { + ok = spi_flash_read_command(CMD_READ_STATUS, read_status_response, 1); + } while ((read_status_response[0] & 0x3) != 0); + delay_ms(1); // why do i need this? + watch_set_pin_level(A3, true); + return ok; } -static void _write_page(accelerometer_data_acquisition_state_t *state) { +static void write_page(accelerometer_data_acquisition_state_t *state) { if (state->next_available_page > 0) { - // write_buffer_to_page((uint8_t *)records, next_available_page); - // wait_for_flash_ready(); + write_buffer_to_page((uint8_t *)(state->records), state->next_available_page); + wait_for_flash_ready(); } - // state->next_available_page = get_next_available_page(); state->next_available_page++; state->pos = 0; memset(state->records, 0xFF, sizeof(state->records)); } -static void _log_data_point(accelerometer_data_acquisition_state_t *state, lis2dw_reading_t reading) { +static void log_data_point(accelerometer_data_acquisition_state_t *state, lis2dw_reading_t reading, uint8_t centiseconds) { accelerometer_data_acquisition_record_t record; record.data.x.record_type = ACCELEROMETER_DATA_ACQUISITION_DATA; record.data.y.lpmode = ACCELEROMETER_LPMODE; record.data.z.filter = ACCELEROMETER_FILTER; - record.data.x.accel = reading.x; - record.data.y.accel = reading.y; - record.data.z.accel = reading.z; - record.data.counter = SECONDS_TO_RECORD - state->reading_ticks + 1; + record.data.x.accel = (reading.x >> 2) + 8192; + record.data.y.accel = (reading.y >> 2) + 8192; + record.data.z.accel = (reading.z >> 2) + 8192; + record.data.counter = 100 * (SECONDS_TO_RECORD - state->reading_ticks + 1) + centiseconds; + printf("logged data point for %d\n", record.data.counter); state->records[state->pos++] = record; - if (deleteme) { - deleteme = false; - uint8_t filter = 0; - switch (record.data.z.filter) { - case LIS2DW_BANDWIDTH_FILTER_DIV2: - filter = 2; - break; - case LIS2DW_BANDWIDTH_FILTER_DIV4: - filter = 4; - break; - case LIS2DW_BANDWIDTH_FILTER_DIV10: - filter = 10; - break; - case LIS2DW_BANDWIDTH_FILTER_DIV20: - filter = 20; - break; - } - printf("LP%d_FILT%d.CSV\n", record.data.y.lpmode + 1, filter); - } - printf("%d, %d, %d, %d\n", record.data.counter, record.data.x.accel, record.data.y.accel, record.data.z.accel); if (state->pos >= 32) { - _write_page(state); + write_page(state); } } -static void continue_reading(accelerometer_data_acquisition_state_t *state) { +static void start_reading(accelerometer_data_acquisition_state_t *state, movement_settings_t *settings) { + printf("Start reading\n"); lis2dw_fifo_t fifo; + lis2dw_read_fifo(&fifo); // dump the fifo, this starts a fresh round of data in continue_reading + accelerometer_data_acquisition_record_t record; + watch_date_time date_time = watch_rtc_get_date_time(); + state->starting_timestamp = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60); + record.header.info.record_type = ACCELEROMETER_DATA_ACQUISITION_HEADER; + record.header.info.range = ACCELEROMETER_RANGE; + record.header.info.temperature = lis2dw_get_temperature(); + record.header.char1 = activity_types[state->activity_type_index][0]; + record.header.char2 = activity_types[state->activity_type_index][1]; + record.header.timestamp = state->starting_timestamp; + + state->records[state->pos++] = record; +} + +static void continue_reading(accelerometer_data_acquisition_state_t *state) { + printf("Continue reading\n"); + lis2dw_fifo_t fifo; lis2dw_read_fifo(&fifo); + + fifo.count = min(fifo.count, 25); // hacky, but we need a consistent data rate; if we got a 26th data point, chuck it. + uint8_t offset = 4 * (25 - fifo.count); // also hacky: we're sometimes short at the start. align to beginning of next second. + for(int i = 0; i < fifo.count; i++) { - _log_data_point(state, fifo.readings[i]); + log_data_point(state, fifo.readings[i], i * 4 + offset); } } static void finish_reading(accelerometer_data_acquisition_state_t *state) { - printf("finishing\n"); + printf("Finish reading\n"); if (state->pos != 0) { - _write_page(state); + write_page(state); } lis2dw_set_data_rate(LIS2DW_DATA_RATE_POWERDOWN); watch_disable_i2c(); diff --git a/movement/watch_faces/sensor/accelerometer_data_acquisition_face.h b/movement/watch_faces/sensor/accelerometer_data_acquisition_face.h index f6ca8c5b..6da79f8b 100644 --- a/movement/watch_faces/sensor/accelerometer_data_acquisition_face.h +++ b/movement/watch_faces/sensor/accelerometer_data_acquisition_face.h @@ -34,29 +34,29 @@ typedef union { struct { - union { - int16_t record_type : 2; // see above, helps us identify record types when reading back - int16_t range : 2; // accelerometer range (see lis2dw_range_t) - int16_t temperature : 12; // raw value from the temperature sensor + struct { + uint16_t record_type : 2; // see above, helps us identify record types when reading back + uint16_t range : 2; // accelerometer range (see lis2dw_range_t) + uint16_t temperature : 12; // raw value from the temperature sensor } info; - int8_t char1 : 8; // First character of the activity type - int8_t char2 : 8; // Second character of the activity type - int32_t timestamp : 32; // UNIX timestamp for the measurement + uint8_t char1 : 8; // First character of the activity type + uint8_t char2 : 8; // Second character of the activity type + uint32_t timestamp : 32; // UNIX timestamp for the measurement } header; struct { - union { - int16_t record_type : 2; // duplicate; this is the same field as info above - int16_t accel : 14; // X acceleration value, raw + struct { + uint16_t record_type : 2; // duplicate; this is the same field as info above + uint16_t accel : 14; // X acceleration value, raw, offset by 16384 } x; - union { - int16_t lpmode : 2; // low power mode (see lis2dw_low_power_mode_t) - int16_t accel : 14; // Y acceleration value, raw + struct { + uint16_t lpmode : 2; // low power mode (see lis2dw_low_power_mode_t) + uint16_t accel : 14; // Y acceleration value, raw, offset by 16384 } y; - union { - int16_t filter : 2; // bandwidth filtering selection (see lis2dw_bandwidth_filtering_mode_t) - int16_t accel : 14; // Z acceleration value, raw + struct { + uint16_t filter : 2; // bandwidth filtering selection (see lis2dw_bandwidth_filtering_mode_t) + uint16_t accel : 14; // Z acceleration value, raw, offset by 16384 } z; - int32_t counter : 16; // number of seconds since timestamp in header + uint32_t counter : 16; // number of seconds since timestamp in header } data; uint64_t value; } accelerometer_data_acquisition_record_t; -- cgit v1.2.3