JT-DT-YD4N02B_4G_RTT_MRS/bsp/src/bsp_rtc.c

499 lines
15 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.

#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