┌──────────────┐
VBAT ─────┤ │
│ BKP Domain │
VDD 断电 │ 10×16bit Reg │
不影响 │ RTC 寄存器 │
└──────────────┘
PWR_BackupAccessCmd(ENABLE) 解锁 ┌──────────────┐
LSE 32.768kHz ──┤ │
(或 LSI/HSE) │ RTC Prescaler│── 1Hz ──▶ 32位计数器
│ (RL[14:0]) │
└──────────────┘
│
┌────────┴────────┐
│ │
秒中断(SEC) 闹钟中断(ALR)
时钟源选择:
| 时钟源 | 频率 | 特点 |
|---|---|---|
| LSE | 32.768 kHz | 最精确,需外接晶振 |
| LSI | 40 kHz | 内置 RC,精度差 |
| HSE/128 | HSE/128 | 需 HSE 存在 |
RTC 配置流程:
1. 使能 PWR + BKP 时钟
2. 使能备份域访问
3. 选择 RTC 时钟源 (LSE)
4. 使能 RTC 时钟
5. 等待 RTC 同步 (WaitForSynchro)
6. 配置预分频 (32767 → 1秒)
7. 设置计数值 (即时间)
| 函数 | 说明 |
|---|---|
void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data) |
写备份寄存器 |
uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR) |
读备份寄存器 |
void BKP_TamperPinCmd(FunctionalState NewState) |
侵入检测引脚使能 |
void BKP_ITConfig(FunctionalState NewState) |
侵入中断使能 |
FlagStatus BKP_GetFlagStatus(void) |
获取侵入检测标志 |
| 函数 | 说明 |
|---|---|
void RTC_SetPrescaler(uint32_t Prescaler) |
设置预分频值 (RL) |
void RTC_SetCounter(uint32_t CounterValue) |
设置计数值 |
uint32_t RTC_GetCounter(void) |
获取计数值 |
void RTC_SetAlarm(uint32_t AlarmValue) |
设置闹钟值 |
uint32_t RTC_GetDivider(void) |
获取当前分频值 |
void RTC_WaitForLastTask(void) |
等待上次操作完成 |
void RTC_WaitForSynchro(void) |
等待 RTC 同步 |
void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState) |
中断配置 |
RTC 中断类型:
| 宏 | 说明 |
|---|---|
RTC_IT_OW (Overflow) |
溢出中断 |
RTC_IT_SEC (Second) |
秒中断 |
RTC_IT_ALR (Alarm) |
闹钟中断 |
RTC 中断映射到 EXTI:
/*------------------------------------------------
* BKP_Example.c
*------------------------------------------------*/
#include "stm32f10x.h"
void BKP_Config(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
PWR_BackupAccessCmd(ENABLE); // 使能备份域访问
}
uint16_t BKP_ReadResetCount(void)
{
return BKP_ReadBackupRegister(BKP_DR1);
}
void BKP_SaveResetCount(uint16_t count)
{
BKP_WriteBackupRegister(BKP_DR1, count);
}
int main(void)
{
uint16_t resetCount;
BKP_Config();
resetCount = BKP_ReadResetCount();
resetCount++;
BKP_SaveResetCount(resetCount);
while (1);
}
/*------------------------------------------------
* RTC_Example.c
* LSE 32.768kHz → 1秒中断, USART1 打印时间
*------------------------------------------------*/
#include "stm32f10x.h"
#include <stdio.h>
static void Delay_ms(uint32_t ms);
/* ---- RTC 初始化 ---- */
/* 时间参考:假设 Unix 时间戳基准 2025-01-01 00:00:00 = 1735689600 */
#define TIME_EPOCH 1735689600UL
void RTC_Config(void)
{
/* 使能 PWR + BKP */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
PWR_BackupAccessCmd(ENABLE);
/* 检查是否已初始化 (通过备份寄存器标记) */
if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5)
{
/* ----- 首次配置 RTC ----- */
RCC_LSEConfig(RCC_LSE_ON); // 开启 LSE
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); // RTC 时钟源 = LSE
RCC_RTCCLKCmd(ENABLE); // 使能 RTC 时钟
RTC_WaitForSynchro(); // 等待同步
RTC_WaitForLastTask();
RTC_SetPrescaler(32767); // 32.768kHz / 32768 = 1Hz
RTC_WaitForLastTask();
RTC_SetCounter(TIME_EPOCH); // 设置初始时间
RTC_WaitForLastTask();
/* 秒中断 */
RTC_ITConfig(RTC_IT_SEC, ENABLE);
RTC_WaitForLastTask();
/* EXTI 17 配置 (RTC 秒中断) */
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Line = EXTI_Line17;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
/* NVIC 配置 (RTC 中断) */
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = RTC_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
BKP_WriteBackupRegister(BKP_DR1, 0xA5A5); // 标记已配置
}
}
/* ---- RTC 中断处理 ---- */
void RTC_IRQHandler(void)
{
if (RTC_GetITStatus(RTC_IT_SEC, RESET) != RESET)
{
RTC_ClearITPendingBit(RTC_IT_SEC);
/* 秒中断每 1 秒触发一次 */
/* 此处可打印或更新显示 */
}
}
/* ---- 读取 RTC 时间 ---- */
uint32_t RTC_GetTime(void)
{
return RTC_GetCounter();
}
/* ---- 时间转换 (Unix → 可读) ---- */
void RTC_UnixToDateTime(uint32_t unix, uint16_t* year, uint8_t* month,
uint8_t* day, uint8_t* hour, uint8_t* min, uint8_t* sec)
{
static const uint8_t daysInMon[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
uint32_t remaining = unix;
uint16_t y;
*sec = remaining % 60; remaining /= 60;
*min = remaining % 60; remaining /= 60;
*hour = remaining % 24; remaining /= 24;
for (y = 1970; ; y++)
{
uint16_t days = (y % 4 == 0 && y % 100 != 0) || y % 400 == 0 ? 366 : 365;
if (remaining < days) break;
remaining -= days;
}
*year = y;
uint8_t leap = (y % 4 == 0 && y % 100 != 0) || y % 400 == 0;
for (*month = 0; *month < 12; (*month)++)
{
uint8_t d = daysInMon[*month];
if (*month == 1 && leap) d = 29;
if (remaining < d) break;
remaining -= d;
}
(*month)++;
*day = remaining + 1;
}
int main(void)
{
uint32_t t;
uint16_t y; uint8_t M, d, h, m, s;
RTC_Config();
while (1)
{
t = RTC_GetTime();
RTC_UnixToDateTime(t, &y, &M, &d, &h, &m, &s);
/* 打印或显示 y-M-d h:m:s */
Delay_ms(1000);
}
}
/* 复用备份寄存器:
* BKP_DR1: 已初始化标记 (0xA5A5)
* BKP_DR2: 复位次数
* BKP_DR3: 用户校准值
*/
void SaveUserData(void)
{
BKP_WriteBackupRegister(BKP_DR2, BKP_ReadBackupRegister(BKP_DR2) + 1);
}
VBAT 必须接电池或电容
复位后 BKP 内容不变 (需 VBAT 供电)
RTC 寄存器需等待同步
RTC_WaitForSynchro() — 上电后调用一次RTC_WaitForLastTask() — 每次操作后调用RTC 配置只能做一次
RTC 中断配置 EXTI
备份域访问保护
PWR_BackupAccessCmd(ENABLE)LSE 起振慢
时间基准
time.h 库)RTC_IT_OW 注意
RTC_IT_OW,不是 RTC_IT_OVF闹钟中断