打印
[STM32F1]

RTC实时时钟

[复制链接]
283|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
adolphcocker|  楼主 | 2024-9-20 09:03 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
RTC使用说明  "RTC"是Real Time Clock 的简称,意为实时时钟。stm32提供了一个秒中断源和一个闹钟中断源,修改计数器的值可以重新设置系统当前的时间和日期。
  RTC模块之所以具有实时时钟功能,是因为它内部维持了一个独立的定时器,通过配置,可以让它准确地每秒钟中断一次。但实际上,RTC就只是一个定时器而已,掉电之后所有信息都会丢失,因此我们需要找一个地方来存储这些信息,于是就找到了备份寄存器。其在掉电后仍然可以通过纽扣电池供电,所以能时刻保存这些数据。
  配置RTC前须知:
  BKP:
  RTC模块和时钟配置系统的寄存器是在后备区域的(即BKP),通过BKP后备区域来存储RTC配置的数据可以让其在系统复位或待机模式下唤醒后,RTC里面配置的数据维持不变。
  PWR:
  PWR为电源的寄存器,我们需要用到的是电源控制寄存器(PWR_CR),通过使能PWR_CR的DBP位来取消后备区域BKP的写保护。
  RTC:
  由一组可编程计数器组成,分成两个模块。第一个模块是RTC的预分频模块,它可编程产生最长为1秒的RTC时间基准TR_CLK。RTC的预分频模块包含了一个20位的可编程分频器(RTC)TR_CLK 周期中RTC产生一个中断(秒中断)。第二个模块是一个32位的可编程计数器,可被初始化为当前的系统时间。系统时间按TR_CLK周期累加并与存储在RTC_ALR寄存器中的可编程时间相比较,如果RTC_CR控制寄存器中设置了相应允许位,比较匹配时,将产生一个闹钟中断。  下面讲解下配置整体过程:
   第一步: 通过设置寄存器 RCC_APB1ENR 的 PWREN 和 BKPEN 位来打开电源和后备接口的时钟   调用库函数:    RCC_APB1PeriphClockCmd (RCC_APB1Periph_PWR | RCC_APB1Periph_BKP,ENABLE );
   第二步:电源控制寄存器(PWR_CR) 的 DBP 位来使能对后备寄存器和 RTC 的访问
   调用库函数:
    PWR_BackupAccessCmd(ENABLE );
   第三步:初始化复位 BKP 寄存器
   调用库函数:
   BKP_DeInit ();
   第四步:设置 RTCCLK,如下图:
  

   我们需要将 RTCCLK 设置为 LSE OSC  这个 32.768KHZ 的晶振。
   调用的库函数:  
   RCC_LSEConfig (RCC_LSE_ON);
   While(!RCC_GetFlagStatus (RCC_FLAG_HSERDY));//设置后需要等待启动

   第五步:将 RTC 输入时钟 选择为 LSE 时钟输入并使能 RTC,等待 RTC 和 APB 时钟同步
   调用库函数:
   RCC_RTCCLKConfig (RCC_RTCCLKSource_LSE);//选择 LSE 为 RTC 设备的时钟
   RCC_RTCCLKCmd (ENABLE );//使能
   RTC RTC_WaitForSynchro();//等待同步
  第六步:配置 RTC 时钟参数。
