RTCtime.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. #include "rtc.h"
  2. #include "main.h"
  3. #include "RTCtime.h"
  4. #include <time.h>
  5. #define FOURYEARDAY (365+365+365+366) //4年一个周期内的总天数(1970~2038不存在2100这类年份,故暂不优化)
  6. #define TIMEZONE (8) //北京时区调整
  7. extern RTC_HandleTypeDef hrtc;
  8. // 月份对应的天数(非闰年)
  9. const uint8_t month_day[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  10. // 闰年时的月份对应的天数
  11. const uint8_t Leap_month_day[12] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  12. // 判断是否为闰年
  13. uint8_t isLeapYear(uint16_t year)
  14. {
  15. return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ? 1 : 0;
  16. }
  17. /**
  18. * @brief 从4G接收缓冲区中提取+CCLK时间并转换为世纪秒
  19. * @param buffer 接收缓冲区内容
  20. * @return 成功返回世纪秒(time_t类型),失败返回-1
  21. */
  22. char time_str[50];
  23. time_t parse_cclk_time(const char *buffer)
  24. {
  25. // 查找+CCLK行
  26. const char *cclk_line = strstr(buffer, "+CCLK:");
  27. if (!cclk_line) {
  28. printf("Not found +CCLK \n");
  29. return 2;
  30. }
  31. // 查找时间字符串的起始和结束位置
  32. const char *quote_start = strchr(cclk_line, '"');
  33. if (!quote_start) {
  34. printf("Not found ""\n");
  35. return 2;
  36. }
  37. const char *quote_end = strchr(quote_start + 1, '"');
  38. if (!quote_end) {
  39. printf("Not found ""\n");
  40. return 2;
  41. }
  42. // 提取时间字符串
  43. size_t time_len = quote_end - quote_start - 1;
  44. strncpy(time_str, quote_start + 1, time_len);
  45. time_str[time_len] = '\0';
  46. // 分离时间和时区部分
  47. char *tz_pos = strpbrk(time_str, "+-");
  48. if (tz_pos) {
  49. *tz_pos = '\0'; // 截断时区部分
  50. }
  51. // 解析日期时间
  52. struct tm tm = {0};
  53. int yy, mm, dd, hh, min, sec;
  54. if (sscanf(time_str, "%d/%d/%d,%d:%d:%d",
  55. &yy, &mm, &dd, &hh, &min, &sec) != 6) {
  56. printf("Parsing_unix_failed\n");
  57. return 2;
  58. }
  59. // 填充struct tm结构体
  60. tm.tm_year = yy + 100; // 2025年表示为125
  61. tm.tm_mon = mm - 1; // 月份0-11
  62. tm.tm_mday = dd;
  63. tm.tm_hour = hh;
  64. tm.tm_min = min;
  65. tm.tm_sec = sec;
  66. tm.tm_isdst = 2; // 自动判断夏令时
  67. // 转换为世纪秒
  68. time_t epoch_time = mktime(&tm);
  69. if (epoch_time == 2) {
  70. printf("Conversion failed\n");
  71. return 2;
  72. }
  73. return epoch_time;
  74. }
  75. /**
  76. * @brief 从WIFI接收缓冲区中提取+CCLK时间并转换为世纪秒
  77. * @param buffer 接收缓冲区内容
  78. * @return 成功返回世纪秒(time_t类型),失败返回-1
  79. */
  80. time_t Parse_WIFI_Time_To_Epoch(const char *response)
  81. {
  82. struct tm timeinfo = {0};
  83. int year, month, day, hour, minute, second;
  84. // 查找 "+TIME:" 开始的位置
  85. const char *time_start = strstr(response, "+TIME:");
  86. if (time_start == NULL) {
  87. return 0; // 未找到,返回 0
  88. }
  89. // 跳过 "+TIME:"
  90. time_start += strlen("+TIME:");
  91. // 格式提取:2025-04-22,15:07:56
  92. if (sscanf(time_start, "%d-%d-%d,%d:%d:%d",
  93. &year, &month, &day, &hour, &minute, &second) != 6) {
  94. return 0; // 解析失败
  95. }
  96. timeinfo.tm_year = year - 1900; // 年份从 1900 开始计
  97. timeinfo.tm_mon = month - 1; // 月份从 0 开始计
  98. timeinfo.tm_mday = day;
  99. timeinfo.tm_hour = hour;
  100. timeinfo.tm_min = minute;
  101. timeinfo.tm_sec = second;
  102. // 本地时间转为时间戳(北京时间)
  103. time_t epoch = mktime(&timeinfo); // mktime默认是本地时区,自带处理时区。
  104. return epoch;
  105. }
  106. /**
  107. * @brief 将Unix时间戳转换为北京时间
  108. * @unixTime 需要判断的Unix时间戳
  109. * @*tempBeijing 返回的北京时间
  110. * @param Unix时间戳(从1970/1/1 00:00:00 到现在的秒数)
  111. */
  112. void covUnixTimeStp2Beijing(uint32_t unixTime, rtc_time_t *tempBeijing)
  113. {
  114. uint32_t totleDayNum=0, totleSecNum=0;
  115. uint16_t remainDayofYear=0, tempYear=0;//, tempDay=0
  116. // uint8_t *pr=NULL;
  117. const uint8_t *pr = NULL;
  118. totleDayNum = unixTime/(24*60*60); //总天数(注意加括号)
  119. totleSecNum = unixTime%(24*60*60); //当天剩余的秒速
  120. memset(tempBeijing, 0x00, sizeof(rtc_time_t));
  121. // 1.先计算时间 HH:MM:SS
  122. tempBeijing->ui8Hour = totleSecNum/3600;
  123. tempBeijing->ui8Minute = (totleSecNum%3600)/60; //error:变量搞错
  124. tempBeijing->ui8Second = (totleSecNum%3600)%60;
  125. // 2.对时间进行时区调整(注意:这里可能造成日期 +1)
  126. tempBeijing->ui8Hour +=TIMEZONE;
  127. if(tempBeijing->ui8Hour>23){
  128. //printf("modify day..\n");
  129. tempBeijing->ui8Hour -= 24;
  130. remainDayofYear++; // 日期+1
  131. }
  132. // 3.计算哪一年
  133. tempBeijing->ui8Year = 1970 + (totleDayNum / FOURYEARDAY) * 4; // 4年为一个周期
  134. remainDayofYear += totleDayNum % FOURYEARDAY;
  135. //printf("year:%d, day:%d.\n", tempBeijing->ui8Year, remainDayofYear);
  136. tempYear = isLeapYear(tempBeijing->ui8Year)?366:365;
  137. while(remainDayofYear >= tempYear) // 计算4年整数倍外的年。
  138. {
  139. tempBeijing->ui8Year++;
  140. remainDayofYear -= tempYear;
  141. tempYear = isLeapYear(tempBeijing->ui8Year)?366:365;
  142. }
  143. // 4.计算哪一月的哪一天
  144. pr = isLeapYear(tempBeijing->ui8Year)?Leap_month_day:month_day;
  145. remainDayofYear++; // 这里开始计算具体日期。remainDayofYear为 0 时其实是 1 号,所以这里要 +1
  146. while(remainDayofYear > *(pr+tempBeijing->ui8Month))
  147. {
  148. remainDayofYear -= *(pr+tempBeijing->ui8Month);
  149. tempBeijing->ui8Month++;
  150. }
  151. //printf("year:%d, day:%d.\n", tempBeijing->ui8Year, remainDayofYear);
  152. tempBeijing->ui8Month++; //month
  153. tempBeijing->ui8DayOfMonth = remainDayofYear; //day
  154. //printf("year:%d, day:%d.\n", tempBeijing->ui8Year, tempBeijing->ui8DayOfMonth);
  155. }
  156. /**
  157. * @brief 将北京时间转换为Unix时间戳
  158. * @year 需要判断的年
  159. * @param Unix时间戳(从1970/1/1 00:00:00 到现在的秒数)
  160. */
  161. uint32_t covBeijing2UnixTimeStp(rtc_time_t *beijingTime)
  162. {
  163. uint32_t daynum=0, SecNum=0; //保存北京时间到起始时间的天数
  164. uint16_t tempYear=1970, tempMonth=0;
  165. //1.年的天数
  166. while(tempYear < beijingTime->ui8Year)
  167. {
  168. if(isLeapYear(tempYear)){
  169. daynum += 366;
  170. }
  171. else{
  172. daynum += 365;
  173. }
  174. tempYear++;
  175. }
  176. //2.月的天数
  177. while(tempMonth < beijingTime->ui8Month-1)
  178. {
  179. if(isLeapYear(beijingTime->ui8Year)){ //闰年
  180. daynum += Leap_month_day[tempMonth];
  181. }
  182. else{
  183. daynum += month_day[tempMonth];
  184. }
  185. tempMonth++;
  186. }
  187. //3.天数
  188. daynum += (beijingTime->ui8DayOfMonth-1);
  189. //4.时分秒
  190. SecNum = daynum*24*60*60; //s
  191. SecNum += beijingTime->ui8Hour*60*60;
  192. SecNum += beijingTime->ui8Minute*60;
  193. SecNum += beijingTime->ui8Second;
  194. //5.时区调整
  195. SecNum -= TIMEZONE*60*60;
  196. return SecNum;
  197. }
  198. /**
  199. * @brief 设置 RTC 时间
  200. * @param epoch_time 需要设置的世纪秒时间
  201. */
  202. void Set_RTC_Time(time_t epoch_time)
  203. {
  204. RTC_TimeTypeDef sTime = {0};
  205. RTC_DateTypeDef sDate = {0};
  206. rtc_time_t beijingTime;
  207. // 转换 Unix 时间戳到 RTC 结构体
  208. covUnixTimeStp2Beijing(epoch_time, &beijingTime);
  209. // 设置 RTC 时间
  210. sTime.Hours = beijingTime.ui8Hour;
  211. sTime.Minutes = beijingTime.ui8Minute;
  212. sTime.Seconds = beijingTime.ui8Second;
  213. sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
  214. sTime.StoreOperation = RTC_STOREOPERATION_RESET;
  215. // 设置 RTC 日期
  216. sDate.Year = beijingTime.ui8Year - 2000; // STM32 的 RTC 年份从 2000 开始
  217. sDate.Month = beijingTime.ui8Month;
  218. sDate.Date = beijingTime.ui8DayOfMonth;
  219. sDate.WeekDay = RTC_WEEKDAY_MONDAY; // 这里不计算星期几,设为默认值
  220. // 写入 RTC
  221. if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
  222. {
  223. Error_Handler();
  224. }
  225. if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
  226. {
  227. Error_Handler();
  228. }
  229. }
  230. /**
  231. * @brief 获取 RTC 时间并转换为世纪秒
  232. * @return 返回当前 RTC 时间的 time_t 值
  233. */
  234. time_t Get_RTC_Time(void)
  235. {
  236. RTC_TimeTypeDef sTime = {0};
  237. RTC_DateTypeDef sDate = {0};
  238. rtc_time_t beijingTime;
  239. // 读取 RTC 时间和日期
  240. HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
  241. HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
  242. // 转换为 `rtc_time_t` 结构体
  243. beijingTime.ui8Year = sDate.Year + 2000;
  244. beijingTime.ui8Month = sDate.Month;
  245. beijingTime.ui8DayOfMonth = sDate.Date;
  246. beijingTime.ui8Hour = sTime.Hours;
  247. beijingTime.ui8Minute = sTime.Minutes;
  248. beijingTime.ui8Second = sTime.Seconds;
  249. // 转换为 Unix 时间戳
  250. return covBeijing2UnixTimeStp(&beijingTime);
  251. }
  252. void printCurrentRTCTime(void)
  253. {
  254. RTC_TimeTypeDef sTime = {0};
  255. RTC_DateTypeDef sDate = {0};
  256. char timeString[20]; // 用于存储格式化的时间字符串
  257. char dataString[20]; // 用于存储格式化的时间字符串
  258. // 获取当前时间
  259. HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
  260. // 获取当前日期
  261. HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
  262. // 格式化时间为字符串 "HH:MM:SS"
  263. snprintf(timeString, sizeof(timeString), "%02d:%02d:%02d",
  264. sTime.Hours, sTime.Minutes, sTime.Seconds);
  265. // 格式化日期为字符串 "YYYY/MM/DD"
  266. snprintf(dataString, sizeof(dataString), "20%02d/%02d/%02d",
  267. sDate.Year, sDate.Month, sDate.Date);
  268. // 打印日期
  269. printf("RTC %s-%s\n", dataString, timeString);
  270. }