> 文章列表 > 17_I.MX6ULL_内部RTC

17_I.MX6ULL_内部RTC

17_I.MX6ULL_内部RTC

目录

I.MX6U RTC简介

相关寄存器

实验源码


I.MX6U RTC简介

实时时钟是很常用的一个外设,通过实时时钟我们就可以知道年、月、日和时间等信息。因此在需要记录时间的场合就需要实时时钟,可以使用专用的实时时钟芯片来完成此功能,但是现在大多数的MCU或者MPU内部就已经自带了实时时钟外设模块。比如I.MX6U内部的SNVS就提供了RTC功能,本章我们就学习如何使用I.MX6U内部的RTC来完成实时时钟功能。

如果学习过STM32的话应该知道,STM32内部有一个RTC外设模块,这个模块需要一个32.768KHz的晶振,对这个RTC模块进行初始化就可以得到一个实时时钟。I.MX6U内部也有个RTC模块,但是不叫作"RTC",而是叫做"SNVS",这一点要注意!

SNVS直译过来就是安全的非易性存储,SNVS里面主要是一些低功耗的外设,包括一个安全的实时计数器(RTC)、一个单调计数器(monotonic counter)和一些通用的寄存器,使用实时计数器(RTC).SNVS里面的外设在芯片掉电以后由电池供电继续运行

因为纽扣电池在掉电以后会继续给SNVS供电,因此实时计数器就会一直运行,这样的话时间信息就不会丢失,除非纽扣电池没电了。在有纽扣电池作为后备电源的情况下,不管系统主电源是否断电,SNVS都正常运行。SNVS有两部分:SNVS_HP和SNVS_LP,系统主电源断电以后SNVS_HP也会断电,但是在后备电源支持下, SNVS_LP是不会断电的,而且SNVS_LP是和芯片复位隔离开的,因此SNVS_LP相关的寄存器的值会一直保存着。

SNVS分为两个子模块:SNVS_HP和SNVS_LP,也就是高功耗域(SNVS_HP)和低功耗域(SNVS_LP),这两个域的电源来源如下:

SNVS_LP:专用的always-powered-on电源域,系统主电源和备用电源都可以为其供电。 SNVS_HP:系统(芯片)电源。

SNVS的这两个子模块的电源如图所示:

 1.VDD_HIGH_IN是系统(芯片)主电源,这个电源会同时供给给SNVS_HP和SNVS_LP。

2.VDD_SNVS_IN是纽扣电池供电的电源,这个电源只会供给给SNVS_LP,保证在系统主电源VDD_HIGH_IN掉电以后SNVS_LP会继续运行。

3.SNVS_HP 部分。

4.SNVS_LP部分,此部分有个SRTC,这个就是我们本章要使用的RTC。

其实不管是SNVS_HP还是SNVS_LP,其内部都有一个SRTC,但是因为SNVS_HP在系⊥统电源掉电以后就会关闭,所以使用的是SNVS_LP内部的SRTC。毕竟我们肯定都不想开发板或者设备每次关闭以后时钟都被清零,然后开机以后先设置时钟。

其实不管是SNVS_HP里面的RTC,还是SNVS_LP里面的SRTC,其本质就是一个定时器,和EPIT定时器一样,只要给它提供时钟,它就会一直运行。SRTC需要外界提供一个32.768KHz 的时钟,I.MX6U核心板上的32.768KHz 的晶振就是提供这个时钟的。

寄存器 SNVS_LPSRTCMR和SNVS_LPSRTCLR保存着秒数,直接读取这两个寄存器的值就知道过了多长时间了。一般以1970年1月1日为起点,加上经过的秒数即可得到现在的时间和日期,原理还是很简单的。SRTC也是带有闹钟功能的,可以在寄存器SNVS-LPAR中写入闹钟时间值,当时钟值和闹钟值匹配的时候就会产生闹钟中断,要使用时钟功能的话还需要进行一些设置

