STM32_RTC君

五一假期已过,大家是否还像五一五二五三那样快乐呢??答案就交给你们自己寻找了哈、、说到五一、、就从五一开始的那一刻起、、就开始计时着、、到五一假期结束、、呵呵、、在这里,智商和情商比我高的人估计又猜到我要说什么了、、关于日期,关于时间、STM32也提供了强大的RTC模块、、至于RTC模块到底是哪三个英文单词的缩写,我就不说了、好了,言归正传、这个语气、请大家想想周星驰唐伯虎点秋香当中华安与对穿王的对白:我俩、、、言归正传:

在这里、先别急,咱们来看看一般的RTC模块(芯片):

1、 描述:RTC芯片是一种能提供日历(年月日)/时钟(时分秒)、数据存储等功能的专用集成电路;

2、应用环境:通常使用后备电池,以保证其在系统掉电的情况下运行;

其时钟源由外部32.768KHz晶振提供,可以实现闹钟功能。

3、作用:应用于某些系统的时钟信号源和参数设置的存储电路。

RTC具有计时准确、耗电低和体积小等特点,特别是在各种嵌入式系统中用于记录事件发生的时间和相关信息, 如通信工程、电力自动化、工业控制等领域。

再来看看STM32中RTC有哪些区别呢(概述,具体分析请别急哈、):

1、不具有提供日历/时钟功能;

2、能够提供一个精确的秒周期信号:配置RTCCLK以及RTC_DIV,使得预分频器产生频率为1秒的秒脉冲(TR_CLK),作为RTC的时钟基准。

3、具有闹钟功能。

4:特点:(1)保护寄存器---防止误操作(请注意这一点哈,非常关键)

(2)3个事件/中断源:秒、溢出、闹钟(连外部中断线17上,用于将MCU从停止模式唤醒)

(3)RTC内核和时钟的设置位于备份区域(也请注意这一点哈,因为要使RTC能记忆,就靠它了):

有独立的VBAT供电;

只能由备份区域复位才能将其复位;

从待机模式唤醒后,RTC的设置仍被保留。

啊哈、、以上摘自STM3210X+其余模块培训资料、、在这里摆出来就是为了给大家有一个比较全面的认识先、、莫告我盗版哈、、好了、、接下来,来看看时钟吧、、

翻开“葵花宝典”第STM32篇之RTC参考手册可以看出:可以选择以下三种RTC的时钟源:(直接看图哈)这下就不说是美丽的涂鸦了、、、

在这里,我们选择独立的32.768KHz晶振(LSE)来作为外部时钟源、提前说下:只要在RTC的预分频转载寄存器中写入0x7fff(也就是32767),就可以产生以秒为单位的信号了、、

接下来,我们来看看参考手册里的一段话:(RTC核心)由一组可编程计数器组成,分成两个主要模块。

第一个模块是RTC的预分频模块,它可编程产生最长为1秒的RTC时间基准TR_CLK。RTC的预分频模块包含了一个20位的可编程分频器(RTC预分频器)。如果在RTC_CR寄存器中设置了相应的允许位,则在每个TR_CLK周期中RTC产生一个中断(秒中断)。

第二个模块是一个32位的可编程计数器,可被初始化为当前的系统时间。系统时间按TR_CLK周期累加并与存储在RTC_ALR寄存器中的可编程时间相比较,如果RTC_CR控制寄存器中设置了相应允许位,比较匹配时将产生一个闹钟中断。

接下来请容许我介绍下几位“大神”:

从红色区域我们可以看出我们这里要实现以秒为单位的计时,就要设置RTC_CRH的最低位为1;

接下来请看(请注意红色区域,再对照前面红色字体的标注):

还有几个寄存器在此就不做具体介绍了,大家可以参考中文手册,在此仅列出:

RTC预分频装载寄存器(RTC_PRLH/RTC_PRLL)

RTC计数器寄存器 (RTC_CNTH /
RTC_CNTL)

注:因为RTC是由独立的时钟源提供,不挂载在ABP桥上,但是,奇怪的是;软件又是通过ABP1桥来访问RTC的寄存器,由于可读寄存器只在RTC
ABP1桥时钟进行同步的RTC时钟上升沿被更新,可读标志位也是这样,这就涉及到一个概念:同步,假如ABP1被刚刚开启,在第一次的寄存器更新之前,从ABP1桥上读取的RTC的寄存器就有可能被破坏了,为了避免此种情况的发生,在读取寄存器的时候一定要等待他们同步、、至于如何操作、、请看上图寄存器的红色标记

