1.由于最近一段时间要进行期末考试,所以很久没有动32了,从现在开始,可以再开始学了
2.RTC(REAL TIME CLOCK)
1.作为Stm32内部的实时时钟,可以看做一个独立的定时器,在代码的配置下,可以作为时钟日历的功能
2.注意:RTC的配置过程十分敏感,因为对于RTC的配置过程中,是在后备区域进行,由于系统在自动复位以后,自动会禁止后备寄存器和RTC(写保护),所以在配置的过程中要进行对写保护的撤销
3.RTC简图
4.配置重点:
因为RTC配置需要对后备区域进行配置,配置的时候要判断是否寄存器完成和同步,也要取消对备份区的写保护
1.初始化过程
#include "delay.h"
#include "usart.h"
#include "rtc.h"
_calendar_obj calendar;//时钟结构体
//实时时钟配置
//初始化RTC时钟,同时检测时钟是否工作正常
//BKP->DR1用于保存是否第一次配置的设置
//返回0:正常
//其他:错误代码
u8 RTC_Init(void)
{
u8 temp=0;
//1.检查是不是第一次配置时钟
if(BKP->DR1!=0X5050)
{
//BKP->DR1 为可设置的位数为16位的寄存器,用来提供下一次配置
//2.使能电源时钟和备份区域时钟
RCC->APB1ENR|=1<<28;//电源时钟
RCC->APB2ENR|=1<<27;//备份区域时钟
//3.取消备份区写保护
PWR->CR|=1<<8;
//4.开始软件复位,开启外围振荡器
RCC->BDCR|=1<<16;
RCC->BDCR&=~(1<<16);//软件复位结束
RCC->BDCR|=1<<0;//开启外围振荡器 提供时序
//5.等待时间,检查晶振是否有问题
while((temp<250)&&(!(RCC->BDCR&0X02)))//未准备就绪,振荡器未被旁路
{
temp++;
delay_ms(10);
}
if(temp>=250)return 1;//晶振失效
//6.确定时钟以及时钟使能
//由于RTC的操纵敏感 注意检测rtc的寄存器上一步是否操作完成和同步
RCC->BDCR|=1<<8;//确定时钟为LSI(内部低速时钟)
RCC->BDCR|=1<<15;//enable the clock
while(!(RTC->CRL&(1<<5)));//等待RTC寄存器操作完成
while(!(RTC->CRL&(1<<3)));//等待RTC寄存器同步
RTC->CRH|=0X01;//秒同步
while(!(RTC->CRL&(1<<5)));//等待RTC寄存器操作完成
RTC->CRL|=1<<4;//允许配置
//7.配置时钟
RTC->PRLH=0X0000;//预分频系数的高16位
RTC->PRLL=32767;//预分频系数的第16位
RTC_Set(2018,7,1,21,03,00);//设置初始化时钟
RTC->CRL&=~(1<<4);//开始配置
while(!(RTC->CRL&(1<<5)));//等待操作完成
BKP->DR1=0x5050;//初始化标志位
printf("FIRST TIME");
}
else
{
while(!(RTC->CRL&(1<<3)));//等待同步
RTC->CRH|=0X01;//允许秒中断
while(!(RTC->CRL&(1<<5)));//等待操作完成
printf("OKn");
}
//设置中断优先级
MY_NVIC_Init(0,0,RTC_IRQn,2);
RTC_Get();
return 0;
}
//RTC时钟中断
//每秒触发一次
void RTC_IRQHandler(void)
{
if(RTC->CRL&0x0001)//秒钟中断
{
RTC_Get();//更新时间
//printf("sec:%drn",calendar.sec);
}
if(RTC->CRL&0x0002)//闹钟中断
{
RTC->CRL&=~(0x0002); //清闹钟中断
//printf("Alarm!n");
}
RTC->CRL&=0X0FFA; //清除溢出,秒钟中断标志
while(!(RTC->CRL&(1<<5)));//等待RTC寄存器操作完成
}
2.判断是否是闰年
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;
}
//设置时钟
//把输入的时钟转换为秒钟
//以1970年1月1日为基准
//1970~2099年为合法年份
//返回值: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};
//syear,smon,sday,hour,min,sec:年月日时分秒
//返回值:设置结果。0,成功;1,失败。
3.设置时间
注
1.所得值放在RTC->CNTR和RTC->CNTL中(2个16位寄存器)
2.设定基准值(以1970 1 1为基准)计算到目标年的秒数(年月日)
计算所得值
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; //由于设定的基准年为1970年,再加上32位的寄存器 直到2106年也不会溢出
for(t=1970;t
{
if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
else seccount+=31536000; //平年的秒钟数
}
smon-=1;
for(t=0;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;//最后的秒钟加上去
//先把所有的秒钟统计 然后再去计算小时 分钟 秒钟
//设置时钟(对RTC的再一次配置)
RCC->APB1ENR|=1<<28;//使能电源时钟
RCC->APB1ENR|=1<<27;//使能备份时钟
PWR->CR|=1<<8; //取消备份区写保护
//上面三步是必须的!
RTC->CRL|=1<<4; //允许配置
RTC->CNTL=seccount&0xffff;//除低16位都清0(确定低16位)
RTC->CNTH=seccount>>16;//确定高16位
RTC->CRL&=~(1<<4);//配置更新
while(!(RTC->CRL&(1<<5)));//等待RTC寄存器操作完成
RTC_Get();//设置完之后更新一下数据
return 0;
}
4.获取时间
读出RTC->CNTH的值(秒)
根据所得值分别计算是某年 某月 某日 某星期
//得到当前的时间,结果保存在calendar结构体里面
//返回值:0,成功;其他:错误代码.
u8 RTC_Get(void)
{
static u16 daycnt=0;
u32 timecount=0;
u32 temp=0;
u16 temp1=0;
//将数据写入 移位写入
timecount=RTC->CNTH;//得到计数器中的值(秒钟数)
timecount<<=16;
timecount+=RTC->CNTL;//已经得到所有的秒数(由于STM32的寄存器为32位 所以可以支持大概136年的计时时间)
temp=timecount/86400; //得到天数(秒钟数对应的)
if(daycnt!=temp)//超过一天了
{
daycnt=temp;
temp1=1970; //从1970年开始
while(temp>=365)
{
if(Is_Leap_Year(temp1))//是闰年
{
if(temp>=366)temp-=366;//闰年的秒钟数
else 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;
}
5.为什么数据会使动态的呢?
设置分频系数(psc=32767),做到每1s对数据进行更新,同时 可以进行进行中断函数的配置 达到闹钟的效果
『本文转载自网络,版权归原作者所有,如有侵权请联系删除』