【STM32 标准库】RTC实时时钟

目录

1. STM32 RTC

1.1 RTC简介

1.2 结构框图

2. 软件配置

3. 硬件设计

4. 软件设计

4.1 功能描述

4.2 软件实现

4.2.1 rtc.c

4.2.2 rtc.h

4.2.3 main.c

4.3 实验结果


1. STM32 RTC

1.1 RTC简介

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

1.2 结构框图

2. 软件配置

RTC 相关库函数在 stm32f10x_rtc.c 和 stm32f10x_bkp.h 文件中。

(1)使能电源时钟和后备域时钟,开启 RTC 后备寄存器写访问

RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);

RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE);

PWR_BackupAccessCmd(ENABLE);

(2)复位备份区域,开启外部低速振荡器

BKP_DeInit();

RCC_LSEConfig(RCC_LSE_ON);

(3)选择 RTC 时钟,并使能

RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);

RCC_RTCCLKCmd(ENABLE);

(4)设置 RTC 的分频以及配置 RTC 时钟

RTC_EnterConfigMode();

RTC_ExitConfigMode();

void RTC_SetPrescaler(uint32_t PrescalerValue);

void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState);

void RTC_SetCounter(uint32_t CounterValue);

(5)更新配置,设置 RTC 中断分组

RTC_ExitConfigMode();

void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data);

uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR);

(6)编写 RTC 中断服务函数

RTC_IRQHandler

FlagStatus RTC_GetFlagStatus(uint32_t RTC_FLAG);

RTC_ClearITPendingBit(RTC_IT_SEC);

3. 硬件设计

4. 软件设计

4.1 功能描述

4.2 软件实现

(1)初始化 RTC,设置 RTC 时间日期初值

(2)开启 RTC 的秒中断,编写 RTC 中断函数

(3)在 RTC 中断内更新时间并打印输出

(4)编写主函数

4.2.1 rtc.c

