应用库函数,RTC挂钟的铺排

F4的RTC与F1的RTC有非常的大的异样。F1密密麻麻的RTC正是二个简约的计数器,要想获得时间,得要好计算。F4的就不要求这么劳苦了,读出来的一向就是光阴。

stm32——RTC实时石英钟

1)
使能电源石英钟和备份区域挂钟。

STM32F4的RTC是八个单身的BCD/挂钟计数器。RTC提供时间日历和两路机械钟中断,一个颇具中断能力的周期可编制程序的环形标记。他啊南方是RTC还应该有所管理低耗能形式的提醒单元。

一、关于时间

  2038年问题

  在管理器应用上,2038年主题素材大概会导致一些软件在2038年无法常常办事。全体应用UNIX时间表示时间的顺序都将将受其震慑,因为它们以自一九七〇年5月1日通过的秒数(忽略闰秒)来表示时间。这种日子表示法在类Unix(Unix-like)操作系统上是三个行业内部,并会影响以其C编制程序语言开垦给任何大部份操作系统使用的软件。

  在大部份的三十八人操作系统上,此“time_t”数据形式选取一个有正负号的32人元整数(signedint32)存款和储蓄计算的秒数。约等于说最大能够计数的秒数为
2^叁十四遍方 能够算得:

                2^31/3600/24/365 ≈
68年

  所以依据此“time_t”标准,在此格式能被代表的尾声时刻是2038年11月三二十日03:14:07,周五(UTC)。抢先此一弹指间,时间将会被掩瞒(wrap
around)且在中间被代表为二个负数,并招致程序不可能职业,因为它们不能将此时间识别为2038年,而或然会依分别实作而跳回一九七零年或一九〇三年。

  对于PC机来讲,时间开头于一九七七年10月1日,并以无正负符号的三11人整数的花样按秒递增,那与UNIX时间极度周边。能够算得:

                 2^32/3600/24/365 ≈
136年

  到2116年,那个寸头将溢出。

  

  Windows
NT使用60个人整数来计时。可是,它应用100阿秒作为增量单位,且时间初叶于1601年1月1日,所以NT将赶过2184年主题素材。

  苹果公司宣称,Mac在29,940年以前不会油可是生时间难题!

 

RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR |
RCC_APB1Periph_BKP, ENABLE); //使能电源机械钟和备份区域机械钟。

RTC的秒、分、时辰、日期、月、年以BCD码的款型积存在叁个叁11个人寄放器中。

二、RTC使用验证

  ”RTC”是Real Time Clock
的简称,意为实时石英钟。stm32提供了二个秒中断源和三个石英钟中断源,修改计数器的值能够再度安装系统当下的时间和日期。

  RTC模块之所以有着实时石英钟功效,是因为它里面维持了三个单独的电磁照应计时器,通过安顿,能够让它纯粹地每分钟中断一次。但实在,RTC就只是三个电磁打点计时器而已,掉电之后有所音信都会遗弃,因而大家要求找三个地点来积攒那一个音信,于是就找到了备份存放器。其在掉电后依然能够透过纽扣电瓶供电,所以能每一天保存那个数量。

  配置RTC前须知:

  BKP:

  RTC模块和石英钟配置体系的贮存器是在后备区域的(即BKP),通过BKP后备区域来囤积RTC配置的多少年足球以让其在系统重新设置或待机形式下唤醒后,RTC里面配备的多寡维持不改变。

  PWR:

  PW宝马7系为电源的存放器,大家要求接纳的是电源调节贮存器(PW途胜_CCRUISER),通过使能PW途观_C景逸SUV的DBP位来撤销后备区域BKP的写珍爱。

  RTC:

  由一组可编制程序计数器组成,分成五个模块。第贰个模块是RTC的预分频模块,它可编程发生最长为1秒的RTC时间尺度THighlander_CLK。RTC的预分频模块蕴含了多少个十叁位的可编程分频器(RTC)T奔驰G级_CLK
周期中RTC产生叁个行车制动器踏板(秒中断)。第2个模块是一个叁拾四个人的可编制程序计数器,可被初阶化为当前的系统时间。系统时间按TLX570_CLK周期累加并与积存在RTC_AL普拉多贮存器中的可编制程序时间绝比较,借使RTC_C普拉多调节寄放器中装置了相应允许位,比较协作时,将生出贰个挂钟中断。

 

  澳门网上正规赌场网址 1

 

  上面讲授下安顿一体化进度:

   第一步:
