STM32
直播中

1652711011.026500

16年用户 11294经验值
私信 关注
[问答]

如何利用STM32的实时时钟RTC去测量日期时间呢

STM32的实时时钟RTC有何功能?怎样去使用它呢?
如何利用STM32的实时时钟RTC去测量日期时间呢?

回帖(2)

张桂荣

2021-12-13 11:10:21
实验目的

本实验旨在掌握STM32的实时时钟RTC的使用,利用其测量日期时间,数据手册请参看16章。
实验简介

STM32的实时时钟(RTC)是一个独立的定时器。STM32的RTC模块拥有一组连续计数的计数器,在相应软件配置下,可以提供时钟日历的功能,修改计数器的值可以重新设置系统当前的时间和日期。
RTC模块和时钟配置系统(RCC_BDCR寄存器)是在后备区域,即在系统复位或从待机模式唤醒后RTC的设置和时间维持不变
系统复位后,会自动禁止访问后备寄存器和RTC,以防止对后备区域(BKP)的意外写操作。所以在要设置时间之前,先要取消备份区域(BKP)写保护,
STM32的RTC具有掉电继续运行的特性,在主电源VDD断开时,为了RTC外设掉电继续运行,必须给STM32芯片通过VBAT引脚接上锂电池,当主电源VDD有效时,由VDD给RTC外设供电。当VDD掉电后,由VBAT给RTC外设供电,但无论由什么电源供电,RTC中的数据都保存在属于RTC的备份区域中,若主电源VDD和VBAT都掉电,那么备份域中保存的所有数据将丢失。备份域除了RTC模块的寄存器,还有42个16位的寄存器可以在VDD掉电的情况下保存用户程序的数据,系统复位或电源复位时,这些数据也不会复位。
从RTC的定时器特性来说,它是一个32位的计数器,只能向上计数。它使用的时钟源有三种,分别为高速外部时钟的128分频:HSE/128;低速内部时钟LSI;使用HSE分频时钟或LSI的话,在主电源VDD掉电的情况下,这两个时钟源都会受到影响,因此没法保证RTC正常工作。因此RTC一般使用低速外部时钟LSE,频率为实时时钟模块中常用的32.768KHz,这是因为32768=2的15次方,分频容易实现,所以它被广泛应用到RTC模块。在主电源VDD有效的情况下(待机),RTC还可以配置闹钟事件使STM32退出待机模式




HAL库代码

main.c

#include "MyIncludes.h"


u16 sys_cnt = 0;
void systick_isr(void)
{
  if(sys_cnt <100 )
  sys_cnt++;
  else
  {
    sys_cnt = 0;
    HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_4|GPIO_PIN_5);
  }
}


void rtc_disp(void)
{
  char date[15];
  //用来存储日期
  char time[15];
  //用来存储时间
  sprintf((char*)date,"20%d%d/%d%d/%d%d ",Calendar.RTC_Year%10,Calendar.RTC_Year%10,Calendar.RTC_Mon/10,Calendar.RTC_Mon%10,Calendar.RTC_Mday/10,Calendar.RTC_Mday%10);
  //sprintf函数打印到字符串中(将数值转换成对应字符串形式,就是变换成ASCALL码)


  sprintf((char*)time,"%d%d:%d%d:%d%dn",Calendar.RTC_Hour/10,Calendar.RTC_Hour%10,Calendar.RTC_Min/10,Calendar.RTC_Min%10,Calendar.RTC_Sec/10,Calendar.RTC_Sec%10);


printf(date);
//发送日期
printf(time);
//发送时间


  
}


int main()
{
  System_Init();
  //系统初始化
  LED_Init();
  //LED初始化
  SysTick_Init(systick_isr);
  //SysTick初始化
  USART1_Init(115200,NULL,NULL);
  //串口1初始化  波特率:115200
  //发送完成回调为空   接收回调为空
  RTC_Init(rtc_disp);
  //实时时钟初始化
  while(1)
  {
    RTC_Process();
    //RTC处理函数
  }
}


rtc.h


#ifndef __RTC_H
#define __RTC_H


#include "stm32f1xx.h"
#include "stm32_types.h"
#include "stm32f1xx_hal.h"


typedef struct
{
   u8 RTC_Sec;
   //秒
   u8 RTC_Min;
   //分
   u8 RTC_Hour;
   //时
   u8 RTC_Mday;
   //日
   u8 RTC_Mon;
   //月
   u8 RTC_Year;
   //年
   u8 RTC_Wday;
   //星期
}_Calendar_obj;
//日历结构体


