HAL_RTC(BKP)

文章目录

一、RTC 简介

1、什么是 RTC

RTC(Real Time Clock,实时时钟)是 STM32 内置的独立硬件时钟外设,专门用于记录年月日时分秒、星期、闹钟时间,是设备计时、日志时间戳、定时唤醒的核心外设。

RTC 拥有独立时钟源、独立供电域,主电源断电后可依靠备用电池继续走时,实现掉电时间不重置。

2、RTC 核心特点

独立工作:不依赖系统主时钟,主程序运行、停机、休眠均可正常计时。

掉电续航:外接纽扣电池(VBAT),断电继续走时。

超低功耗:32.768K低频晶振,功耗极低。

自带日历:自动适配年月日、星期、闰年。

支持闹钟中断:可定时唤醒单片机、触发任务。

配合低功耗:是 Stop/Standby 模式最常用唤醒源。

3、RTC 三种时钟源

LSE 外部32.768K晶振(推荐):精度最高、稳定,工程标配。

LSI 内部40KRC振荡器:无需外部晶振,精度差、会走时不准。

HSE 分频时钟:极少使用,功耗高、不适合计时。

4、RTC 核心功能

万年历计时:年、月、日、时、分、秒、星期。

RTC 闹钟中断:定时触发任务、定时开机。

周期性秒中断:精准1s节拍。

低功耗唤醒:休眠模式定时唤醒设备。

BKP 备份寄存器:掉电保存用户参数。

二、RTC 模块详解

0、RTC模块框图

  

1、后备区域(灰色部分)

由 VBAT 电池供电,待机、主电源掉电时也持续工作,是 RTC 计时的核心。

1)时钟输入与预分频器

输入信号:RTCCLK(RTC 时钟源,通常是 32.768kHz LSE 晶振)。

预分频器:RTC_DIV,分频计数器,对 RTCCLK 进行分频,输出 TR_CLK(驱动 32 位计数器的时钟)。

重装载寄存器:RTC_PRL,重装载寄存器,配置分频系数,决定最终计时精度。

2)32 位可编程计数器(RTC计时核心)

32 位计数器:

  RTC_CNT,对 TR_CLK 脉冲进行计数,本质是一个 "自增寄存器",每来一个 TR_CLK 脉冲就 + 1。

  存储时间戳:它存储的不是直观的"年月日",而是从起始点(如 1970-01-01 或自定义时间)开始累计的 总秒数(Unix 时间戳)。

闹钟比较寄存器:

  RTC_ALR,硬件实时比较器,不断对比 RTC_CNT 的当前值和预设的闹钟值。

  当两者相等时,立刻输出 RTC_Alarm 信号,无需 CPU 干预。

3)退出待机模式(WKP_STDBY)

RTC 闹钟不仅能触发 CPU 中断,还能直接唤醒低功耗模式:

输入信号:

  RTC_Alarm、WKUP pin(外部唤醒引脚)。

逻辑功能:

  两个信号通过或门后,直接触发 "退出待机模式" 电路,在主 CPU 完全休眠时,也能靠 RTC 闹钟或外部引脚唤醒芯片,实现低功耗定时控制。

2、主域部分

由主电源 VDD 供电,待机 / 掉电时不工作,仅在主系统运行时提供配置、中断、总线交互功能。

1)RTC_CR 控制与中断逻辑模块

输入信号:

  RTC_Second(秒脉冲)、RTC_Overflow(计数器溢出)、RTC_Alarm(闹钟匹配)

内部逻辑:

  SECF/SECIE:秒中断标志 / 使能,RTC_Second 到来时置位标志,开启中断后触发 CPU 中断。

  OWF/OWIE:溢出中断标志 / 使能,计数器溢出时触发,用于处理日历进位。

  ALRF/ALRIE:闹钟中断标志 / 使能,RTC_Alarm 信号到来时置位,触发闹钟中断。

输出信号:

  所有中断信号通过或门汇总,发送到 NVIC中断控制器,最终通知 CPU 处理。

2)APB1 接口模块

CPU 与 RTC 后备域交互的桥梁:

输入:

  来自 APB1总线 的配置指令、读写操作。

功能:

  接收 CPU 通过 APB1 总线发来的配置,写入 RTC_PRL、RTC_CNT、RTC_ALR 等后备域寄存器。

  将 RTC 的状态数据(当前时间、闹钟状态)反馈给 CPU。

