working data acquisition face!
This commit is contained in:
		
							parent
							
								
									1ce103b6f6
								
							
						
					
					
						commit
						4cd86bff53
					
				| @ -5,31 +5,41 @@ | ||||
| #include "watch.h" | ||||
| #include "watch_utility.h" | ||||
| #include "spiflash.h" | ||||
| #include "lis2dw.h" | ||||
| 
 | ||||
| #define ACCELEROMETER_TRAINING_RECORD_DELETED ((uint64_t)(0b00)) | ||||
| #define ACCELEROMETER_TRAINING_RECORD_DATA ((uint64_t)(0b01)) | ||||
| #define ACCELEROMETER_TRAINING_RECORD_HEADER ((uint64_t)(0b10)) | ||||
| #define ACCELEROMETER_TRAINING_RECORD_INVALID ((uint64_t)(0b11)) | ||||
| #define ACCELEROMETER_DATA_ACQUISITION_INVALID ((uint64_t)(0b11))   // all bits are 1 when the flash is erased
 | ||||
| #define ACCELEROMETER_DATA_ACQUISITION_HEADER ((uint64_t)(0b10)) | ||||
| #define ACCELEROMETER_DATA_ACQUISITION_DATA ((uint64_t)(0b01))     | ||||
| #define ACCELEROMETER_DATA_ACQUISITION_DELETED ((uint64_t)(0b00))   // You can always write a 0 to any 1 bit
 | ||||
| 
 | ||||
| typedef union { | ||||
|     struct { | ||||
|         int16_t temperature : 16; | ||||
|         int8_t char1 : 8; | ||||
|         int8_t char2 : 8; | ||||
|         int32_t timestamp : 32; | ||||
|         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; | ||||
|         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 { | ||||
|         int16_t x_accel : 16; | ||||
|         int16_t y_accel : 16; | ||||
|         int16_t z_accel : 16; | ||||
|         int32_t counter : 16; | ||||
|         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; | ||||
|         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; | ||||
|         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; | ||||
|         uint32_t counter : 16;          // number of seconds since timestamp in header
 | ||||
|     } data; | ||||
|     uint64_t value; | ||||
| } acceleromter_training_record_t; | ||||
| 
 | ||||
| static void cb_alarm_pressed(void) { | ||||
|     printf("Alarm button was pressed!\n"); | ||||
| } | ||||
| } accelerometer_data_acquisition_record_t; | ||||
| 
 | ||||
