17_I.MX6ULL_内部RTC

news2025/1/27 12:35:52

目录

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;

}



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/413518.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一、Locust快速 入门

1 . 介绍 Locust 是一种易于使用、可编写脚本且可扩展的性能测试工具。 您可以在常规 Python 代码中定义用户的行为&#xff0c;而不是被困在 UI 或限制性领域特定语言中。 这使得 Locust 可以无限扩展并且对开发人员非常友好。 用普通的旧 Python 编写测试场景 如果您希望…

【华为OD机试】1038 - 学英语

文章目录一、题目&#x1f538;题目描述&#x1f538;输入输出&#x1f538;样例1二、代码参考作者&#xff1a;KJ.JK&#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x…

基于价值认同的需求侧电能共享分布式交易策略(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

惊喜开箱!品牌可以从 Ledger 引领的顶级体验之一中学到什么?

Ledger 是加密货币硬件钱包的领先供应商&#xff0c;它通过进入 The Sandbox 并创建游戏化体验来扩大其教育计划&#xff0c;从而在虚拟世界中掀起波澜。通过在这个令人兴奋的新空间打造自己的品牌&#xff0c;Ledger 正在接触更广泛的受众&#xff0c;并以有趣的方式与人们互动…

回收站清空了怎么恢复?快来get实用方法!

案例&#xff1a;回收站清空了怎么恢复&#xff1f; 【真的栓Q了&#xff0c;我刚点击回收站&#xff0c;不知道按错了什么&#xff0c;回收站被清空了&#xff0c;大家有什么方法可以恢复回收站里的文件吗&#xff1f;请大家给我出出主意吧&#xff01;谢谢啦&#xff01;】 …

不愧是比亚迪!

最近这段时间&#xff0c;因为我自己准备买车嘛&#xff0c;然后先后去试驾了比亚迪汉、小鹏P7i、蔚来ET5、智己LS7这几辆车&#xff0c;接下来想分4篇文章依次给大家分享一下这四个品牌的车试驾体验。比亚迪汉小鹏P7i蔚来ET5这四个品牌总共花了三天时间&#xff0c;也算是比较…

STC89C52定时器的简介

一、序言 针对于STC89C52RC而言&#xff0c;这个芯片内部包含了三个定时器——T0、T1和T2&#xff0c;他们的中断优先级分别是1、3和5。 怎么还有一个定时器2呢&#xff1f;博主也是今天整理这篇博客的时候&#xff0c;翻阅芯片手册才发现的。如果说&#xff0c;我们经常用的…

00后面试华为软件测试工程师,竭尽全力拿到15K。。。。。

不废话&#xff0c;直接重点 一般软件测试的面试分为三轮&#xff1a;笔试&#xff0c;HR面试&#xff0c;技术面试。 前两轮&#xff0c;根据不同企业&#xff0c;或有或无&#xff0c;但最后一个技术面试是企业了解你“行不行”的关键环节&#xff0c;每个企业都会有的。 在…

从C语言到C++(第一章_C++入门_中篇)缺省参数+函数重载+引用

目录 1.缺省参数 1.1缺省参数概念 1.2缺省参数的使用&#xff1a; 1.3缺省参数的分类 1.3.1 全缺省参数 1.3.2 半缺省参数 1.4缺省参数的应用场景 2. 函数重载 2.1函数重载的概念 2.2不支持函数重载的情况 3.引用 3.1引用的概念 3.2引用的特性 3.3引用做参数 3…

C++内存管理(new和delete)

目录 1. new/delete操作内置类型 2. new和delete操作自定义类型 3. operator new与operator delete函数 4 .new和delete的实现原理 1 .内置类型 2 .自定义类型 new的原理 delete的原理 new T[N]的原理 delete[]的原理 5. 定位new表达式(placement-new) 6. malloc/f…

【JavaScript】原生js实现省市区联动效果

&#x1f609;博主&#xff1a;初映CY的前说(前端领域) ,&#x1f4d2;本文核心&#xff1a;用原生js实现省市区联动 【前言】今日在复习省市县三级联动的时候&#xff0c;有点忘了原生的js应该怎么样处理省市县的联动&#xff0c;特此写下来再次复习下 目录⭐实现思路⭐思路转…

Node【六】内置模块 【url模块与queryString】

文章目录&#x1f31f;前言&#x1f31f;url 模块&#x1f31f; URL各部分说明&#x1f31f; 将URL字符串转换为对象&#x1f31f; 将对象格式化为URL字符串&#xff1a;url.format(urlObj)&#x1f31f; URL路径处理&#xff1a;url.resolve(from, to)&#x1f31f; queryStri…

Dapr和Rainbond集成,实现云原生BaaS和模块化微服务开发

背景 Dapr 是一个开源的分布式应用运行时&#xff0c;帮助开发者构建松耦合的分布式应用程序&#xff0c;具有良好的可扩展性和可维护性。Rainbond 是一款企业级的云原生应用管理平台&#xff0c;提供了丰富的功能和工具&#xff0c;方便开发者管理和部署应用。Rainbond 和 Da…

如何通过 kubernetes ingress 或者 istio ingressgateway 来暴露 TCP 的服务

点击上方“程序猿技术大咖”&#xff0c;关注并选择“设为星标”回复“加群”获取入群讨论资格&#xff01;在 kubernetes 或 istio 应用中&#xff0c;一般都是通过 kubernetes ingress 或者 istio ingressgateway 来暴露 HTTP/HTTPS 的服务。但是在实际应用中&#xff0c;还是…

纷享销客张睿:快消数字化新时代,数“智”引领新增长

4月6日&#xff0c;在2023年&#xff08;第八届&#xff09;中国快消品创新大会主论坛上&#xff0c;纷享销客经营副总裁兼快消行业部总经理张睿作为嘉宾出席&#xff0c;并以《快消数字化新时代&#xff0c;数“智”引领新增长》为题发表了演讲。他有20余年的市场营销、销售及…

【蓝桥杯省赛真题37】Scratch冰上滑行 少儿编程scratch编程蓝桥杯省赛比赛真题讲解

目录 scratch冰上滑行 一、题目要求 编程实现 二、案例分析 1、角色分析

JUC源码系列-ReentrantReadWriteLock

继承关系 ReadLock和WriteLock是ReentrantReadWriteLock的两个内部类&#xff0c;Lock的上锁和释放锁都是通过AQS来实现的。 AQS定义了独占模式的acquire()和release()方法&#xff0c;共享模式的acquireShared()和releaseShared()方法。 还定义了抽象方法tryAcquire()、tryA…

男生|女生漫画头像怎么制作,分享3种免费制作方法,不用求人

大家发现没有&#xff0c;最近特别流行卡通漫画头像&#xff01;一些小伙伴们通过处理自己的照片&#xff0c;把照片制作成漫画头像&#xff0c;让照片看起来更有趣。那么&#xff0c;男生、女生漫画头像怎么制作呢&#xff1f;需要用到哪些工具?今天给大家分享3种免费制作漫画…

数据结构之线性表3

我们的目标&#xff1a; 1、了解线性结构的特点 掌握顺序表的定义、查找、插入和删除。 2、掌握链表的定义、创建、查找、插入和删除。 3、能够从时间和空间复杂度的角度比较两种存储结构的不同特点及其适用场合。&#xff08;持续更新&#xff09; 前言 本章节内容主要介绍…

linux服务器怎么搭建网站

linux服务器怎么搭建网站 我是艾西&#xff0c;今天又是和想学习linux系统的小伙伴分享服务器系统操作小知识的时间。 这篇文章艾西会告诉大家怎么用linux系统搭建网站&#xff0c;网站是一个展示页面&#xff0c;用于商业的公告栏、门面以及用户容易记住的点&#xff0c;通过…