相关寄存器

SNVS_HPCOMR寄存器,这个寄存器我们只用到了位:NPSWA_EN(bit31),这个位是非特权软件访问控制位,如果非特权软件要访问SNVS的话此位必须为1。

SNVS_LPCR寄存器,此寄存器也只用到了一个位:SRTC_ENV(bit0),此位为1的话就使能STC计数器。

寄存器SNVS SRTCMR和SNVS SRTCLR,这两个寄存器保存着RTC的秒

SRTC计数器是32 位

SNVS_SRTCM的bit14:0这15位是SRTC计数器的高15位。

SNVS_SRTCLR的bit31:bit15这17位是SRTC计数器的低17位。

实验源码

#ifndef __BSP_RTC_H_
#define __BSP_RTC_H_#include "imx6ul.h"/*跟实际有关的宏定义*/
#define SECONDS_IN_A_DAY      (86400)
#define SECONDS_IN_A_HOUR     (3600)
#define SECONDS_IN_A_MINUTE   (60)
#define DAYS_IN_A_YEAR      (365)
#define YEAR_RANGE_START    (1970)
#define YEAR_RANGE_END      (2099)/*跟时间有关的结构体*/
struct rtc_datetime
{unsigned short year;unsigned char  month;unsigned char  day;  unsigned char  hour;unsigned char  minute;unsigned char  second;  
};void rtc_enable(void);
void rtc_disable(void);
void rtc_init(void);
unsigned int rtc_coverdate_to_seconds( struct rtc_datetime *datetime);
void rtc_setdatetime( struct rtc_datetime *datetime);
void rtc_convertseconds_to_datetime(u64 seconds, struct rtc_datetime *datetime);
unsigned int rtc_getseconds(void);
void rtc_getdatetime( struct rtc_datetime *datetime);
unsigned char rtc_isleapyear(unsigned short year);#endif
#include "bsp_rtc.h"
#include "stdio.h"/ @description	: rtc初始化函数* @param 		: 无* @return 		: 无*/
void rtc_init(void)
{/ 设置HPCOMR寄存器* bit[31] 1 : 允许访问SNVS寄存器,一定要置1* bit[8]  1 : 此位置1,需要签署NDA协议才能看到此位的详细说明,*             这里不置1也没问题*/SNVS->HPCOMR |= (1 << 31) | (1 << 8);/*使能rtc*/rtc_enable();
}/ @description	: 使能rtc函数* @param 		: 无* @return 		: 无*/
void rtc_enable(void)
{/ LPCR寄存器bit0置1,使能RTC*/SNVS->LPCR |= 1 << 0;	 while(!(SNVS->LPCR & 0X01));//等待使能完成
}/ @description	: 关闭rtc函数* @param 		: 无* @return 		: 无*/
void rtc_disable(void)
{/ LPCR寄存器bit0置0,关闭RTC*/SNVS->LPCR &= ~(1 << 0);	while(SNVS->LPCR & 0X01);//等待关闭完成
}/ @description		: 将时间转换为秒数* @param - datetime: 要转换日期和时间。* @return 			: 转换后的秒数*/
unsigned int rtc_coverdate_to_seconds(struct rtc_datetime *datetime)
{	unsigned short i = 0;unsigned int 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;	
}/ @description		: 设置时间和日期* @param - datetime: 要设置的日期和时间* @return 			: 无*/
void rtc_setdatetime(struct rtc_datetime *datetime)
{unsigned int seconds = 0;unsigned int tmp = SNVS->LPCR; rtc_disable();	/* 设置寄存器HPRTCMR和HPRTCLR的时候一定要先关闭RTC *//* 先将时间转换为秒 */seconds = rtc_coverdate_to_seconds(datetime);SNVS->LPSRTCMR = (unsigned int)(seconds >> 17); /* 设置高16位 */SNVS->LPSRTCLR = (unsigned int)(seconds << 15); /* 设置地16位 *//* 如果此前RTC是打开的在设置完RTC时间以后需要重新打开RTC */if (tmp & 0x1)rtc_enable();
}/ @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;}/ @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	: 获取RTC当前秒数* @param 		: 无* @return 		: 当前秒数 */
unsigned int rtc_getseconds(void)
{unsigned int seconds = 0;seconds = (SNVS->LPSRTCMR << 17) | (SNVS->LPSRTCLR >> 15);return seconds;
}/ @description		: 获取当前时间* @param - datetime: 获取到的时间,日期等参数* @return 			: 无 */
void rtc_getdatetime(struct rtc_datetime *datetime)
{//unsigned int seconds = 0;u64 seconds;seconds = rtc_getseconds();rtc_convertseconds_to_datetime(seconds, datetime);	
}
#include "bsp_clk.h"#include "bsp_delay.h"#include "bsp_led.h"#include "bsp_beep.h"#include "bsp_key_filter.h"#include "bsp_key.h"#include "bsp_interrupt.h"#include "bsp_uart.h"#include "bsp_lcd.h"#include "bsp_lcdapi.h"#include "bsp_rtc.h"#include "stdio.h"/ @description	: main函数* @param 		: 无* @return 		: 无*/int main(void){unsigned char key = 0;int i = 3, t = 0;char buf[160];struct rtc_datetime rtcdate;unsigned char state = OFF;int_init(); 		/* 初始化中断(一定要最先调用!)*/imx6u_clkinit();	/* 初始化系统时钟*/clk_enable();		/* 使能所有的时钟*/delay_init();		/* 延时初始化 */uart_init();		/*初始化串口*/	led_init();			/* 初始化led*/beep_init();		/* 初始化凤鸣器*/lcd_init();			/* 初始化LCD*/rtc_init(); 		/* 初始化RTC*/tftlcd_dev.forecolor = LCD_RED;lcd_show_string(50, 10, 400, 24, 24, (char*)"Hello World"); 	lcd_show_string(50, 40, 200, 16, 16, (char*)"Hello World");lcd_show_string(50, 60, 100, 12, 12,(char*)"Hello World");      					 tftlcd_dev.forecolor = LCD_BLUE;memset(buf, 0, sizeof(buf));	while(1){if(t==100)	//1s时间到了{t=0;printf("will be running %d s......\\r", i);lcd_fill(50, 90, 370, 110, tftlcd_dev.backcolor); /* 清屏 */sprintf(buf, "will be running %ds......", i);lcd_show_string(50, 90, 300, 16, 16, buf); i--;if(i < 0)break;}key = key_getvalue();if(key == KEY0_VALUE){rtcdate.year = 2023;rtcdate.month = 4;rtcdate.day = 8;rtcdate.hour = 22;rtcdate.minute = 38;rtcdate.second = 0;rtc_setdatetime(&rtcdate); /* 初始化时间和日期 */printf("\\r\\n RTC Init finish\\r\\n");break;}delay_ms(10);t++;}tftlcd_dev.forecolor = LCD_RED;lcd_fill(50, 90, 370, 110, tftlcd_dev.backcolor); /* 清屏 */lcd_show_string(50, 90, 200, 16, 16, (char*)"Current Time:");  			/* 显示字符串 */tftlcd_dev.forecolor = LCD_BLUE;while(1)					{	rtc_getdatetime(&rtcdate);sprintf(buf,"%d/%d/%d %d:%d:%d",rtcdate.year, rtcdate.month, rtcdate.day, rtcdate.hour, rtcdate.minute, rtcdate.second);lcd_fill(50,110, 300,130, tftlcd_dev.backcolor);lcd_show_string(50, 110, 250, 16, 16,(char*)buf);  /* 显示字符串 */state = !state;led_switch(LED0,state);delay_ms(1000);	/* 延时一秒 */}return 0;}