rtc.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  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. static RTC_HandleTypeDef hrtc;
  16. /* 时间打印线程相关 */
  17. #define RTC_PRINT_THREAD_STACK_SIZE (256+128) // 太大没必要
  18. #define RTC_PRINT_THREAD_PRIORITY 9
  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. /**
  23. * @brief 判断是否为闰年
  24. * @param year 年份(2000-2099)
  25. * @return 1:闰年, 0:平年
  26. */
  27. static rt_uint8_t is_leap_year(rt_uint16_t year)
  28. {
  29. if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
  30. return 1;
  31. return 0;
  32. }
  33. /**
  34. * @brief 将年月日转换为星期
  35. * @param year 年份(2000-2099)
  36. * @param month 月份(1-12)
  37. * @param date 日期(1-31)
  38. * @return 星期(0=周日, 1=周一, ..., 6=周六)
  39. */
  40. static rt_uint8_t calc_weekday(rt_uint16_t year, rt_uint8_t month, rt_uint8_t date)
  41. {
  42. /* Zeller公式计算星期 */
  43. rt_int16_t y = year;
  44. rt_int8_t m = month;
  45. rt_int8_t d = date;
  46. rt_int8_t c = y / 100;
  47. y = y % 100;
  48. if (m < 3)
  49. {
  50. m += 12;
  51. y -= 1;
  52. }
  53. rt_int8_t w = (y + y / 4 + c / 4 - 2 * c + 26 * (m + 1) / 10 + d - 1) % 7;
  54. return (w < 0) ? (w + 7) : w;
  55. }
  56. /**
  57. * @brief 检查日期时间是否有效
  58. * @param time 时间结构体指针
  59. * @return 1:有效, 0:无效
  60. */
  61. static rt_uint8_t is_time_valid(const struct rtc_time *time)
  62. {
  63. const rt_uint8_t days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  64. rt_uint8_t max_day;
  65. if (time->year < 2000 || time->year > 2099)
  66. return 0;
  67. if (time->month < 1 || time->month > 12)
  68. return 0;
  69. max_day = days_in_month[time->month - 1];
  70. if (time->month == 2 && is_leap_year(time->year))
  71. max_day = 29;
  72. if (time->date < 1 || time->date > max_day)
  73. return 0;
  74. if (time->hour > 23)
  75. return 0;
  76. if (time->minute > 59)
  77. return 0;
  78. if (time->second > 59)
  79. return 0;
  80. return 1;
  81. }
  82. /**
  83. * @brief 将BCD码转换为二进制
  84. */
  85. static rt_uint8_t bcd_to_bin(rt_uint8_t bcd)
  86. {
  87. return ((bcd >> 4) * 10) + (bcd & 0x0F);
  88. }
  89. /**
  90. * @brief 将二进制转换为BCD码
  91. */
  92. static rt_uint8_t bin_to_bcd(rt_uint8_t bin)
  93. {
  94. return ((bin / 10) << 4) | (bin % 10);
  95. }
  96. /**
  97. * @brief 获取RTC时间
  98. * @param time 时间结构体指针
  99. * @return 0:成功, -1:失败
  100. */
  101. int rtc_get_time(struct rtc_time *time)
  102. {
  103. RTC_DateTypeDef sdate;
  104. RTC_TimeTypeDef stim;
  105. if (time == RT_NULL)
  106. return -1;
  107. /* 获取时间和日期 - 必须先获取时间再获取日期 */
  108. HAL_RTC_GetTime(&hrtc, &stim, RTC_FORMAT_BCD);
  109. HAL_RTC_GetDate(&hrtc, &sdate, RTC_FORMAT_BCD);
  110. /* 转换为标准格式 */
  111. time->year = 2000 + bcd_to_bin(sdate.Year);
  112. time->month = bcd_to_bin(sdate.Month);
  113. time->date = bcd_to_bin(sdate.Date);
  114. time->weekday = bcd_to_bin(sdate.WeekDay);
  115. time->hour = bcd_to_bin(stim.Hours);
  116. time->minute = bcd_to_bin(stim.Minutes);
  117. time->second = bcd_to_bin(stim.Seconds);
  118. return 0;
  119. }
  120. /**
  121. * @brief 设置RTC时间
  122. * @param time 时间结构体指针
  123. * @return 0:成功, -1:失败
  124. */
  125. int rtc_set_time(struct rtc_time *time)
  126. {
  127. RTC_DateTypeDef sdate;
  128. RTC_TimeTypeDef stim;
  129. if (time == RT_NULL || !is_time_valid(time))
  130. return -1;
  131. /* 填充日期结构体 */
  132. sdate.Year = bin_to_bcd(time->year - 2000);
  133. sdate.Month = bin_to_bcd(time->month);
  134. sdate.Date = bin_to_bcd(time->date);
  135. sdate.WeekDay = bin_to_bcd(calc_weekday(time->year, time->month, time->date));
  136. /* 填充时间结构体 */
  137. stim.Hours = bin_to_bcd(time->hour);
  138. stim.Minutes = bin_to_bcd(time->minute);
  139. stim.Seconds = bin_to_bcd(time->second);
  140. stim.TimeFormat = RTC_HOURFORMAT12_AM;
  141. stim.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
  142. stim.StoreOperation = RTC_STOREOPERATION_RESET;
  143. /* 设置日期和时间 */
  144. if (HAL_RTC_SetDate(&hrtc, &sdate, RTC_FORMAT_BCD) != HAL_OK)
  145. return -1;
  146. if (HAL_RTC_SetTime(&hrtc, &stim, RTC_FORMAT_BCD) != HAL_OK)
  147. return -1;
  148. return 0;
  149. }
  150. /**
  151. * @brief 将RTC时间转换为字符串
  152. * @param time 时间结构体指针
  153. * @param buf 缓冲区
  154. * @param size 缓冲区大小
  155. */
  156. void rtc_time_to_str(const struct rtc_time *time, char *buf, rt_size_t size)
  157. {
  158. const char *week_str[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
  159. if (time == RT_NULL || buf == RT_NULL || size < 32)
  160. return;
  161. rt_snprintf(buf, size, "%04d-%02d-%02d %02d:%02d:%02d %s",
  162. time->year, time->month, time->date,
  163. time->hour, time->minute, time->second,
  164. week_str[time->weekday % 7]);
  165. }
  166. /**
  167. * @brief 写入默认时间到RTC
  168. */
  169. static void rtc_write_default_time(void)
  170. {
  171. struct rtc_time time;
  172. time.year = 2000 + RTC_DEFAULT_YEAR;
  173. time.month = RTC_DEFAULT_MONTH;
  174. time.date = RTC_DEFAULT_DATE;
  175. time.hour = RTC_DEFAULT_HOUR;
  176. time.minute = RTC_DEFAULT_MINUTE;
  177. time.second = RTC_DEFAULT_SECOND;
  178. if (rtc_set_time(&time) == 0)
  179. {
  180. LOG_I("RTC default time set: %04d-%02d-%02d %02d:%02d:%02d",
  181. time.year, time.month, time.date,
  182. time.hour, time.minute, time.second);
  183. }
  184. else
  185. {
  186. LOG_E("RTC set default time failed!");
  187. }
  188. }
  189. /**
  190. * @brief 时间打印线程入口函数
  191. * @param parameter 线程参数
  192. */
  193. static void rtc_print_thread_entry(void *parameter)
  194. {
  195. char time_str[32];
  196. static char once = 3;
  197. while (1)
  198. {
  199. /* 获取当前时间 */
  200. if (rtc_get_time(&time_now) == 0)
  201. {
  202. rtc_time_to_str(&time_now, time_str, sizeof(time_str));
  203. if(once)
  204. {
  205. once--;
  206. LOG_D(" Current Time: %s\n", time_str);
  207. }
  208. }
  209. else
  210. {
  211. LOG_I(" Get time failed!\n");
  212. }
  213. /* 延时1秒 */
  214. rt_thread_mdelay(1000);
  215. }
  216. }
  217. /**
  218. * @brief 创建时间打印线程
  219. */
  220. static void rtc_print_thread_create(void)
  221. {
  222. rtc_print_tid = rt_thread_create("rtc_s",
  223. rtc_print_thread_entry,
  224. RT_NULL,
  225. RTC_PRINT_THREAD_STACK_SIZE,
  226. RTC_PRINT_THREAD_PRIORITY,
  227. RTC_PRINT_THREAD_TICK);
  228. if (rtc_print_tid != RT_NULL)
  229. {
  230. rt_thread_startup(rtc_print_tid);
  231. LOG_I("RTC print thread started");
  232. }
  233. else
  234. {
  235. LOG_E("RTC print thread create failed!");
  236. }
  237. }
  238. /**
  239. * @brief RTC硬件初始化
  240. * @return 0:成功, -1:失败
  241. */
  242. int rt_hw_rtc_init(void)
  243. {
  244. rt_uint32_t init_flag;
  245. /* 使能PWR时钟和备份域访问 */
  246. __HAL_RCC_PWR_CLK_ENABLE();
  247. HAL_PWR_EnableBkUpAccess();
  248. /* 使能RTC时钟 */
  249. __HAL_RCC_RTC_ENABLE();
  250. /* 配置RTC */
  251. hrtc.Instance = RTC;
  252. hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
  253. /* LSE = 32.768kHz, 分频计算:
  254. * 32768 / 128 / 256 = 1Hz
  255. */
  256. hrtc.Init.AsynchPrediv = 127; /* 128分频 (127 + 1) */
  257. hrtc.Init.SynchPrediv = 255; /* 256分频 (255 + 1) */
  258. hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
  259. hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
  260. hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
  261. /* 读取备份寄存器中的初始化标记 */
  262. init_flag = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_FLAG);
  263. if (init_flag != RTC_INIT_FLAG)
  264. {
  265. /* 首次初始化或备份域丢失 */
  266. LOG_I("RTC first init or backup domain reset detected");
  267. /* 初始化RTC */
  268. if (HAL_RTC_Init(&hrtc) != HAL_OK)
  269. {
  270. LOG_E("RTC HAL_Init failed!");
  271. return -1;
  272. }
  273. /* 写入默认时间 */
  274. rtc_write_default_time();
  275. /* 在备份寄存器中写入初始化标记 */
  276. HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_FLAG, RTC_INIT_FLAG);
  277. LOG_I("RTC initialized with default time");
  278. }
  279. else
  280. {
  281. /* 已初始化过,直接读取当前时间 */
  282. struct rtc_time time;
  283. char time_str[32];
  284. /* 只需初始化HAL句柄,时间和日期已保持 */
  285. if (HAL_RTC_Init(&hrtc) != HAL_OK)
  286. {
  287. LOG_E("RTC HAL_Init failed!");
  288. return -1;
  289. }
  290. /* 读取并显示当前时间 */
  291. if (rtc_get_time(&time) == 0)
  292. {
  293. rtc_time_to_str(&time, time_str, sizeof(time_str));
  294. LOG_I("RTC already initialized, current time: %s", time_str);
  295. }
  296. }
  297. /* 创建时间打印线程 */
  298. rtc_print_thread_create();
  299. return 0;
  300. }
  301. /**
  302. * @brief 自动初始化 - 在系统启动时自动调用
  303. * 使用INIT_DEVICE_EXPORT确保在设备初始化阶段执行
  304. */
  305. INIT_DEVICE_EXPORT(rt_hw_rtc_init);
  306. //#ifdef RT_USING_FINSH
  307. //#include <finsh.h>
  308. //
  309. ///**
  310. // * @brief Finsh/MSH命令: 获取RTC时间
  311. // */
  312. //static int rtc_get(int argc, char **argv)
  313. //{
  314. // struct rtc_time time;
  315. // char time_str[32];
  316. //
  317. // (void)argc;
  318. // (void)argv;
  319. //
  320. // if (rtc_get_time(&time) == 0)
  321. // {
  322. // rtc_time_to_str(&time, time_str, sizeof(time_str));
  323. // rt_kprintf("RTC Time: %s\n", time_str);
  324. // return 0;
  325. // }
  326. //
  327. // rt_kprintf("Get RTC time failed!\n");
  328. // return -1;
  329. //}
  330. //MSH_CMD_EXPORT(rtc_get, Get RTC current time);
  331. //
  332. ///**
  333. // * @brief Finsh/MSH命令: 设置RTC时间
  334. // * Usage: rtc_set <year> <month> <date> <hour> <minute> <second>
  335. // * Example: rtc_set 2024 1 15 12 30 0
  336. // */
  337. //static int rtc_set(int argc, char **argv)
  338. //{
  339. // struct rtc_time time;
  340. //
  341. // if (argc != 7)
  342. // {
  343. // rt_kprintf("Usage: rtc_set <year> <month> <date> <hour> <minute> <second>\n");
  344. // rt_kprintf("Example: rtc_set 2024 1 15 12 30 0\n");
  345. // return -1;
  346. // }
  347. //
  348. // time.year = (rt_uint16_t)atoi(argv[1]);
  349. // time.month = (rt_uint8_t)atoi(argv[2]);
  350. // time.date = (rt_uint8_t)atoi(argv[3]);
  351. // time.hour = (rt_uint8_t)atoi(argv[4]);
  352. // time.minute = (rt_uint8_t)atoi(argv[5]);
  353. // time.second = (rt_uint8_t)atoi(argv[6]);
  354. //
  355. // if (rtc_set_time(&time) == 0)
  356. // {
  357. // rt_kprintf("RTC time set to: %04d-%02d-%02d %02d:%02d:%02d\n",
  358. // time.year, time.month, time.date,
  359. // time.hour, time.minute, time.second);
  360. // return 0;
  361. // }
  362. //
  363. // rt_kprintf("Set RTC time failed!\n");
  364. // return -1;
  365. //}
  366. //MSH_CMD_EXPORT(rtc_set, Set RTC time: rtc_set <year> <month> <date> <hour> <minute> <second>);
  367. //
  368. ///**
  369. // * @brief Finsh/MSH命令: 重置RTC为默认时间
  370. // */
  371. //static int rtc_reset(int argc, char **argv)
  372. //{
  373. // (void)argc;
  374. // (void)argv;
  375. //
  376. // /* 清除备份寄存器标记,下次重启将使用默认时间 */
  377. // HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_FLAG, 0);
  378. //
  379. // /* 立即写入默认时间 */
  380. // rtc_write_default_time();
  381. //
  382. // /* 重新写入标记 */
  383. // HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_FLAG, RTC_INIT_FLAG);
  384. //
  385. // rt_kprintf("RTC reset to default time\n");
  386. // return 0;
  387. //}
  388. //MSH_CMD_EXPORT(rtc_reset, Reset RTC to default time);
  389. //
  390. //#endif /* RT_USING_FINSH */