23.RTC实时时钟

1.STM32 RTC介绍:

(1)RTC简介:

STM32的实时时钟(RTC)是一个独立的定时器。STM32的RTC模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。

(2)RTC结构框图:

23.RTC实时时钟_第1张图片

 注意:
  1. 存在:秒中断、闹钟中断;
  2. 不存在:溢出中断;
  3. 一般使用外部时钟,32.768K晶振时钟频率;

 2.RTC配置步骤:

RTC相关库函数在stm32f10x_rtc.c和stm32f10x_rtc.h文件中;
  1. 使能电源时钟和后备域时钟,开启RTC后备存储器写访问:RCC_APB1PeriphClockCmd()、PWR_BackupAccessCmd();
  2. 复位备份区域,开启外部低速振荡器:BKP_DeInit()、RCC_LSEConfig();
  3. 选择RTC时钟,并使能:RCC_RTCCLKConfig()、RCC_RTCCLKCmd();
  4. 设置RTC的分频以及配置RTC时钟:RTC_EnterConfigMode()、RTC_ExitConfigMode()、     void RTC_SetPrescaler()、void RTC_ITConfig()、void RTC_SetCounter();
  5. 更新配置,设置RTC中断分组:RTC_ExitConfigMode()、void BKP_WriteBackupRegister()、uint16_t BKP_ReadBackupRegister();
  6. 编写RTC中断服务函数:RTC_IRQHandler、FlagStatus RTC_GetFlagStatus()、RTC_ClearITPendingBit();
注意:要访问RTC和RTC备份域,必须先使能电源和后备域的时钟,然后再使能RTC后备域的写访问;

3.RTC实时时钟实验:

功能实现:设置RTC时间日期初值,在RTC秒中断内使用串口打印出RTC日期和时间,LED0指示灯闪烁提示系统运行。

(1)主函数:

#include "delay.h"
#include "led.h"
#include "key.h"
#include "usart1.h"
#include "rtc.h"



int main(){
    
    u8 i=0;    
   
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);      //设置分组
    delay_init();                                        //延时初始化
    LED_Init();  
    usart1_Init(9600);                                   //串口通信初始化
    RTC_Init();                                          //RTC初始化
    
    while(1)
       {
         
          //系统指示灯闪烁
          i++;
          if(i%20==0)
          {
             LED0=!LED0;
          }
          delay_ms(10);
          
      }       
}

(2)头文件:

#ifndef __RTC_H
#define __RTC_H

typedef unsigned char u8;
typedef unsigned short u16;

u8 RTC_Init(void);            //RTC初始化
u8 Is_Leap_Year(u16 year);    //判断是否是闰年
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);   //设置当前时间
u8 RTC_Get(void);             //获取当前时间
u8 RTC_Get_Week(u16 year,u8 month,u8 day);      //获取当前是星期几
u8 RTC_Alarm_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);      //初始化闹钟

#endif

(3)RTC实时时钟功能函数:

#include "stm32f10x.h"
#include "delay.h"
#include "stdio.h"
#include "rtc.h"


//时间结构体
typedef struct
{
   u8 hour;       //时
   u8 min;        //分
   u8 sec;        //秒
   
   u16 w_year;    //年
   u8 w_month;    //月
   u8 w_date;     //日
   u8 week;       //周
   
}_calendar;   

_calendar calendar;     //定义结构体日历calendar


/*
   功能:中断管理
*/
static void RTC_NVIC_Config(void)
{
   NVIC_InitTypeDef NVIC_InitStruct;
   
   NVIC_InitStruct.NVIC_IRQChannel=RTC_IRQn;                   //设置中断通道
   NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
   NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;        //抢占优先级
   NVIC_InitStruct.NVIC_IRQChannelSubPriority=2;               //子优先级
   NVIC_Init(&NVIC_InitStruct);
   
}



