单片机 > 单片机程序设计 > 详情

STM32系列第16篇--RTC实时时钟

发布时间:2020-08-31 发布时间:
|

RCT特征:

  1. 可编程的预分频系数,分频系数最高2的20次方。

  2. 32位可编程计数,用于较长时间段的测量。

  3. 2个分离的时钟。

  4. 可以选择三种RTC时钟源:HSE/128;LSE振荡器;LSI振荡器。

  5. 2个独立的复位类型:APB1由系统复位;RTC由后备域复位。

  6. 三个专门的可屏蔽中断:闹钟中断;秒中断(一个可编程周期,最长可达1s);溢出中断。

RTC工作原理框图

RTCCLK经过RTC_DIV预分频,RTC_PRL设置预分频系数,然后得到TR_CLK时钟信号,我们一般设置其周期为1s,RTC_CNT计数器计数,假如1970设置为时间起点为0s,通过当前时间的秒数计算得到当前的时间。RTC_ALR是设置闹钟时间,RTC_CNT计数到RTC_ALR就会产生计数中断,RTC_Second为秒中断,用于刷新时间,RTC_Overflow是溢出中断。

实验例程:

#include "sys.h"#include "delay.h"#include "usart.h"#include "rtc.h"            _calendar_obj calendar;//时钟结构体 static void RTC_NVIC_Config(void)
{   
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;      //RTC全局中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;     //使能该通道中断
    NVIC_Init(&NVIC_InitStructure);     //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器}//实时时钟配置//初始化RTC时钟,同时检测时钟是否工作正常//BKP->DR1用于保存是否第一次配置的设置//返回0:正常//其他:错误代码u8 RTC_Init(void)
{    //检查是不是第一次配置时钟
    u8 temp=0;
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);    //使能PWR和BKP外设时钟   
    PWR_BackupAccessCmd(ENABLE);    //使能后备寄存器访问  
    if (BKP_ReadBackupRegister(BKP_DR1) != 0x5050)      //从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎
        {               
        BKP_DeInit();   //复位备份区域    
        RCC_LSEConfig(RCC_LSE_ON);  //设置外部低速晶振(LSE),使用外设低速晶振
        while (RCC_GetFlagStatus(RCC_FLAG_LSERDY)==RESET&&temp<250) //检查指定的RCC标志位设置与否,等待低速晶振就绪
            {
            temp++;
            delay_ms(10);
            }        if(temp>=250)return 1;//初始化时钟失败,晶振有问题       
        RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);     //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟    
        RCC_RTCCLKCmd(ENABLE);  //使能RTC时钟  
        RTC_WaitForLastTask();  //等待最近一次对RTC寄存器的写操作完成
        RTC_WaitForSynchro();       //等待RTC寄存器同步  
        RTC_ITConfig(RTC_IT_SEC, ENABLE);       //使能RTC秒中断
        RTC_WaitForLastTask();  //等待最近一次对RTC寄存器的写操作完成
        RTC_EnterConfigMode();/// 允许配置  
        RTC_SetPrescaler(32767); //设置RTC预分频的值
        RTC_WaitForLastTask();  //等待最近一次对RTC寄存器的写操作完成
        RTC_Set(2015,1,14,17,42,55);  //设置时间    
        RTC_ExitConfigMode(); //退出配置模式  
        BKP_WriteBackupRegister(BKP_DR1, 0X5050);   //向指定的后备寄存器中写入用户程序数据
        }    else//系统继续计时
        {

        RTC_WaitForSynchro();   //等待最近一次对RTC寄存器的写操作完成
        RTC_ITConfig(RTC_IT_SEC, ENABLE);   //使能RTC秒中断
        RTC_WaitForLastTask();  //等待最近一次对RTC寄存器的写操作完成
        }
    RTC_NVIC_Config();//RCT中断分组设置                                
    RTC_Get();//更新时间    
    return 0; //ok}                           
//RTC时钟中断//每秒触发一次  //extern u16 tcnt; void RTC_IRQHandler(void)
{        
    if (RTC_GetITStatus(RTC_IT_SEC) != RESET)//秒钟中断
    {                           
        RTC_Get();//更新时间   
    }    if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)//闹钟中断
    {
        RTC_ClearITPendingBit(RTC_IT_ALR);      //清闹钟中断     
      RTC_Get();                //更新时间   
    printf("Alarm Time:%d-%d-%d %d:%d:%d\n",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);//输出闹铃时间  

    }                                                
    RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW);        //清闹钟中断
    RTC_WaitForLastTask();                                           
}//判断是否是闰年函数//月份   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//输入:年份//输出:该年份是不是闰年.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; 
}                  
//设置时钟//把输入的时钟转换为秒钟//以1970年1月1日为基准//1970~2099年为合法年份//返回值: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};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);
}





『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
PIC单片机基础知识之二