13.RTC实时时钟

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)

这里需要理解一下!

最后一般不在初始化时设置时钟。

相关推荐
国科安芯2 小时前
FreeRTOS 在 AS32系列RISC-V 架构MCU电机驱动中的应用实践与优化
单片机·嵌入式硬件·安全·架构·压力测试·risc-v·安全性测试
染予2 小时前
GPIO中断实现流程
单片机·嵌入式硬件
门思科技2 小时前
LoRaWAN通信协议详解:架构、加密机制与核心应用
运维·服务器·网络·嵌入式硬件·物联网
二进制coder2 小时前
Linux RTC 驱动子系统详细实现方案
linux·运维·实时音视频
小柯博客2 小时前
STM32MP1 没有硬件编解码,如何用 CPU 实现 H.264 编码支持 WebRTC?
c语言·stm32·嵌入式硬件·webrtc·h.264·h264·v4l2
淮北4942 小时前
linux系统学习(10.shell基础)
linux·运维·服务器·学习
Lolo_fi3 小时前
记录Fedora43上安装向日葵
linux
noravinsc3 小时前
两台 centos 7.9 部署 pbs version 18.1.4 集群
linux·运维·centos
你的微笑,乱了夏天3 小时前
linux centos常用命令整理
linux·运维·centos