/*
   功能:RTC初始化
   变量:无
   返回值:返回值不为0:表示初始化成功;返回值为0:表示初始化不成功
*/
u8 RTC_Init()
{
   u8 time;
   
   //1.使能电源时钟和后备域时钟,开启RTC后备寄存器写访问
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);          //使能电源时钟
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE);          //使能后备域时钟
   PWR_BackupAccessCmd(ENABLE);	                              //使能后备寄存器访问  
   
   //2.判断初始化是否完成
   if(BKP_ReadBackupRegister(BKP_DR1)!=0xA0A0)                 //从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎
   {
      //3.复位备份区域,开启外部低速振荡器
      BKP_DeInit();                                            //复位备份区域
      RCC_LSEConfig(RCC_LSE_ON);                               //开启外部低速振荡器
      
      //4.判断RTC外部时钟好坏
      while((RCC_GetFlagStatus(RCC_FLAG_LSERDY)==RESET)&&(time<100))
      {
         time++;
         delay_ms(10);
          
      }
      
      //5.若出现超时,则晶振可能出现问题
      if(time>100)
      {
         return 1;      //初始化时钟失败,晶振存在问题
      }
      
      //6.选择RTC时钟,并使能
      RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);         //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟    
      RCC_RTCCLKCmd(ENABLE);                          //使能RTC时钟  
      
      //7.等待APB1时钟和RTC时钟同步
      RTC_WaitForLastTask();                          //等待最近一次对RTC寄存器的写操作完成
      RTC_WaitForSynchro();                           //等待RTC寄存器同步
      RTC_ITConfig(RTC_IT_SEC,ENABLE);                //使能RTC秒中断
      RTC_WaitForLastTask();                          //等待最近一次对RTC寄存器的写操作完成
      
      //8.设置RTC的分频以及配置RTC时钟
      RTC_EnterConfigMode();                          //进入配置模式
      RTC_SetPrescaler(32767);                        //设置RTC预分频的值,因为外部低速晶振32.768KHz,计数一次为1s
      RTC_WaitForLastTask();                          //等待最近一次对RTC寄存器的写操作完成
      
      //9.设置初始化时间
      RTC_Set(2023,7,10,22,20,50);                    //设置时间
      RTC_ExitConfigMode();                           //退出配置模式
      
      //10.写入初始化完成标志值
      BKP_WriteBackupRegister(BKP_DR1,0xA0A0);        //对后备域写入标志位。方便下一次对RTC进行操作,不必再次设置时间  
       
   }
   else           //已经初始化成功,系统继续计时
   {
      RTC_WaitForSynchro();                           //等待RTC寄存器同步
      RTC_ITConfig(RTC_IT_SEC,ENABLE);                //使能RTC秒中断
      RTC_WaitForLastTask();                          //等待最近一次对RTC寄存器的写操作完成
      
   }
   
   //11.配置中断优先级
   RTC_NVIC_Config();
   
   //12.获取当前日历
   RTC_Get();     
   
   return 0;
    
}



/*
   功能:中断服务函数
   变量:无
   返回值:无
*/
void RTC_IRQHandler()
{
   if(RTC_GetITStatus(RTC_IT_SEC))           //判断是否获取到秒中断标志位
   {
      RTC_Get();                             //每一秒更新时间
      printf("RTC TIME:%d-%d-%d  %d:%d:%:\r\n",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);                            //打印出日历数据
      
   }
    
}


/*
   功能:判断是否是闰年
   变量:year:要判断的年份
   返回值:返回1则是闰年,返回0则不是闰年
   注意:
      月份:   1 2 3 4 5 6 7 8 9 10 11 12
      闰年:   31 29 31 30 31 30 31 31 30 31 30 31
      非闰年: 31 28 31 30 31 30 31 31 30 31 30 31
*/
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};


/*
   功能:设置日期时间(以1970年1月1日为基准,把输入的时钟转换为秒钟),1970~2099年为合法年份
   变量:变量分别为:年、月、日、时、分、秒
   返回值:返回值为1则设置失败,返回值为0则设置成功
*/
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;t2099)
      return 1;	   
	for(t=1970;t=365)
		{				 
			if(Is_Leap_Year(temp1))       //是闰年
			{
				if(temp>=366)
               temp-=366;              //闰年的秒钟数
				else 
               {
                  temp1++;
                  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;
   
}



/*
   功能:获取当前星期几(输入日期得到星期,只允许1901-2099年)
   变量:分别是年、月、日
   返回值:星期号
*/
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);
   
}
      


注意:串口需要输入模式需要设置为浮空输入模式:

23.RTC实时时钟_第2张图片 

(4)实验结果:

23.RTC实时时钟_第3张图片

你可能感兴趣的:(实时音视频)