| static bool wait_for_flash_ready(void) { | ||||
|     watch_set_pin_level(A3, false); | ||||
| @ -43,125 +53,6 @@ static bool wait_for_flash_ready(void) { | ||||
|     return ok; | ||||
| } | ||||
| 
 | ||||
| static void print_records_at_page(uint16_t page) { | ||||
|     if (page < 650) return; | ||||
|     acceleromter_training_record_t records[32]; | ||||
|     static uint32_t timestamp = 0; | ||||
|     static uint16_t temperature = 0; | ||||
|     wait_for_flash_ready(); | ||||
|     spi_flash_read_data(page * 256, (void *)records, 256); | ||||
|     for(int i = 0; i < 32; i++) { | ||||
|         switch (records[i].header.temperature >> 14) { | ||||
|             case ACCELEROMETER_TRAINING_RECORD_DELETED: | ||||
|                 break; | ||||
|             case ACCELEROMETER_TRAINING_RECORD_DATA: | ||||
|                 printf("%ld,%d,%d,%d,%d\n", timestamp + records[i].data.counter, records[i].data.x_accel, records[i].data.y_accel, records[i].data.z_accel, temperature); | ||||
|                 break; | ||||
|             case ACCELEROMETER_TRAINING_RECORD_HEADER: | ||||
|                 printf("=== BEGIN %c%c EVENT AT %d ===\n", records[i].header.char1, records[i].header.char2, records[i].header.timestamp); | ||||
|                 printf("timestamp,x_raw,y_raw,z_raw,temperature\n"); | ||||
|                 timestamp = records[i].header.timestamp; | ||||
|                 temperature = records[i].header.temperature & 0x3FFF; | ||||
|                 break; | ||||
|             case ACCELEROMETER_TRAINING_RECORD_INVALID: | ||||
|                 printf(",,,,\n"); | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void print_records() { | ||||
|     uint8_t buf[256]; | ||||
|     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++) { | ||||
|             uint8_t pages_written = buf[j]; | ||||
|             uint8_t start = 0; | ||||
|             if (i == 0 && j == 0) { | ||||
|                 pages_written <<= 4; | ||||
|                 start = 4; | ||||
|             } | ||||
|             for(int k = start; k < 7; k++) { | ||||
|                 if ((pages_written & 0x80) == 0) { | ||||
|                     print_records_at_page(i * 2048 + j * 8 + k); | ||||
|                 } | ||||
|                 pages_written <<= 1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void app_init(void) { | ||||
|     delay_ms(5000); | ||||
|     spi_flash_init(); | ||||
|     watch_register_extwake_callback(BTN_ALARM, cb_alarm_pressed, true); | ||||
| 
 | ||||
|     bool erase = false; | ||||
|     if (erase) { | ||||
|         printf("Erasing...\n"); | ||||
|         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_command(CMD_CHIP_ERASE); | ||||
|         delay_ms(10000); | ||||
|     } | ||||
| 
 | ||||
|     uint8_t buf[256] = {0xFF}; | ||||
| 
 | ||||
|     wait_for_flash_ready(); | ||||
|     spi_flash_read_data(0, buf, 256); | ||||
|     printf("byte 0 was %02x\n", buf[0]); | ||||
|     if (buf[0] & 0xF0) { | ||||
|         buf[0] = 0x0F; | ||||
|         printf("setting it to 0x0F\n"); | ||||
|         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); | ||||
|     } | ||||
| 
 | ||||
|     print_records(); | ||||
| } | ||||
| 
 | ||||
| void app_wake_from_backup(void) { | ||||
| } | ||||
| 
 | ||||
| void app_setup(void) { | ||||
| } | ||||
| 
 | ||||
| void app_prepare_for_standby(void) { | ||||
| } | ||||
| 
 | ||||
| void app_wake_from_standby(void) { | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (page >= 8192) return -1; | ||||
| 
 | ||||
|     return page; | ||||
| } | ||||
| 
 | ||||
| static void write_buffer_to_page(uint8_t *buf, uint16_t page) { | ||||
|     uint32_t address = 256 * page; | ||||
| 
 | ||||
| @ -184,14 +75,6 @@ static void write_buffer_to_page(uint8_t *buf, uint16_t page) { | ||||
|     uint8_t used_byte = 0x7F >> (page % 8); | ||||
|     uint8_t offset_in_buf = address_to_mark_used % 256; | ||||
| 
 | ||||
|     printf("\tWe wrote 256 bytes to address %ld, which was 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]); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     watch_set_pin_level(A3, false); | ||||
|     spi_flash_read_data(header_page * 256, used_pages, 256); | ||||
|     used_pages[offset_in_buf] = used_byte; | ||||
| @ -203,58 +86,134 @@ static void write_buffer_to_page(uint8_t *buf, uint16_t page) { | ||||
|     wait_for_flash_ready(); | ||||
| } | ||||
| 
 | ||||
| bool app_loop(void) { | ||||
|     // delay_ms(5000);
 | ||||
| static void print_records_at_page(uint16_t page) { | ||||
|     accelerometer_data_acquisition_record_t records[32]; | ||||
|     static uint64_t timestamp = 0; | ||||
|     // static uint16_t temperature = 0;
 | ||||
|     static lis2dw_range_t range = LIS2DW_RANGE_2_G; | ||||
|     static double lsb_value = 1; | ||||
|     static bool printing_header = false; | ||||
| 
 | ||||
|     return; | ||||
|          | ||||
|     // simulate logging 15 seconds of data
 | ||||
|     watch_date_time date_time = watch_rtc_get_date_time(); | ||||
|     acceleromter_training_record_t record; | ||||
| 
 | ||||
|     record.header.temperature = 0xC30; | ||||
|     record.header.temperature |= (ACCELEROMETER_TRAINING_RECORD_HEADER << 14); | ||||
|     record.header.char1 = 'W'; | ||||
|     record.header.char2 = 'A'; | ||||
|     record.header.timestamp = watch_utility_date_time_to_unix_time(date_time, 0);; | ||||
| 
 | ||||
|     acceleromter_training_record_t records[32]; | ||||
|     memset(records, 0xFF, sizeof(records)); | ||||
|     records[0] = record; | ||||
|     uint16_t pos = 1; | ||||
|     uint32_t counter = 0; | ||||
| 
 | ||||
|     printf("logging 15*25 data points for timestamp %ld\n", record.header.timestamp); | ||||
|     for(uint8_t i = 0; i < 15; i++) { | ||||
|         for(uint8_t j = 0; j < 25; j++) { | ||||
|             record.data.x_accel = arc4random() & 0x3FFF; | ||||
|             record.data.x_accel |= ACCELEROMETER_TRAINING_RECORD_DATA << 14; | ||||
|             record.data.y_accel = arc4random() & 0x3FFF; | ||||
|             record.data.z_accel = arc4random() & 0x3FFF; | ||||
|             record.data.counter = i; | ||||
|             records[pos++] = record; | ||||
|             if (pos >= 32) { | ||||
|                 printf("pos overflowed at counter %ld\n", counter); | ||||
|                 int16_t next_available_page = get_next_available_page(); | ||||
|                 if (next_available_page > 0) { | ||||
|                     write_buffer_to_page((uint8_t *)records, next_available_page); | ||||
|     wait_for_flash_ready(); | ||||
|     spi_flash_read_data(page * 256, (void *)records, 256); | ||||
|     for(int i = 0; i < 32; i++) { | ||||
|         switch (records[i].header.info.record_type) { | ||||
|             case ACCELEROMETER_DATA_ACQUISITION_HEADER: | ||||
|                 printing_header = true; | ||||
|                 timestamp = records[i].header.timestamp; | ||||
|                 // temperature = records[i].header.info.temperature;
 | ||||
|                 printf("%c%c.sample%lld.", records[i].header.char1, records[i].header.char2, timestamp); | ||||
|                 range = records[i].header.info.range; | ||||
|                 break; | ||||
|             case ACCELEROMETER_DATA_ACQUISITION_DATA: | ||||
|                 if (printing_header) { | ||||
|                     printing_header = false; | ||||
|                     uint8_t filter = 0; | ||||
|                     switch (records[i].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; | ||||
|                     } | ||||
|                 pos = 0; | ||||
|                 memset(records, 0xFF, sizeof(records)); | ||||
|                     switch (range) { | ||||
|                         case LIS2DW_RANGE_16_G: | ||||
|                             lsb_value = (records[i].data.y.lpmode == LIS2DW_LP_MODE_1) ? 7.808 : 1.952; | ||||
|                             break; | ||||
|                         case LIS2DW_RANGE_8_G: | ||||
|                             lsb_value = (records[i].data.y.lpmode == LIS2DW_LP_MODE_1) ? 3.904 : 0.976; | ||||
|                             break; | ||||
|                         case LIS2DW_RANGE_4_G: | ||||
|                             lsb_value = (records[i].data.y.lpmode == LIS2DW_LP_MODE_1) ? 1.952 : 0.488; | ||||
|                             break; | ||||
|                         case LIS2DW_RANGE_2_G: | ||||
|                             lsb_value = (records[i].data.y.lpmode == LIS2DW_LP_MODE_1) ? 0.976 : 0.244; | ||||
|                             break; | ||||
|                     } | ||||
|                     printf("RANGE%d_LP%d_FILT%d.CSV\n", range, records[i].data.y.lpmode + 1, filter); | ||||
|                     printf("timestamp,accX,accY,accZ\n"); | ||||
|                 } | ||||
|                 printf("%lld,%f,%f,%f\n", | ||||
|                         (timestamp * 100 + records[i].data.counter) * 10, | ||||
|                         9.80665 * ((double)(records[i].data.x.accel - 8192)) * lsb_value / 1000, | ||||
|                         9.80665 * ((double)(records[i].data.y.accel - 8192)) * lsb_value / 1000, | ||||
|                         9.80665 * ((double)(records[i].data.z.accel - 8192)) * lsb_value / 1000); | ||||
|                 break; | ||||
|             case ACCELEROMETER_DATA_ACQUISITION_INVALID: | ||||
|             case ACCELEROMETER_DATA_ACQUISITION_DELETED: | ||||
|                 // don't print anything
 | ||||
|                 break; | ||||
|         } | ||||
|     if (records[0].header.temperature >> 14 != ACCELEROMETER_TRAINING_RECORD_INVALID) { | ||||
|         int16_t next_available_page = get_next_available_page(); | ||||
|         if (next_available_page > 0) { | ||||
|             printf("Partial write\n"); | ||||
|             write_buffer_to_page((uint8_t *)records, next_available_page); | ||||
|             wait_for_flash_ready(); | ||||
|         } | ||||
|         records[i].header.info.record_type = ACCELEROMETER_DATA_ACQUISITION_DELETED; | ||||
|     } | ||||
| 
 | ||||
|     delay_ms(60000); | ||||
| 
 | ||||
|     return false; | ||||
|     // uncomment this to mark all pages deleted
 | ||||
|     // write_buffer_to_page((uint8_t *)records, page);
 | ||||
| } | ||||
| 
 | ||||
| static void print_records() { | ||||
|     uint8_t buf[256]; | ||||
|     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++) { | ||||
|             uint8_t pages_written = buf[j]; | ||||
|             uint8_t start = 0; | ||||
|             if (i == 0 && j == 0) { | ||||
|                 pages_written <<= 4; | ||||
|                 start = 4; | ||||
|             } | ||||
|             for(int k = start; k < 8; k++) { | ||||
|                 if ((pages_written & 0x80) == 0) { | ||||
|                     print_records_at_page(i * 2048 + j * 8 + k); | ||||
|                 } | ||||
|                 pages_written <<= 1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void app_init(void) { | ||||
| } | ||||
| 
 | ||||
| void app_wake_from_backup(void) { | ||||
| } | ||||
| 
 | ||||
| void app_setup(void) { | ||||
|     spi_flash_init(); | ||||
|     delay_ms(5000); | ||||
| 
 | ||||
|     // bool erase = false;
 | ||||
|     // if (erase) {
 | ||||
|     //     printf("Erasing...\n");
 | ||||
|     //     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_command(CMD_CHIP_ERASE);
 | ||||
|     //     delay_ms(10000);
 | ||||
|     // }
 | ||||
|   | ||||
|     print_records(); | ||||
| } | ||||
| 
 | ||||
| void app_prepare_for_standby(void) { | ||||
| } | ||||
| 
 | ||||
| void app_wake_from_standby(void) { | ||||
| } | ||||
| 
 | ||||
| bool app_loop(void) { | ||||
|     delay_ms(5000); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @ -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,18 +317,110 @@ 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; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (page >= 8192) return -1; | ||||
| 
 | ||||
|     return page; | ||||
| } | ||||
| 
 | ||||
| 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]); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     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(); | ||||
| } | ||||
| 
 | ||||
| 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) { | ||||
|     if (state->next_available_page > 0) { | ||||
|         write_buffer_to_page((uint8_t *)(state->records), state->next_available_page); | ||||
|         wait_for_flash_ready(); | ||||
|     } | ||||
|     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, 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 >> 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 (state->pos >= 32) { | ||||
|         write_page(state); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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(); | ||||
|     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(); | ||||
| @ -310,89 +432,26 @@ static void start_reading(accelerometer_data_acquisition_state_t *state, movemen | ||||
|     record.header.char2 = activity_types[state->activity_type_index][1]; | ||||
|     record.header.timestamp = state->starting_timestamp; | ||||
| 
 | ||||
|     uint8_t range = 0; | ||||
| 
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|     state->records[state->pos++] = record; | ||||
| 
 | ||||
|     printf("TRAINING_%c%c_%d_RANGE%d_", record.header.char1, record.header.char2, record.header.timestamp, range); | ||||
| 
 | ||||
|     deleteme = true; | ||||
| } | ||||
| 
 | ||||
| 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();
 | ||||
|     } | ||||
|     // 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) { | ||||
|     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; | ||||
|     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); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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(); | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user