rtc.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. /*
  2. * Copyright (c) 2024, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2024-xx-xx user first version
  9. */
  10. #define DBG_TAG "rtc"
  11. #define DBG_LVL DBG_INFO
  12. #include <rtdbg.h>
  13. #include "rtc.h"
  14. /* RTC句柄 */
  15. RTC_HandleTypeDef hrtc;
  16. /* 时间打印线程相关 */
  17. #define RTC_PRINT_THREAD_STACK_SIZE (256 + 128) // 太大没必要
  18. #define RTC_PRINT_THREAD_PRIORITY 5
  19. #define RTC_PRINT_THREAD_TICK 10
  20. static rt_thread_t rtc_print_tid = RT_NULL;
  21. struct rtc_time time_now = {0};
  22. RTC_DateTypeDef GetDate;
  23. RTC_TimeTypeDef GetTime;
  24. /**
  25. * @brief 判断是否为闰年
  26. * @param year 年份(2000-2099)
  27. * @return 1:闰年, 0:平年
  28. */
  29. static rt_uint8_t is_leap_year(rt_uint16_t year)
  30. {
  31. if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
  32. return 1;
  33. return 0;
  34. }
  35. /**
  36. * @brief 将年月日转换为星期
  37. * @param year 年份(2000-2099)
  38. * @param month 月份(1-12)
  39. * @param date 日期(1-31)
  40. * @return 星期(0=周日, 1=周一, ..., 6=周六)
  41. */
  42. static rt_uint8_t calc_weekday(rt_uint16_t year, rt_uint8_t month, rt_uint8_t date)
  43. {
  44. /* Zeller公式计算星期 */
  45. rt_int16_t y = year;
  46. rt_int8_t m = month;
  47. rt_int8_t d = date;
  48. rt_int8_t c = y / 100;
  49. y = y % 100;
  50. if (m < 3)
  51. {
  52. m += 12;
  53. y -= 1;
  54. }
  55. rt_int8_t w = (y + y / 4 + c / 4 - 2 * c + 26 * (m + 1) / 10 + d - 1) % 7;
  56. return (w < 0) ? (w + 7) : w;
  57. }
  58. /**
  59. * @brief 检查日期时间是否有效
  60. * @param time 时间结构体指针
  61. * @return 1:有效, 0:无效
  62. */
  63. static rt_uint8_t is_time_valid(const struct rtc_time *time)
  64. {
  65. const rt_uint8_t days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  66. rt_uint8_t max_day;
  67. if (time->year < 2000 || time->year > 2099)
  68. return 0;
  69. if (time->month < 1 || time->month > 12)
  70. return 0;
  71. max_day = days_in_month[time->month - 1];
  72. if (time->month == 2 && is_leap_year(time->year))
  73. max_day = 29;
  74. if (time->date < 1 || time->date > max_day)
  75. return 0;
  76. if (time->hour > 23)
  77. return 0;
  78. if (time->minute > 59)
  79. return 0;
  80. if (time->second > 59)
  81. return 0;
  82. return 1;
  83. }
  84. /**
  85. * @brief 将BCD码转换为二进制
  86. */
  87. static rt_uint8_t bcd_to_bin(rt_uint8_t bcd)
  88. {
  89. return ((bcd >> 4) * 10) + (bcd & 0x0F);
  90. }
  91. /**
  92. * @brief 将二进制转换为BCD码
  93. */
  94. static rt_uint8_t bin_to_bcd(rt_uint8_t bin)
  95. {
  96. return ((bin / 10) << 4) | (bin % 10);
  97. }
  98. /**
  99. * @brief 获取RTC时间
  100. * @param time 时间结构体指针
  101. * @return 0:成功, -1:失败
  102. */
  103. int rtc_get_time(struct rtc_time *time)
  104. {
  105. RTC_DateTypeDef sdate;
  106. RTC_TimeTypeDef stim;
  107. if (time == RT_NULL)
  108. return -1;
  109. /* 获取时间和日期 - 必须先获取时间再获取日期 */
  110. HAL_RTC_GetTime(&hrtc, &stim, RTC_FORMAT_BCD);
  111. HAL_RTC_GetDate(&hrtc, &sdate, RTC_FORMAT_BCD);
  112. /* 转换为标准格式 */
  113. time->year = 2000 + bcd_to_bin(sdate.Year);
  114. time->month = bcd_to_bin(sdate.Month);
  115. time->date = bcd_to_bin(sdate.Date);
  116. time->weekday = bcd_to_bin(sdate.WeekDay);
  117. time->hour = bcd_to_bin(stim.Hours);
  118. time->minute = bcd_to_bin(stim.Minutes);
  119. time->second = bcd_to_bin(stim.Seconds);
  120. return 0;
  121. }
  122. /**
  123. * @brief 设置RTC时间
  124. * @param time 时间结构体指针
  125. * @return 0:成功, -1:失败
  126. */
  127. int rtc_set_time(struct rtc_time *time)
  128. {
  129. RTC_DateTypeDef sdate;
  130. RTC_TimeTypeDef stim;
  131. if (time == RT_NULL || !is_time_valid(time))
  132. return -1;
  133. /* 填充日期结构体 */
  134. sdate.Year = bin_to_bcd(time->year - 2000);
  135. sdate.Month = bin_to_bcd(time->month);
  136. sdate.Date = bin_to_bcd(time->date);
  137. sdate.WeekDay = bin_to_bcd(calc_weekday(time->year, time->month, time->date));
  138. /* 填充时间结构体 */
  139. stim.Hours = bin_to_bcd(time->hour);
  140. stim.Minutes = bin_to_bcd(time->minute);
  141. stim.Seconds = bin_to_bcd(time->second);
  142. stim.TimeFormat = RTC_HOURFORMAT12_AM;
  143. stim.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
  144. stim.StoreOperation = RTC_STOREOPERATION_RESET;
  145. /* 设置日期和时间 */
  146. if (HAL_RTC_SetDate(&hrtc, &sdate, RTC_FORMAT_BCD) != HAL_OK)
  147. return -1;
  148. if (HAL_RTC_SetTime(&hrtc, &stim, RTC_FORMAT_BCD) != HAL_OK)
  149. return -1;
  150. return 0;
  151. }
  152. /**
  153. * @brief 将RTC时间转换为字符串
  154. * @param time 时间结构体指针
  155. * @param buf 缓冲区
  156. * @param size 缓冲区大小
  157. */
  158. void rtc_time_to_str(const struct rtc_time *time, char *buf, rt_size_t size)
  159. {
  160. const char *week_str[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
  161. if (time == RT_NULL || buf == RT_NULL || size < 32)
  162. return;
  163. rt_snprintf(buf, size, "%04d-%02d-%02d %02d:%02d:%02d %s",
  164. time->year, time->month, time->date,
  165. time->hour, time->minute, time->second,
  166. week_str[time->weekday % 7]);
  167. }
  168. /**
  169. * @brief 写入默认时间到RTC
  170. */
  171. static void rtc_write_default_time(void)
  172. {
  173. struct rtc_time time;
  174. time.year = 2000 + RTC_DEFAULT_YEAR;
  175. time.month = RTC_DEFAULT_MONTH;
  176. time.date = RTC_DEFAULT_DATE;
  177. time.hour = RTC_DEFAULT_HOUR;
  178. time.minute = RTC_DEFAULT_MINUTE;
  179. time.second = RTC_DEFAULT_SECOND;
  180. if (rtc_set_time(&time) == 0)
  181. {
  182. LOG_I("RTC default time set: %04d-%02d-%02d %02d:%02d:%02d",
  183. time.year, time.month, time.date,
  184. time.hour, time.minute, time.second);
  185. }
  186. else
  187. {
  188. LOG_E("RTC set default time failed!");
  189. }
  190. }
  191. /**
  192. * @brief 时间打印线程入口函数
  193. * @param parameter 线程参数
  194. */
  195. static void rtc_print_thread_entry(void *parameter)
  196. {
  197. char time_str[32];
  198. static char once = 3;
  199. while (1)
  200. {
  201. /* 获取当前时间 */
  202. if (rtc_get_time(&time_now) == 0)
  203. {
  204. GetDate.Year = time_now.year - 2000;
  205. GetDate.Month = time_now.month;
  206. GetDate.Date = time_now.date;
  207. GetTime.Hours = time_now.hour;
  208. GetTime.Minutes = time_now.minute;
  209. GetTime.Seconds = time_now.second;
  210. rtc_time_to_str(&time_now, time_str, sizeof(time_str));
  211. if (once)
  212. {
  213. once--;
  214. LOG_D(" Current Time: %s\n", time_str);
  215. }
  216. }
  217. else
  218. {
  219. LOG_I(" Get time failed!\n");
  220. }
  221. /* 延时1秒 */
  222. rt_thread_mdelay(1000);
  223. }
  224. }
  225. /**
  226. * @brief 创建时间打印线程
  227. */
  228. static void rtc_print_thread_create(void)
  229. {
  230. rtc_print_tid = rt_thread_create("rtc_s",
  231. rtc_print_thread_entry,
  232. RT_NULL,
  233. RTC_PRINT_THREAD_STACK_SIZE,
  234. RTC_PRINT_THREAD_PRIORITY,
  235. RTC_PRINT_THREAD_TICK);
  236. if (rtc_print_tid != RT_NULL)
  237. {
  238. rt_thread_startup(rtc_print_tid);
  239. //LOG_I("RTC print thread started");
  240. }
  241. else
  242. {
  243. LOG_E("RTC print thread create failed!");
  244. }
  245. }
  246. /**
  247. * @brief RTC硬件初始化
  248. * @return 0:成功, -1:失败
  249. */
  250. int rt_hw_rtc_init(void)
  251. {
  252. rt_uint32_t init_flag;
  253. /* 使能PWR时钟和备份域访问 */
  254. __HAL_RCC_PWR_CLK_ENABLE();
  255. HAL_PWR_EnableBkUpAccess();
  256. /* 使能RTC时钟 */
  257. __HAL_RCC_RTC_ENABLE();
  258. /* 配置RTC */
  259. hrtc.Instance = RTC;
  260. hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
  261. /* LSE = 32.768kHz, 分频计算:
  262. * 32768 / 128 / 256 = 1Hz
  263. */
  264. hrtc.Init.AsynchPrediv = 127; /* 128分频 (127 + 1) */
  265. hrtc.Init.SynchPrediv = 255; /* 256分频 (255 + 1) */
  266. hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
  267. hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
  268. hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
  269. /* 读取备份寄存器中的初始化标记 */
  270. init_flag = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_FLAG);
  271. if (init_flag != RTC_INIT_FLAG)
  272. {
  273. /* 首次初始化或备份域丢失 */
  274. LOG_I("RTC first init or backup domain reset detected");
  275. /* 初始化RTC */
  276. if (HAL_RTC_Init(&hrtc) != HAL_OK)
  277. {
  278. LOG_E("RTC HAL_Init failed!");
  279. return -1;
  280. }
  281. /* 写入默认时间 */
  282. rtc_write_default_time();
  283. /* 在备份寄存器中写入初始化标记 */
  284. HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_FLAG, RTC_INIT_FLAG);
  285. LOG_I("RTC initialized with default time");
  286. }
  287. else
  288. {
  289. /* 已初始化过,直接读取当前时间 */
  290. struct rtc_time time;
  291. char time_str[32];
  292. /* 只需初始化HAL句柄,时间和日期已保持 */
  293. if (HAL_RTC_Init(&hrtc) != HAL_OK)
  294. {
  295. LOG_E("RTC HAL_Init failed!");
  296. return -1;
  297. }
  298. /* 读取并显示当前时间 */
  299. if (rtc_get_time(&time) == 0)
  300. {
  301. rtc_time_to_str(&time, time_str, sizeof(time_str));
  302. LOG_I("RTC already initialized, current time: %s", time_str);
  303. }
  304. }
  305. /* 创建时间打印线程 */
  306. rtc_print_thread_create();
  307. return 0;
  308. }
  309. /**
  310. * @brief 自动初始化 - 在系统启动时自动调用
  311. * 使用INIT_DEVICE_EXPORT确保在设备初始化阶段执行
  312. */
  313. INIT_ENV_EXPORT(rt_hw_rtc_init);
  314. // #ifdef RT_USING_FINSH
  315. // #include <finsh.h>
  316. //
  317. ///**
  318. // * @brief Finsh/MSH命令: 获取RTC时间
  319. // */
  320. // static int rtc_get(int argc, char **argv)
  321. //{
  322. // struct rtc_time time;
  323. // char time_str[32];
  324. //
  325. // (void)argc;
  326. // (void)argv;
  327. //
  328. // if (rtc_get_time(&time) == 0)
  329. // {
  330. // rtc_time_to_str(&time, time_str, sizeof(time_str));
  331. // rt_kprintf("RTC Time: %s\n", time_str);
  332. // return 0;
  333. // }
  334. //
  335. // rt_kprintf("Get RTC time failed!\n");
  336. // return -1;
  337. //}
  338. // MSH_CMD_EXPORT(rtc_get, Get RTC current time);
  339. //
  340. ///**
  341. // * @brief Finsh/MSH命令: 设置RTC时间
  342. // * Usage: rtc_set <year> <month> <date> <hour> <minute> <second>
  343. // * Example: rtc_set 2024 1 15 12 30 0
  344. // */
  345. // static int rtc_set(int argc, char **argv)
  346. //{
  347. // struct rtc_time time;
  348. //
  349. // if (argc != 7)
  350. // {
  351. // rt_kprintf("Usage: rtc_set <year> <month> <date> <hour> <minute> <second>\n");
  352. // rt_kprintf("Example: rtc_set 2024 1 15 12 30 0\n");
  353. // return -1;
  354. // }
  355. //
  356. // time.year = (rt_uint16_t)atoi(argv[1]);
  357. // time.month = (rt_uint8_t)atoi(argv[2]);
  358. // time.date = (rt_uint8_t)atoi(argv[3]);
  359. // time.hour = (rt_uint8_t)atoi(argv[4]);
  360. // time.minute = (rt_uint8_t)atoi(argv[5]);
  361. // time.second = (rt_uint8_t)atoi(argv[6]);
  362. //
  363. // if (rtc_set_time(&time) == 0)
  364. // {
  365. // rt_kprintf("RTC time set to: %04d-%02d-%02d %02d:%02d:%02d\n",
  366. // time.year, time.month, time.date,
  367. // time.hour, time.minute, time.second);
  368. // return 0;
  369. // }
  370. //
  371. // rt_kprintf("Set RTC time failed!\n");
  372. // return -1;
  373. //}
  374. // MSH_CMD_EXPORT(rtc_set, Set RTC time: rtc_set <year> <month> <date> <hour> <minute> <second>);
  375. //
  376. ///**
  377. // * @brief Finsh/MSH命令: 重置RTC为默认时间
  378. // */
  379. // static int rtc_reset(int argc, char **argv)
  380. //{
  381. // (void)argc;
  382. // (void)argv;
  383. //
  384. // /* 清除备份寄存器标记,下次重启将使用默认时间 */
  385. // HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_FLAG, 0);
  386. //
  387. // /* 立即写入默认时间 */
  388. // rtc_write_default_time();
  389. //
  390. // /* 重新写入标记 */
  391. // HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_FLAG, RTC_INIT_FLAG);
  392. //
  393. // rt_kprintf("RTC reset to default time\n");
  394. // return 0;
  395. //}
  396. // MSH_CMD_EXPORT(rtc_reset, Reset RTC to default time);
  397. //
  398. // #endif /* RT_USING_FINSH */
  399. void Get_Time_Stampe(struct TM *tm)
  400. {
  401. tm->tm_year = time_now.year - 2000;
  402. tm->tm_mon = time_now.month;
  403. tm->tm_mday = time_now.date;
  404. tm->tm_hour = time_now.hour;
  405. tm->tm_min = time_now.minute;
  406. tm->tm_sec = time_now.second;
  407. }
  408. // extern char ef_cfged;
  409. // #include <easyflash.h>
  410. // /**
  411. // * @brief 记录ecode
  412. // *
  413. // * @param code code值
  414. // * @param para 参数
  415. // * @param deta 描述
  416. // *
  417. // * V2.0 241030
  418. // */
  419. // void Recode_e_code(ecode_list code, uint16_t para, char *detail)
  420. // {
  421. // if (ef_cfged == 1)
  422. // {
  423. // Record ecode = {0};
  424. // uint32_t used = ef_log_get_used_size();
  425. // uint32_t cnt = used / sizeof(Record);
  426. // if (cnt > 0)
  427. // {
  428. // ef_log_read((cnt - 1) * sizeof(Record), (uint32_t *)&ecode, sizeof(Record));
  429. // }
  430. // LOG_I("e_code used %d cnt %d\n", used, cnt);
  431. // if ((used + sizeof(Record) + 0x0C) >= LOG_AREA_SIZE)
  432. // {
  433. // ef_log_clean();
  434. // Get_Time_Stampe(&ecode.Time_Stampe);
  435. // ecode.para = code;
  436. // ecode.code = e_clearecode;
  437. // ecode.num++;
  438. // snprintf(ecode.detail, sizeof(ecode.detail), "ecode存满后清空"); // 本次信息
  439. // }
  440. // else
  441. // {
  442. // Get_Time_Stampe(&ecode.Time_Stampe);
  443. // ecode.para = para;
  444. // ecode.code = code;
  445. // ecode.num++;
  446. // snprintf(ecode.detail, sizeof(ecode.detail), "%s", detail); // 本次信息
  447. // }
  448. // LOG_W("e_code SET %d c %d p %d", ef_log_write((uint32_t *)&ecode, sizeof(Record)), code, para);
  449. // // rt_kprintf("%d %d %d %s\n\n" , ecode.num , ecode.code , ecode.para , ecode.detail);
  450. // }
  451. // }
  452. // /**
  453. // * @brief printf_ecode
  454. // *
  455. // */
  456. // void printf_ecode(void)
  457. // {
  458. // RTC_DateTypeDef r_date = {0};
  459. // RTC_TimeTypeDef r_time = {0};
  460. // uint32_t used = ef_log_get_used_size();
  461. // uint32_t cnt = used / sizeof(Record);
  462. // LOG_I("e_code used %d cnt %d\n", used, cnt);
  463. // Record ecode = {0};
  464. // for (uint32_t a = 0; a < cnt; a++)
  465. // {
  466. // if (ef_log_read(a * sizeof(Record), (uint32_t *)&ecode, sizeof(Record)) == EF_NO_ERR)
  467. // {
  468. // LOG_I("%d - %02d/%02d/%02d %02d:%02d:%02d:(%d-%d) %s",
  469. // ecode.num,
  470. // ecode.Time_Stampe.tm_year, ecode.Time_Stampe.tm_mon, ecode.Time_Stampe.tm_mday,
  471. // ecode.Time_Stampe.tm_hour, ecode.Time_Stampe.tm_min, ecode.Time_Stampe.tm_sec,
  472. // ecode.code, ecode.para, ecode.detail);
  473. // memset(&ecode, 0, sizeof(ecode));
  474. // }
  475. // else
  476. // {
  477. // LOG_I("ef_log_read %d fail\n", a);
  478. // }
  479. // }
  480. // }
  481. // void recode_test(void)
  482. // {
  483. // Recode_e_code(e_test_fun, 6, "3-6是一条msh测试数据123456789");
  484. // }
  485. // MSH_CMD_EXPORT(recode_test, recode_test);
  486. // void pre(void)
  487. // {
  488. // printf_ecode();
  489. // }
  490. // MSH_CMD_EXPORT(pre, pre);
  491. // void delecode(void)
  492. // {
  493. // ef_log_clean();
  494. // }
  495. // MSH_CMD_EXPORT(delecode, delecode);