13.RTC实时时钟
说明:
6ULL内部自带了一个RTC外设,确切来说叫SRTC,所在内容在参考文档中的SNVS章节。
RTC分为两种LP(SRTC)和HP(RTC),但是本开发板的HP的RTC掉点以后数据就丢失了,就算使用了纽扣电池也没有用。所以必须使用LP,也就是SRTC
SNVS章节有些与加密有关,需要与NXP签订保密协议才能查看。
- 内部RTC精度不高,建议使用外部RTC来使用。如PCF8563
RTC类似于定时器,外接37.768KHz的晶振,然后就开始计时。
RTC使用两个寄存器来保存相关的数值.,需要进行数据拼接。
寄存器
SNVS_HPCOMR
SNVS_HPCOMR[3]:置1,表示所有软件均可访问RTC
SNVS_HPCOMR[8]:与安全相关,置1,0均可。
SNVS_LPCR
SNVS_LPCR[0] : RTC使能位,为1打开后,SNVS_LPSRTCMR寄存器和SNVS_LPSRTCLR寄存器开始计数,
SNVS_LPSRTCMR
SNVS_LPSRTCMR为高15位RTC计数寄存器
SNVS_LPSRTCLR
与SNVS_LPSRTCMR寄存器共同组成了SRTC计数器,每一秒数据加1。
相对时间
指的是时间全部转换成秒数,相对于1970年0时0分0秒开始计数
使用步骤
一、打开RTC
初始化RTC,需要使用几个时间转化函数分钟数转标准格式,需要修改RTC_TYPE,
c
#include "bsp_rtc.h"
#include "imx6ul.h"
void rtc_init(void){
struct rtc_datetime rtcData;
SNVS->HPCOMR |= (1<<31)|(1<<8);
rtcData.year =2025 ;
rtcData.month = 6;
rtcData.day =24 ;
rtcData.hour = 15;
rtcData.minute = 30;
rtcData.second = 0;
rtc_setdatatime(&rtcData);
rtc_enable();
}
void rtc_enable(void){
SNVS->LPCR |= (1<<0);
//使能
while(( SNVS->LPCR & 0x01) == 0) ;
}
void rtc_disable(void){
SNVS->LPCR &= ~(1<<0);
while(( SNVS->LPCR & 0x01) == 1) ;
}
//将年月日转化成相对时间
/*
* @description : 将时间转换为秒数
* @param - datetime: 要转换日期和时间。
* @return : 转换后的秒数
*/
uint64_t rtc_coverdate_to_seconds(struct rtc_datetime *datetime)
{
unsigned short i = 0;
uint64_t seconds = 0;
unsigned int days = 0;
unsigned short monthdays[] = {0U, 0U, 31U, 59U, 90U, 120U, 151U, 181U, 212U, 243U, 273U, 304U, 334U};
for(i = 1970; i < datetime->year; i++)
{
days += DAYS_IN_A_YEAR; /* 平年,每年365天 */
if(rtc_isleapyear(i)) days += 1;/* 闰年多加一天 */
}
days += monthdays[datetime->month];
if(rtc_isleapyear(i) && (datetime->month >= 3)) days += 1;/* 闰年,并且当前月份大于等于3月的话加一天 */
days += datetime->day - 1;
seconds = days * SECONDS_IN_A_DAY +
datetime->hour * SECONDS_IN_A_HOUR +
datetime->minute * SECONDS_IN_A_MINUTE +
datetime->second;
return seconds;
}
//设置时间
void rtc_setdatatime(struct rtc_datetime *datetime){
unsigned int temp = SNVS->LPCR;
uint64_t seconds= 0;
rtc_disable();
seconds = rtc_coverdate_to_seconds(datetime);
// SNVS->LPSRTCMR = (unsigned int )(seconds>>32);
// SNVS->LPSRTCLR = (unsigned int )(seconds & 0xFFFFFFFF);
SNVS->LPSRTCMR = (unsigned int)(seconds >> 17); /* 设置高16位 */
SNVS->LPSRTCLR = (unsigned int)(seconds << 15); /* 设置地16位 */
if(temp & 0x01)
rtc_enable();
}
uint64_t rtc_getseconds(void){
uint64_t seconds= 0;
seconds = (SNVS->LPSRTCMR << 17) | (SNVS->LPSRTCLR >> 15);
return seconds ;
}
/*
* @description : 判断指定年份是否为闰年,闰年条件如下:
* @param - year: 要判断的年份
* @return : 1 是闰年,0 不是闰年
*/
unsigned char rtc_isleapyear(unsigned short year)
{
unsigned char value=0;
if(year % 400 == 0)
value = 1;
else
{
if((year % 4 == 0) && (year % 100 != 0))
value = 1;
else
value = 0;
}
return value;
}
/*
* @description : 将秒数转换为时间
* @param - seconds : 要转换的秒数
* @param - datetime: 转换后的日期和时间
* @return : 无
*/
void rtc_convertseconds_to_datetime(u64 seconds, struct rtc_datetime *datetime)
{
u64 x;
u64 secondsRemaining, days;
unsigned short daysInYear;
/* 每个月的天数 */
unsigned char daysPerMonth[] = {0U, 31U, 28U, 31U, 30U, 31U, 30U, 31U, 31U, 30U, 31U, 30U, 31U};
secondsRemaining = seconds; /* 剩余秒数初始化 */
days = secondsRemaining / SECONDS_IN_A_DAY + 1; /* 根据秒数计算天数,加1是当前天数 */
secondsRemaining = secondsRemaining % SECONDS_IN_A_DAY; /*计算天数以后剩余的秒数 */
/* 计算时、分、秒 */
datetime->hour = secondsRemaining / SECONDS_IN_A_HOUR;
secondsRemaining = secondsRemaining % SECONDS_IN_A_HOUR;
datetime->minute = secondsRemaining / 60;
datetime->second = secondsRemaining % SECONDS_IN_A_MINUTE;
/* 计算年 */
daysInYear = DAYS_IN_A_YEAR;
datetime->year = YEAR_RANGE_START;
while(days > daysInYear)
{
/* 根据天数计算年 */
days -= daysInYear;
datetime->year++;
/* 处理闰年 */
if (!rtc_isleapyear(datetime->year))
daysInYear = DAYS_IN_A_YEAR;
else /*闰年,天数加一 */
daysInYear = DAYS_IN_A_YEAR + 1;
}
/*根据剩余的天数计算月份 */
if(rtc_isleapyear(datetime->year)) /* 如果是闰年的话2月加一天 */
daysPerMonth[2] = 29;
for(x = 1; x <= 12; x++)
{
if (days <= daysPerMonth[x])
{
datetime->month = x;
break;
}
else
{
days -= daysPerMonth[x];
}
}
datetime->day = days;
}
void rtc_getdatatime(struct rtc_datetime *datetime){
uint64_t seconds= 0;
seconds = rtc_getseconds();
rtc_convertseconds_to_datetime(seconds,datetime);
}
rtc.h
c
#ifndef __BSP_RTC_H
#define __BSP_RTC_H
#include "imx6ul.h"
/* 相关宏定义 */
#define SECONDS_IN_A_DAY (86400) /* 一天86400秒 */
#define SECONDS_IN_A_HOUR (3600) /* 一个小时3600秒 */
#define SECONDS_IN_A_MINUTE (60) /* 一分钟60秒 */
#define DAYS_IN_A_YEAR (365) /* 一年365天 */
#define YEAR_RANGE_START (1970) /* 开始年份1970年 */
#define YEAR_RANGE_END (2099) /* 结束年份2099年 */
/* 时间日期结构体 */
struct rtc_datetime
{
unsigned short year; /* 范围为:1970 ~ 2099 */
unsigned char month; /* 范围为:1 ~ 12 */
unsigned char day; /* 范围为:1 ~ 31 (不同的月,天数不同).*/
unsigned char hour; /* 范围为:0 ~ 23 */
unsigned char minute; /* 范围为:0 ~ 59 */
unsigned char second; /* 范围为:0 ~ 59 */
};
void rtc_init(void);
void rtc_enable(void);
void rtc_disable(void);
unsigned char rtc_isleapyear(unsigned short year);
uint64_t rtc_coverdate_to_seconds(struct rtc_datetime *datetime);
void rtc_convertseconds_to_datetime(u64 seconds, struct rtc_datetime *datetime);
uint64_t rtc_getseconds(void);
void rtc_setdatatime(struct rtc_datetime *datetime);
void rtc_getdatatime(struct rtc_datetime *datetime);
#endif
二、不断获取RTC的值
main.c
c
rtc_init();
struct rtc_datetime rtcdate;
char buf[160];
memset(buf, 0, sizeof(buf));
while (1)
{
rtc_getdatatime(&rtcdate);
state = ! state;
sprintf(buf,"%d/%d/%d %d:%d:%d\r\n",rtcdate.year, rtcdate.month, rtcdate.day, rtcdate.hour, rtcdate.minute, rtcdate.second);
lcd_show_string(10,400,500,32,32,buf);
printf(buf);
led_switch(LED0,state);
delay_ms(1000);
}
代码解释
c
//获取时间
uint64_t rtc_getseconds(void){
uint64_t seconds= 0;
seconds = (SNVS->LPSRTCMR << 17) | (SNVS->LPSRTCLR >> 15);
return seconds ;
}
//设置时间
void rtc_setdatatime(struct rtc_datetime *datetime){
unsigned int temp = SNVS->LPCR;
uint64_t seconds= 0;
rtc_disable(); //确保关闭再配置参数
seconds = rtc_coverdate_to_seconds(datetime); //将结构体中的年月日转化为秒数
// SNVS->LPSRTCMR = (unsigned int )(seconds>>32);
// SNVS->LPSRTCLR = (unsigned int )(seconds & 0xFFFFFFFF);
SNVS->LPSRTCMR = (unsigned int)(seconds >> 17); /* 设置高16位 */ //写进高32位寄存器
SNVS->LPSRTCLR = (unsigned int)(seconds << 15); /* 设置地16位 */ //写进低32位寄存器
if(temp & 0x01) //如果RTC实时时钟是关闭的话,就开启
rtc_enable();
}
可以看到主要是配置SNVS的两个寄存器LPSRTCMR和LPSRTCLR来写入秒数就可以实现每秒自动加1
左移和右移为什么17 和15?
为了匹配32768hz 每秒需要从0-32768 计数 算出每秒计数值,即左移15位也就是LPSRTCLR低17位
即,LPSRTCLR[32:15]
LPSRTCLR的0-15位存放时钟频率即 0-32768 到达32768 后第16位上+1

因此需要seconds(64位) = (LR>>15) | (MR<<17)
同理 LR |= (uint32_t)(seconds<<15)
MR |= (uint32_t)(seconds>>17)
这里需要理解一下!
最后一般不在初始化时设置时钟。