499 lines
15 KiB
C
499 lines
15 KiB
C
#include "bsp_rtc.h"
|
||
#include "bsp_flash.h"
|
||
#include "time.h"
|
||
// 用到了atoi
|
||
#include <stdlib.h>
|
||
|
||
#define LOG_TAG "bsp_rtc" // 该模块对应的标签。不定义时,默认:NO_TAG
|
||
#define LOG_LVL LOG_LVL_DBG // 该模块对应的日志输出级别。不定义时,默认:调试级别
|
||
#include <ulog.h>
|
||
|
||
#define RTC_FIRST_INIT_VALUE (0x1314)
|
||
|
||
TsRtcDateTime RtcDateTime;
|
||
|
||
uint8_t const month_table[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||
|
||
/**
|
||
* @description: Judging whether it is a leap year.
|
||
* @param {uint16_t} year
|
||
* @return {*} 1 - Yes
|
||
* 0 - No
|
||
*/
|
||
// 月份 1 2 3 4 5 6 7 8 9 10 11 12
|
||
// 闰年 31 29 31 30 31 30 31 31 30 31 30 31
|
||
// 非闰年 31 28 31 30 31 30 31 31 30 31 30 31
|
||
static uint8_t Is_LeapYear(u16 year)
|
||
{
|
||
if (year % 4 == 0)
|
||
{
|
||
if (year % 100 == 0)
|
||
{
|
||
if (year % 400 == 0)
|
||
return 1;
|
||
else
|
||
return 0;
|
||
}
|
||
else
|
||
return 1;
|
||
}
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @description: Get the current day of the week.
|
||
* 输入公历日期得到星期(起始时间为: 公元0年3月1日开始, 输入往后的任何日期, 都可以获取正确的星期)
|
||
* 使用 基姆拉尔森计算公式 计算, 原理说明见此贴:
|
||
* https://www.cnblogs.com/fengbohello/p/3264300.html
|
||
* @param {uint16_t} year
|
||
* @param {uint8_t} month
|
||
* @param {uint8_t} day
|
||
* @return {*}
|
||
*/
|
||
uint8_t RTC_GetWeek(uint16_t year, uint8_t month, uint8_t day)
|
||
{
|
||
uint8_t week = 0;
|
||
|
||
if (month < 3)
|
||
{
|
||
month += 12;
|
||
--year;
|
||
}
|
||
|
||
week = (day + 1 + 2 * month + 3 * (month + 1) / 5 + year + (year >> 2) - year / 100 + year / 400) % 7;
|
||
return week;
|
||
}
|
||
|
||
/**
|
||
* @description: 将年月日时分秒转换成秒钟数
|
||
* @note 以1970年1月1日为基准, 1970年1月1日, 0时0分0秒, 表示第0秒钟
|
||
* 最大表示到2105年, 因为uint32_t最大表示136年的秒钟数(不包括闰年)!
|
||
* 本代码参考只linux mktime函数, 原理说明见此贴:
|
||
* http://www.openedv.com/thread-63389-1-1.html
|
||
* @param {uint16_t} syear
|
||
* @param {uint8_t} smon
|
||
* @param {uint8_t} sday
|
||
* @param {uint8_t} hour
|
||
* @param {uint8_t} min
|
||
* @param {uint8_t} sec
|
||
* @return {*} 转换后的秒钟数
|
||
*/
|
||
uint32_t DateTime2Seconds(uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min, uint8_t sec)
|
||
{
|
||
ASSERT((syear > 1970 || syear < 2099));
|
||
|
||
uint32_t y, m, d, x, t;
|
||
signed char monx = smon; /* 将月份转换成带符号的值, 方便后面运算 */
|
||
|
||
if (0 >= (monx -= 2)) /* 1..12 -> 11,12,1..10 */
|
||
{
|
||
monx += 12; /* Puts Feb last since it has leap day */
|
||
syear -= 1;
|
||
}
|
||
|
||
y = (syear - 1) * 365 + syear / 4 - syear / 100 + syear / 400; /* 公元元年1到现在的闰年数 */
|
||
m = 367 * monx / 12 - 30 + 59;
|
||
d = sday - 1;
|
||
x = y + m + d - 719162; /* 减去公元元年到1970年的天数 */
|
||
t = ((x * 24 + hour) * 60 + min) * 60 + sec; /* 总秒钟数 */
|
||
return t;
|
||
}
|
||
|
||
uint32_t RTC_Time2TimeStamp(void)
|
||
{
|
||
RTC_GetTime();
|
||
uint32_t time_stamp = DateTime2Seconds(RtcDateTime.year, RtcDateTime.month, RtcDateTime.day, RtcDateTime.hour, RtcDateTime.minute, RtcDateTime.second) - (8 * 3600);
|
||
|
||
return time_stamp;
|
||
}
|
||
|
||
void Seconds2DateTime(uint32_t seconds, TsRtcDateTime *pDateTime)
|
||
{
|
||
uint16_t daycnt = 0;
|
||
uint32_t timecount = 0;
|
||
uint32_t temp = 0;
|
||
uint16_t temp1 = 0;
|
||
|
||
timecount = seconds;
|
||
temp = timecount / 86400;
|
||
if (daycnt != temp)
|
||
{
|
||
daycnt = temp;
|
||
temp1 = 1970;
|
||
while (temp >= 365)
|
||
{
|
||
if (Is_LeapYear(temp1))
|
||
{
|
||
if (temp >= 366)
|
||
temp -= 366;
|
||
else
|
||
{
|
||
temp1++;
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
temp -= 365;
|
||
temp1++;
|
||
}
|
||
pDateTime->year = temp1;
|
||
temp1 = 0;
|
||
while (temp >= 28)
|
||
{
|
||
if (Is_LeapYear(pDateTime->year) && temp1 == 1)
|
||
{
|
||
if (temp >= 29)
|
||
temp -= 29;
|
||
else
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
if (temp >= month_table[temp1])
|
||
temp -= month_table[temp1];
|
||
else
|
||
break;
|
||
}
|
||
temp1++;
|
||
}
|
||
pDateTime->month = temp1 + 1;
|
||
pDateTime->day = temp + 1;
|
||
}
|
||
temp = timecount % 86400;
|
||
pDateTime->hour = temp / 3600;
|
||
pDateTime->minute = (temp % 3600) / 60;
|
||
pDateTime->second = (temp % 3600) % 60;
|
||
pDateTime->week = RTC_GetWeek(pDateTime->year, pDateTime->month, pDateTime->day);
|
||
}
|
||
|
||
/**
|
||
* @description: 设置时间, 包括年月日时分秒
|
||
* @note 以1970年1月1日为基准, 往后累加时间
|
||
* 合法年份范围为: 1970 ~ 2105年
|
||
HAL默认为年份起点为2000年
|
||
* @param {uint16_t} syear
|
||
* @param {uint8_t} smon
|
||
* @param {uint8_t} sday
|
||
* @param {uint8_t} hour
|
||
* @param {uint8_t} min
|
||
* @param {uint8_t} sec
|
||
* @return {*}
|
||
*/
|
||
void RTC_SetTime(rt_uint16_t syear, rt_uint8_t smon, rt_uint8_t sday, rt_uint8_t hour, rt_uint8_t min, rt_uint8_t sec)
|
||
{
|
||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
|
||
PWR_BackupAccessCmd(ENABLE);
|
||
|
||
RTC_SetCounter(DateTime2Seconds(syear, smon, sday, hour, min, sec));
|
||
RTC_WaitForLastTask();
|
||
}
|
||
|
||
// refresh RtcDateTime
|
||
/**
|
||
* @description: 该函数不直接返回时间, 时间数据保存在RtcDateTime结构体里面
|
||
* @return {*}
|
||
*/
|
||
void RTC_GetTime(void)
|
||
{
|
||
uint16_t daycnt = 0;
|
||
uint32_t timecount = 0;
|
||
uint32_t temp = 0;
|
||
uint16_t temp1 = 0;
|
||
|
||
timecount = RTC_GetCounter();
|
||
temp = timecount / 86400;
|
||
if (daycnt != temp)
|
||
{
|
||
daycnt = temp;
|
||
temp1 = 1970;
|
||
while (temp >= 365)
|
||
{
|
||
if (Is_LeapYear(temp1))
|
||
{
|
||
if (temp >= 366)
|
||
temp -= 366;
|
||
else
|
||
{
|
||
temp1++;
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
temp -= 365;
|
||
temp1++;
|
||
}
|
||
RtcDateTime.year = temp1;
|
||
temp1 = 0;
|
||
while (temp >= 28)
|
||
{
|
||
if (Is_LeapYear(RtcDateTime.year) && temp1 == 1)
|
||
{
|
||
if (temp >= 29)
|
||
temp -= 29;
|
||
else
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
if (temp >= month_table[temp1])
|
||
temp -= month_table[temp1];
|
||
else
|
||
break;
|
||
}
|
||
temp1++;
|
||
}
|
||
RtcDateTime.month = temp1 + 1;
|
||
RtcDateTime.day = temp + 1;
|
||
}
|
||
temp = timecount % 86400;
|
||
RtcDateTime.hour = temp / 3600;
|
||
RtcDateTime.minute = (temp % 3600) / 60;
|
||
RtcDateTime.second = (temp % 3600) % 60;
|
||
RtcDateTime.week = RTC_GetWeek(RtcDateTime.year, RtcDateTime.month, RtcDateTime.day);
|
||
}
|
||
|
||
static void RTC_NVIC_Config(void)
|
||
{
|
||
NVIC_InitTypeDef NVIC_InitStructure;
|
||
NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn; // RTC全局中断
|
||
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 先占优先级1位,从优先级3位
|
||
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 6; // 先占优先级0位,从优先级4位
|
||
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能该通道中断
|
||
NVIC_Init(&NVIC_InitStructure); // 根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
|
||
}
|
||
|
||
static uint8_t RTC_FirstInit(void)
|
||
{
|
||
uint8_t temp = 0;
|
||
|
||
RCC_LSEConfig(RCC_LSE_ON);
|
||
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET && temp < 250)
|
||
{
|
||
temp++;
|
||
rt_thread_mdelay(5);
|
||
}
|
||
if (temp >= 250)
|
||
{
|
||
LOG_E("RTC_FirstInit: LSE not ready");
|
||
return 1;
|
||
}
|
||
|
||
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
|
||
RCC_RTCCLKCmd(ENABLE);
|
||
RTC_WaitForLastTask();
|
||
RTC_WaitForSynchro();
|
||
RTC_WaitForLastTask();
|
||
RTC_SetPrescaler(32767);
|
||
RTC_WaitForLastTask();
|
||
|
||
// RTC_SetTime(2024, 9, 5, 0, 0, 0); //设置出厂日期
|
||
// RTC_WaitForLastTask();
|
||
//
|
||
// RTC_SetAlarm(20 + RTC_GetCounter()); //闹钟值设定为当前时间的10秒后
|
||
// RTC_WaitForLastTask();
|
||
//
|
||
// RTC_ExitConfigMode(); //退出配置模式
|
||
|
||
// RTC_ITConfig(RTC_IT_ALR, ENABLE);
|
||
// RTC_ITConfig(RTC_IT_SEC, ENABLE);
|
||
// RTC_WaitForLastTask();
|
||
|
||
/* Is it the first configuration */
|
||
BKP_WriteBackupRegister(BKP_DR1, RTC_FIRST_INIT_VALUE);
|
||
|
||
TuFlashProductTimeLimitFrame LimitTime;
|
||
if (Flash_GetProductTimeLimit(&LimitTime, kFactoryTimeId) == READY)
|
||
{
|
||
RTC_SetTime(LimitTime.Struct.year, LimitTime.Struct.month, LimitTime.Struct.day,
|
||
LimitTime.Struct.hour, LimitTime.Struct.minute, 0); /* Setup Time */
|
||
LOG_D("RTC_SetTime:%04d-%02d-%02d,%02d:%02d",
|
||
LimitTime.Struct.year, LimitTime.Struct.month, LimitTime.Struct.day,
|
||
LimitTime.Struct.hour, LimitTime.Struct.minute);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
int BSP_RTC_Init(void)
|
||
{
|
||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
|
||
PWR_BackupAccessCmd(ENABLE);
|
||
|
||
LOG_D("RCC_GetFlagStatus(RCC_FLAG_LSERDY): %d", RCC_GetFlagStatus(RCC_FLAG_LSERDY));
|
||
/* Is it the first configuration */
|
||
if (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET || BKP_ReadBackupRegister(BKP_DR1) != RTC_FIRST_INIT_VALUE)
|
||
{
|
||
LOG_W("RTC hasn't been configured, please use [RTC_Cfg_Time] to config.");
|
||
RTC_FirstInit();
|
||
}
|
||
else
|
||
{
|
||
// 外部手动复位
|
||
// if (RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET)
|
||
// {
|
||
// LOG_D("外部硬件复位信号");
|
||
// }
|
||
// 上电/掉电复位标志
|
||
if (RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET)
|
||
{
|
||
LOG_D("上电复位");
|
||
}
|
||
// 软件复位
|
||
if (RCC_GetFlagStatus(RCC_FLAG_SFTRST) != RESET)
|
||
{
|
||
LOG_D("软件复位");
|
||
}
|
||
// 独立看门狗复位
|
||
if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET)
|
||
{
|
||
LOG_D("独立看门狗复位");
|
||
}
|
||
// 窗口看门狗复位
|
||
if (RCC_GetFlagStatus(RCC_FLAG_WWDGRST) != RESET)
|
||
{
|
||
LOG_D("窗口看门狗复位");
|
||
}
|
||
// 低功耗复位
|
||
if (RCC_GetFlagStatus(RCC_FLAG_LPWRRST) != RESET)
|
||
{
|
||
LOG_D("低功耗复位");
|
||
}
|
||
|
||
// 虽然RTC模块不需要重新配置,且掉电后依靠后备电池依然运行
|
||
// 但是每次上电后,还是要使能RTCCLK
|
||
RCC_RTCCLKCmd(ENABLE); // 使能RTCCLK
|
||
RTC_WaitForLastTask();
|
||
RTC_WaitForSynchro(); // 等待RTC时钟与APB1时钟同步
|
||
|
||
// RTC_ITConfig(RTC_IT_ALR, ENABLE);
|
||
// RTC_ITConfig(RTC_IT_SEC, ENABLE);
|
||
RTC_WaitForLastTask();
|
||
}
|
||
RTC_NVIC_Config();
|
||
RTC_GetTime();
|
||
LOG_D("%4d-%02d-%02d, %02d:%02d:%02d", RtcDateTime.year, RtcDateTime.month, RtcDateTime.day,
|
||
RtcDateTime.hour, RtcDateTime.minute, RtcDateTime.second);
|
||
LOG_I("BSP_RTC_Init!");
|
||
|
||
return 0;
|
||
}
|
||
// INIT_PREV_EXPORT(BSP_RTC_Init);
|
||
|
||
#if 1
|
||
void RTC_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
|
||
void RTC_IRQHandler(void)
|
||
{
|
||
GET_INT_SP();
|
||
/* enter interrupt */
|
||
rt_interrupt_enter();
|
||
|
||
if (RTC_GetITStatus(RTC_IT_SEC) != RESET) // 秒钟中断
|
||
{
|
||
RTC_ClearITPendingBit(RTC_IT_SEC | RTC_IT_OW); // 清秒中断
|
||
RTC_GetTime(); // 更新时间
|
||
LOG_D("%4d-%02d-%02d, %02d:%02d:%02d", RtcDateTime.year, RtcDateTime.month, RtcDateTime.day,
|
||
RtcDateTime.hour, RtcDateTime.minute, RtcDateTime.second);
|
||
}
|
||
|
||
// if (RTC_GetITStatus(RTC_IT_ALR) != RESET) // 闹钟中断
|
||
// {
|
||
// RTC_ClearITPendingBit(RTC_IT_ALR); // 清闹钟中断
|
||
// RTC_GetTime(); // 更新时间
|
||
// RTC_WaitForLastTask();
|
||
// }
|
||
|
||
rt_interrupt_leave();
|
||
FREE_INT_SP();
|
||
}
|
||
#endif
|
||
|
||
// 将时间戳转换为 TsRtcDateTime 结构体
|
||
void Timestamp_To_Rtc_DateTime(time_t timestamp, TsRtcDateTime *rtc_dt)
|
||
{
|
||
struct tm *tm_info;
|
||
time_t local_timestamp = timestamp + 8 * 3600; // 北京时间比UTC时间快8小时
|
||
|
||
tm_info = localtime(&local_timestamp);
|
||
if (tm_info != NULL)
|
||
{
|
||
rtc_dt->year = tm_info->tm_year + 1900;
|
||
rtc_dt->month = tm_info->tm_mon + 1;
|
||
rtc_dt->day = tm_info->tm_mday;
|
||
rtc_dt->hour = tm_info->tm_hour;
|
||
rtc_dt->minute = tm_info->tm_min;
|
||
rtc_dt->second = tm_info->tm_sec;
|
||
rtc_dt->week = RTC_GetWeek(rtc_dt->year, rtc_dt->month, rtc_dt->day);
|
||
|
||
// rtc_dt->week = tm_info->tm_wday + 1; // tm_wday 的范围是0-6,对应星期日到星期六
|
||
}
|
||
else
|
||
{
|
||
// 处理时间转换失败的情况
|
||
rtc_dt->year = 0;
|
||
rtc_dt->month = 0;
|
||
rtc_dt->day = 0;
|
||
rtc_dt->hour = 0;
|
||
rtc_dt->minute = 0;
|
||
rtc_dt->second = 0;
|
||
rtc_dt->week = 0;
|
||
}
|
||
}
|
||
|
||
#ifdef TEST_ENABLE
|
||
|
||
static void RTC_ShowTime(void)
|
||
{
|
||
RTC_GetTime();
|
||
LOG_D("RTC Timer : %04d-%02d-%02d %02d:%02d:%02d week[%d]", RtcDateTime.year, RtcDateTime.month, RtcDateTime.day,
|
||
RtcDateTime.hour, RtcDateTime.minute, RtcDateTime.second, RtcDateTime.week);
|
||
}
|
||
MSH_CMD_EXPORT(RTC_ShowTime, "get rtc timer");
|
||
|
||
static void RTC_Cfg_Time(int argc, char **argv)
|
||
{
|
||
if (argc == 7)
|
||
{
|
||
/*年 月 日 时 分 秒*/
|
||
RTC_SetTime((rt_uint16_t)(atoi(argv[1])), (rt_uint8_t)(atoi(argv[2])), (rt_uint8_t)(atoi(argv[3])),
|
||
(rt_uint8_t)(atoi(argv[4])), (rt_uint8_t)(atoi(argv[5])), (rt_uint8_t)(atoi(argv[6])));
|
||
RTC_ShowTime();
|
||
}
|
||
else
|
||
{
|
||
LOG_E("RTC_Cfg_Time --use [y m d h m s]");
|
||
}
|
||
}
|
||
MSH_CMD_EXPORT(RTC_Cfg_Time, "y m d h m s");
|
||
|
||
void RTC_Control(int argc, char **argv)
|
||
{
|
||
if (argc == 2)
|
||
{
|
||
int flag = atoi(argv[1]);
|
||
if (flag == 0)
|
||
{
|
||
RTC_ITConfig(RTC_IT_SEC, DISABLE);
|
||
RTC_WaitForLastTask();
|
||
}
|
||
else if (flag == 1)
|
||
{
|
||
RTC_ITConfig(RTC_IT_SEC, ENABLE);
|
||
RTC_WaitForLastTask();
|
||
}
|
||
else if (flag == 2)
|
||
{
|
||
RTC_GetTime();
|
||
LOG_D("%4d-%02d-%02d, week:%d, %02d:%02d:%02d", RtcDateTime.year, RtcDateTime.month, RtcDateTime.day,
|
||
RtcDateTime.week, RtcDateTime.hour, RtcDateTime.minute, RtcDateTime.second);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
LOG_E("Show rtc time --use _cmd_ [0:关IT/1:开IT/2:show rtc time(once)]");
|
||
}
|
||
}
|
||
MSH_CMD_EXPORT(RTC_Control, "0, 1(1s)show time: 2-show time once");
|
||
|
||
#endif // TSET_BSP_RTC
|