extern _Calendar_obj Calendar;
//结构体声明


typedef struct
{
  void (*Operation)(void);
  //函数指针
}_RTC_Fun;


void RTC_SetDateTime(_Calendar_obj datatime);
//设置RTC的日期时间函数


_Calendar_obj RTC_GetDateTime(void);
//获取RTC日期时间函数


void RTC_Init(void(*rtc)(void));
//RTC初始化函数


void RTC_Process(void);
//RTC处理


#endif


rtc.c


#include "rtc.h"
_Calendar_obj Calendar;
//日历时钟结构体变量声明
RTC_HandleTypeDef RTCHandle;
//时间句柄结构体变量声明
_RTC_Fun rtc_fun;
//用户函数结构体变量声明


u8 const table_week[12] = {0,3,3,6,1,4,6,2,5,0,3,5};
//月修正表数据
/*假设1月1日是星期一,那么2月1日是星期四,(4-1=3),故是3
3月1日是星期四,(4-1=3),4月1日是星期日(7-1=6),依次类推,
前提这一年是平年
*/


u8 RTC_Get_Week(u16 year,u8 month,u8 day)
//作用算出周几
{
  u16 temp2;
  u8 yearH,yearL;


  yearH = year/100;
  yearL = year%100;
  if(yearH >19 )
  yearL+=100;
  //这几行算出本年距离1900年的差
  temp2 = yearL + yearL/4;
  //算出除了一年中除正周外累计多出来的天数     365%7=1   闰年的个数:366-365=1
  temp2 = temp2%7;
  //除去整周的天数
  temp2 = temp2+day+table_week[month-1];
  //现在temp2是除去整周的天数,加上本日的天数 在加上月修正表数据
  if(yearL%4 == 0 && month <3 )
  temp2--;
  //如果是闰年1月2月的话要减去1,因为yearl多出来的一天加上去了,也就是
  //2月29日,多出来的一天再三月份以后才能加
  return (temp2%7);
  //这个就是本周星期几
  /*在这里我们举个栗子吧:假设时间是2020年2月29日,那么首先yearH=20,
  yearl=20,因为yearH = 20 >19 那么yeal+=100  那么yeal = 120天  
  temp2 = yearL + yearL/4;   120+30 = 150 天, 150%7 = 3,  
  3+29+3 = 35  35-1=34  34%7=6  也就是星期六,我们看下日历确实
  是星期六*/
}


void RTC_SetDateTime(_Calendar_obj datetime)
//先看void RTC_Init(void (*rtc)(void))函数
//设置RTC的日期和时间
{
  RTC_DateTypeDef SetDate;
  //设置日期结构体声明
  RTC_TimeTypeDef SetTime;
  //设置时间结构体声明
  
  SetDate.Date = datetime.RTC_Mday;
  SetDate.Month = datetime.RTC_Mon;
  SetDate.Year = datetime.RTC_Year;
  //0~99
  SetDate.WeekDay = datetime.RTC_Wday;
  SetTime.Hours = datetime.RTC_Hour;
  SetTime.Minutes = datetime.RTC_Min;
  SetTime.Seconds = datetime.RTC_Sec;
  
  HAL_RTC_SetDate(&RTCHandle,&SetDate,RTC_FORMAT_BIN);
  //设置日期
  HAL_RTC_SetTime(&RTCHandle,&SetTime,RTC_FORMAT_BIN);
  //设置时间
  return ;
}


_Calendar_obj RTC_GetDateTime(void)
//先看void RTC_Process(void)
//获取RTC日期时间
{
   RTC_DateTypeDef SetDate;
   RTC_TimeTypeDef SetTime;
   _Calendar_obj datetime;


   HAL_RTC_GetTime(&RTCHandle,&SetTime,RTC_FORMAT_BIN);
   //获取时间
   HAL_RTC_GetDate(&RTCHandle,&SetDate,RTC_FORMAT_BIN);
   //获取日期
   datetime.RTC_Sec = SetTime.Seconds;
   datetime.RTC_Min = SetTime.Minutes;
   datetime.RTC_Hour = SetTime.Hours;
   datetime.RTC_Mday = SetDate.Date;
   datetime.RTC_Mon = SetDate.Month;
   datetime.RTC_Year = SetDate.Year;
   datetime.RTC_Wday = SetDate.WeekDay;


   return datetime;
}


