2、自动/手动互相切换都闪 2 下灯;APP 发停止后再发自动模式可正常启动,手动残留停止不再挡住自动 3、自动模式 1~6 单次最长跑 10 分钟后回待机;关机时长按 12 秒进 OTA,OTA 期间红绿交替闪
367 lines
9.1 KiB
C
367 lines
9.1 KiB
C
/******************************************************************************
|
||
* @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, 首次发布,实现板级硬件 RTC 对时与本地日历读取
|
||
* - 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 USR_RTC_OK=成功,USR_RTC_ERR_NOT_INIT=设备打开失败或 RTC 未使能
|
||
*/
|
||
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 USR_RTC_OK=成功,USR_RTC_ERR_NOT_INIT=未初始化,USR_RTC_ERR_PARAM=参数非法,USR_RTC_ERR_INVALID=年份非法
|
||
*/
|
||
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 USR_RTC_OK=成功,USR_RTC_ERR_PARAM=参数非法,USR_RTC_ERR_NOT_INIT=未初始化
|
||
*/
|
||
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 输出本地日历
|
||
* @return 无
|
||
*/
|
||
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 加载时区
|
||
* @return 无
|
||
*/
|
||
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 时区偏移秒
|
||
* @return 无
|
||
*/
|
||
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));
|
||
}
|