#include "bsp_rtc.h" #include "bsp_flash.h" #include "time.h" // 用到了atoi #include #define LOG_TAG "bsp_rtc" // 该模块对应的标签。不定义时,默认:NO_TAG #define LOG_LVL LOG_LVL_DBG // 该模块对应的日志输出级别。不定义时,默认:调试级别 #include #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