注意:

  该模块待机时不供电,因此待机时 CPU 无法直接读写 RTC 寄存器,仅后备域硬件在运行。

3、工作流程

1)计时流程:

RTCCLK → RTC预分频器 → TR_CLK → RTC_CNT 计数 → RTC_Second 秒脉冲 → 驱动日历进位。

2)闹钟触发流程:

RTC_CNT 与 RTC_ALR 匹配 → RTC_Alarm 信号 → 分两路:

  一路到 RTC_CR 模块,置位 ALRF 标志,触发 CPU 中断。

  一路到 WKP_STDBY 逻辑,直接唤醒待机模式的芯片。

3)CPU 交互流程:

CPU 通过 APB1总线 → APB1接口 → 读写后备域寄存器,配置时间、闹钟、分频参数。

4)计时原理

标准 LSE 32768Hz 时钟,经过分频后得到精准 1Hz 秒脉冲,每1秒计数器+1,硬件自动累加时间、更新日历信息,无需软件累加。

分频公式:RTC秒中断频率 = LSE时钟 / (PRL+1)

三、CubeMX 配置 RTC

1、基础配置流程

1)打开 CubeMX → 点击 Timers → RTC。

2)Activate Clock Source:勾选 Activate LSE Clock。

3)Activate Calendar:开启日历功能。

  

4)RCC开启低速时钟RCC→ Low Speed Clock(LSE):Crystal/Ceramic Resonator。

  

5)时钟源选择:LSE 32.768K。

  

6)Tamper 入侵检测配置

可配置 Tamper引脚,设置触发边沿(上升 / 下降),用于防拆检测。

一般项目无需配置,仅防盗 / 加密设备使用。

  

2、参数配置

General:

Auto Predivider Calculation:Enabled(使用自动预分频器计算)

Asynchronous Predivider value:Automatic Predivider Calculation Enabled(异步预分频系数:使用自动)

Output:输出引脚输出选项。

  

3、中断配置

1)NVIC 设置:开启 RTC 全局中断。

  

四、RTC编程模块

1、RTC 结构体

1) RTC 句柄(RTC_HandleTypeDef)

复制代码
		typedef struct {
		  RTC_TypeDef                 *Instance;    // 寄存器基地址(RTC)
		  RTC_InitTypeDef             Init;         // 初始化参数
		  HAL_LockTypeDef             Lock;         // 锁
		  __IO HAL_RTC_StateTypeDef   State;        // 运行状态
		} RTC_HandleTypeDef;

2)初始化参数(RTC_InitTypeDef)

复制代码
		typedef struct {
		  uint32_t HourFormat;      // RTC_HOURFORMAT_24 / 12
		  uint32_t AsynchPrediv;    // F103 固定 127
		  uint32_t SynchPrediv;     // F103 固定 255
		  uint32_t OutPut;           // 输出引脚配置
		  uint32_t OutPutPolarity;
		  uint32_t OutPutType;
		} RTC_InitTypeDef;

3)时间结构体(RTC_TimeTypeDef)

复制代码
		typedef struct {
		  uint8_t Hours;    // 0-23
		  uint8_t Minutes;  // 0-59
		  uint8_t Seconds;  // 0-59
		} RTC_TimeTypeDef;

4)闹钟结构体(RTC_AlarmTypeDef)

复制代码
		typedef struct {
		  RTC_TimeTypeDef AlarmTime;        // 闹钟时:分:秒
		  uint32_t AlarmMask;               // 掩码(忽略某些字段)
		  uint32_t AlarmDateWeekDaySel;     // 选日期或星期
		  uint8_t AlarmDateWeekDay;         // 日期/星期值
		  uint32_t Alarm;                    // 闹钟开关
		} RTC_AlarmTypeDef;

2、RTC API

1)初始化与基础控制

复制代码
		// 初始化 RTC
		HAL_StatusTypeDef HAL_RTC_Init(RTC_HandleTypeDef *hrtc);
		// 反初始化
		HAL_StatusTypeDef HAL_RTC_DeInit(RTC_HandleTypeDef *hrtc);
		// MSP 初始化(底层:开 LSE、开电源备份域)
		void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc);
		void HAL_RTC_MspDeInit(RTC_HandleTypeDef *hrtc);

2)时间 / 日期 设置与读取(最常用)

