[APM32F4] APM32F402 RTC(LSE)秒计时实战笔记

[复制链接]
216|1
Peixu 发表于 2025-12-1 11:45 | 显示全部楼层 |阅读模式
, , , se,
本帖最后由 Peixu 于 2025-12-1 11:47 编辑

APM32F402 RTC(LSE)秒计时实战笔记
最近在折腾 APM32F402 的 RTC 功能,想基于外部 32.768kHz 的 LSE 晶振做精准的秒级计时,顺便加个串口打印时间、LED 每秒翻转的功能,把过程和踩的坑记下来,方便以后回看。

一、为啥选 LSE 不选 LSI?
一开始用的是内部的 LSI 晶振,结果计时偏差贼大,1 分钟能差个几秒,查手册才知道 LSI 内部低速晶振频率是大概 45kHz,精度还偏差还挺多,只能适合临时测试但没法用在正经计时里。LSE 是外部 32.768kHz 的无源晶振,精度高多了,虽然要焊外部元件,但胜在准,这波还是得选 LSE。
39934692d0f9ad92e7.png

二、代码实现用的是 Geehy 官方的 APM32F402 标准库,核心代码就是 main.c,主要改了 RTC 配置函数,还加了秒中断的处理,先放完整代码,再讲关键修改点。

  1. void RTC_LSI_Config(void)
  2. {
  3.     RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_PMU);

  4.     /* unLock RTC write protection */
  5.     PMU_EnableBackupAccess();
  6.     RCM_EnableLSI();
  7.     while(RCM_ReadStatusFlag(RCM_FLAG_LSIRDY) == RESET);
  8.     RCM_ConfigRTCCLK(RCM_RTCCLK_LSI);
  9.     RCM_EnableRTCCLK();

  10.     /* Wait for RTC clock source synchro */
  11.     RTC_WaitForSynchro();
  12.     RTC_WaitForLastTask();

  13.     RTC_ConfigPrescaler(50000);
  14.     RTC_WaitForLastTask();

  15.     /* Configure counter 0 */
  16.     RTC_ConfigCounter(0);
  17.     RTC_WaitForLastTask();

  18.     /* Enable RTC alarm interrupt */
  19.     RTC_EnableInterrupt(RTC_INT_SEC);
  20.     RTC_WaitForLastTask();

  21.     NVIC_EnableIRQRequest(RTC_IRQn, 0, 0);
  22. }


  23. /* RTC核心配置:LSE时钟源+1Hz计时+秒中断 */
  24. void RTC_LSE_Config(void)
  25. {
  26.     // 1. 使能PMU时钟,访问RTC必须先开这个
  27.     RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_PMU);
  28.     // 2. 解锁备份域,不然没法改RTC寄存器
  29.     PMU_EnableBackupAccess();

  30.     // 3. 开启LSE晶振,等它稳定
  31.     RCM_ConfigLSE(RCM_LSE_OPEN);
  32.     while(RCM_ReadStatusFlag(RCM_FLAG_LSERDY) == RESET);  // 没焊晶振会卡在这
  33.     // 选择LSE作为RTC时钟源,然后使能RTC时钟
  34.     RCM_ConfigRTCCLK(RCM_RTCCLK_LSE);
  35.     RCM_EnableRTCCLK();

  36.     // 4. 等RTC时钟同步,不然配置会失效
  37.     RTC_WaitForSynchro();
  38.     RTC_WaitForLastTask();

  39.     // 5. 配置预分频器,
  40.     RTC_ConfigPrescaler(32768);
  41.     RTC_WaitForLastTask();

  42.     // 6. 计数器初始值设为0,从0秒开始计时
  43.     RTC_ConfigCounter(0);
  44.     RTC_WaitForLastTask();

  45.     // 7. 使能秒中断,每秒触发一次
  46.     RTC_EnableInterrupt(RTC_INT_SEC);
  47.     RTC_WaitForLastTask();

  48.     // 8. 配置NVIC,开启RTC中断
  49.     NVIC_EnableIRQRequest(RTC_IRQn, 0, 0);
  50. }

  51. /* RTC中断服务函数 */
  52. void RTC_Isr(void)
  53. {
  54.     if(RTC_ReadIntFlag(RTC_INT_SEC))  // 检查是不是秒中断
  55.     {
  56.         uint32_t time, h, m, s;
  57.         time = RTC_ReadCounter();  // 读取当前计数值(单位:秒)

  58.         // 把总秒数转成时分秒
  59.         h = (time / 3600) % 24;
  60.         m = (time / 60) % 60;
  61.         s = time % 60;

  62.         // 串口打印时间,补零看起来更整齐
  63.         printf("%02ldh:%02ldm:%02lds\r\n", h, m, s);
  64.         BOARD_LED_Toggle(LED2);  // LED每秒翻转一次

  65.         RTC_ClearIntFlag(RTC_INT_SEC);  // 一定要清标志位,不然会一直进中断
  66.     }
  67. }
三、测试效果

编译下载后,打开串口助手(波特率 115200,8N1),能看到串口每秒打印一次时间,从 00h:00m:00s 开始递增,开发板上的 计时很准,基本没偏差,比用 LSI 的时候强太多了。

90890692d0fb979c9a.png



抒情黎明 发表于 2025-12-1 20:23 | 显示全部楼层
从串口的打印结果看也不准呀
您需要登录后才可以回帖 登录 | 注册

本版积分规则

34

主题

63

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部
0