1.#include "rtc.h"
2.
3._calendar calendar;//时钟结构体 
4. 
5.static void RTC_NVIC_Config(void)
6.{ 
7. NVIC_InitTypeDef NVIC_InitStructure;
8. NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;  //RTC全局中断
9. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级1位,从优先级3位
10. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //先占优先级0位,从优先级4位
11. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  //使能该通道中断
12. NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
13.}
14.
15.
16./* ---------------------------------函数声明-------------------------------------
17.*函数名称:u8 RTC_Iint(void)
18.*输入参数:无
19.*返回参数:u8(返回值=0,初始化正常;返回值=1,初始化失败)
20.*功    能:初始化RTC
21.*作    者:lph
22.*日    期:2024/04/25
23.------------------------------------------------------------------------------ */
24.u8 RTC_Init(void)
25.{
26. //检查是不是第一次配置时钟
27. u8 temp=0;
28. RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP,ENABLE); //使能PWR和BKP外设时钟   
29. PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问  
30. if (BKP_ReadBackupRegister(BKP_DR1) != 0xA0A0)  //从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎
31. {     
32.  BKP_DeInit(); //复位备份区域  
33.  RCC_LSEConfig(RCC_LSE_ON); //设置外部低速晶振(LSE),使用外设低速晶振
34.  while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&temp<250) //检查指定的RCC标志位设置与否,等待低速晶振就绪
35.  {
36.   temp++;
37.   delay_ms(10);
38.  }
39.  if(temp>=250)return 1;//初始化时钟失败,晶振有问题     
40.  RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);  //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟    
41.  RCC_RTCCLKCmd(ENABLE); //使能RTC时钟  
42.  RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
43.  RTC_WaitForSynchro();  //等待RTC寄存器同步  
44.  RTC_ITConfig(RTC_IT_SEC, ENABLE);  //使能RTC秒中断
45.  RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
46.  RTC_EnterConfigMode();// 允许配置 
47.  RTC_SetPrescaler(32767); //设置RTC预分频的值
48.  RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
49.  RTC_Set(2024,4,25,20,37,40);  //设置时间 
50.  RTC_ExitConfigMode(); //退出配置模式  
51.  BKP_WriteBackupRegister(BKP_DR1, 0XA0A0); //向指定的后备寄存器中写入用户程序数据
52. }
53. else//系统继续计时
54. {
55.  RTC_WaitForSynchro(); //等待最近一次对RTC寄存器的写操作完成
56.  RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中断
57.  RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
58. }
59. RTC_NVIC_Config();//RCT中断分组设置               
60. RTC_Get();//更新时间 
61. return 0; //ok
62.
63.}  
64.
65.
66.//RTC时钟中断
67.//每秒触发一次  
68.//extern u16 tcnt; 
69.void RTC_IRQHandler(void)
70.{   
71. if (RTC_GetITStatus(RTC_IT_SEC) != RESET)//秒钟中断
72. {       
73.  RTC_Get();//更新时间  
74.  printf("RTC Time:%d-%d-%d %d:%d:%d\r\n",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);//输出闹铃时间 
75.    
76.  }
77. if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)//闹钟中断
78. {
79.  RTC_ClearITPendingBit(RTC_IT_ALR);  //清闹钟中断    
80.  RTC_Get();    //更新时间   
81.  printf("Alarm Time:%d-%d-%d %d:%d:%d\r\n",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);//输出闹铃时间 
82.  
83.   }                
84. RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW);  //清闹钟中断
85. RTC_WaitForLastTask();                     
86.}
87.
88.
89.//判断是否是闰年函数
90.//月份   1  2  3  4  5  6  7  8  9  10 11 12
91.//闰年   31 29 31 30 31 30 31 31 30 31 30 31
92.//非闰年 31 28 31 30 31 30 31 31 30 31 30 31
93.//输入:年份
94.//输出:该年份是不是闰年.1,是.0,不是
95.u8 Is_Leap_Year(u16 year)
96.{     
97. if(year%4==0) //必须能被4整除
98. { 
99.  if(year%100==0) 
100.  { 
101.   if(year%400==0)return 1;//如果以00结尾,还要能被400整除     
102.   else return 0;   
103.  }else return 1;   
104. }else return 0; 
105.}        
106.
107.
108.//月份数据表            
109.u8 const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正数据表   
110.//平年的月份日期表
111.const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};
112.
113.
114./* ---------------------------------函数声明-------------------------------------
115.*函数名称:RTC_Set
116.*输入参数:syear:年;smon:月;sday:日;
117.     hour:时;min:分 ;sec:秒  
118.*返回参数:u8(0,成功
119.           1,失败)
120.*功    能:RTC设置日期时间函数(以1970年1月1日为基准,把输入的时钟转换为秒钟)——1970~2099年为合法年份
121.*作    者:lph
122.*日    期:2024/04/25
123.------------------------------------------------------------------------------ */
124.u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
125.{
126. u16 t;
127. u32 seccount=0;
128. if(syear<1970||syear>2099)return 1;    
129. for(t=1970;t<syear;t++) //把所有年份的秒钟相加
130. {
131.  if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
132.  else seccount+=31536000;     //平年的秒钟数
133. }
134. smon-=1;
135. for(t=0;t<smon;t++)    //把前面月份的秒钟数相加
136. {
137.  seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加
138.  if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数    
139. }
140. seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加 
141. seccount+=(u32)hour*3600;//小时秒钟数
142.    seccount+=(u32)min*60;  //分钟秒钟数
143. seccount+=sec;//最后的秒钟加上去
144.
145. RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟  
146. PWR_BackupAccessCmd(ENABLE); //使能RTC和后备寄存器访问 
147. RTC_SetCounter(seccount); //设置RTC计数器的值
148.
149. RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成   
150. return 0;     
151.}
152.
153.
154.//初始化闹钟    
155.//以1970年1月1日为基准
156.//1970~2099年为合法年份
157.//syear,smon,sday,hour,min,sec:闹钟的年月日时分秒   
158.//返回值:0,成功;其他:错误代码.
159.u8 RTC_Alarm_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
160.{
161. u16 t;
162. u32 seccount=0;
163. if(syear<1970||syear>2099)return 1;    
164. for(t=1970;t<syear;t++) //把所有年份的秒钟相加
165. {
166.  if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
167.  else seccount+=31536000;     //平年的秒钟数
168. }
169. smon-=1;
170. for(t=0;t<smon;t++)    //把前面月份的秒钟数相加
171. {
172.  seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加
173.  if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数    
174. }
175. seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加 
176. seccount+=(u32)hour*3600;//小时秒钟数
177.    seccount+=(u32)min*60;  //分钟秒钟数
178. seccount+=sec;//最后的秒钟加上去        
179. //设置时钟
180. RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟   
181. PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问  
182. //上面三步是必须的!
183. 
184. RTC_SetAlarm(seccount);
185. 
186. RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成   
187. 
188. return 0;     
189.}
190.
191.
192.//得到当前的时间
193.//返回值:0,成功;其他:错误代码.
194.u8 RTC_Get(void)
195.{
196. static u16 daycnt=0;
197. u32 timecount=0; 
198. u32 temp=0;
199. u16 temp1=0;   
200.    timecount=RTC_GetCounter();  
201.  temp=timecount/86400;   //得到天数(秒钟数对应的)
202. if(daycnt!=temp)//超过一天了
203. {   
204.  daycnt=temp;
205.  temp1=1970; //从1970年开始
206.  while(temp>=365)
207.  {     
208.   if(Is_Leap_Year(temp1))//是闰年
209.   {
210.    if(temp>=366)temp-=366;//闰年的秒钟数
211.    else {temp1++;break;}  
212.   }
213.   else temp-=365;   //平年 
214.   temp1++;  
215.  }   
216.  calendar.w_year=temp1;//得到年份
217.  temp1=0;
218.  while(temp>=28)//超过了一个月
219.  {
220.   if(Is_Leap_Year(calendar.w_year)&&temp1==1)//当年是不是闰年/2月份
221.   {
222.    if(temp>=29)temp-=29;//闰年的秒钟数
223.    else break; 
224.   }
225.   else 
226.   {
227.    if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年
228.    else break;
229.   }
230.   temp1++;  
231.  }
232.  calendar.w_month=temp1+1; //得到月份
233.  calendar.w_date=temp+1;   //得到日期 
234. }
235. temp=timecount%86400;       //得到秒钟数       
236. calendar.hour=temp/3600;      //小时
237. calendar.min=(temp%3600)/60;  //分钟 
238. calendar.sec=(temp%3600)%60;  //秒钟
239. calendar.week=RTC_Get_Week(calendar.w_year,calendar.w_month,calendar.w_date);//获取星期   
240. return 0;
241.} 
242.
243.
244.//获得现在是星期几
245.//功能描述:输入公历日期得到星期(只允许1901-2099年)
246.//输入参数:公历年月日 
247.//返回值:星期号                       
248.u8 RTC_Get_Week(u16 year,u8 month,u8 day)
249.{ 
250. u16 temp2;
251. u8 yearH,yearL;
252. 
253. yearH=year/100; yearL=year%100; 
254. // 如果为21世纪,年份数加100  
255. if (yearH>19)yearL+=100;
256. // 所过闰年数只算1900年之后的  
257. temp2=yearL+yearL/4;
258. temp2=temp2%7; 
259. temp2=temp2+day+table_week[month-1];
260. if (yearL%4==0&&month<3)temp2--;
261. return(temp2%7);
262.}     