复制代码
		// 设置时间
		HAL_StatusTypeDef HAL_RTC_SetTime(
		    RTC_HandleTypeDef *hrtc,
		    RTC_TimeTypeDef *sTime,
		    uint32_t Format);  // RTC_FORMAT_BIN / BCD
		// 读取时间
		HAL_StatusTypeDef HAL_RTC_GetTime(
		    RTC_HandleTypeDef *hrtc,
		    RTC_TimeTypeDef *sTime,
		    uint32_t Format);
		// 设置日期
		HAL_StatusTypeDef HAL_RTC_SetDate(
		    RTC_HandleTypeDef *hrtc,
		    RTC_DateTypeDef *sDate,
		    uint32_t Format);
		// 读取日期
		HAL_StatusTypeDef HAL_RTC_GetDate(
		    RTC_HandleTypeDef *hrtc,
		    RTC_DateTypeDef *sDate,
		    uint32_t Format);
		F103 铁律:先 GetTime,再 GetDate。

3)闹钟(Alarm)相关

复制代码
		// 设置闹钟(查询方式)
		HAL_StatusTypeDef HAL_RTC_SetTime(RTC_HandleTypeDef *hrtc,
		                                    RTC_AlarmTypeDef *sAlarm,
		                                    uint32_t Format);
		// 设置闹钟并开启中断(常用)
		HAL_StatusTypeDef HAL_RTC_SetAlarm_IT(RTC_HandleTypeDef *hrtc,
		                                         RTC_AlarmTypeDef *sAlarm,
		                                         uint32_t Format);
		// 关闭闹钟
		HAL_StatusTypeDef HAL_RTC_DeactivateAlarm(RTC_HandleTypeDef *hrtc);
		// 闹钟中断回调
		__weak void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc);

4)秒中断(Second Interrupt)

复制代码
		// 开启秒中断
		HAL_StatusTypeDef HAL_RTC_SetSecond_IT(RTC_HandleTypeDef *hrtc);
		// 关闭秒中断
		HAL_StatusTypeDef HAL_RTC_DisableSecond_IT(RTC_HandleTypeDef *hrtc);
		// 秒中断回调(F1 HAL 统一事件回调)
		__weak void HAL_RTC_RtcEventCallback(RTC_HandleTypeDef *hrtc);

5)唤醒定时器(Wakeup,低功耗常用)

复制代码
		// 设置唤醒定时器
		HAL_StatusTypeDef HAL_RTC_SetWakeupTimer(RTC_HandleTypeDef *hrtc,
		                                             uint32_t WakeupCounter,
		                                             uint32_t WakeupClock);
		// 开启唤醒中断
		HAL_StatusTypeDef HAL_RTC_SetWakeupTimer_IT(RTC_HandleTypeDef *hrtc,
		                                               uint32_t WakeupCounter,
		                                               uint32_t WakeupClock);
		// 唤醒回调
		__weak void HAL_RTC_WakeupEventCallback(RTC_HandleTypeDef *hrtc);

6)后备寄存器(断电保存标志 / 少量数据)

复制代码
		// 读后备寄存器(1~10)
		uint32_t HAL_RTCEx_BKUPRead(RTC_HandleTypeDef *hrtc, uint32_t BKP_DR);
		// 写后备寄存器
		void HAL_RTCEx_BKUPWrite(RTC_HandleTypeDef *hrtc,
		                           uint32_t BKP_DR,
		                           uint32_t Data);
		典型用法:第一次上电写 0x55AA,以后上电读,判断是否需要重设时间。

7)中断通用处理

复制代码
		// RTC 中断服务函数(放在 stm32f1xx_it.c)
		void RTC_IRQHandler(void);
		// 中断使能(NVIC)
		HAL_NVIC_EnableIRQ(RTC_IRQn);
		HAL_NVIC_SetPriority(RTC_IRQn, 0, 0);

3、常用宏 / Format 取值

复制代码
	// 格式
	#define RTC_FORMAT_BIN   0x00000000U
	#define RTC_FORMAT_BCD   0x00000001U
	
	// 小时制
	#define RTC_HOURFORMAT_24   0x00000000U
	#define RTC_HOURFORMAT_12   0x00000040U
	
	// 中断标志
	#define RTC_IT_SEC    0x00000001U  // 秒中断
	#define RTC_IT_ALRA   0x00000002U  // 闹钟A
	#define RTC_IT_WUT    0x00000004U  // 唤醒

五、RTC 应用实例

1、读取并打印实时时间

1)开启外部晶振