查询 RTOFF 位,直到 RTOFF 的值变为’1’置 CNF 值为 1 ,进入配置模式对一个或多个 RTC 寄存器进行写操作清除 CNF 标志位,退出配置模式查询 RTOFF,直至 RTOFF 位变为’1’ 以确认写操作已经完成。仅当 CNF 标志位被清除时,写操作才能进行,这个过程至少需要 3 个 RTCCLK 周期。  按照上述步骤用库函数来配置:
复制代码/* 1.    查询 RTOFF 位,直到 RTOFF 的值变为’1’ */
RTC_WaitForLastTask();//大家可以打开函数库看看这个函数内部的代码,就是查询 RTOFF的值
/*
2.置 CNF 值为 1 ,进入配置模式
3.对一个或多个 RTC 寄存器进行写操作
4.清除 CNF 标志位,退出配置模式
*/
RTC_SetPrescaler(32767); // 这里配置了预分频值,大家可以打开函数库看看这个函数的内部的代码,里面就有包含了 2、3、4 讲述的操作。
/*每完成一个操作一般都要查询 RTOFF 来判断是否 RTC 正在更新数据,如果是则等待它完成!!!*/RTC_WaitForLastTask();//等待更新结束
RTC_ITConfig(RTC_IT_SEC, ENABLE);//配置秒中断
RTC_WaitForLastTask();//等待更新结束复制代码
三、程序演示  rtc.h
复制代码#ifndef __RTC_H#define __RTC_H#include "stm32f10x.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 RCC_Configuration(void);void RTC_Init(void);u8 RTC_Set(u16 year,u8 mon,u8 day,u8 hour,u8 min,u8 sec);u8 RTC_Get(void);#endif复制代码  rtc.c
复制代码#include "rtc.h"_calendar_obj calendar;    //时钟结构体//平均的月份日期表const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};/*rtc中断向量配置*/void NVIC_Configuration(void){    NVIC_InitTypeDef NVIC_InitStructure;    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);     NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;    NVIC_Init(&NVIC_InitStructure);}
void RTC_Configuration(void)
{    /* 使能PWR和BKP时钟 */    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP,ENABLE);    /* 使能对后备寄存器的访问 */     PWR_BackupAccessCmd(ENABLE);    /* 复位BKP寄存器 */     BKP_DeInit();    /* 使能LSE */     RCC_LSEConfig(RCC_LSE_ON);    /*等待启动完成 */    while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) {}    /* 将 RTC时钟设置为LSE这个32.768KHZ的晶振*/     RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);    /* 使能RTC Clock */     RCC_RTCCLKCmd(ENABLE);    /* 等待同步 */     RTC_WaitForSynchro();    /* 等待对RTC寄存器最后的写操作完成*/                 RTC_WaitForLastTask();    /* 配置了预分频值: 设置RTC时钟周期为1s */    RTC_SetPrescaler(32767); /* RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1)*/    /* 等待对RTC寄存器最后的写操作完成 */    RTC_WaitForLastTask();    /* 使能RTC秒中断 */     RTC_ITConfig(RTC_IT_SEC, ENABLE);    /* 等待对RTC寄存器最后的写操作完成 */             RTC_WaitForLastTask();
void RTC_Init(void){    /*如果是第一次配置时钟,则执行RCC_Configuration()进行配置*/    if(BKP_ReadBackupRegister(BKP_DR1)!=0x1016)    {            RCC_Configuration();            RTC_Set(2016,5,11,9,7,55);            GPIO_SetBits(GPIOD, GPIO_Pin_13);//点亮D1            BKP_WriteBackupRegister(BKP_DR1, 0x1016);//向执行的后备寄存器中写入用户程序数据    }    else    {        RTC_WaitForSynchro();//等待RTC寄存器同步完成        RTC_ITConfig(RTC_IT_SEC, ENABLE);//使能RTC秒中断        RTC_WaitForLastTask();//等待最近一次对RTC寄存器的写操作完成        GPIO_SetBits(GPIOG, GPIO_Pin_14);//点亮D2    }    NVIC_Configuration();    RTC_Get();//更新时间}u8 Is_Leap_Year(u16 pyear){    if(pyear%4==0)//首先需能被4整除    {        if(pyear%100==0)        {            if(pyear%400==0)    return 1;//如果以00结尾,还要能被400整除            else    return 0;        }        else            return 1;    }    else        return 0;}/**设置时钟*把输入的时钟转换为秒钟*以1970年1月1日为基准*1970~2099年为合法年份返回值:0,成功;其它:错误*/u8 RTC_Set(u16 year,u8 mon,u8 day,u8 hour,u8 min,u8 sec){    u16 t;    u32 secCount=0;    if(year<1970||year>2099)        return 1;//³ö´í    for(t=1970;t<year;t++)    //把所有年份的秒钟相加    {        if(Is_Leap_Year(t))//闰年            secCount+=31622400;//闰年的秒钟数        else            secCount+=31536000;        }    mon-=1;//先减掉一个月再算秒数(如现在是5月10日,则只需要算前4个月的天数,再加上10天,然后计算秒数)    for(t=0;t<mon;t++)    {        secCount+=(u32)mon_table[t]*86400;//月份秒钟数相加        if(Is_Leap_Year(year)&&t==1)            secCount+=86400;//闰年,2月份增加一天的秒钟数    }        secCount+=(u32)(day-1)*86400;//把前面日期的秒钟数相加(这一天还没过完,所以-1)    secCount+=(u32)hour*3600;//小时秒钟数    secCount+=(u32)min*60;//分钟秒钟数    secCount+=sec;//    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR    | RCC_APB1Periph_BKP,ENABLE);//    PWR_BackupAccessCmd(ENABLE);    RTC_SetCounter(secCount);//设置RTC计数器的值    RTC_WaitForLastTask();    //等待最近一次对RTC寄存器的写操作完成    RTC_Get();//更新时间    return 0;}
/*得到当前的时间成功返回0,错误返回其它*/u8 RTC_Get(void){        static u16 dayCount=0;        u32 secCount=0;        u32 tmp=0;        u16 tmp1=0;        secCount=RTC_GetCounter();        tmp=secCount/86400;//得到天数        if(dayCount!=tmp)//超过一天        {            dayCount=tmp;            tmp1=1970;//从1970年开始            while(tmp>=365)            {                if(Is_Leap_Year(tmp1))//是闰年                {                    if(tmp>=366)                            tmp-=366;//减掉闰年的天数                    else                    {                    //    tmp1++;                        break;                    }                }                else                    tmp-=365;//平年                tmp1++;            }            calendar.w_year=tmp1;//得到年份            tmp1=0;            while(tmp>=28)//超过一个月            {                if(Is_Leap_Year(calendar.w_year)&&tmp1==1)/当年是闰年且轮循到2月                {                    if(tmp>=29)                            tmp-=29;                    else                        break;                }                else                {                    if(tmp>=mon_table[tmp1])//平年                        tmp-=mon_table[tmp1];                    else                        break;                }                tmp1++;            }            calendar.w_month=tmp1+1;//得到月份,tmp1=0表示1月,所以要加1            calendar.w_date=tmp+1;    //得到日期,因为这一天还没过完,所以tmp只到其前一天,但是显示的时候要显示正常日期        }        tmp=secCount%86400;//得到秒钟数        calendar.hour=tmp/3600;//小时        calendar.min=(tmp%3600)/60;//分钟        calendar.sec=(tmp%3600)%60;//秒        return 0;}/*RTC时钟中断每秒触发一次*/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_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW);//清闹钟中断    RTC_WaitForLastTask();                                                   }复制代码  main.c
复制代码#include "stm32f10x.h"#include "usart1.h"#include "LED.h"#include "delay.h"#include "flash.h"#include "rtc.h"#include "stdio.h"int main(void){    u8 t=0;    USART1_Config();    GPIO_Configuration();    RTC_Init();    while(1)    {        if(t!=calendar.sec)        {            t=calendar.sec;            printf("\r\n now is %d 年 %d 月 %d 日 %d 时 %d 分 %d 秒 \r\n ",       calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);        }        Delay(0x02FFFF);    }        }   

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

20

主题

1276

帖子

0

粉丝