void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
//在HAL_RTC_Init中调用
{
  RCC_OscInitTypeDef RCC_OscInitStruct;
  //RCC内部/外部振荡器(HSE、HSI、LSE和LSI)配置结构变量声明
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;
  //RCC扩展时钟结构变量声明
  __HAL_RCC_PWR_CLK_ENABLE();
  //备份电源使能


  HAL_PWR_EnableBkUpAccess();
  //电源使能唤醒




  __HAL_RCC_BKP_CLK_ENABLE();
    //使能BKP时钟
   //配置时钟源
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;
  //要配置的振荡器   
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  //PLL的状态   未配置
  RCC_OscInitStruct.LSEState = RCC_LSE_ON;
  //振荡器状态  振荡器开启
  HAL_RCC_OscConfig(&RCC_OscInitStruct);
  //RCC配置

  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
  //要配置的拓展时钟  外部时钟 0x0000001
  PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
  //指定RTC时钟源  用作RTC时钟的LSE振荡器时钟
  HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
//配置拓展时钟
  __HAL_RCC_RTC_ENABLE();
// 使能RTC
}


void RTC_Init(void (*rtc)(void))
{
_Calendar_obj DateTime;
//日历结构体变量声明


RTCHandle.Instance = RTC;
//寄存器基址 RTC
RTCHandle. Init.AsynchPrediv = RTC_AUTO_1_SECOND;
//指定BIC  异分频系数    0xffffffff  自动获取1s时机基
RTCHandle.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
//指定那个信号路由到RTC针   0x00000000
//篡改针上没有输出
HAL_RTC_Init(&RTCHandle);
//初始化RTC参数

DateTime.RTC_Hour = 23;
DateTime.RTC_Min = 59;
DateTime.RTC_Sec = 50;
DateTime.RTC_Year = 16;
DateTime.RTC_Mon = 8;
DateTime.RTC_Mday = 21;
DateTime.RTC_Wday = RTC_Get_Week(DateTime.RTC_Year+2000,DateTime.RTC_Mon,DateTime.RTC_Mday);
//2016年8月21日23时59分50秒
RTC_SetDateTime(DateTime);
//设置RTC的时间和日期
rtc_fun.Operation = rtc;


return ;
}


u8 last_sec = 0;
void RTC_Process(void)
//RTC处理
{
  Calendar = RTC_GetDateTime();
  if(last_sec != Calendar.RTC_Sec)
  {
    last_sec = Calendar.RTC_Sec;
    if(rtc_fun.Operation != NULL)
    rtc_fun.Operation();
    //每一秒刷新一次
  }
}


寄存器代码
main.c


#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "timer.h"
#include "key.h"
#include "rtc.h"




int main(void)
{
        char t[20];
        Stm32_Clock_Init(9);        //ϵͳʱÖÓÉèÖÃ
        uart_init(72,115200);        //´®¿Ú³õʼ»¯Îª115200
        delay_init(72);                            //ÑÓʱ³õʼ»¯
        LED_Init();                                  //³õʼ»¯ÓëLEDÁ¬½ÓµÄÓ²¼þ½Ó¿Ú
        while(RTC_Init())
        {
               
        }
       
        while(1)
        {
               
       
                                LED2=!LED2;
                delay_ms(100);
                  sprintf((char*)t,"%d:%d:%drn",calendar.w_year,calendar.w_month,calendar.w_date);
                  printf(t);
               
               
               
               
        }
}


RTC.h


#ifndef __RTC_H
#define __RTC_H          
#include "sys.h"


typedef struct
{
        vu8 hour;
        vu8 min;
        vu8 sec;                       
        vu16 w_year;
        vu8  w_month;
        vu8  w_date;
        vu8  week;       
}_calendar_obj;                                         
extern _calendar_obj calendar;
//日历结构体
                                                                                                    
void Disp_Time(u8 x,u8 y,u8 size);       
//在制定位置开始显示时间
void Disp_Week(u8 x,u8 y,u8 size,u8 lang);       
//在指定位置显示星期
u8 RTC_Init(void);
//初始化RTC 失败返回1 成功返回0                                              
u8 Is_Leap_Year(u16 year);               
//平年瑞年判断                       
u8 RTC_Get(void);
//获取时间                                       
u8 RTC_Get_Week(u16 year,u8 month,u8 day);
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);
//设置时间               
u8 RTC_Alarm_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);
//设置闹钟                 
#endif
举报

俞敏东

2021-12-13 11:10:37
RTC.C