好了、、人需要记忆、、否则失忆了,对彼此都不是一件好事、、当然、RTC也一样、、你不希望RTC在系统复位或者一些意外后而是RTC失效、、所以、、RTC也要有记忆的功能、至于如何记忆呢?STM32也为我们提供了BKP模块:翻开“葵花宝典”第STM32篇之BKP备份寄存器有这么一段描述:

备份寄存器是42个16位的寄存器,可用来存储84个字节的用户应用程序数据。他们处在备份域里,当VDD电源被切断,他们仍然由VBAT维持供电。当系统在待机模式下被唤醒,或系统复位或电源复位时,他们也不会被复位。

所以,我们在这就要开启BKP的时钟、、

然而、、事情并不是那么的简单、、在这里、、仅仅打开BKP的时钟还不够、、因为某种特殊原因、、请看

因为系统复位后,RTC和后备寄存器处于被保护状态以防意外写入、、所以在此也要打开PWR的时钟,在这里,我们需要取消保护,向后备寄存器写入一个字节0x5050或者0x0505都行,以标注时钟已经配置过了、、到时检查这个字节来决定是否要重新配置、、

而BKP PWR时钟都是挂载在ABP1桥上的:

那我们要怎么来操作呢:请看代码注释:


 1 u8 RTC_Init(void)
2 {
3 //检查是不是第一次配置时钟
4 u8 temp=0;
5
6 if(BKP_ReadBackupRegister(BKP_DR1)!= 0x5050)//从指定的后备寄存器中读出数据,读出了与写入的指定数据不相同,这时候需要初始化
7 {
8 //1/通过使能PER和BKP外设时钟来打开电源盒后备接口的时钟使其能对后备寄存器和RTC的访问
11 RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能时钟
12 PWR_BackupAccessCmd(ENABLE); //取消备份域写保护13 //2/复位备份域,开启外部低速振荡器
14 /* Reset the BKP registers */ BKP_DeInit();
15 /* Enable the HSE */ RCC_LSEConfig(RCC_LSE_ON);
16
17 while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)//等待低速时钟准备就绪,在选择时钟前一定要等待时钟就绪、、否则、后果严重
18 {
19 temp++;
20 delay_ms(10);
21 }
22 if(temp >= 250)
23 {
24 return 1;
25 }
26 //3/选择时钟和使能时钟
27 /* Select the LSE as RTC clock source */RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
28 /* Enable the RTC clock */RCC_RTCCLKCmd(ENABLE);
29 RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成。注:一定要等写操作完成
30 RTC_WaitForSynchro(); //等待RTC寄存器同步
31
32 RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能秒中断
33
34 RTC_WaitForLastTask(); //等待最近一次对RTC的写操作完成
35 RTC_EnterConfigMode();/// 允许配置
36
37 RTC_SetPrescaler(32767); //设置RTC的预分频值
38
39 RTC_WaitForLastTask(); //等待最近一次对RTC写操作的完成
40
41 RTC_Set(2014,5,4,12,0,5); //设置时间
42
43 RTC_ExitConfigMode(); //退出配置
44
45 BKP_WriteBackupRegister(BKP_DR1, 0X5050); // 向指定的后备域写入一字节
46
47 }
48 else //否则,系统继续计时
49 {
50 RTC_WaitForSynchro();
51 RTC_ITConfig(RTC_IT_SEC, ENABLE); //开启秒中断
52 RTC_WaitForLastTask();
53 }
54 RTC_NVIC_Config();
55 RTC_Get();//更新时间
56 return 0;
57 }

可以看出、、如果我们要配置下一次,一定要等待上次对RTC的操作是否完成,如果没有,就必须等待上次结束才能开始下一次的操作、、同时,如果要对相关的寄存器进行写操作。、一定要进入配置,配置完之后记得退出配置//..

接下来,给中秒中断的中断服务函数,相信大家也比较清楚了:


 3 void RTC_IRQHandler(void)
4 {
5 if (RTC_GetITStatus(RTC_IT_SEC) != RESET)
6 {
7 RTC_Get();
8 }
9 if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)
10 {
11 RTC_ClearITPendingBit(RTC_IT_ALR);
12 }
13 RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW);
14 RTC_WaitForLastTask();
15 }

