首页 > 其他分享 >IMX6ULL裸机-RTC定时器

IMX6ULL裸机-RTC定时器

时间:2023-10-12 17:16:09浏览次数:34  
标签:__ RTC rtc SNVS datetime 裸机 IMX6ULL uint32

1 引入RTC定时器

RTC定时器被叫做实时时钟(real time clock)。 CPU内部有很多定时器,像看门狗WDT,PWM定时器,高精度定时器Timer等等, 只在“启动”即“通电时”运行,断电时停止。当然,如果时钟不能连续跟踪时间,则必须手动设置。那么当关机后就没办法自动计数统计时间了。
定时器的本质就是计数器,有向上计数,也有向下计数。RTC有一个与主机单独分离的电源,如纽扣电池(备用电池),即使主机电源关闭,它也保持计数定时功能。这也是为什么我们手机关机后时间还能保持准确。再比如以前的老诺基亚手机,拆掉电池就时间不准了,因为rtc电源被切断了,无法在计数,RTC定时器的计数器会被清0,需要手动设置当前时间。
RTC一般都是用纽扣电池给外部晶振和电路供电。
!image

2 RTC定时器原理

以IMX6U芯片的RTC定时器为例,I.MX6U 内部也有 个 RTC 模块,但是不叫作“RTC”,而是叫做“SNVS”。
RTC模块结构图如下:
image
SNVS 分为两个子模块:SNVS_HP 和 SNVS_LP,也就是高功耗域(SNVS_HP)和低功耗域(SNVS_LP),这两个域的电源来源如下:

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

系统主电源断电以后 SNVS_HP 也会断电,但是在备用电源支持下,SNVS_LP 是不会断电的,而且 SNVS_LP 是和芯片复位隔离开的,因此 SNVS_LP 相关的寄存器的值会一直保存着, 也就是low Power Domain是不受系统电源影响。
上图各个序号含义如下:
①、VDD_HIGH_IN 是系统(芯片)主电源,这个电源会同时供给给 SNVS_HP 和 SNVS_LP。
②、VDD_SNVS_IN 是纽扣电池供电的电源,这个电源只会供给给 SNVS_LP,保证在系统主电源 VDD_HIGH_IN 掉电以后 SNVS_LP 会继续运行。
③、SNVS_HP 部分。
④、SNVS_LP 部分,此部分有个 SRTC,这个就是要使用的 RTC。
SRTC 需要外界提供一个 32.768KHz 的时钟,I.MX6U-ALPHA 核心板上的 32.768KHz 的晶振就是提供这个时钟的。
image

3 RTC定时器寄存器

SNVS_SRTCMR[14:0]代表SRTC计数器的高15位
SNVS_SRTCLR[31:15]代表SRTC计数器的低17位
注意:是以 1970 年 1 月 1 日0点0分0秒为起点,加上经过的总秒数即可得到现在的时间点。
SNVS_HPCOMR[31], NPSWA_EN位,非特权软件访问控制位,如果非特权软件要访问 SNVS 的话此位必须为 1。
SNVS_LPCR[0], SRTC_ENV位,使能 STC 计数器。

4 RTC裸机源码展示

NXP 官方 SDK 包是针对 I.MX6ULL 编写的,因此文件 MCIMX6Y2.h中的结构体 SNVS_Type 里面的寄存器是不全的,我们需要在其中加入本章实验所需要的寄存器,修改 SNVS_Type 为如下所示:

/*!
 * @addtogroup SNVS_Peripheral_Access_Layer SNVS Peripheral Access Layer
 * @{
 */