复制代码
		RCC → HSE 选择 Crystal/Ceramic Resonator。
		RCC → LSE 选择 Crystal/Ceramic Resonator(RTC 必须用 32.768KHz 低速晶振)。

2)配置 RTC

复制代码
		RTC → 勾选 Active Clock Source。
		RTC Out 不勾选。
		RTC 参数默认即可(CubeMX 会自动配置分频)。

3)配置串口(用于打印时间)

复制代码
		USART1 → 模式选择 Asynchronous(异步)。。
		波特率:115200,8 位数据,无校验,1 位停止位

4)工程生成

复制代码
		项目名(HAL_PrintTime)、路径自行设置。
		栈大小建议设置为:0x400。
		生成代码后用 Keil 打开。

5)代码

复制代码
		#include "main.h"
		#include "rtc.h"
		#include "usart.h"
		#include "gpio.h"
		#include "stdio.h"
		#include "string.h"
		
		/* 重定向printf */
		#ifdef __GNUC__
		#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
		#else
		#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
		#endif
		PUTCHAR_PROTOTYPE
		{
		        HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
		        return ch;
		}
		
		/* RTC 结构体 */
		RTC_TimeTypeDef sTime = {0};
		RTC_DateTypeDef sDate = {0};
		
		int main(void)
		{
		        HAL_Init();
		        SystemClock_Config();
		        MX_GPIO_Init();
		        MX_RTC_Init();
		        MX_USART1_UART_Init();
		
		        /* 1. 初始化 RTC 时间:2026年6月6日 星期三 12:00:00 */
		        sTime.Hours = 12;    // 时
		        sTime.Minutes = 00;  // 分
		        sTime.Seconds = 00;  // 秒
		        if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
		        {
		          Error_Handler();
		        }
		        
		        sDate.Date = 6;          // 日
		        sDate.Month = RTC_MONTH_JUNE; // 月
		        sDate.Year = 26;         // 年(只写后两位)
		        sDate.WeekDay = RTC_WEEKDAY_WEDNESDAY; // 星期
		        if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
		        {
		          Error_Handler();
		        }
		        
		        while (1)
		        {
		          /* 读取实时时间 */
		          HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
		          HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
		        
		          /* 打印 */
		          printf("20%02d-%02d-%02d %02d:%02d:%02d\r\n",
		               sDate.Year,        // 年
		               sDate.Month,       // 月
		               sDate.Date,        // 日
		               sTime.Hours,       // 时
		               sTime.Minutes,     // 分
		               sTime.Seconds);    // 秒
		        
		          HAL_Delay(1000);
		        }
		}

6)执行结果

复制代码
		串口助手(115200 波特率)会输出:
			2026-06-06 12:00:00 
			2026-06-06 12:00:01 
			2026-06-06 12:00:02 

2、RTC 闹钟定时中断

1)项目功能

复制代码
		RTC 实时时钟走时。
		闹钟定时中断(12:00:10 触发)。
		中断触发:翻转 LED + 串口打印。
		串口:USART1(PA9/PA10)。
		LED:PB5。
		中断:RTC 闹钟中断。

2)实例代码(无CubeMX)

