I.MX6U-ALPHA开发板(RTC实验)

一、RTC原理详解

​ 1、6U内部自带到了一个RTC外设,确切的说是SRTC。6U和6ULL的RTC内容在SNVS章节。6U的RTC分为LP和HP。LP叫做SRTC,HP是RTC,但是HP的RTC掉电以后数据就丢失了,即使用了纽扣电池也没用。所以必须要使用LP,也就是SRTC。
​ SNVS章节有些是跟加密有关的,需要与NXP签订NDA协议才可以拿到。
​ RTC分为SNVS_LP和SNVS_HP,
​ 如果做产品,建议使用外置RTC芯片,PCF8563。
​ RTC很类似定时器,外接32.768KHz的晶振,然后就开始计时,RTC使用两个寄存器来保存计数值。
​ RTC使用很简单,打开RTC,然后RTC就开始工作,我们要做的就是不断地读取RTC计数寄存器,获取时间值,或者向RTC计数器写入时间值,也就是调整时间。
​ SNVS_HPCOMR的bit31置1,表示所有的软件都可以访问SNVS所有寄存器。Bit8也是和安全有关的,我们置1,也可以不置1.
​ SNVS_LPCR寄存器,bit0置1,开始SRTC功能。
​ SNVS_LPSRTCMR的bit14:0为RTC计数寄存器的高15位
​ SNVS_LPSRTCLR是低32为RTC计数器,与LPSRTCMR共同组成了SRTC计数器,,每1秒数据加1。
​ 6U的RTC模式从1970年1月1日0时0点0分0秒。

二、时间乱码的问题

​ 1、问题
​ 当我们按照6U的参考手册编写代码,读取SRTC的LPSRTCMR和LPSRTCLR获取时间值的时候,发现按照手册的说法,时间是错误的。
​ 手册上写的:LPSRTCMR是SRTC的高15bit。LPSRTCLR寄存器是SRTC的低32位。RTC计数器是47bit。

1、问题解决方法
LPSRTCMR作为SRTC计数器的高15位,但是LPSRTCLR寄存器bit31:15作为SRTC计数器的低17位。相当于SRTC的计数器是个32位的。不是47位!

// bsp_rtc.h
#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 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
#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);	
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值