/** SNVS - Register Layout Typedef */
/* zuozhongkai 2018/12/13 */
typedef struct {
  __IO uint32_t HPLR;                              /**< SNVS_HP Lock register, offset: 0x0 */
  __IO uint32_t HPCOMR;                            /**< SNVS_HP Command register, offset: 0x4 */
  __IO uint32_t HPCR;                              /**< SNVS_HP Control register, offset: 0x8 */
  __IO uint32_t HPSICR;                              /**< SNVS_HP Control register, offset: 0x8 */
  __IO uint32_t HPSVCR;   
  __IO uint32_t HPSR;   
  __IO uint32_t HPSVSR;
  __IO uint32_t HPHACIVR;
  __IO uint32_t HPHACR;
  __IO uint32_t HPRTCMR;
  __IO uint32_t HPRTCLR;
  __IO uint32_t HPTAMR;
  __IO uint32_t HPTALR;
  __IO uint32_t LPLR;
  __IO uint32_t LPCR;
  __IO uint32_t LPMKCR;
  __IO uint32_t LPSVCR;
  __IO uint32_t LPTGFCR;
  __IO uint32_t LPTDCR;
  __IO uint32_t LPSR;
  __IO uint32_t LPSRTCMR;
  __IO uint32_t LPSRTCLR;
  __IO uint32_t LPTAR;
  __IO uint32_t LPSMCMR;
  __IO uint32_t LPSMCLR;
}SNVS_Type;
bsp_rtc.h
#ifndef _BSP_RTC_H
#define _BSP_RTC_H
/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名	: 	 bsp_rtc.c
作者	   : 左忠凯
版本	   : V1.0
描述	   : RTC驱动头文件。
其他	   : 无
论坛 	   : www.wtmembed.com
日志	   : 初版V1.0 2019/1/3 左忠凯创建
***************************************************************/
#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 int rtc_coverdate_to_seconds(struct rtc_datetime *datetime);
unsigned int rtc_getseconds(void);
void rtc_setdatetime(struct rtc_datetime *datetime);
void rtc_getdatetime(struct rtc_datetime *datetime);
#endif
bsp_rtc.c
/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名	: 	 bsp_rtc.c
作者	   : 左忠凯
版本	   : V1.0
描述	   : RTC驱动文件。
其他	   : 无
论坛 	   : www.wtmembed.com
日志	   : 初版V1.0 2019/1/3 左忠凯创建
***************************************************************/
#include "bsp_rtc.h"
#include "stdio.h"

/* 
 * 描述:初始化RTC
 */
void rtc_init(void)
{
	/*
     * 设置HPCOMR寄存器
     * bit[31] 1 : 允许访问SNVS寄存器,一定要置1
     * bit[8]  1 : 此位置1,需要签署NDA协议才能看到此位的详细说明,
     *             这里不置1也没问题
	 */
	SNVS->HPCOMR |= (1 << 31) | (1 << 8);
	
#if 0
	struct rtc_datetime rtcdate;

	rtcdate.year = 2018U;
    rtcdate.month = 12U;
    rtcdate.day = 13U;
    rtcdate.hour = 14U;
    rtcdate.minute = 52;
    rtcdate.second = 0;
	rtc_setDatetime(&rtcdate); //初始化时间和日期
#endif
	
	rtc_enable();	//使能RTC

}

/*
 * 描述: 开启RTC
 */
void rtc_enable(void)
{
	/*
	 * LPCR寄存器bit0置1,使能RTC
 	 */
	SNVS->LPCR |= 1 << 0;	
	while(!(SNVS->LPCR & 0X01));//等待使能完成
	
}

/*
 * 描述: 关闭RTC
 */
void rtc_disable(void)
{
	/*
	 * LPCR寄存器bit0置0,关闭RTC
 	 */
	SNVS->LPCR &= ~(1 << 0);	
	while(SNVS->LPCR & 0X01);//等待关闭完成
}

