From 5d332f1244aef7391df5ecfe12f219c33a2f0123 Mon Sep 17 00:00:00 2001 From: habibialireza Date: Wed, 13 May 2026 11:26:42 +0200 Subject: [PATCH 1/3] FW: timestamp of created at for files not global time --- Kconfig.defaults | 3 + prj.conf | 1 + prj_fota.conf | 1 + src/SD_Card/SD_Card_Manager/CMakeLists.txt | 1 + src/SD_Card/SD_Card_Manager/fatfs_time.c | 105 +++++++++++++++++++++ src/time_sync/time_sync.c | 11 ++- src/time_sync/time_sync.h | 13 ++- 7 files changed, 130 insertions(+), 5 deletions(-) create mode 100644 src/SD_Card/SD_Card_Manager/fatfs_time.c diff --git a/Kconfig.defaults b/Kconfig.defaults index 3e98206f..c07438d6 100644 --- a/Kconfig.defaults +++ b/Kconfig.defaults @@ -110,6 +110,9 @@ config FILE_SYSTEM config FAT_FILESYSTEM_ELM default y +config FS_FATFS_HAS_RTC + default y + config FS_FATFS_LFN default y choice FS_FATFS_LFN_MODE diff --git a/prj.conf b/prj.conf index 6e2a1100..1a0a04c0 100644 --- a/prj.conf +++ b/prj.conf @@ -172,6 +172,7 @@ CONFIG_SDHC=y CONFIG_LOG=y CONFIG_FILE_SYSTEM=y CONFIG_FAT_FILESYSTEM_ELM=y +CONFIG_FS_FATFS_HAS_RTC=y # Enable SDHC interface CONFIG_DISK_DRIVERS=y diff --git a/prj_fota.conf b/prj_fota.conf index 6b7263a0..0aee2b1b 100644 --- a/prj_fota.conf +++ b/prj_fota.conf @@ -188,6 +188,7 @@ CONFIG_SDHC=y CONFIG_LOG=y CONFIG_FILE_SYSTEM=y CONFIG_FAT_FILESYSTEM_ELM=y +CONFIG_FS_FATFS_HAS_RTC=y # Enable SDHC interface CONFIG_DISK_DRIVERS=y diff --git a/src/SD_Card/SD_Card_Manager/CMakeLists.txt b/src/SD_Card/SD_Card_Manager/CMakeLists.txt index d93a7c09..ae3bf3a0 100644 --- a/src/SD_Card/SD_Card_Manager/CMakeLists.txt +++ b/src/SD_Card/SD_Card_Manager/CMakeLists.txt @@ -1,4 +1,5 @@ target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/SD_Card_Manager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/fatfs_time.c ) diff --git a/src/SD_Card/SD_Card_Manager/fatfs_time.c b/src/SD_Card/SD_Card_Manager/fatfs_time.c new file mode 100644 index 00000000..3652cb64 --- /dev/null +++ b/src/SD_Card/SD_Card_Manager/fatfs_time.c @@ -0,0 +1,105 @@ +#include +#include + +#include + +#include "time_sync.h" + +#define FAT_YEAR_MIN 1980U +#define FAT_YEAR_MAX 2107U +#define UNIX_SECONDS_PER_DAY 86400ULL +#define UNIX_SECONDS_1980_01_01 315532800ULL +#define UNIX_US_PER_SECOND 1000000ULL + +#define FATFS_FALLBACK_TIME \ + (((DWORD)(2022U - FAT_YEAR_MIN) << 25) | ((DWORD)1U << 21) | ((DWORD)1U << 16)) + +struct fatfs_calendar_time { + uint32_t year; + uint32_t month; + uint32_t day; + uint32_t hour; + uint32_t minute; + uint32_t second; +}; + +static bool is_leap_year(uint32_t year) +{ + return ((year % 4U) == 0U && (year % 100U) != 0U) || ((year % 400U) == 0U); +} + +static uint32_t days_in_year(uint32_t year) +{ + return is_leap_year(year) ? 366U : 365U; +} + +static uint32_t days_in_month(uint32_t year, uint32_t month) +{ + static const uint8_t month_days[] = { + 31U, 28U, 31U, 30U, 31U, 30U, 31U, 31U, 30U, 31U, 30U, 31U + }; + + if (month == 2U && is_leap_year(year)) { + return 29U; + } + + return month_days[month - 1U]; +} + +static bool unix_seconds_to_calendar(uint64_t seconds, struct fatfs_calendar_time *calendar) +{ + uint64_t days = seconds / UNIX_SECONDS_PER_DAY; + uint32_t seconds_today = (uint32_t)(seconds % UNIX_SECONDS_PER_DAY); + uint32_t year = 1970U; + + while (days >= days_in_year(year)) { + days -= days_in_year(year); + year++; + if (year > FAT_YEAR_MAX) { + return false; + } + } + + uint32_t month = 1U; + while (days >= days_in_month(year, month)) { + days -= days_in_month(year, month); + month++; + } + + calendar->year = year; + calendar->month = month; + calendar->day = (uint32_t)days + 1U; + calendar->hour = seconds_today / 3600U; + calendar->minute = (seconds_today % 3600U) / 60U; + calendar->second = seconds_today % 60U; + + return true; +} + +DWORD get_fattime(void) +{ + struct fatfs_calendar_time calendar; + uint64_t now_us; + uint64_t now_seconds; + + if (!time_sync_is_synced()) { + return FATFS_FALLBACK_TIME; + } + + now_us = get_current_time_us(); + now_seconds = now_us / UNIX_US_PER_SECOND; + + if (now_seconds < UNIX_SECONDS_1980_01_01 || + !unix_seconds_to_calendar(now_seconds, &calendar) || + calendar.year < FAT_YEAR_MIN || + calendar.year > FAT_YEAR_MAX) { + return FATFS_FALLBACK_TIME; + } + + return (((DWORD)(calendar.year - FAT_YEAR_MIN) << 25) | + ((DWORD)calendar.month << 21) | + ((DWORD)calendar.day << 16) | + ((DWORD)calendar.hour << 11) | + ((DWORD)calendar.minute << 5) | + ((DWORD)(calendar.second / 2U))); +} diff --git a/src/time_sync/time_sync.c b/src/time_sync/time_sync.c index f1841e4e..4d8a0c16 100644 --- a/src/time_sync/time_sync.c +++ b/src/time_sync/time_sync.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -40,6 +41,7 @@ struct __packed time_sync_packet { }; int64_t time_offset_us = 0; +static atomic_t time_synced; bool notify_rtt_enabled = false; @@ -137,6 +139,7 @@ static ssize_t write_time_offset( int64_t delta; memcpy(&delta, buf, sizeof(delta)); time_offset_us += delta; + atomic_set(&time_synced, 1); LOG_DBG("Received time offset update: %lld us, new time offset: %lld us", delta, time_offset_us); return len; @@ -152,7 +155,11 @@ int init_time_sync(void) { return 0; } -inline uint64_t get_current_time_us() { +bool time_sync_is_synced(void) { + return atomic_get(&time_synced) != 0; +} + +uint64_t get_current_time_us(void) { uint64_t base_u = get_time_since_boot_us(); int64_t base_s = (base_u > (uint64_t)INT64_MAX) ? INT64_MAX : (int64_t)base_u; int64_t now_s = base_s + time_offset_us; @@ -167,7 +174,7 @@ inline uint64_t get_current_time_us() { return (uint64_t)now_s; } -inline uint64_t get_time_since_boot_us() { +uint64_t get_time_since_boot_us(void) { return k_ticks_to_us_floor64(k_uptime_ticks()); } diff --git a/src/time_sync/time_sync.h b/src/time_sync/time_sync.h index a9ee8cd3..f4fe3ed1 100644 --- a/src/time_sync/time_sync.h +++ b/src/time_sync/time_sync.h @@ -4,19 +4,26 @@ extern "C" { #endif +#include #include /** * @brief Get time since boot in microseconds. * @return Time since boot in microseconds. */ -uint64_t get_time_since_boot_us(); +uint64_t get_time_since_boot_us(void); /** * @brief Get the current synchronized time in microseconds (since 1. January 1970). * @return Current synchronized time in microseconds. */ -uint64_t get_current_time_us(); +uint64_t get_current_time_us(void); + +/** + * @brief Check whether the wall-clock time has been synchronized. + * @return True after the Time Offset characteristic has been written. + */ +bool time_sync_is_synced(void); /** * @brief Initialize the time synchronization module. @@ -27,4 +34,4 @@ int init_time_sync(); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif From 47efe28fdb9db2a4d09d62dde146fd3b46e54ef2 Mon Sep 17 00:00:00 2001 From: habibialireza Date: Wed, 17 Jun 2026 12:49:11 +0200 Subject: [PATCH 2/3] used time.h to fix file timestamps --- src/SD_Card/SD_Card_Manager/fatfs_time.c | 92 +++++------------------- 1 file changed, 19 insertions(+), 73 deletions(-) diff --git a/src/SD_Card/SD_Card_Manager/fatfs_time.c b/src/SD_Card/SD_Card_Manager/fatfs_time.c index 3652cb64..0787369c 100644 --- a/src/SD_Card/SD_Card_Manager/fatfs_time.c +++ b/src/SD_Card/SD_Card_Manager/fatfs_time.c @@ -1,6 +1,6 @@ #include #include - +#include #include #include "time_sync.h" @@ -14,71 +14,10 @@ #define FATFS_FALLBACK_TIME \ (((DWORD)(2022U - FAT_YEAR_MIN) << 25) | ((DWORD)1U << 21) | ((DWORD)1U << 16)) -struct fatfs_calendar_time { - uint32_t year; - uint32_t month; - uint32_t day; - uint32_t hour; - uint32_t minute; - uint32_t second; -}; - -static bool is_leap_year(uint32_t year) -{ - return ((year % 4U) == 0U && (year % 100U) != 0U) || ((year % 400U) == 0U); -} - -static uint32_t days_in_year(uint32_t year) -{ - return is_leap_year(year) ? 366U : 365U; -} - -static uint32_t days_in_month(uint32_t year, uint32_t month) -{ - static const uint8_t month_days[] = { - 31U, 28U, 31U, 30U, 31U, 30U, 31U, 31U, 30U, 31U, 30U, 31U - }; - - if (month == 2U && is_leap_year(year)) { - return 29U; - } - - return month_days[month - 1U]; -} - -static bool unix_seconds_to_calendar(uint64_t seconds, struct fatfs_calendar_time *calendar) -{ - uint64_t days = seconds / UNIX_SECONDS_PER_DAY; - uint32_t seconds_today = (uint32_t)(seconds % UNIX_SECONDS_PER_DAY); - uint32_t year = 1970U; - - while (days >= days_in_year(year)) { - days -= days_in_year(year); - year++; - if (year > FAT_YEAR_MAX) { - return false; - } - } - - uint32_t month = 1U; - while (days >= days_in_month(year, month)) { - days -= days_in_month(year, month); - month++; - } - - calendar->year = year; - calendar->month = month; - calendar->day = (uint32_t)days + 1U; - calendar->hour = seconds_today / 3600U; - calendar->minute = (seconds_today % 3600U) / 60U; - calendar->second = seconds_today % 60U; - - return true; -} DWORD get_fattime(void) { - struct fatfs_calendar_time calendar; + uint64_t now_us; uint64_t now_seconds; @@ -89,17 +28,24 @@ DWORD get_fattime(void) now_us = get_current_time_us(); now_seconds = now_us / UNIX_US_PER_SECOND; - if (now_seconds < UNIX_SECONDS_1980_01_01 || - !unix_seconds_to_calendar(now_seconds, &calendar) || - calendar.year < FAT_YEAR_MIN || - calendar.year > FAT_YEAR_MAX) { + + time_t now = (time_t)now_seconds; + struct tm calendar; + + if ((uint64_t)now != now_seconds || gmtime_r(&now, &calendar) == NULL) { + return FATFS_FALLBACK_TIME; + } + + uint32_t year = (uint32_t)calendar.tm_year + 1900U; + + if (year < FAT_YEAR_MIN || year > FAT_YEAR_MAX) { return FATFS_FALLBACK_TIME; } - return (((DWORD)(calendar.year - FAT_YEAR_MIN) << 25) | - ((DWORD)calendar.month << 21) | - ((DWORD)calendar.day << 16) | - ((DWORD)calendar.hour << 11) | - ((DWORD)calendar.minute << 5) | - ((DWORD)(calendar.second / 2U))); + return (((DWORD)(year - FAT_YEAR_MIN) << 25) | + ((DWORD)(calendar.tm_mon + 1) << 21) | + ((DWORD)calendar.tm_mday << 16) | + ((DWORD)calendar.tm_hour << 11) | + ((DWORD)calendar.tm_min << 5) | + ((DWORD)(calendar.tm_sec / 2))); } From 197fbe0360f460c3e27e7981a2c4f00c11a4537a Mon Sep 17 00:00:00 2001 From: habibialireza Date: Mon, 29 Jun 2026 11:40:03 +0200 Subject: [PATCH 3/3] docs: clarify FatFs timestamp packing and some variables --- src/SD_Card/SD_Card_Manager/fatfs_time.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/SD_Card/SD_Card_Manager/fatfs_time.c b/src/SD_Card/SD_Card_Manager/fatfs_time.c index 0787369c..1e862792 100644 --- a/src/SD_Card/SD_Card_Manager/fatfs_time.c +++ b/src/SD_Card/SD_Card_Manager/fatfs_time.c @@ -7,10 +7,10 @@ #define FAT_YEAR_MIN 1980U #define FAT_YEAR_MAX 2107U -#define UNIX_SECONDS_PER_DAY 86400ULL -#define UNIX_SECONDS_1980_01_01 315532800ULL #define UNIX_US_PER_SECOND 1000000ULL +#define TM_YEAR_BASE 1900U /* struct tm.tm_year stores years since 1900. */ +/* 2022-01-01 matches FatFs/Zephyr's no-RTC fallback date and is used until phone time sync is available. */ #define FATFS_FALLBACK_TIME \ (((DWORD)(2022U - FAT_YEAR_MIN) << 25) | ((DWORD)1U << 21) | ((DWORD)1U << 16)) @@ -36,12 +36,16 @@ DWORD get_fattime(void) return FATFS_FALLBACK_TIME; } - uint32_t year = (uint32_t)calendar.tm_year + 1900U; + uint32_t year = (uint32_t)calendar.tm_year + TM_YEAR_BASE; if (year < FAT_YEAR_MIN || year > FAT_YEAR_MAX) { return FATFS_FALLBACK_TIME; } + /* + * FAT directory entries store timestamps as packed date/time fields. + * FatFs asks the application for that packed value via get_fattime(). + */ return (((DWORD)(year - FAT_YEAR_MIN) << 25) | ((DWORD)(calendar.tm_mon + 1) << 21) | ((DWORD)calendar.tm_mday << 16) |