STM32-RTC

📅 2026/7/30 8:57:53
STM32-RTC
一、RTC 简介RTC 的全称是Real Time Clock即实时时钟。从本质上看RTC 是一个用于记录时间的计数器。它通常以秒为单位计数可以在系统运行过程中提供当前时间也可以在 MCU 掉电后依靠后备电源继续运行。1. RTC 的作用RTC 常用于这些场景记录当前时间。生成定时唤醒事件。给日志、故障记录、采样数据添加时间戳。在系统掉电后继续保持时间。配合后备寄存器保存少量关键数据。2. RTC 的主要特性STM32F1 RTC 具有以下特点特性说明本质以固定频率计数的计数器常用计数频率1 Hz也就是 1 秒计数一次低功耗可在低功耗场景下继续工作掉电运行配合VBAT和外部电池可在主电源掉电后继续计时后备域RTC 和后备寄存器位于后备域中独立复位RTC 核心不受普通系统复位影响STM32F1 的 RTC 和后续系列差异较大。F1 的 RTC 更像是一个“秒计数器”它没有直接提供年、月、日、时、分、秒等日历寄存器。实际使用时通常通过软件算法把秒计数转换为日历时间。二、F1 RTC 框图理解STM32F1 RTC 的核心可以简化为RTC时钟源 - RTC预分频器 - 32位可编程计数器 - 秒计数/闹钟/中断F1 RTC 主要由以下部分组成模块作用RTC 时钟源为 RTC 提供基础时钟RTC 预分频器将输入时钟分频到目标计数频率32 位计数器保存当前 RTC 计数值RTC 控制寄存器配置中断、状态和访问控制APB1 接口CPU 通过 APB1 总线访问 RTC 寄存器如果把 RTC 配置为 1 Hz 计数那么 32 位计数器的值就可以理解为从某个起始时间开始累计的总秒数。三、RTC 时钟源STM32F1 RTC 可选择多个时钟源。时钟源说明特点LSE外部 32.768 kHz 低速晶振精度高稳定推荐使用LSI内部低速 RC常按 40 kHz 估算不需要外部晶振但精度较低有温漂HSE/128外部高速晶振经过 128 分频使用较少依赖主时钟相关配置实际项目中RTC 通常优先选择 LSE。原因是 32.768 kHz 晶振专门用于实时时钟容易分频得到 1 Hz而且稳定性比 LSI 更好。1. 为什么推荐 LSELSE 的典型频率为32768 Hz这个频率可以方便地分频为 1 Hz32768 / 32768 1 Hz也就是说配置合适的预分频值后RTC 计数器可以做到每秒加 1。2. LSI 的问题LSI 是芯片内部 RC 时钟优点是不需要外部晶振硬件更简单。但它的缺点也很明显频率精度低。容易受温度、电压影响。长时间计时误差较大。如果只是粗略计时或低成本方案可以使用 LSI如果需要较准确的时钟应使用 LSE。四、后备域与掉电保持RTC 的一个重要特点是可以在主电源掉电后继续工作。STM32F1 中RTC 和后备寄存器通过一个供电开关供电VDD 有效 - 使用 VDD 供电 VDD 失效 - 使用 VBAT 引脚供电只要VBAT上接有电池或备用电源RTC 就可以在主系统掉电后继续计时。1. 后备寄存器后备寄存器可用于保存少量用户数据。它们和 RTC 同属于后备域不会因为普通系统复位或电源复位而立即丢失。常见用途标记 RTC 是否已经初始化。保存设备运行状态。保存少量配置参数。保存异常复位标志。2. 两类复位F1 RTC 相关复位可以分为两类类型说明APB1 接口复位由系统复位影响RTC 核心复位由后备域复位影响这意味着普通系统复位后RTC 的核心计数值仍可能保持不变。初始化 RTC 时通常需要通过后备寄存器判断是否已经配置过 RTC避免每次复位都重新设置时间。五、F1 RTC 关键寄存器STM32F1 的 RTC 常见寄存器包括RTC_PRL、RTC_CNT、RTC_DIV、RTC_CRL、RTC_ALR等。1. RTC_PRL 预分频装载寄存器RTC_PRL用于设置 RTC 的预分频值。当使用 LSE 32768 Hz并希望 RTC 计数器每秒增加 1 次时可将预分频装载值设置为RTC_PRL 32768 - 1 32767因为计数器通常按PRL 1的方式分频。2. RTC_CNT 计数器寄存器RTC_CNT是 32 位可编程计数器用于保存当前 RTC 计数值。如果 RTC 配置为 1 HzRTC_CNT就可以保存从设定起点开始经过的总秒数。例如RTC_CNT 0 - 起始时刻 RTC_CNT 3600 - 起始时刻后 1 小时 RTC_CNT 86400 - 起始时刻后 1 天STM32F1 没有直接的年月日时分秒寄存器因此需要把RTC_CNT与日历算法结合起来使用。3. RTC_DIV 分频余数寄存器RTC_DIV用于读取当前预分频器的计数状态。实际应用中普通日历功能不一定需要直接使用RTC_DIV但它可以帮助理解 RTC 内部预分频和秒计数之间的关系。4. RTC_CRL 控制低寄存器RTC_CRL是 F1 RTC 配置中非常关键的寄存器。常见状态位包括位作用RTOFFRTC 操作完成标志CNF配置模式控制位RSF寄存器同步标志OWF溢出标志ALRF闹钟标志SECF秒标志配置 RTC 寄存器前需要等待RTOFF 1然后置位CNF进入配置模式。配置完成后清除CNF再等待RTOFF 1表示写操作完成。5. RTC_ALR 闹钟寄存器RTC_ALR用于设置闹钟值。当RTC_CNT的值与RTC_ALR匹配时可以产生闹钟事件。F1 的闹钟机制同样基于计数器比较而不是直接基于年月日时分秒字段。六、F1 RTC 基本配置流程STM32F1 RTC 的初始化通常按以下步骤进行。1. 使能 PWR 和 BKP 时钟访问 RTC 和后备寄存器前需要先使能电源控制和备份域相关时钟使能 PWR 时钟 使能 BKP 时钟HAL 宏通常包括__HAL_RCC_PWR_CLK_ENABLE(); __HAL_RCC_BKP_CLK_ENABLE();2. 使能后备域访问默认情况下后备域受写保护。配置 RTC 前需要先打开访问权限HAL_PWR_EnableBkUpAccess();这一步非常重要。如果没有使能后备域访问后续 RTC 时钟源、预分频器等配置可能无法生效。3. 配置 RTC 时钟源常见做法是开启 LSE并选择 LSE 作为 RTC 时钟源开启 LSE 等待 LSE 就绪 选择 RTC 时钟源为 LSE 使能 RTC 时钟相关寄存器主要是RCC_BDCR。如果使用 HAL 库常见函数包括HAL_RCC_OscConfig(); HAL_RCCEx_PeriphCLKConfig(); __HAL_RCC_RTC_ENABLE();4. 进入 RTC 配置模式进入配置模式前需要等待上一次 RTC 写操作完成等待 RTOFF 1 设置 CNF 1进入配置模式后才能修改RTC_PRL、RTC_CNT、RTC_ALR等寄存器。5. 设置预分频值和计数值初始化时一般先设置 RTC 预分频值使 RTC 计数频率变为 1 Hz。使用 LSE 时RTC_PRL 32767计数值RTC_CNT可根据具体需求设置。如果是第一次初始化 RTC需要把当前日期时间转换为总秒数后写入RTC_CNT。6. 退出配置模式配置完成后清除 CNF 等待 RTOFF 1等待RTOFF的目的是确认 RTC 寄存器写入已经完成。七、F1 RTC 与 HAL 库的关系在 STM32F1 中RTC 常常需要结合 HAL 库和寄存器操作来实现完整日历功能。常见 HAL 函数或宏包括函数或宏作用HAL_RTC_Init()初始化 RTC 基本参数HAL_RTC_MspInit()RTC 底层初始化回调HAL_RCC_OscConfig()配置 LSE/LSI 等振荡器HAL_RCCEx_PeriphCLKConfig()配置 RTC 外设时钟源HAL_PWR_EnableBkUpAccess()使能后备域访问__HAL_RCC_PWR_CLK_ENABLE()使能 PWR 时钟__HAL_RCC_BKP_CLK_ENABLE()使能 BKP 时钟__HAL_RCC_RTC_ENABLE()使能 RTC需要注意F1 的日历设置和读取通常不是直接依赖HAL_RTC_SetTime()、HAL_RTC_GetTime()这类日期时间接口而是通过操作 RTC 秒计数器再用软件算法实现rtc_set_time()和rtc_get_time()。八、F1 时间设置与读取STM32F1 RTC 的关键思想是设置时间日历时间 - 总秒数 - 写入 RTC_CNT 读取时间读取 RTC_CNT - 总秒数 - 转换为日历时间1. 定义日历结构体工程中通常定义一个日历结构体用于保存当前时间信息typedef struct { uint16_t year; uint8_t month; uint8_t date; uint8_t hour; uint8_t min; uint8_t sec; uint8_t week; } rtc_calendar_t;然后定义一个全局变量保存当前日历rtc_calendar_t calendar;2. 判断闰年计算日期时必须判断闰年。常用规则能被 4 整除且不能被 100 整除 - 闰年 能被 400 整除 - 闰年 其他情况 - 平年函数形式可以设计为uint8_t rtc_is_leap_year(uint16_t year);3. 日历时间转总秒数设置 RTC 时间时需要把年月日时分秒转换为总秒数。函数形式可以设计为long rtc_date2sec(uint16_t year, uint8_t month, uint8_t date, uint8_t hour, uint8_t min, uint8_t sec);转换思路累加从起始年份到目标年份前一年的总天数。根据平年/闰年累加目标年份中目标月份之前的总天数。加上当前月份已经过去的天数。将天数转换为秒数。加上时、分、秒对应的秒数。4. 总秒数转日历时间读取 RTC 时间时需要把RTC_CNT中的总秒数转换为年月日时分秒。函数形式可以设计为void rtc_get_time(void);转换思路读取 RTC 当前总秒数。计算已经过去的总天数和当天剩余秒数。根据年天数逐年扣减得到年份。根据月份天数逐月扣减得到月份和日期。根据当天剩余秒数计算时、分、秒。计算星期并更新全局日历结构体。5. 计算星期星期通常可以通过某个基准日期推算。函数形式可以设计为uint8_t rtc_get_week(uint16_t year, uint8_t month, uint8_t date);计算星期时要明确返回值定义例如0 - 星期日 1 - 星期一 ... 6 - 星期六如果项目中 UI 或串口输出使用1 ~ 7表示星期也要统一转换避免显示混乱。九、RTC 初始化状态判断RTC 的初始化不应该每次上电或复位都重新执行完整时间设置否则会把已经保持的时间覆盖掉。常见做法是使用后备寄存器保存一个标志值后备寄存器标志有效 - RTC 已经初始化过只同步寄存器并读取时间 后备寄存器标志无效 - 第一次初始化 RTC配置时钟源、预分频器并设置初始时间这种方式可以区分普通系统复位和 RTC 第一次初始化。需要重新初始化 RTC 的典型情况包括后备域被复位。VBAT 电池断电。LSE 启动失败后切换时钟源。用户主动重新设置时间。