复制代码
		#include "stm32f10x.h"
		#include "stm32f10x_hal.h"
		
		// 句柄定义
		RTC_HandleTypeDef hrtc;
		UART_HandleTypeDef huart1;
		
		// 时间结构体
		RTC_TimeTypeDef sTime;
		RTC_DateTypeDef sDate;
		RTC_AlarmTypeDef sAlarm;
		
		// 重定向 printf
		int fputc(int ch, FILE *f)
		{
		  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 100);
		  return ch;
		}
		
		// 系统时钟初始化(72MHz)
		void SystemClock_Config(void)
		{
		  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
		  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
		
		  // 启用 HSE + LSE
		  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE | RCC_OSCILLATORTYPE_LSE;
		  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
		  RCC_OscInitStruct.LSEState = RCC_LSE_ON;
		  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
		  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
		  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
		  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
		  HAL_RCC_OscConfig(&RCC_OscInitStruct);
		
		  // 时钟配置
		  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
		                              | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
		  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
		  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
		  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
		  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
		  HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
		
		  // 使能 RTC 时钟
		  __HAL_RCC_RTC_ENABLE();
		  __HAL_RCC_PWR_CLK_ENABLE();
		  __HAL_RCC_BKP_CLK_ENABLE();
		  HAL_PWR_EnableBkUpAccess(); // 解锁备份域
		}
		
		// USART1 初始化
		void MX_USART1_UART_Init(void)
		{
		  huart1.Instance = USART1;
		  huart1.Init.BaudRate = 115200;
		  huart1.Init.WordLength = UART_WORDLENGTH_8B;
		  huart1.Init.StopBits = UART_STOPBITS_1;
		  huart1.Init.Parity = UART_PARITY_NONE;
		  huart1.Init.Mode = UART_MODE_TX_RX;
		  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
		  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
		  HAL_UART_Init(&huart1);
		
		  // 手动配置 PA9/PA10
		  __HAL_RCC_GPIOA_CLK_ENABLE();
		  GPIO_InitTypeDef GPIO_InitStruct = {0};
		
		  // PA9 TX
		  GPIO_InitStruct.Pin = GPIO_PIN_9;
		  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
		  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
		  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
		
		  // PA10 RX
		  GPIO_InitStruct.Pin = GPIO_PIN_10;
		  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
		  GPIO_InitStruct.Pull = GPIO_NOPULL;
		  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
		}
		
		// RTC 初始化(纯手写)
		void MX_RTC_Init(void)
		{
		  hrtc.Instance = RTC;
		  hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
		  hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
		  HAL_RTC_Init(&hrtc);
		
		  // 设置初始时间:12:00:00
		  sTime.Hours = 12;
		  sTime.Minutes = 00;
		  sTime.Seconds = 00;
		  HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
		
		  // 设置日期
		  sDate.Date = 1;
		  sDate.Month = RTC_MONTH_JANUARY;
		  sDate.Year = 26;
		  HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
		}
		
		// LED 初始化 PB5
		void MX_GPIO_Init(void)
		{
		  __HAL_RCC_GPIOC_CLK_ENABLE();
		  GPIO_InitTypeDef GPIO_InitStruct = {0};
		
		  GPIO_InitStruct.Pin = GPIO_PIN_5;
		  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
		  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
		  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
		
		  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET);
		}
		
		// RTC 闹钟中断配置
		void RTC_SetAlarm_IT(void)
		{
		  // 闹钟时间:12:00:10
		  sAlarm.Alarm = RTC_ALARM_A;
		  sAlarm.AlarmTime.Hours = 12;
		  sAlarm.AlarmTime.Minutes = 00;
		  sAlarm.AlarmTime.Seconds = 10;
		  sAlarm.AlarmMask = RTC_ALARMMASK_NONE;
		
		  HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN);
		
		  // 开启中断
		  HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 0, 0);
		  HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
		}
		
		// 闹钟中断服务函数
		void RTC_Alarm_IRQHandler(void)
		{
		  HAL_RTC_AlarmIRQHandler(&hrtc);
		}
		
		// 闹钟中断回调函数(核心)
		void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
		{
		  printf("\r\n===== 闹钟中断触发成功!=====\r\n");
		  HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 翻转LED
		}
		
		int main(void)
		{
		  // 初始化
		  HAL_Init();
		  SystemClock_Config();
		  MX_GPIO_Init();
		  MX_USART1_UART_Init();
		  MX_RTC_Init();
		  RTC_SetAlarm_IT();
		
		  printf("RTC 闹钟中断系统启动成功\r\n");
		  printf("闹钟时间:12:00:10\r\n\r\n");
		
		  while (1)
		  {
		    // 循环读取并打印时间
		    HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
		    HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
		
		    printf("当前时间:20%02d-%02d-%02d %02d:%02d:%02d\r\n",
		           sDate.Year, sDate.Month, sDate.Date,
		           sTime.Hours, sTime.Minutes, sTime.Seconds);
		
		    HAL_Delay(1000);
		  }
		}

3、BKP备份寄存器保存掉电参数

1)开启外部晶振

RCC → HSE 选择 Crystal/Ceramic Resonator。

RCC → LSE 选择 Crystal/Ceramic Resonator(RTC 必须用 32.768KHz 低速晶振)。

2)配置 RTC

RTC → 勾选 Active Clock Source。

RTC Out 不勾选。

RTC 参数默认即可(CubeMX 会自动配置分频)。

3)配置串口(用于打印时间)

USART1 → 模式选择 Asynchronous(异步)。。

波特率:115200,8 位数据,无校验,1 位停止位

4)工程生成

项目名(HAL_BKP)、路径自行设置。

