Files
JBao_Door_Clamp_FW/apps/usr_periph/usr_rtc.c
T
wuchuyuan fdb43011b7 1、低电 3.7V 只闪红灯不再 1 分钟后关机;电量低于约 5% 自动关机,未充电开机先闪 5 下红灯再睡回,只能充电后才能开机
2、自动/手动互相切换都闪 2 下灯;APP 发停止后再发自动模式可正常启动,手动残留停止不再挡住自动
3、自动模式 1~6 单次最长跑 10 分钟后回待机;关机时长按 12 秒进 OTA,OTA 期间红绿交替闪
2026-08-27 10:20:48 +08:00

367 lines
9.1 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/******************************************************************************
* @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));
}