diff --git a/CMakeLists.txt b/CMakeLists.txt index d87f31d..45ae35a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,7 @@ target_sources(${CMAKE_PROJECT_NAME} PRIVATE src/gps.c src/int.c src/menu.c + src/mcu_time.c ) diff --git a/src/gps.c b/src/gps.c index 21ca73a..8eaffca 100644 --- a/src/gps.c +++ b/src/gps.c @@ -4,6 +4,7 @@ #include "stm32f1xx_hal_uart.h" #include "usart.h" #include "eeprom.h" +#include "mcu_time.h" #include #include #include @@ -394,6 +395,30 @@ void gps_parse(char* line) // Terminaute time string gps_time[8] = '\0'; + // Sync MCU time with compensated/timezone-adjusted GPS time + { + char mcu_sync_time[sizeof(gps_time)]; + memcpy(mcu_sync_time, gps_time, sizeof(gps_time)); + // Remove the +1s PPS compensation before syncing MCU time + // to avoid double-counting with TIM2 ISR increment + { + int hour = (mcu_sync_time[0]-'0') * 10 + (mcu_sync_time[1]-'0'); + int min = (mcu_sync_time[3]-'0') * 10 + (mcu_sync_time[4]-'0'); + int sec = (mcu_sync_time[6]-'0') * 10 + (mcu_sync_time[7]-'0'); + sec--; + if(sec < 0) { sec = 59; min--; } + if(min < 0) { min = 59; hour--; } + if(hour < 0) { hour = 23; } + mcu_sync_time[0] = (char)((hour/10)+'0'); + mcu_sync_time[1] = (char)((hour%10)+'0'); + mcu_sync_time[3] = (char)((min/10)+'0'); + mcu_sync_time[4] = (char)((min%10)+'0'); + mcu_sync_time[6] = (char)((sec/10)+'0'); + mcu_sync_time[7] = (char)((sec%10)+'0'); + } + mcu_time_sync_from_string(mcu_sync_time, true); + } + pch = strtok(NULL, ","); // Latitude gps_latitude_double = gps_parse_coordinate(pch,gps_latitude,sizeof(gps_latitude)); pch = strtok(NULL, ","); // N/S diff --git a/src/int.c b/src/int.c index 078769a..bb9ae43 100644 --- a/src/int.c +++ b/src/int.c @@ -3,6 +3,7 @@ #include "frequency.h" #include "tim.h" #include "menu.h" +#include "mcu_time.h" #include #include @@ -73,6 +74,8 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim) pps_led_toogle = !pps_led_toogle; // Update uptime device_uptime++; + // Increment MCU time-of-day (drives display during flywheel) + mcu_time_increment(); if(HAL_GetTick() - last_pps > 1500) { // No GPS PPS output, blink 'x' icon diff --git a/src/main.c b/src/main.c index f3775b7..5ce37ad 100644 --- a/src/main.c +++ b/src/main.c @@ -6,6 +6,7 @@ #include "menu.h" #include "int.h" #include "tim.h" +#include "mcu_time.h" #include #include #include @@ -18,7 +19,6 @@ void gpsdo(void) { - HAL_TIM_Base_Start_IT(&htim2); EE_Init(&ee_storage, sizeof(ee_storage_t)); EE_Read(); @@ -102,6 +102,9 @@ void gpsdo(void) ee_storage.gps_time_offset = -MIN_TIME_OFFSET; } gps_time_offset = ee_storage.gps_time_offset+MIN_TIME_OFFSET; + // Initialize MCU timekeeping before enabling TIM2 ISR + mcu_time_init((int8_t)gps_time_offset); + HAL_TIM_Base_Start_IT(&htim2); if (ee_storage.gps_date_format == 0xff) { ee_storage.gps_date_format = DATE_FORMAT_UTC; } diff --git a/src/mcu_time.c b/src/mcu_time.c new file mode 100644 index 0000000..dbf9eab --- /dev/null +++ b/src/mcu_time.c @@ -0,0 +1,115 @@ +#include "mcu_time.h" +#include + +volatile mcu_time_t mcu_time; +volatile char mcu_time_string[9]; +volatile bool mcu_time_dirty = false; + +static void fmt_time(char* buf, uint8_t h, uint8_t m, uint8_t s) { + buf[0] = '0' + (h / 10); buf[1] = '0' + (h % 10); + buf[2] = ':'; + buf[3] = '0' + (m / 10); buf[4] = '0' + (m % 10); + buf[5] = ':'; + buf[6] = '0' + (s / 10); buf[7] = '0' + (s % 10); + buf[8] = '\0'; +} + +void mcu_time_init(int8_t initial_tz_offset) { + mcu_time.hours = 0; + mcu_time.minutes = 0; + mcu_time.seconds = 0; + mcu_time.timezone_offset = initial_tz_offset; + mcu_time.gps_disciplined = false; + mcu_time.seconds_since_gps = 0; + + char tmp[9]; + fmt_time(tmp, 0, 0, 0); + memcpy((void*)mcu_time_string, tmp, sizeof(tmp)); + mcu_time_dirty = false; +} + +void mcu_time_increment(void) { + uint8_t h = mcu_time.hours; + uint8_t m = mcu_time.minutes; + uint8_t s = mcu_time.seconds; + + // increment one second + s++; + if (s >= 60) { + s = 0; + m++; + if (m >= 60) { + m = 0; + h = (h + 1) % 24; + } + } + + mcu_time.hours = h; + mcu_time.minutes = m; + mcu_time.seconds = s; + + // track discipline age + if (mcu_time.seconds_since_gps < 0xFFFFFFFFu) { + mcu_time.seconds_since_gps++; + } + + // minimally update formatted string (ISR-friendly) + char tmp[9]; + fmt_time(tmp, h, m, s); + memcpy((void*)mcu_time_string, tmp, sizeof(tmp)); +} + +void mcu_time_sync_from_string(const char* str, bool formatted_hh_colon_mm_colon_ss) { + uint8_t h, m, s; + if (formatted_hh_colon_mm_colon_ss) { + // "HH:MM:SS" + h = (uint8_t)((str[0] - '0') * 10 + (str[1] - '0')); + m = (uint8_t)((str[3] - '0') * 10 + (str[4] - '0')); + s = (uint8_t)((str[6] - '0') * 10 + (str[7] - '0')); + } else { + // "HHMMSS" + h = (uint8_t)((str[0] - '0') * 10 + (str[1] - '0')); + m = (uint8_t)((str[2] - '0') * 10 + (str[3] - '0')); + s = (uint8_t)((str[4] - '0') * 10 + (str[5] - '0')); + } + + if (h > 23) h = 0; + if (m > 59) m = 0; + if (s > 59) s = 0; + + mcu_time.hours = h; + mcu_time.minutes = m; + mcu_time.seconds = s; + + mcu_time.gps_disciplined = true; + mcu_time.seconds_since_gps = 0; + + char tmp[9]; + fmt_time(tmp, h, m, s); + memcpy((void*)mcu_time_string, tmp, sizeof(tmp)); +} + +void mcu_time_set(uint8_t h, uint8_t m, uint8_t s) { + h %= 24; m %= 60; s %= 60; + mcu_time.hours = h; + mcu_time.minutes = m; + mcu_time.seconds = s; + + char tmp[9]; + fmt_time(tmp, h, m, s); + memcpy((void*)mcu_time_string, tmp, sizeof(tmp)); +} + +void mcu_time_set_timezone(int8_t offset) { + mcu_time.timezone_offset = offset; +} + +const char* mcu_time_get_status(void) { + if (mcu_time.gps_disciplined) { + if (mcu_time.seconds_since_gps <= 10) { + return "GPS-Sync"; + } + return "GPS-Disc"; + } + return "Free-Run"; +} diff --git a/src/mcu_time.h b/src/mcu_time.h new file mode 100644 index 0000000..5a79622 --- /dev/null +++ b/src/mcu_time.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +// Simple MCU-maintained time-of-day, disciplined by GPS PPS via TIM2 ISR. +typedef struct { + uint8_t hours; // 0-23 + uint8_t minutes; // 0-59 + uint8_t seconds; // 0-59 + int8_t timezone_offset; // -14..+14 hours (optional; not actively used yet) + bool gps_disciplined; // true if recently synced to GPS time + uint32_t seconds_since_gps; // seconds since last GPS sync +} mcu_time_t; + +extern volatile mcu_time_t mcu_time; + +// "HH:MM:SS\0" - keep updates atomic via memcpy of 9 bytes +extern volatile char mcu_time_string[9]; + +// Optional dirty flag if you prefer formatting outside ISR (not used by default) +extern volatile bool mcu_time_dirty; + +// Initialize module. Provide initial timezone offset if desired (can pass 0). +void mcu_time_init(int8_t initial_tz_offset); + +// Increment by one second (call from TIM2 PeriodElapsed ISR). +void mcu_time_increment(void); + +// Sync time from a string: +// - If formatted_hh_colon_mm_colon_ss is true, expects "HH:MM:SS" +// - Otherwise expects "HHMMSS" +void mcu_time_sync_from_string(const char* str, bool formatted_hh_colon_mm_colon_ss); + +// Manual setters (optional) +void mcu_time_set(uint8_t h, uint8_t m, uint8_t s); +void mcu_time_set_timezone(int8_t offset); + +// Returns a static string literal describing discipline state: +// "GPS-Sync" (if synced within last 10s), "GPS-Disc" (synced, but older), or "Free-Run" +const char* mcu_time_get_status(void); diff --git a/src/menu.c b/src/menu.c index f72e85e..adc54ee 100644 --- a/src/menu.c +++ b/src/menu.c @@ -12,6 +12,7 @@ #include "stm32f1xx_hal_gpio.h" #include "int.h" #include "menu.h" +#include "mcu_time.h" /// All times in ms #define DEBOUNCE_TIME 50 @@ -418,7 +419,7 @@ static void menu_draw() LCD_Puts(1, 0, screen_buffer); if(current_menu_screen == SCREEN_MAIN) { - LCD_Puts(0, 1, gps_time); + LCD_Puts(0, 1, (const char*)mcu_time_string); } else if(current_menu_screen == SCREEN_DATE) { @@ -430,7 +431,7 @@ static void menu_draw() uint32_t duration = now - last_hour_date_screen_update; if(duration <= DATE_TIME_DURATION) { - LCD_Puts(0, 1, gps_time); + LCD_Puts(0, 1, (const char*)mcu_time_string); } else { @@ -638,7 +639,7 @@ static void menu_draw() { snprintf(screen_buffer, SCREEN_BUFFER_SIZE, "GPS:%02d\5", num_sats); LCD_Puts(1, 0, screen_buffer); - LCD_Puts(0, 1, gps_time); + LCD_Puts(0, 1, (const char*)mcu_time_string); } else { @@ -649,7 +650,7 @@ static void menu_draw() default: case SCREEN_GPS_TIME: LCD_Puts(1, 0, "Time:"); - LCD_Puts(0, 1, gps_time); + LCD_Puts(0, 1, (const char*)mcu_time_string); break; case SCREEN_GPS_LATITUDE: snprintf(screen_buffer, SCREEN_BUFFER_SIZE, "Lat.: %s", gps_n_s);