STM32 RTC 功能详解与代码示例

本文主要讲解如何在 STM32F405RG单片机上实现实时时钟(RTC)的功能,包括初始化 RTC、设置时间和日期、配置唤醒和闹钟中断等内容,并结合示例代码进行详细说明。

一、RTC 简介

RTC是一种独立于主 MCU 工作的时钟模块,能够在主芯片掉电时通过电池供电继续计时,非常适合需要记录时间的应用场景,例如定时任务、计时器、唤醒功能等。

STM32 的 RTC 模块支持:

  • 秒、分钟、小时、星期、日期、月份、年份的完整时间记录。
  • 闹钟功能,可触发中断或事件。
  • 唤醒功能,用于定时唤醒 MCU。
  • 二、RTC 初始化

    初始化代码

    以下是 RTC 初始化的代码

    #define Access  0xf00
    void rtc_Init(void)
    { 
      RTC_InitTypeDef RTC_InitSturcture;  
       //允许访问
       RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
       PWR_BackupAccessCmd(ENABLE);  //启用备份存储区
      
      //设置时间标志位,不用每次设置
      if(RTC_ReadBackupRegister(RTC_BKP_DR0)!=Access)
      {
           RCC_LSEConfig(RCC_LSE_ON);//外部低速时钟  
           while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET){}  // 等待LSE准备好
          
          // 设置 RTC 时钟源为 LSE
           RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
           RCC_RTCCLKCmd(ENABLE);
          
           RTC_StructInit(&RTC_InitSturcture);// 初始化结构体
            
           RTC_InitSturcture.RTC_AsynchPrediv = 127;  // 异步预分频值  32768/128 = 256
           RTC_InitSturcture.RTC_SynchPrediv = 255;   // 同步预分频值
           RTC_InitSturcture.RTC_HourFormat = RTC_HourFormat_24;
           RTC_Init(&RTC_InitSturcture);
             
           rtc_SetDateTime(24,11,26,2,9,10,30);
          RTC_WriteBackupRegister(RTC_BKP_DR0,Access);
      }
    }

    代码解析

    1. 备份寄存器访问
    2. 通过 PWR_BackupAccessCmd(ENABLE) 允许对 RTC 和备份寄存器的访问。
    3. 时钟源配置
    4. 使用外部低速晶振 (LSE) 作为 RTC 的时钟源,保证时间精度。
    5. 预分频设置
    6. 异步预分频为 127,同步预分频为 255,实现 1 秒的计时周期。
    7. 时间初始化
    8. 设置初始时间为 2024 年 11 月 26 日 09:10:30。

    三、时间和日期设置

    使用以下函数可以方便地设置 RTC 的时间和日期:

    /*设置日期和时间 年月日 星期 时分秒   */
    void rtc_SetDateTime(uint8_t year, uint8_t month, uint8_t date, uint8_t weekday,uint8_t hours,uint8_t minutes,uint8_t seconds)
    {
        RTC_DateTypeDef RTC_DateStructure;
        RTC_TimeTypeDef  RTC_TimeStructure;
        
        RTC_DateStructure.RTC_Year = year;
        RTC_DateStructure.RTC_Month = month;
        RTC_DateStructure.RTC_Date = date;
        RTC_DateStructure.RTC_WeekDay = weekday;
      
        RTC_TimeStructure.RTC_Hours = hours;
        RTC_TimeStructure.RTC_Minutes = minutes;
        RTC_TimeStructure.RTC_Seconds = seconds;
        RTC_TimeStructure.RTC_H12 = RTC_H12_AM;  //24小时
      
        RTC_SetTime(RTC_Format_BIN,&RTC_TimeStructure);
        RTC_SetDate(RTC_Format_BIN, &RTC_DateStructure);
    }
    
    // 设置时间函数
    void rtc_SeTime(uint8_t hours,uint8_t minutes,uint8_t seconds)
    {
        RTC_TimeTypeDef  RTC_TimeStructure;   
        RTC_TimeStructure.RTC_Hours = hours;
        RTC_TimeStructure.RTC_Minutes = minutes;
        RTC_TimeStructure.RTC_Seconds = seconds;
        RTC_TimeStructure.RTC_H12 = RTC_H12_AM;  //24小时
      
        RTC_SetTime(RTC_Format_BIN,&RTC_TimeStructure);
    
    }

     

    四、RTC 唤醒功能

    RTC 的唤醒功能可以定期唤醒 MCU,适用于低功耗应用。

    唤醒初始化

    //设置唤醒
    void WKUP_init(uint32_t WUCKSEL,uint32_t CNT)
    {
      u32 timeout;
      NVIC_InitTypeDef NVIC_InitStruct={0};
      EXTI_InitTypeDef EXTI_InitStruct={0};
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);//开启线时钟
    
      RTC_WakeUpCmd(DISABLE);//关闭定时器
      
      while(RTC_GetFlagStatus(RTC_FLAG_WUTF)==RESET&&timeout<100)// 准备就绪
      {
        timeout++;
        delay_ms(10);
      }  
      
      RTC_ClearFlag(RTC_FLAG_WUTF);  
      RTC_WakeUpClockConfig(WUCKSEL);//最大计时18小时 一般16位
      RTC_SetWakeUpCounter(CNT-1);
      
      //EXTI_ClearITPendingBit(RTC_IT_WUT);
      EXTI_ClearITPendingBit(EXTI_Line22);//清0 线22 
      
      EXTI_InitStruct.EXTI_LineCmd = ENABLE;//使能线
      EXTI_InitStruct.EXTI_Line = EXTI_Line22; 
      EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿有效
      EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
      EXTI_Init(&EXTI_InitStruct);
      
      
      NVIC_InitStruct.NVIC_IRQChannel= RTC_WKUP_IRQn; //开启唤醒中断
    	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;//使能
    	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1; //抢占
    	NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;  //响应
    	NVIC_Init(&NVIC_InitStruct);
      
      RTC_ITConfig(RTC_IT_WUT,ENABLE);//开启唤醒中断
      RTC_WakeUpCmd(ENABLE);
    }

    唤醒中断处理

     

    RTC_TimeTypeDef  RTC_Time={0};
    
    //设置唤醒中断
    void RTC_WKUP_IRQHandler(void)
    { 
      if(RTC_GetITStatus(RTC_IT_WUT)==SET)
      {
        RTC_GetTime(RTC_Format_BIN,&RTC_Time);
      }
      RTC_ClearITPendingBit(RTC_IT_WUT);
      EXTI_ClearITPendingBit(EXTI_Line22);
    }

    五、闹钟功能

    RTC 支持两个闹钟(A 和 B),可根据需要设置。

    设置闹钟

     

    /*******************
        设置闹钟
        RTC_AlarmX      选择闹钟 RTC_Alarm_A  | RTC_Alarm_B
        hh  min,sec ,week    小时 分钟 ,秒 星期 
    *******************/
    void rtc_Alarm(uint32_t RTC_AlarmX,uint8_t hh,uint8_t min,uint8_t sec,uint8_t week)
    {
      RTC_AlarmTypeDef RTC_AlarmStruct={0};
      RTC_TimeTypeDef RTC_AlarmTime={0};
      NVIC_InitTypeDef NVIC_InitStruct={0};
      EXTI_InitTypeDef EXTI_InitStruct={0};
      
      RTC_AlarmCmd(RTC_AlarmX,DISABLE);
      if(RTC_AlarmX==RTC_Alarm_A)
      {
        while(RTC_GetFlagStatus(RTC_FLAG_ALRAWF)==RESET);// 准备就绪  0x01
      }
      else  if(RTC_AlarmX==RTC_Alarm_B)
        {
          while(RTC_GetFlagStatus(RTC_FLAG_ALRBWF)==RESET);  //0x02
        }
      RTC_ClearFlag(RTC_FLAG_ALRAWF); 
      RTC_ClearFlag(RTC_FLAG_ALRBWF); 
      RTC_ClearITPendingBit(RTC_IT_ALRA);
      RTC_ClearITPendingBit(RTC_IT_ALRB);
      
      EXTI_ClearITPendingBit(EXTI_Line17);//清0 线17
      EXTI_InitStruct.EXTI_LineCmd = ENABLE;//使能线
      EXTI_InitStruct.EXTI_Line = EXTI_Line17; 
      EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿有效
      EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
      EXTI_Init(&EXTI_InitStruct);
      
      NVIC_InitStruct.NVIC_IRQChannel= RTC_Alarm_IRQn; //开启中断
    	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;//使能
    	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=3; //抢占
    	NVIC_InitStruct.NVIC_IRQChannelSubPriority=3;  //响应
    	NVIC_Init(&NVIC_InitStruct);  
      
      RTC_AlarmStruct.RTC_AlarmMask = RTC_AlarmMask_None; //是日期才选匹配
      RTC_AlarmStruct.RTC_AlarmDateWeekDaySel = RTC_AlarmDateWeekDaySel_WeekDay;  //星期闹钟
        
       RTC_AlarmStruct.RTC_AlarmDateWeekDay = week;
       
       RTC_AlarmTime.RTC_H12 = RTC_H12_AM;
       RTC_AlarmTime.RTC_Hours = hh;
       RTC_AlarmTime.RTC_Minutes = min;
       RTC_AlarmTime.RTC_Seconds = sec;
       
       RTC_AlarmStruct.RTC_AlarmTime = RTC_AlarmTime;  //设置闹钟的时间
       
       RTC_SetAlarm(RTC_Format_BIN,RTC_AlarmX,&RTC_AlarmStruct);
       
       if(RTC_AlarmX==RTC_Alarm_A)
         {
           RTC_ITConfig(RTC_IT_ALRA,ENABLE);
         }
       else  if(RTC_AlarmX==RTC_Alarm_B)
         {
           RTC_ITConfig(RTC_IT_ALRB,ENABLE);
         }
        RTC_AlarmCmd(RTC_AlarmX,ENABLE);
    }

    闹钟中断处理

    //设置闹钟中断
    void RTC_Alarm_IRQHandler(void)
    {  
      if(RTC_GetITStatus(RTC_IT_ALRA)==SET)
      {
        BEEP_Alarm(2,200);//蜂鸣器提示
        RTC_ClearITPendingBit(RTC_IT_ALRA);
      }else
      if(RTC_GetITStatus(RTC_IT_ALRB)==SET)
      {
        BEEP_Alarm(2,200);
        RTC_ClearITPendingBit(RTC_IT_ALRB);
      }    
      EXTI_ClearITPendingBit(EXTI_Line17);
    }

    六、主程序使用

    mian.c 

    int main(void)
    {
      //....
      rtc_Init();
      WKUP_init(RTC_WakeUpClock_CK_SPRE_16bits,1);//1秒唤醒一次
      rtc_Alarm(RTC_Alarm_A,20,50,0,2);  //Alarm_A
     while(1)
    {
         /** RTC更新时间,LCD显示 **/
        if(oldtime!=RTC_Time.RTC_Seconds)
        {
          oldtime=RTC_Time.RTC_Seconds;
    sprintf((char*)buff1,"%2d:%02d:%02d",RTC_Time.RTC_Hours,RTC_Time.RTC_Minutes,RTC_Time.RTC_Seconds);
    LCD_ShowString2(40,80,(u8*)buff1,BLACK,32); 
    printf("%2d:%02d:%02d\r\n",RTC_Time.RTC_Hours,RTC_Time.RTC_Minutes,RTC_Time.RTC_Seconds);
              
        }
    }
    }

     

    作者:ghs980

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32 RTC 功能详解与代码示例

    发表回复