STM32的实时时钟(RTC)是一个独立的定时器。STM32的RTC模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
#include "delay.h"
#include "led.h"
#include "key.h"
#include "usart1.h"
#include "rtc.h"
int main(){
u8 i=0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置分组
delay_init(); //延时初始化
LED_Init();
usart1_Init(9600); //串口通信初始化
RTC_Init(); //RTC初始化
while(1)
{
//系统指示灯闪烁
i++;
if(i%20==0)
{
LED0=!LED0;
}
delay_ms(10);
}
}
#ifndef __RTC_H
#define __RTC_H
typedef unsigned char u8;
typedef unsigned short u16;
u8 RTC_Init(void); //RTC初始化
u8 Is_Leap_Year(u16 year); //判断是否是闰年
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec); //设置当前时间
u8 RTC_Get(void); //获取当前时间
u8 RTC_Get_Week(u16 year,u8 month,u8 day); //获取当前是星期几
u8 RTC_Alarm_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec); //初始化闹钟
#endif
#include "stm32f10x.h"
#include "delay.h"
#include "stdio.h"
#include "rtc.h"
//时间结构体
typedef struct
{
u8 hour; //时
u8 min; //分
u8 sec; //秒
u16 w_year; //年
u8 w_month; //月
u8 w_date; //日
u8 week; //周
}_calendar;
_calendar calendar; //定义结构体日历calendar
/*
功能:中断管理
*/
static void RTC_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel=RTC_IRQn; //设置中断通道
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1; //抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority=2; //子优先级
NVIC_Init(&NVIC_InitStruct);
}
/*
功能:RTC初始化
变量:无
返回值:返回值不为0:表示初始化成功;返回值为0:表示初始化不成功
*/
u8 RTC_Init()
{
u8 time;
//1.使能电源时钟和后备域时钟,开启RTC后备寄存器写访问
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE); //使能电源时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE); //使能后备域时钟
PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问
//2.判断初始化是否完成
if(BKP_ReadBackupRegister(BKP_DR1)!=0xA0A0) //从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎
{
//3.复位备份区域,开启外部低速振荡器
BKP_DeInit(); //复位备份区域
RCC_LSEConfig(RCC_LSE_ON); //开启外部低速振荡器
//4.判断RTC外部时钟好坏
while((RCC_GetFlagStatus(RCC_FLAG_LSERDY)==RESET)&&(time<100))
{
time++;
delay_ms(10);
}
//5.若出现超时,则晶振可能出现问题
if(time>100)
{
return 1; //初始化时钟失败,晶振存在问题
}
//6.选择RTC时钟,并使能
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟
RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
//7.等待APB1时钟和RTC时钟同步
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_WaitForSynchro(); //等待RTC寄存器同步
RTC_ITConfig(RTC_IT_SEC,ENABLE); //使能RTC秒中断
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
//8.设置RTC的分频以及配置RTC时钟
RTC_EnterConfigMode(); //进入配置模式
RTC_SetPrescaler(32767); //设置RTC预分频的值,因为外部低速晶振32.768KHz,计数一次为1s
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
//9.设置初始化时间
RTC_Set(2023,7,10,22,20,50); //设置时间
RTC_ExitConfigMode(); //退出配置模式
//10.写入初始化完成标志值
BKP_WriteBackupRegister(BKP_DR1,0xA0A0); //对后备域写入标志位。方便下一次对RTC进行操作,不必再次设置时间
}
else //已经初始化成功,系统继续计时
{
RTC_WaitForSynchro(); //等待RTC寄存器同步
RTC_ITConfig(RTC_IT_SEC,ENABLE); //使能RTC秒中断
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
}
//11.配置中断优先级
RTC_NVIC_Config();
//12.获取当前日历
RTC_Get();
return 0;
}
/*
功能:中断服务函数
变量:无
返回值:无
*/
void RTC_IRQHandler()
{
if(RTC_GetITStatus(RTC_IT_SEC)) //判断是否获取到秒中断标志位
{
RTC_Get(); //每一秒更新时间
printf("RTC TIME:%d-%d-%d %d:%d:%:\r\n",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec); //打印出日历数据
}
}
/*
功能:判断是否是闰年
变量:year:要判断的年份
返回值:返回1则是闰年,返回0则不是闰年
注意:
月份: 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) //必须能被4整除
{
if(year%100==0)
{
if(year%400==0)return 1;//如果以00结尾,还要能被400整除
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 u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};
/*
功能:设置日期时间(以1970年1月1日为基准,把输入的时钟转换为秒钟),1970~2099年为合法年份
变量:变量分别为:年、月、日、时、分、秒
返回值:返回值为1则设置失败,返回值为0则设置成功
*/
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);
}