#include "delay.h"
#include "usart.h"
#include "rtc.h"        
#include "LED.h"


          
_calendar_obj calendar;
//时钟结构体
   


u8 RTC_Init(void)
{
       
        u8 temp=0;
        //检查是不是第一次配置时钟
        if(BKP->DR1!=0X5050)//第一次配置
        {         
                  RCC->APB1ENR|=1<<28;  
                  //使用电源时钟  
                RCC->APB1ENR|=1<<27;   
                //使能备份时钟  
                PWR->CR|=1<<8;  
                //取消备份区写保护        
                RCC->BDCR|=1<<16;   
                //备份区域软复位      
                RCC->BDCR&=~(1<<16);  
                //备份区域软复位结束           
            RCC->BDCR|=1<<0;
            //开启外部低速振荡器      
            while((!(RCC->BDCR&0X02))&&temp<250)
            //等待外部时钟就绪         
                {
                        temp++;
                        delay_ms(10);
                };
               
       
               
                if(temp>=250)return 1;       
                //初始化时钟失败,晶振有问题   
                RCC->BDCR|=1<<8;
                //LSE作为RTC时钟
                RCC->BDCR|=1<<15;
                //RTC时钟使能
                  while(!(RTC->CRL&(1<<5)));
                  //等待RTC寄存器操作完成
            while(!(RTC->CRL&(1<<3)));
            //等待RTC寄存器同步
            RTC->CRH|=0X01;
            //允许秒中断                 
            RTC->CRH|=0X02;
            //允许闹钟中断                    
            while(!(RTC->CRL&(1<<5)));
            //等待RTC寄存器操作完成
                RTC->CRL|=1<<4;
                //允许配置        
                RTC->PRLH=0X0000;
                RTC->PRLL=32767;  
                //时钟周期设置   
                RTC_Set(2015,1,14,17,42,55);
                //设置时间
                RTC->CRL&=~(1<<4);
                //配置更新         
                while(!(RTC->CRL&(1<<5)));
                //等待RTC寄存器操作完成                                                                                    
                BKP->DR1=0X5050;  
                 printf("FIRST TIMEn");
        }else//系统继续计时
        {
            while(!(RTC->CRL&(1<<3)));
            //等待RTC寄存器同步
            RTC->CRH|=0X01;  
            //允许秒中断                  
            while(!(RTC->CRL&(1<<5)));
            //等待RTC寄存器操作完成
                printf("OKn");
        }                                                      
        MY_NVIC_Init(0,0,RTC_IRQn,2);
        //优先级设置   
        RTC_Get();
        //更新时间;
        return 0;
}                                                     




//RTC时钟中断
//每秒触发一次         
void RTC_IRQHandler(void)
{                 
        if(RTC->CRL&0x0001)       
        //秒中断               
        {                                                       
                RTC_Get();
                //更新时间                               
               
        }
        if(RTC->CRL&0x0002)       
        //闹钟中断               
        {
                RTC->CRL&=~(0x0002);
                //清除闹钟中断         
                RTC_Get();
                //更新时间                               
                  printf("Alarm Time:%d-%d-%d %d:%d:%dn",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);   
          }                                                                                                    
    RTC->CRL&=0X0FFA;
    //清除溢出,秒钟中断标志                
        while(!(RTC->CRL&(1<<5)));
        //等待RTC寄存器操作完成                                                                                         
}




//判断是否是不是闰年 1是 0不是
u8 Is_Leap_Year(u16 year)
{                          
        if(year%4==0)//必须能被4整除
        {
                if(year%100==0)
                {
                        if(year%400==0)return 1;  //如果以00结尾,还要能被400整除
                        else return 0;   
                }else return 1;   
        }else return 0;       
}                                   
                                                                                 
u8 const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5};   
//月修正数据表
const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};
//平年得月份日期表


