123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- #include "rtc.h"
- #include "main.h"
- #include "RTCtime.h"
- #include <time.h>
- #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);
- }
|