STM32的RTC外设

基本概念
image

框图分析
image

控制流程
image
image

程序设计
image
image

/**
 * @brief  RTC 实时时钟初始化配置函数
 * @note   功能链:解锁备份域写保护 → NVIC + EXTI22 配置唤醒中断 →
 *         启动 LSE 并选为 RTC 时钟源 → 设置初始时间/日期 →
 *         配置预分频产生 1Hz 秒时钟 → 开启唤醒定时器
 */
void RTC_Config(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;                       // 定义 NVIC 中断优先级配置结构体变量,用于配置内核中断控制器
	EXTI_InitTypeDef  EXTI_InitStructure;                      // 定义外部中断/事件线配置结构体变量,用于配置 EXTI 线

	/* ---------- 解锁备份域写保护 ---------- */
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);        // 使能 APB1 总线上的 PWR(电源控制外设)时钟;访问备份域前必须先开 PWR 时钟
	PWR_BackupAccessCmd(ENABLE);                               // 解除备份域(RTC、备份寄存器)写保护,此后才能修改 RTC 时钟源、时间、日期等寄存器

	/* ---------- 1. NVIC:配置 RTC 唤醒定时器中断 ---------- */
	NVIC_InitStructure.NVIC_IRQChannel = RTC_WKUP_IRQn;        // 指定中断通道为 RTC 唤醒定时器中断(RTC_WKUP_IRQn)
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  // 抢占优先级设为 1(数值越小,抢占优先级越高)
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;         // 子优先级设为 0,与抢占优先级配合决定中断嵌套顺序
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            // 使能该 NVIC 通道,允许内核响应此中断请求
	NVIC_Init(&NVIC_InitStructure);                            // 将结构体参数写入 NVIC 寄存器,完成优先级分组与通道使能配置

	/* ---------- 2. EXTI:RTC 唤醒事件固定映射到 EXTI 线 22 ---------- */
	EXTI_ClearITPendingBit(EXTI_Line22);                      // 先清除 EXTI22 线的中断挂起标志,避免配置前残留标志导致中断立即误触发
	EXTI_InitStructure.EXTI_Line = EXTI_Line22;               // 选择 EXTI 第 22 线(该线在芯片内部固定连接到 RTC 唤醒事件)
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;       // 工作模式选中断模式(产生中断请求送到 NVIC,而非事件输出)
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;    // 触发方式为上升沿触发(RTC 唤醒事件有效时输出上升沿)
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;                 // 使能该 EXTI 线,允许该线产生中断请求
	EXTI_Init(&EXTI_InitStructure);                           // 参数写入 EXTI 寄存器,完成 EXTI22 的中断配置

	/* ---------- 3. 启动 LSE 外部晶振,选为 RTC 时钟源 ---------- */
	RCC_LSEConfig(RCC_LSE_ON);                                 // 启动外部低速晶振 LSE(板上外接 32.768kHz 晶振,走时精度高、功耗低)
	while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);        // 循环轮询 LSE 就绪标志,直到晶振起振稳定后才继续,防止后续配置失步
	RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);                    // 选择 LSE 作为 RTC 时钟源(备选:LSI 内部 RC、HSE 外部高速晶振分频)
	RCC_RTCCLKCmd(ENABLE);                                     // 使能 RTC 时钟,将时钟送入 RTC 外设
	RTC_WaitForSynchro();                                      // 等待 RTC 影子寄存器与时钟同步完成,保证后续写时间/日期/预分频寄存器有效

	/* ---------- 4. 设置初始时间:23:59:50(BCD 码格式) ---------- */
	RTC_TimeStructure.RTC_Hours   = 0x23;                      // 小时 = 23;BCD 格式下高 4 位是十位、低 4 位是个位,0x23 即十进制 23
	RTC_TimeStructure.RTC_Minutes = 0x59;                      // 分钟 = 59(BCD 格式,0x59 即十进制 59)
	RTC_TimeStructure.RTC_Seconds = 0x50;                      // 秒 = 50(BCD 格式,0x50 即十进制 50)
	RTC_SetTime(RTC_Format_BCD, &RTC_TimeStructure);           // 把时间写入 RTC 时间寄存器,格式参数指定为 BCD 码

	/* ---------- 5. 设置初始日期:2026 年 10 月 27 日 星期二(BCD 码格式) ---------- */
	RTC_DateStructure.RTC_Month 	= 0x10;                      // 月份 = 10 月(BCD 格式,0x10 即十进制 10)
	RTC_DateStructure.RTC_Date  	= 0x27;                      // 日 = 27 日(BCD 格式,0x27 即十进制 27)
	RTC_DateStructure.RTC_Year  	= 0x26;                      // 年份后两位 = 26(RTC 只存两位年份,工程上约定代表 2026 年)
	RTC_DateStructure.RTC_WeekDay = 0x02;                        // 星期 = 星期二(取值约定:1=星期一,2=星期二……7=星期日)
	RTC_SetDate(RTC_Format_BCD, &RTC_DateStructure);             // 把日期写入 RTC 日期寄存器,格式参数指定为 BCD 码

	/* ---------- 6. RTC 预分频与小时格式配置 ---------- */
	RTC_InitStructure.RTC_AsynchPrediv = 128-1;                // 异步预分频系数 = 127(计数区间 0~127,共 128 个时钟);32.768kHz 先经异步级分频
	RTC_InitStructure.RTC_SynchPrediv =  256-1;                // 同步预分频系数 = 255(计数区间 0~255,共 256 个时钟);再经同步级分频得到秒时钟
	RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24;      // 小时格式选 24 小时制(备选 RTC_HourFormat_12 为 12 小时制,带 AM/PM)
	RTC_Init(&RTC_InitStructure);                              // 参数写入 RTC 配置寄存器;分频结果 = 32768 ÷ 128 ÷ 256 = 1Hz,即每秒产生一次日历更新

	/* ---------- 7. 配置并启动 RTC 唤醒定时器 ---------- */
	RTC_WakeUpClockConfig(RTC_WakeUpClock_CK_SPRE_16bits);     // 唤醒定时器时钟源选内部 1Hz 备用时钟 CK_SPRE(16 位计数器模式),不直接依赖 LSE 分频
	RTC_SetWakeUpCounter(1-1);                                 // 写入唤醒计数值 0;计数器在所选时钟沿递减,减到 0 后产生唤醒事件(等效 1 个时钟周期)
	RTC_ITConfig(RTC_IT_WUT, ENABLE);                          // 在 RTC 内核侧使能唤醒定时器中断(WUT 中断),RTC 才会向外发出中断请求
	RTC_ClearITPendingBit(RTC_IT_WUT);                         // 清除 RTC 内部唤醒中断挂起标志,避免配置前的残留标志导致首次进中断
	RTC_WakeUpCmd(ENABLE);                                     // 使能唤醒定时器开始递减计数;计数到 0 后经 EXTI22 触发 NVIC 的 RTC_WKUP_IRQn,进入 RTC_WKUP_IRQHandler
}