4.2.2 rtc.h

1.#ifndef __rtc_H
2.#define __rtc_H
3.
4.#include "system.h"
5.#include "SysTick.h"
6.#include "usart.h"
7.
8.//时间结构体
9.typedef struct 
10.{
11. u8 hour;
12. u8 min;
13. u8 sec; 
14. 
15. //公历日月年周
16. u16 w_year;
17. u8  w_month;
18. u8  w_date;
19. u8  week;   
20.}_calendar;  
21.
22.extern _calendar calendar; //日历结构体
23.
24.u8 RTC_Init(void);        //初始化RTC,返回0,失败;1,成功;
25.u8 Is_Leap_Year(u16 year);//平年,闰年判断
26.u8 RTC_Alarm_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);
27.u8 RTC_Get(void);         //更新时间   
28.u8 RTC_Get_Week(u16 year,u8 month,u8 day);
29.u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);//设置时间 
30.
31.#endif

4.2.3 main.c

1./* Includes ------------------------------------------------------------------*/
2.#include "system.h"
3.#include "LED.h"
4.#include "SysTick.h"
5.#include "USART.h"
6.#include "rtc.h"
7.
8./* 主函数main() --------------------------------------------------------------*/
9.int main()
10.{   
11.    // 自定义变量
12.    u8 i=0;
13.    
14.    // 初始化
15.    SysTick_Iint(72);
16.    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  // 中断分组
17.    LED_Init();
18.    USART1_Iint(115200);   
19.    RTC_Init();
20.    
21.    while(1)
22.    {
23.       
24.    }
25.}

4.3 实验结果

  • 16
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值