通过安装存放器 RCC_APB1EN福睿斯 的 PWREN 和 BKPEN 位来展开电源和后备接口的机械钟

   调用库函数:

    RCC_APB1PeriphClockCmd (RCC_APB1Periph_PWR |
RCC_APB1Periph_BKP,ENABLE );

   第二步:电源调节存放器(PWPAJERO_C揽胜) 的 DBP 位来使能对后备贮存器和 RTC 的拜见

   调用库函数:

    PWR_BackupAccessCmd(ENABLE );

   第三步:早先化重新复苏设置 BKP 寄存器 

   调用库函数:

   BKP_DeInit ();

   第四步:设置 RTCCLK,如下图:

  澳门网上正规赌场网址 2

 

   我们必要将 RTCCLK 设置为 LSE
OSC  这些 32.768KHZ 的晶振。

   调用的库函数:  

   RCC_LSEConfig (RCC_LSE_ON);

   While(!RCC_GetFlagStatus
(RCC_FLAG_HSEXC90DY));//设置后需求等待运转

 

   第五步:将 RTC 输入石英钟选拔为 LSE 石英钟输入并使能 RTC,等待 RTC 和 APB 时钟同步

   调用库函数:

   RCC_RTCCLKConfig
(RCC_RTCCLKSource_LSE);//选择 LSE
为 RTC 设备的石英表

   RCC_RTCCLKCmd (ENABLE );//使能 

   RTC
RTC_WaitForSynchro();//等待同步

  第六步:配置 RTC 时钟参数。

  1. 询问 RTOFF 位,直到
    RTOFF 的值变为’1’
  2. 置 CNF 值为 1 ,进入配置形式
  3. 对三个或几个RTC 寄放器举行写操作
  4. 清除 CNF 标记位,退出配置形式
  5. 查询 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();//等待更新结束

 

2) 撤废备份区写尊崇。

每月的造化自动管理,同不经常候还具备日间省电成效。可编制程序的挂钟新闻囤积在另二个贮存器中。

三、程序演示

  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);
    }


}    

 

要向备份区域写入数据,将要先撤废备份区域写珍贵,不然是力不胜任向备份区域写入数据的。
PWR_BackupAccessCmd(ENABLE); //使能 RTC 和后备寄放器访谈
3) 重置备份区域,开启外部低速振荡器。
BKP_DeInit();//重新载入参数备份区域
RCC_LSEConfig(RCC_LSE_ON);//
开启外部低速振荡器
4) 选择
RTC 时钟,并使能。
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //选择 LSE 作为 RTC 时钟
RCC_RTCCLKCmd(ENABLE); //使能 RTC 时钟
5) 设置 RTC 的分频,以及配备 RTC 时钟。
在张开 RTC 配置此前率先要开发允许配置位(CNF),库函数是:
RTC_EnterConfigMode();// 允许配置
在布局完毕以往,千万别忘记更新配备同一时间退出配置形式,函数是:
RTC_ExitConfigMode();//退出配置方式, 更新配备
设置 RTC 石英钟分频数,
库函数是:
void RTC_SetPrescaler(uint32_t
PrescalerValue);
接下来是设置秒中断允许, RTC
使能暂停的函数是:
void RTC_ITConfig(uint16_t RTC_IT,
FunctionalState NewState);
RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能 RTC 秒中断
库函数中装置 RTC 计数值的办法是:
void
RTC_SetCounter(uint32_t CounterValue)

数字标定成效,可用来拍卖晶振的差错

6) 更新配备,设置 RTC 中断分组。

上电时,RTC寄放器被保险以幸免误写入数据。

 

不管MCU处于这种职业格局,只要供电电压在可操作范围,RTC就不会停下。

RTC_ExitConfigMode();//退出配置形式,更新配备

RTC挂钟能够行使LSE、LSI和不超过1M的HSE作为石英钟,对机械钟分频可得到1HZ的时钟供RTC使用。

往备份区域写客商数量的函数是:
void BKP_WriteBackupRegister(uint16_t
BKP_DR, uint16_t Data);