栈大小建议设置为:0x400。

生成代码后用 Keil 打开。

5)代码实例

复制代码
		#include "main.h"
		#include "rtc.h"
		#include "usart.h"
		#include "gpio.h"
		#include "stdio.h"
		#include "string.h"
		
		/* 重定向printf */
		#ifdef __GNUC__
		#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
		#else
		#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
		#endif
		PUTCHAR_PROTOTYPE
		{
		        HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
		        return ch;
		}
		
		/* 要掉电保存的参数结构体 */
		typedef struct {
		  uint16_t device_id;      // 设备ID
		  uint16_t alarm_hour;     // 闹钟小时
		  uint16_t alarm_min;      // 闹钟分钟
		  uint16_t power_cnt;      // 上电次数
		  uint16_t save_flag;      // 保存标志 0xA5A5 = 已初始化
		} Backup_Config;
		
		Backup_Config bkp_cfg;
		
		// 从BKP读取参数
		void BKP_ReadConfig(Backup_Config *cfg)
		{
		  cfg->device_id  = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1);
		  cfg->alarm_hour = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR2);
		  cfg->alarm_min  = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR3);
		  cfg->power_cnt  = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR4);
		  cfg->save_flag  = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR5);
		}
		
		// 保存参数到BKP
		void BKP_SaveConfig(Backup_Config *cfg)
		{
		  HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, cfg->device_id);
		  HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR2, cfg->alarm_hour);
		  HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR3, cfg->alarm_min);
		  HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR4, cfg->power_cnt);
		  HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR5, cfg->save_flag);
		}
		//上电初始化 BKP 配置
		void BKP_Init(void)
		{
		  BKP_ReadConfig(&bkp_cfg);
		
		  // 判断是否第一次上电
		  if(bkp_cfg.save_flag != 0xA5A5)
		  {
		    printf("第一次上电,加载默认参数!\r\n");
		
		    bkp_cfg.device_id  = 1001;
		    bkp_cfg.alarm_hour = 8;
		    bkp_cfg.alarm_min  = 30;
		    bkp_cfg.power_cnt  = 1;
		    bkp_cfg.save_flag = 0xA5A5;
		
		    BKP_SaveConfig(&bkp_cfg);
		  }
		  else
		  {
		    printf("从BKP恢复掉电参数!\r\n");
		    bkp_cfg.power_cnt++;  // 上电次数+1
		    BKP_SaveConfig(&bkp_cfg);
		  }
		}
		int main(void)
		{
		  HAL_Init();
		  SystemClock_Config();
		  MX_GPIO_Init();
		  MX_RTC_Init();
		  MX_USART1_UART_Init();
		
		  // ==================== BKP 初始化 ====================
		  BKP_Init();
		
		  // 打印参数
		  printf("=====================================\r\n");
		  printf("设备ID   : %d\r\n", bkp_cfg.device_id);
		  printf("闹钟时间 : %02d:%02d\r\n", bkp_cfg.alarm_hour, bkp_cfg.alarm_min);
		  printf("上电次数 : %d\r\n", bkp_cfg.power_cnt);
		  printf("=====================================\r\n");
		
		  RTC_TimeTypeDef sTime;
		  RTC_DateTypeDef sDate;
		
		  while (1)
		  {
		    HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
		    HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
		
		    printf("时间:20%02d-%02d-%02d %02d:%02d:%02d\r\n",
		           sDate.Year, sDate.Month, sDate.Date,
		           sTime.Hours, sTime.Minutes, sTime.Seconds);
		
		    HAL_Delay(1000);
		  }
		}

4、RTC唤醒低功耗模式

1)开启外部晶振

RCC → HSE 选择 Crystal/Ceramic Resonator。

RCC → LSE 选择 Crystal/Ceramic Resonator(RTC 必须用 32.768KHz 低速晶振)。

2)配置 RTC

RTC → 勾选 Activate Clock Source。

RTC → 勾选 Activate Calendar。

RTC Out 不勾选。

NVIC Settings​ 中,勾选 RTC global interrupt​ 和 RTC Alarm through EXTI line interrupt

RTC 参数默认即可(CubeMX 会自动配置分频)。

3)配置串口(用于打印时间)

USART1 → 模式选择 Asynchronous(异步)。。

波特率:115200,8 位数据,无校验,1 位停止位

4)工程生成

项目名(HAL_RTC_STOP)、路径自行设置。

栈大小建议设置为:0x400。

