/****************************************************************************** * @file usr_rtc.c * @brief 板级 RTC 外设实现(BR28 硬件 rtc_dev_ops) * @author cyWu <1917507415@qq.com> * @date 2026.08.19 * @version V1.1.0 * @history * - V1.0.0, 2026.08.19, cyWu, 从模组固件移植至 SOC SDK * - V1.1.0, 2026.08.19, cyWu, 精简公共接口与缓存逻辑 ******************************************************************************/ #include "system/includes.h" #include "app_config.h" #include "usr_rtc.h" #include "asm/rtc.h" #define LOG_TAG_CONST APP #define LOG_TAG "[USR_RTC]" #define LOG_ERROR_ENABLE #define LOG_DEBUG_ENABLE #define LOG_INFO_ENABLE #include "debug.h" /*============================================================================*/ /* 私有宏 / 类型 */ /*============================================================================*/ #define USR_RTC_TZ_MAGIC (0x52544331u) /**< 'RTC1',校验 VM 时区 */ typedef struct { uint32_t magic; int32_t tz_s; } UsrRtcTzVm_t; typedef struct { uint8_t inited; uint8_t tz_valid; int32_t tz_s; void *dev; } UsrRtc_t; /*============================================================================*/ /* 私有变量 */ /*============================================================================*/ static UsrRtc_t s_rtc; /*============================================================================*/ /* 私有函数声明 */ /*============================================================================*/ static uint8_t usr_rtc_is_leap(uint16_t year); static uint8_t usr_rtc_days_in_month(uint16_t year, uint8_t month); static uint8_t usr_rtc_year_ok(uint16_t year); static void usr_rtc_unix_to_calendar(uint32_t unix_utc, int32_t tz_s, struct sys_time *t); static UsrRtc_Ret_t usr_rtc_write(const struct sys_time *t); static void usr_rtc_load_tz(void); static void usr_rtc_save_tz(int32_t tz_s); /*============================================================================*/ /* 公有函数 */ /*============================================================================*/ /** * @brief 打开 rtc 设备并加载时区 * @return UsrRtc_Ret_t */ UsrRtc_Ret_t usr_rtc_init(void) { struct sys_time t; if (s_rtc.inited) { return USR_RTC_OK; } #if TCFG_APP_RTC_EN s_rtc.dev = dev_open("rtc", NULL); if (!s_rtc.dev) { log_error("open rtc fail"); return USR_RTC_ERR_NOT_INIT; } #else log_error("TCFG_APP_RTC_EN=0"); return USR_RTC_ERR_NOT_INIT; #endif usr_rtc_load_tz(); s_rtc.inited = 1; memset(&t, 0, sizeof(t)); read_sys_time(&t); log_info("rtc open %u-%02u-%02u %02u:%02u:%02u tz=%ld tz_ok=%u", t.year, t.month, t.day, t.hour, t.min, t.sec, (long)s_rtc.tz_s, s_rtc.tz_valid); return USR_RTC_OK; } /** * @brief 获取当前本地时间 * @param t 成功时赋值;失败不改动 * @return 1/0 */ uint8_t usr_rtc_get_time(struct sys_time *t) { struct sys_time tmp; if (!t || !s_rtc.inited || !s_rtc.tz_valid) { return 0; } memset(&tmp, 0, sizeof(tmp)); read_sys_time(&tmp); if (!usr_rtc_year_ok(tmp.year)) { return 0; } *t = tmp; return 1; } /** * @brief UTC 秒 + 时区 → 本地日历写入 RTC * @param unix_utc UTC 秒 * @param tz_s 时区偏移秒 * @return UsrRtc_Ret_t */ UsrRtc_Ret_t usr_rtc_set_from_unix(uint64_t unix_utc, int32_t tz_s) { struct sys_time t; UsrRtc_Ret_t ret; if (!s_rtc.inited) { return USR_RTC_ERR_NOT_INIT; } if (unix_utc == 0) { log_debug("unix=0, ignore"); return USR_RTC_ERR_PARAM; } /* 网关采样到写入大约慢 1s,在 UTC 上补偿 */ unix_utc += USR_RTC_SYNC_COMPENSATE_S; if ((tz_s > USR_RTC_TZ_ABS_MAX_S) || (tz_s < -USR_RTC_TZ_ABS_MAX_S)) { log_error("tz out of range %ld", (long)tz_s); return USR_RTC_ERR_PARAM; } if (unix_utc > 0xFFFFFFFFull) { log_error("unix too large"); return USR_RTC_ERR_PARAM; } usr_rtc_unix_to_calendar((uint32_t)unix_utc, tz_s, &t); if (!usr_rtc_year_ok(t.year)) { log_error("year %u not in %u~%u, wait sync", t.year, USR_RTC_YEAR_MIN, USR_RTC_YEAR_MAX); return USR_RTC_ERR_INVALID; } ret = usr_rtc_write(&t); if (ret != USR_RTC_OK) { return ret; } usr_rtc_save_tz(tz_s); log_info("set rtc %u-%02u-%02u %02u:%02u:%02u tz=%ld", t.year, t.month, t.day, t.hour, t.min, t.sec, (long)tz_s); return USR_RTC_OK; } /*============================================================================*/ /* 私有函数 */ /*============================================================================*/ /** * @brief 写硬件日历 * @param t 本地日历 * @return UsrRtc_Ret_t */ static UsrRtc_Ret_t usr_rtc_write(const struct sys_time *t) { struct sys_time tmp; if (!t) { return USR_RTC_ERR_PARAM; } if (!s_rtc.inited) { return USR_RTC_ERR_NOT_INIT; } if (!usr_rtc_year_ok(t->year) || (t->month < 1) || (t->month > 12) || (t->day < 1) || (t->hour > 23) || (t->min > 59) || (t->sec > 59)) { return USR_RTC_ERR_PARAM; } if (t->day > usr_rtc_days_in_month(t->year, t->month)) { return USR_RTC_ERR_PARAM; } tmp = *t; write_sys_time(&tmp); return USR_RTC_OK; } /** * @brief 是否闰年 * @param year 公历年 * @return 1/0 */ static uint8_t usr_rtc_is_leap(uint16_t year) { if ((year % 400u) == 0) { return 1; } if ((year % 100u) == 0) { return 0; } return ((year % 4u) == 0) ? 1 : 0; } /** * @brief 某月天数 * @param year 年 * @param month 月 1~12 * @return 天数,非法月返回 0 */ static uint8_t usr_rtc_days_in_month(uint16_t year, uint8_t month) { static const uint8_t days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; if ((month < 1) || (month > 12)) { return 0; } if ((month == 2) && usr_rtc_is_leap(year)) { return 29; } return days[month - 1]; } /** * @brief 年份是否在合法窗口 * @param year 年 * @return 1/0 */ static uint8_t usr_rtc_year_ok(uint16_t year) { return ((year >= USR_RTC_YEAR_MIN) && (year <= USR_RTC_YEAR_MAX)) ? 1 : 0; } /** * @brief UTC 秒 + 时区 → 本地日历 * @param unix_utc UTC 秒 * @param tz_s 时区偏移 * @param t 输出 */ static void usr_rtc_unix_to_calendar(uint32_t unix_utc, int32_t tz_s, struct sys_time *t) { int64_t local; uint32_t days; uint32_t tod; uint16_t y; uint8_t m; uint8_t dim; memset(t, 0, sizeof(*t)); local = (int64_t)unix_utc + (int64_t)tz_s; if (local < 0) { local = 0; } tod = (uint32_t)(local % 86400); days = (uint32_t)(local / 86400); t->sec = (uint8_t)(tod % 60u); t->min = (uint8_t)((tod / 60u) % 60u); t->hour = (uint8_t)(tod / 3600u); y = 1970; while (1) { uint32_t diy = usr_rtc_is_leap(y) ? 366u : 365u; if (days < diy) { break; } days -= diy; y++; if (y > 4095u) { break; } } t->year = y; for (m = 1; m <= 12u; m++) { dim = usr_rtc_days_in_month(y, m); if (days < dim) { break; } days -= dim; } t->month = m; t->day = (uint8_t)(days + 1u); } /** * @brief 从 VM 加载时区 */ static void usr_rtc_load_tz(void) { UsrRtcTzVm_t vm; int ret; s_rtc.tz_valid = 0; s_rtc.tz_s = 0; memset(&vm, 0, sizeof(vm)); ret = syscfg_read(CFG_USER_TIM_FLAG, (u8 *)&vm, sizeof(vm)); if ((ret == (int)sizeof(vm)) && (vm.magic == USR_RTC_TZ_MAGIC)) { if ((vm.tz_s <= USR_RTC_TZ_ABS_MAX_S) && (vm.tz_s >= -USR_RTC_TZ_ABS_MAX_S)) { s_rtc.tz_s = vm.tz_s; s_rtc.tz_valid = 1; log_info("tz from vm %ld", (long)s_rtc.tz_s); return; } } log_debug("tz vm empty"); } /** * @brief 时区写入 VM * @param tz_s 时区偏移秒 */ static void usr_rtc_save_tz(int32_t tz_s) { UsrRtcTzVm_t vm; s_rtc.tz_s = tz_s; s_rtc.tz_valid = 1; vm.magic = USR_RTC_TZ_MAGIC; vm.tz_s = tz_s; syscfg_write(CFG_USER_TIM_FLAG, (u8 *)&vm, sizeof(vm)); }