本实验DISCOVE奥迪Q7Y板子上从未有过LSE,外接晶振8M,也不能够用。做RTC实验只好采用LSI石英钟。

 

这些试验首先应使能LSI石英钟。并将LSI机械钟设置成RTC石英钟。被操作是因此RCC设置的,在RTC部分未有陈设这么些的寄放器:

例如BKP_DR1 写入
0x5050,方法是 :

RCC_LSICmd;
while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);

BKP_WriteBackupRegister(BKP_DR1, 0X5050);
再者,有写便有读,读取备份区域内定寄放器的客户数量的函数是:
uint16_t BKP_ReadBackupRegister(uint16_t
BKP_DR);

RCC_RTCCLKCmd;
RTC_WaitForSynchro();

 

上电时,RTC寄放器写爱护。要开拓写操作,供给向RTC_WP兰德索罗德存放器中写多少个行列。先写入0xCA,再写入0x53就可以关闭写爱戴。写别的数额将关闭写爱戴,举例写入0x00等。

7)
编写中断服务函数。

日历最初化和配备

 

遵照以下步骤设置RTC的命宫日期以及对石英钟输入的预分频:

知识来源  STM32F1 开垦指南(库函数版)
 

1、设置初阶化和境况寄放器RTC_IS兰德本田CR-V中的INIT为为1走入开首化方式。在那几个格局下,RTC时间存放器计数甘休,可被更新

2、判断RTC_ISOdyssey中的INITF位,剖断RTC是还是不是业已被起始化过。当读出多少为1时,就可以步入初阶化形式。这一个历程将持续大致多个RTC挂钟周期

3、为了博取1Hz的计数器石英钟,先对RTC_PRE中华V存放器中的同步分频值PREDIV_S[14:0]打开安装再对该存放器中的一片段频值PREDIV_A[6:0]打开设置。尽管独有二个分频值须要写入,另二个分频值也供给再写入一次。

4、将在设置的时光和日期写入RTC_CR和RTC_DPAJERO的影子贮存器,在RTC_C奥德赛后装置时间格式。

5、向INTI位写入0,退出伊始化情势。这是,RTC_DR和RTC_C昂科威就能将日期新闻从阴影寄放器中加载到存放器中,并在4个RTC石英钟周期后活动开始运维。

落到实处代码如下:

void RTC_Config
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);
PWR_BackupAccessCmd;//使能备份贮存器操作

RCC_LSICmd;
while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
RCC_RTCCLKCmd;
RTC_WaitForSynchro();

if(RTC_ReadBackupRegister(RTC_BKP_DR0) != 0x9527)
//三个变量,看看RTC初步化没
{

RTC_WriteProtectionCmd;

RTC_EnterInitMode();
RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24;
RTC_InitStructure.RTC_AsynchPrediv = 0x7D-1;
RTC_InitStructure.RTC_SynchPrediv = 0xFF-1;
RTC_Init(&RTC_InitStructure);

RTC_TimeStructure.RTC_Seconds = 0x00;
RTC_TimeStructure.RTC_Minutes = 0x01;
澳门网上正规赌场网址,RTC_TimeStructure.RTC_Hours = 0x01;
RTC_TimeStructure.RTC_H12 = RTC_H12_AM;
RTC_SetTime(RTC_Format_BCD,&RTC_TimeStructure);

RTC_DateStructure.RTC_Date = 30;
RTC_DateStructure.RTC_Month = 5;
RTC_DateStructure.RTC_WeekDay= RTC_Weekday_Thursday;
RTC_DateStructure.RTC_Year = 12;
RTC_SetDate(RTC_Format_BCD,&RTC_DateStructure);

RTC_ExitInitMode();
RTC_WriteBackupRegister(RTC_BKP_DR0,0X9527);
RTC_WriteProtectionCmd;
RTC_WriteBackupRegister(RTC_BKP_DXC900,0x9527); //初步化达成,设置标识
}
PWR_BackupAccessCmd;
}

编写翻译运转,在主函数中动用RTC_GetTime读取时间。追踪到这边能够看来时间在变化

日后只要每一趟用RTC_Get提姆e就能够读取时间了。

要安装时间,只供给用RTC_SetTime就行了,RTC_GetTime函数自个儿会打开写保养。

Post Author: admin

发表评论

电子邮件地址不会被公开。 必填项已用*标注