生成代码后用 Keil 打开。

5)代码实例

复制代码
		#include "main.h"
		#include "rtc.h"
		#include "usart.h"
		#include "gpio.h"
		#include "stdio.h"
		#include "string.h"
		
		/* 重定向printf */
		#ifdef __GNUC__
		#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
		#else
		#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
		#endif
		PUTCHAR_PROTOTYPE
		{
		        HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
		        return ch;
		}
		
		//全局时间日期变量
		RTC_TimeTypeDef sTime = {0};
		RTC_DateTypeDef sDate = {0};
		RTC_AlarmTypeDef sAlarm = {0};
		
		//唤醒后重新初始化系统时钟
		void SystemClock_ReConfig(void)
		{
		  SystemClock_Config();
		}
		//进入 STOP 低功耗模式函数
		void Enter_STOP_Mode(void)
		{
		  // 进入低功耗前,关闭串口、SPI等外设,降低功耗
		  HAL_SuspendTick();  // 暂停系统滴答定时器(防止唤醒)
		
		  // 进入 STOP 模式
		  HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
		
		  // ========== 唤醒后执行以下代码 ==========
		  SystemClock_ReConfig();  // 重新配置时钟
		  HAL_ResumeTick();        // 恢复滴答定时器
		}
		
		//闹钟中断回调函数
		void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
		{
		        /* 清除闹钟标志 */
		  __HAL_RTC_ALARM_CLEAR_FLAG(hrtc, RTC_FLAG_ALRAF);
		}
		
		// 获取当前时间函数
		void Get_RTC_Time(void) {
		    RTC_TimeTypeDef sTime = {0};
		    RTC_DateTypeDef sDate = {0};
		    
		    HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
		    HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
		    
		    printf("Time: %02d:%02d:%02d\n", 
		           sTime.Hours, sTime.Minutes, sTime.Seconds);
		}
		
		int main(void)
		{
		  HAL_Init();
		  SystemClock_Config();
		  MX_GPIO_Init();
		  MX_RTC_Init();
		  MX_USART1_UART_Init();
		
		        /* 设置闹钟 */
		        sAlarm.AlarmTime.Hours = 0;       // 0点
		        sAlarm.AlarmTime.Minutes = 0;     // 0分
		        sAlarm.AlarmTime.Seconds = 10;    // 10秒
		        sAlarm.Alarm = RTC_ALARM_A;
		        if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BCD) != HAL_OK) {
		                Error_Handler();
		        }
		        //获取当前时间
		        Get_RTC_Time();
		        printf("即将进入低功耗模式...\r\n");
		        HAL_Delay(500);
		        //进入 STOP 低功耗,等待 RTC 唤醒
		  Enter_STOP_Mode();
		
		  while (1)
		  {
		    Get_RTC_Time();
		    HAL_Delay(1000);
		  }
		}

六、RTC 核心要点与避坑大全

1、必考核心知识点

RTC 是独立时钟外设,主电源断电可依靠VBAT电池继续计时。

工程必须使用 LSE 32.768K 晶振保证精度。

RTC 可生成秒中断、闹钟中断,是低功耗唤醒核心。

BKP备份寄存器掉电保存少量参数,无需外部存储。

RTC时间只需初始化一次,重复初始化会重置时间。

2、高频坑点

未焊接32.768K晶振,LSE无法启动,RTC不走时。

未接VBAT备用电池,断电时间清零。

每次开机都初始化时间,导致时间永远固定。

中断回调内使用延时、阻塞函数,导致计时异常。

LSI内部时钟精度差,长时间运行时间严重偏差。

3、工程万能应用场景

智能设备实时时间显示、日志时间戳。

定时开关机、定时采集数据。

低功耗设备定时唤醒工作。

设备参数掉电保存(BKP)。

4、一句话总结

RTC是STM32独立实时时钟外设,依靠32.768K外部晶振精准计时,支持掉电续航、日历计时、闹钟中断、低功耗唤醒,搭配BKP备份寄存器可实现少量参数掉电保存,是智能设备计时与低功耗开发的核心外设。

七、全篇总结

复制代码
RTC 是嵌入式产品计时与低功耗开发的必备外设,结构简单、稳定性高、功耗极低。
掌握RTC时间读写、闹钟中断、BKP备份、低功耗唤醒,即可满足所有智能仪表、智能家居、低功耗设备、毕设项目的时间功能开发需求。