#include "rtc.h" #include "main.h" #include "RTCtime.h" #include #define FOURYEARDAY (365+365+365+366) //4年一个周期内的总天数(1970~2038不存在2100这类年份,故暂不优化) #define TIMEZONE (8) //北京时区调整 extern RTC_HandleTypeDef hrtc; // 月份对应的天数(非闰年) const uint8_t month_day[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // 闰年时的月份对应的天数 const uint8_t Leap_month_day[12] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // 判断是否为闰年 uint8_t isLeapYear(uint16_t year) { return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ? 1 : 0; } /** * @brief 从4G接收缓冲区中提取+CCLK时间并转换为世纪秒 * @param buffer 接收缓冲区内容 * @return 成功返回世纪秒(time_t类型),失败返回-1 */ char time_str[50]; time_t parse_cclk_time(const char *buffer) { // 查找+CCLK行 const char *cclk_line = strstr(buffer, "+CCLK:"); if (!cclk_line) { printf("Not found +CCLK \n"); return 2; } // 查找时间字符串的起始和结束位置 const char *quote_start = strchr(cclk_line, '"'); if (!quote_start) { printf("Not found ""\n"); return 2; } const char *quote_end = strchr(quote_start + 1, '"'); if (!quote_end) { printf("Not found ""\n"); return 2; } // 提取时间字符串 size_t time_len = quote_end - quote_start - 1; strncpy(time_str, quote_start + 1, time_len); time_str[time_len] = '\0'; // 分离时间和时区部分 char *tz_pos = strpbrk(time_str, "+-"); if (tz_pos) { *tz_pos = '\0'; // 截断时区部分 } // 解析日期时间 struct tm tm = {0}; int yy, mm, dd, hh, min, sec; if (sscanf(time_str, "%d/%d/%d,%d:%d:%d", &yy, &mm, &dd, &hh, &min, &sec) != 6) { printf("Parsing_unix_failed\n"); return 2; } // 填充struct tm结构体 tm.tm_year = yy + 100; // 2025年表示为125 tm.tm_mon = mm - 1; // 月份0-11 tm.tm_mday = dd; tm.tm_hour = hh; tm.tm_min = min; tm.tm_sec = sec; tm.tm_isdst = 2; // 自动判断夏令时 // 转换为世纪秒 time_t epoch_time = mktime(&tm); if (epoch_time == 2) { printf("Conversion failed\n"); return 2; } return epoch_time; } /** * @brief 从WIFI接收缓冲区中提取+CCLK时间并转换为世纪秒 * @param buffer 接收缓冲区内容 * @return 成功返回世纪秒(time_t类型),失败返回-1 */ time_t Parse_WIFI_Time_To_Epoch(const char *response) { struct tm timeinfo = {0}; int year, month, day, hour, minute, second; // 查找 "+TIME:" 开始的位置 const char *time_start = strstr(response, "+TIME:"); if (time_start == NULL) { return 0; // 未找到,返回 0 } // 跳过 "+TIME:" time_start += strlen("+TIME:"); // 格式提取:2025-04-22,15:07:56 if (sscanf(time_start, "%d-%d-%d,%d:%d:%d", &year, &month, &day, &hour, &minute, &second) != 6) { return 0; // 解析失败 } timeinfo.tm_year = year - 1900; // 年份从 1900 开始计 timeinfo.tm_mon = month - 1; // 月份从 0 开始计 timeinfo.tm_mday = day; timeinfo.tm_hour = hour; timeinfo.tm_min = minute; timeinfo.tm_sec = second; // 本地时间转为时间戳(北京时间) time_t epoch = mktime(&timeinfo); // mktime默认是本地时区,自带处理时区。 return epoch; } /** * @brief 将Unix时间戳转换为北京时间 * @unixTime 需要判断的Unix时间戳 * @*tempBeijing 返回的北京时间 * @param Unix时间戳(从1970/1/1 00:00:00 到现在的秒数) */ void covUnixTimeStp2Beijing(uint32_t unixTime, rtc_time_t *tempBeijing) { uint32_t totleDayNum=0, totleSecNum=0; uint16_t remainDayofYear=0, tempYear=0;//, tempDay=0 // uint8_t *pr=NULL; const uint8_t *pr = NULL; totleDayNum = unixTime/(24*60*60); //总天数(注意加括号) totleSecNum = unixTime%(24*60*60); //当天剩余的秒速 memset(tempBeijing, 0x00, sizeof(rtc_time_t)); // 1.先计算时间 HH:MM:SS tempBeijing->ui8Hour = totleSecNum/3600; tempBeijing->ui8Minute = (totleSecNum%3600)/60; //error:变量搞错 tempBeijing->ui8Second = (totleSecNum%3600)%60; // 2.对时间进行时区调整(注意:这里可能造成日期 +1) tempBeijing->ui8Hour +=TIMEZONE; if(tempBeijing->ui8Hour>23){ //printf("modify day..\n"); tempBeijing->ui8Hour -= 24; remainDayofYear++; // 日期+1 } // 3.计算哪一年 tempBeijing->ui8Year = 1970 + (totleDayNum / FOURYEARDAY) * 4; // 4年为一个周期 remainDayofYear += totleDayNum % FOURYEARDAY; //printf("year:%d, day:%d.\n", tempBeijing->ui8Year, remainDayofYear); tempYear = isLeapYear(tempBeijing->ui8Year)?366:365; while(remainDayofYear >= tempYear) // 计算4年整数倍外的年。 { tempBeijing->ui8Year++; remainDayofYear -= tempYear; tempYear = isLeapYear(tempBeijing->ui8Year)?366:365; } // 4.计算哪一月的哪一天 pr = isLeapYear(tempBeijing->ui8Year)?Leap_month_day:month_day; remainDayofYear++; // 这里开始计算具体日期。remainDayofYear为 0 时其实是 1 号,所以这里要 +1 while(remainDayofYear > *(pr+tempBeijing->ui8Month)) { remainDayofYear -= *(pr+tempBeijing->ui8Month); tempBeijing->ui8Month++; } //printf("year:%d, day:%d.\n", tempBeijing->ui8Year, remainDayofYear); tempBeijing->ui8Month++; //month tempBeijing->ui8DayOfMonth = remainDayofYear; //day //printf("year:%d, day:%d.\n", tempBeijing->ui8Year, tempBeijing->ui8DayOfMonth); } /** * @brief 将北京时间转换为Unix时间戳 * @year 需要判断的年 * @param Unix时间戳(从1970/1/1 00:00:00 到现在的秒数) */ uint32_t covBeijing2UnixTimeStp(rtc_time_t *beijingTime) { uint32_t daynum=0, SecNum=0; //保存北京时间到起始时间的天数 uint16_t tempYear=1970, tempMonth=0; //1.年的天数 while(tempYear < beijingTime->ui8Year) { if(isLeapYear(tempYear)){ daynum += 366; } else{ daynum += 365; } tempYear++; } //2.月的天数 while(tempMonth < beijingTime->ui8Month-1) { if(isLeapYear(beijingTime->ui8Year)){ //闰年 daynum += Leap_month_day[tempMonth]; } else{ daynum += month_day[tempMonth]; } tempMonth++; } //3.天数 daynum += (beijingTime->ui8DayOfMonth-1); //4.时分秒 SecNum = daynum*24*60*60; //s SecNum += beijingTime->ui8Hour*60*60; SecNum += beijingTime->ui8Minute*60; SecNum += beijingTime->ui8Second; //5.时区调整 SecNum -= TIMEZONE*60*60; return SecNum; } /** * @brief 设置 RTC 时间 * @param epoch_time 需要设置的世纪秒时间 */ void Set_RTC_Time(time_t epoch_time) { RTC_TimeTypeDef sTime = {0}; RTC_DateTypeDef sDate = {0}; rtc_time_t beijingTime; // 转换 Unix 时间戳到 RTC 结构体 covUnixTimeStp2Beijing(epoch_time, &beijingTime); // 设置 RTC 时间 sTime.Hours = beijingTime.ui8Hour; sTime.Minutes = beijingTime.ui8Minute; sTime.Seconds = beijingTime.ui8Second; sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; sTime.StoreOperation = RTC_STOREOPERATION_RESET; // 设置 RTC 日期 sDate.Year = beijingTime.ui8Year - 2000; // STM32 的 RTC 年份从 2000 开始 sDate.Month = beijingTime.ui8Month; sDate.Date = beijingTime.ui8DayOfMonth; sDate.WeekDay = RTC_WEEKDAY_MONDAY; // 这里不计算星期几,设为默认值 // 写入 RTC if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK) { Error_Handler(); } if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK) { Error_Handler(); } } /** * @brief 获取 RTC 时间并转换为世纪秒 * @return 返回当前 RTC 时间的 time_t 值 */ time_t Get_RTC_Time(void) { RTC_TimeTypeDef sTime = {0}; RTC_DateTypeDef sDate = {0}; rtc_time_t beijingTime; // 读取 RTC 时间和日期 HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN); HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN); // 转换为 `rtc_time_t` 结构体 beijingTime.ui8Year = sDate.Year + 2000; beijingTime.ui8Month = sDate.Month; beijingTime.ui8DayOfMonth = sDate.Date; beijingTime.ui8Hour = sTime.Hours; beijingTime.ui8Minute = sTime.Minutes; beijingTime.ui8Second = sTime.Seconds; // 转换为 Unix 时间戳 return covBeijing2UnixTimeStp(&beijingTime); } void printCurrentRTCTime(void) { RTC_TimeTypeDef sTime = {0}; RTC_DateTypeDef sDate = {0}; char timeString[20]; // 用于存储格式化的时间字符串 char dataString[20]; // 用于存储格式化的时间字符串 // 获取当前时间 HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN); // 获取当前日期 HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN); // 格式化时间为字符串 "HH:MM:SS" snprintf(timeString, sizeof(timeString), "%02d:%02d:%02d", sTime.Hours, sTime.Minutes, sTime.Seconds); // 格式化日期为字符串 "YYYY/MM/DD" snprintf(dataString, sizeof(dataString), "20%02d/%02d/%02d", sDate.Year, sDate.Month, sDate.Date); // 打印日期 printf("RTC %s-%s\n", dataString, timeString); }