/*
 * @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 - 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	: 获取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);	
}
可以看到RTC定时器是以秒为计时单位的,每过1s SRTC计数器的值加1。 首先调用rtc_init初始化并启动,然后调用rtc_setdatetime设定当前日期时间,调用rtc_getdatetime获取当前日期时间,期间会利用rtc_convertseconds_to_datetime把总秒数转换成当前的日期和时间。

标签:__,RTC,rtc,SNVS,datetime,裸机,IMX6ULL,uint32
From: https://www.cnblogs.com/fuzidage/p/17759700.html

相关文章

  • 网易云信4K 8K RTC助力远程医疗的技术实践
     //  编者按:随着近年来国家关于缓解医疗资源分配不均的一系列政策出台,远程医疗作为平衡医疗资源分配的有力手段,目前正处于强劲发展阶段。网易云信运用超高清RTC视频技术助力医疗行业实现了远程高清视频病理分析和手术示教等能力。LiveVideoStackCon2023上海站邀请了来自网易......
  • 如何让裸机的虚拟机上的k8s拥有LoadBalace的能力?
    很久之前就接触k8s了,但是一直没有深入学习。最近一段时间刚好有空,所以开始了复习的路程。我们以一个小项目作为试验。1.看部署的yaml文件  在k8s中,运行kubectlapply-fdemo.yaml之后,会创建service和pod的资源对象,但是如果没有安装第三方插件metalab的话,不会......
  • AbortController创建一个可中断的异步任务执行函数---【已解决】
    1、需求背景使用异步操作(promise)或者多个循环时,遇到不能及时中断操作,回收资源时2、代码/***创建一个可中断的异步任务执行函数。*@param{function}taskFunction-要执行的异步任务函数,接受一个AbortSignal参数用于中断。*@returns{object}包含执行任务和中断......
  • 嵌入式裸机设计思想——时间片轮裸机开发架构+状态机+定时器调度机制
    前言(1)(2)在MCU开发的时候,很多入门者会固执的认为,做项目一定要上实时操作系统。但是真的是这样的吗?(3)我曾经阅读过一位10年嵌入式开发经验的大佬分享的公众号,这位大佬感叹到,其实对于绝大多数时候,MCU开发不需要上操作系统。只要任务分配的合理,百分之九十的项目不上操作系统都是能够跑......
  • VMware ESXi 7.0 Update 3o 下载 - 领先的裸机 Hypervisor (重大更新)
    VMwareESXi7.0Update3o下载-领先的裸机Hypervisor(重大更新)VMwareESXi7.0Update3oStandard&AllCustomImageforESXi7.0U3InstallCD新增了22个服务器机型(Dell、HPE和Lenovo)和多个驱动对vSphereQuickBoot的支持,以及71个功能问题修复,属于”重大......
  • 第8期ThreadX视频教程:应用实战,将裸机工程移植到RTOS的任务划分,驱动和应用层交互,中断DM
    视频教程汇总帖:https://www.armbbs.cn/forum.php?mod=viewthread&tid=110519 这个是我们初学RTOS面临的最直接问题,很多时候,简单的RTOS机制明白了,API也会调用了,就是添加到RTOS后,总感觉那里不对劲,怎么使用才是正确姿势。针对这些问题,本期视频教程,我们ThreadX内核教程穿插一期实......
  • “国产双系统”出炉,RK3568J非对称AMP:Linux+RTOS/裸机
    “非对称AMP”双系统是什么AMP(AsymmetricMulti-Processing),即非对称多处理架构。“非对称AMP”双系统是指多个核心相对独立运行不同的操作系统或裸机应用程序,如Linux+RTOS/裸机,但需一个主核心来控制整个系统以及其它从核心。每个处理器核心相互隔离,拥有属于自己的内存,既可各......
  • 警惕!Smartcoinfx诈骗平台的代操盘陷阱 ——外汇110网
    不知道大家有没有发现,操盘手大多是要给你指定平台的,而“操盘手+黑平台”这一诈骗模式也是最常见的诈骗手段之一,类似的案例我们报道过很多很多。近期,又有汇友被所谓的“操盘手”给坑了,而配套的黑平台则是Smartcoinfx。掉坑Smartcoinfx平台的代操盘陷阱据汇友描述,他此前在网上认识了......
  • WEBRTC回声消除-AECM算法源码解析之参数解析
    一概述 webrtc针对回声问题一共开源了3种回声消除算法,分别为aec,aecm,以及aec3,其中aec是最早期的版本,在后续的更新中aec3的出现代替了aec在webrtc中的地位,而aecm主要是针对计算能力较弱的移动端或是嵌入式设备而开发的,但同时也带来了它自己的劣势;本文主要介绍AECM算法的计......
  • STUN,TURN,ICE,WebRTC
    参考:KavirajanST  : WhatisWebRTCandHowtoSetupSTUN/TURNServerforWebRTCCommunication?AndreyB. :Еnvironment:signaling,STUNandTURNserversMeddane : DemystifyingNATTraversalwithSTUNTURNandICE STUNSTUN的唯一目的是让防火墙后面的设......