注:对于相关的库函数,由于篇幅的原因,在此就不仔细列出来了、请大家谅解、、大家可以参照固件库、、

在这里给出战舰原子的时间算法程序:就不细讲了、、大家好好研究、、说实话、、我也有些不太懂、所以请大家多多交流:


//判断是否是闰年函数//月份   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) //
{
if(year%100==0)
{
if(year%400==0)return 1;
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 表示这个数组里的值不允许改变
//平年的月份日期表
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;t<syear;t++) //把所有年份的秒钟相加
{
if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒数
else seccount+=31536000; //否则为平年
}
smon-=1;
for(t=0;t<smon;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_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
PWR_BackupAccessCmd(ENABLE);

RTC_SetCounter(seccount);

RTC_WaitForLastTask(); //注意、、此时也要等待、、
return 0;

}
//返回当前的时间
u8 RTC_Get(void)
{
static u16 daycnt=0;
u32 timecount=0;
u32 temp=0;
u16 temp1=0;
timecount=RTC_GetCounter();
temp=timecount/86400;
if(daycnt!=temp)
{
daycnt=temp;
temp1=1970;
while(temp>=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)
{
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;
temp2=yearL+yearL/4;
temp2=temp2%7;
temp2=temp2+day+table_week[month-1];
if (yearL%4==0&&month<3)temp2--;
return(temp2%7);
}

有时候,想知道公元某年某月某日是星期几,可以用下面的公式算出来:                

   这里的方括号表示只取商的整数部分。式中:

  x:这一年是公元多少年。

  y:这一天是这一年的第几天。

  s:星期几。不过要先除以7,再取余数。没有余数是星期日,余数是1、2、3、4、5、6,

    分别是星期一、星期二、星期三、星期四、星期五、星期六。

比如,今年国庆节(2010年10月1日)是星期几?

  x=2010。

  y=31+28+31+30+31+30+31+31+30+1=31×5+30×3+28+1=274。

  s=2010-1+502-20+5+274=2770,2770÷7余5。

所以,今年国庆节是星期五。

如果,你只想知道这个公式怎样用,到这儿就可以了。而要想知道这个公式的道理是什么,那可就说来话长了。

“星期制”是公元321年3月7日,古罗马皇帝君士坦丁宣布开始实行的,并且规定这一天为星期一。实际上,就是把公元元年元旦(公元1年1月1日)规定为星期一。(相当于公式中的x=1,y=1,所以s=1。)

通常1年有365天,365÷7=52……1,就是说比52个星期多1天。所以,同一个日期,下一年是星期几,就要比上一年向后推1天。比如,上一年元旦是星期三,下一年元旦就是星期四。

通常,每过1年,同一日期是星期几就要向后推1天”,是理解这个公式的关键。

要想知道某年某月某日是星期几,首先,要知道这一年元旦以公元元年元旦是星期一为起点,已经把星期几向后推了多少天,还要知道这一天是这一年的第几天。而要知道这一年元旦已经把星期几向后推了多少天,可以从公元元年到这一年已经过了多少年算起,先按1年向后推1天计算,再根据闰年的规定进行调整。

闰年的规定是:年份是4的倍数的一般都是闰年,其中,年份是整百数的一般不是闰年,只有年份是400的倍数的才是闰年。

现在,可以解释公式中各部分的含义了。


 这样一来,s就是在公元元年元旦是星期一的基础上,需要把这一天是星期几向后推的总天数。所以,s除以7取余数,就能说明这一天是星期几。(摘抄,敬请谅解哈)

到这里、、也接近尾声了、、看了一下时间、、呵呵、、又忘了睡觉了、、待会还要上课、、好吧、、篇幅有点多、、希望大家耐心的看完、、相信看到前面一句话,也算是差不多看完了、、希望能对你们和以后的我有一个很好的理解上的帮助、、有写得不对的地方希望能告诉我、、旁观者清嘛、、谢谢大家、、五一过后、、大家仍需努力、、为生活而奋斗、、、

时间: 2024-08-10 02:11:15

STM32_RTC君的相关文章

初识Python,望君多多关照

在学习Python之前,我们接触过数据结构和网页制作.前者让我们学习如何把C语言运用的更加整齐规范,而后者让我们亲身学习如何运用所学,制作一个静态网页.通过这些课程的学习,让我对C语言产生了比较大的压力,以至于对编程.对这学期的Python课程都有一种如临大敌的感觉. 但是真的学习了这门课程,体会了编码过程中的一些固定运用方法和套路之后,也许过程中对这门课程隐隐约约产生了一点点朦胧的感觉,仿佛他也并没有想象中的那么困难,起码现在的学习让我认为,他可能没有C语言那么繁琐和麻烦.当然,以一个初学者的

作业1:计算机是如何工作的?20135115臧文君

计算机是如何工作的? 注:作者:臧文君,原创作品转载请注明出处,<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.存储程序计算机工作模型 1.冯诺依曼体系结构:指存储程序计算机 (1)硬件的角度:分为CPU+内存+总线 CPU中的一个寄存器---IP:Instruction Pointer,指向内存的CS:Code Segment 16位的CPU上叫IP,32位叫EIP,64位叫IIP CPU通过IP从内存中

问君此去几时还,来时莫徘徊

public static void main(){ System.out.println("问君此去几时还,来时莫徘徊."); }/*** 来自词人和歌者最殷切的叮嘱,感人至深.* 而我们新世纪的青年,好像再也不去思考命运,再也不去回顾历史,只有那轻飘飘的无来由地欢笑与追逐,浮于那白炽炽的阳光下,空空荡荡,不知归去.....**/

蒜头君打地鼠

蒜头君打地鼠 蒜头君最近迷上了打地鼠,但他发现同时出现在面板上的地鼠太多,于是他想改进一下他的锤子,于是他拿出了一款 k \times kk×k 大小的正方形锤子,但是遗憾的是,这个锤子只能斜着砸.如下图所示: 当 k=2k=2 时,若蒜头君敲击黑点,黑点和图中所有蓝色点将一并被敲到. 当 k=3k=3 时,锤子的图案如下所示: 1 - - * - - 2 - * * * - 3 * * x * * 4 - * * * - 5 - - * - - kk 取其他值时以此类推. 注意:蒜头君只能敲击

蒜头君的树

蒜头君的树 蒜头君有一棵有根树,树的每一边都有边权,蒜头君想知道任意两点间最短距离之和为多少.另外,由于各种原因,蒜头君的树的边的边权会发生若干次改变,蒜头君想让你告诉他,每一次改变后,任意两点间最短距离之和为多少? 输入格式 第一行一个正整数 nn,表示蒜头君的树上的结点个数. 接下来 n-1n?1 行,每行两个正整数 x_i,y_ix?i??,y?i??,x_ix?i?? 表示 i+1i+1 号结点的父亲结点的编号,保证其父结点编号小于自己编号.y_iy?i?? 表示 i+1i+1 号结点的

《恋君已是二十年

天边低悬,晨光里那颗蓝星的幽光,唤醒了你我心中,一缕不死的忧伤,在妖娆的红尘俗世里,我并没有把你遗忘,你始终种植在我灵魂深处.这一世,我等得太久了,这一世,你太辛苦了,这一世的我们脱离不了命运的魔咒--闭上疲倦的眼,听风中的歌声,让我的灵魂一直上升,上升,到有你的国度-- 今生今世,我们是否可以,再续前缘. 生生世世,谁在谁的掌心,绽放如花? 展翔,我的叔叔,1989年初次的相逢,七岁的我因为偷吃未熟的菱角掉到河里,被展翔救起,我把珍藏的乾隆通宝铜钱悄悄塞到他的手心.因为他,我安静且努力了七年,

高橋君とカード / Tak and Cards

高橋君とカード / Tak and Cards Time limit : 2sec / Stack limit : 256MB / Memory limit : 256MB Score : 300 points Problem Statement Tak has N cards. On the i-th (1≤i≤N) card is written an integer xi. He is selecting one or more cards from these N cards, so t

高橋君とホテル / Tak and Hotels

高橋君とホテル / Tak and Hotels Time limit : 3sec / Stack limit : 256MB / Memory limit : 256MB Score : 700 points Problem Statement N hotels are located on a straight line. The coordinate of the i-th hotel (1≤i≤N) is xi. Tak the traveler has the following t

君生我未生,我生君已老

<君生我未生,我生君已老> 君生我未生,我生君已老. 君恨我生迟,我恨君生早. 君生我未生,我生君已老. 恨不生同时,日日与君好. 我生君未生,君生我已老. 我离君天涯,君隔我海角. 我生君未生,君生我已老. 化蝶去寻花,夜夜栖芳草.