//RTC设置
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
{
        u16 t;
        u32 seccount=0;
        if(syear<1970||syear>2099)return 1;          
        for(t=1970;t         {
                if(Is_Leap_Year(t))seccount+=31622400;
                //闰年的秒钟数
                else seccount+=31536000;
                //平年的秒钟数                         
        }
        smon-=1;
        for(t=0;t         //把前面月份的秒钟数相加
        {
                seccount+=(u32)mon_table[t]*86400;
                //月份秒钟数相加
                if(Is_Leap_Year(syear)&&t==1)seccount+=86400;       
                //闰年2月份增加一天的秒钟数   
        }
        seccount+=(u32)(sday-1)*86400;
        //把前面日期的秒钟数相加
        seccount+=(u32)hour*3600;
        //小时秒钟数
    seccount+=(u32)min*60;       
    //分钟秒钟数
        seccount+=sec;
        //最后的秒钟数加上去
                                                                                                            
       
    RCC->APB1ENR|=1<<28;
    //使能电源时钟
    RCC->APB1ENR|=1<<27;
    //使能备份时钟
        PWR->CR|=1<<8;   
        //取消备份区写保护
       
        RTC->CRL|=1<<4;  
        //允许配置
       
        RTC->CNTL=seccount&0xffff;
        RTC->CNTH=seccount>>16;
        RTC->CRL&=~(1<<4);
        //配置更新
        while(!(RTC->CRL&(1<<5)));
        //等待RTC寄存器操作完成
        RTC_Get();
        //设置完之后更新一下数据
        return 0;            
}




//初始化闹钟
u8 RTC_Alarm_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
{
        u16 t;
        u32 seccount=0;
        if(syear<1970||syear>2099)return 1;          
        for(t=1970;t         {
                if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
                else seccount+=31536000;                         //平年的秒钟数
        }
        smon-=1;
        for(t=0;t         {
                seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加
                if(Is_Leap_Year(syear)&&t==1)seccount+=86400;
                //闰年2月份增加一天秒钟数  
        }
        seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加
        seccount+=(u32)hour*3600;//小时秒钟数
    seccount+=(u32)min*60;         //分钟秒钟数
        seccount+=sec;                    //最后的秒钟数加上去
       
    RCC->APB1ENR|=1<<28;//使能电源时钟
    RCC->APB1ENR|=1<<27;//使能备份时钟
        PWR->CR|=1<<8;    //取消备份区写保护
       
        RTC->CRL|=1<<4;  //允许配置
        RTC->ALRL=seccount&0xffff;
        RTC->ALRH=seccount>>16;
        RTC->CRL&=~(1<<4);//配置更新
        while(!(RTC->CRL&(1<<5))); //等待RTC寄存器操作完成
        return 0;            
}


//得到当前时间
u8 RTC_Get(void)
{
        static u16 daycnt=0;
        u32 timecount=0;
        u32 temp=0;
        u16 temp1=0;          
        timecount=RTC->CNTH;
        //得到计数器中的值(秒钟数)
        timecount<<=16;
        timecount+=RTC->CNTL;                         


        temp=timecount/86400;  //得到天数
        if(daycnt!=temp)//超过一天
        {          
                daycnt=temp;
                temp1=1970;//从1970年开始       
                while(temp>=365)
                {                                 
                        if(Is_Leap_Year(temp1))//是闰年
                        {
                                if(temp>=366)temp-=366;//闰年的秒钟数
                                else break;  
                        }
                        else temp-=365;         //平年
                        temp1++;  
                }   
                calendar.w_year=temp1;//得到年份
                temp1=0;
                while(temp>=28)//超过了一个月
                {
                        if(Is_Leap_Year(calendar.w_year)&&temp1==1)
                        //是不是闰年2月份
                        {
                                if(temp>=29)temp-=29;//闰年的秒钟数
                                else break;
                        }
                        else
                        {
                                if(temp>=mon_table[temp1])temp-=mon_table[temp1];
                                //平年
                                else break;
                        }
                        temp1++;  
                }
                calendar.w_month=temp1+1;
                //得到月份
                calendar.w_date=temp+1;
                //得到日期         
        }
        temp=timecount%86400;  
        //得到秒钟数                  
        calendar.hour=temp/3600;  
        //小时          
        calendar.min=(temp%3600)/60;
        //分钟       
        calendar.sec=(temp%3600)%60;
        //秒钟       
        calendar.week=RTC_Get_Week(calendar.w_year,calendar.w_month,calendar.w_date);  
        return 0;
}         


//获得现在是星期几                                                                                                                                                                 
u8 RTC_Get_Week(u16 year,u8 month,u8 day)
{       
        u16 temp2;
        u8 yearH,yearL;
       
        yearH=year/100;        yearL=year%100;
        //如果是21世纪,年份加100
        if (yearH>19)yearL+=100;
        //所过闰年数只算1900年之后的
        temp2=yearL+yearL/4;
        temp2=temp2%7;
        temp2=temp2+day+table_week[month-1];
        if (yearL%4==0&&month<3)temp2--;
        return(temp2%7);
}
举报

更多回帖

发帖
×
20
完善资料,
赚取积分