void RTC_WKUP_IRQHandler(void)
{
  if(RTC_GetITStatus(RTC_IT_WUT) != RESET)
  {
     uwTimeDisplay = 1;
     RTC_ClearITPendingBit(RTC_IT_WUT);
     EXTI_ClearITPendingBit(EXTI_Line22);
  }
}


int main()
{
	//1.硬件的初始化
	USART1_Config(9600);
	RTC_Config();
	
	
	
	while(1)
	{
		if (uwTimeDisplay == 1)
		{
			//显示时间
			RTC_GetTime(RTC_Format_BIN, &RTC_TimeStructure);
			RTC_GetDate(RTC_Format_BIN, &RTC_DateStructure);
			sprintf((char*)aShowTime,"%0.2d:%0.2d:%0.2d\r\n",RTC_TimeStructure.RTC_Hours, RTC_TimeStructure.RTC_Minutes, RTC_TimeStructure.RTC_Seconds);
			sprintf((char*)aShowDate,"20%0.2d-%0.2d-%0.2d\r\n",RTC_DateStructure.RTC_Year, RTC_DateStructure.RTC_Month, RTC_DateStructure.RTC_Date);
			
			USART1_SendString((char*)aShowTime);
			USART1_SendString((char*)aShowDate);
			uwTimeDisplay = 0;
		}	
	}
}


posted @ 2026-10-27 16:27  !城北徐公.!  阅读(6)  评论(0)    收藏  举报