当前位置: 首页 > article >正文

38. 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。

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

三、代码

参考:7、I.MX6U参考资料\3、I.MX6ULL SDK包\devices\MCIMX6Y2\drivers

//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(unsigned int seconds, struct rtc_datetime *datetime)
{
    unsigned int x;
    unsigned int  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;
    seconds = rtc_getseconds();
    rtc_convertseconds_to_datetime(seconds, datetime);  
}

//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

//main.c

/**************************************************************
描述     : I.MX6U开发板裸机实验17 RTC实时时钟实验
其他     : 本实验学习如何编写I.MX6U内部的RTC驱动,使用内部RTC可以实现
         一个实时时钟。
**************************************************************/
#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_led.h"
#include "bsp_beep.h"
#include "bsp_key.h"
#include "bsp_int.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();            /* 初始化系统时钟          */
    delay_init();               /* 初始化延时            */
    clk_enable();               /* 使能所有的时钟          */
    led_init();                 /* 初始化led           */
    beep_init();                /* 初始化beep          */
    uart_init();                /* 初始化串口,波特率115200 */
    lcd_init();                 /* 初始化LCD           */
    rtc_init();                 /* 初始化RTC           */

    tftlcd_dev.forecolor = LCD_RED;
    lcd_show_string(50, 10, 400, 24, 24, (char*)"ZERO-IMX6UL RTC TEST");    /* 显示字符串 */
    lcd_show_string(50, 40, 200, 16, 16, (char*)"ATOM@ALIENTEK");  
    lcd_show_string(50, 60, 200, 16, 16, (char*)"2019/3/27");  
    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 = 2018;
            rtcdate.month = 1;
            rtcdate.day = 15;
            rtcdate.hour = 16;
            rtcdate.minute = 23;
            rtcdate.second = 0;
            rtc_setdatetime(&rtcdate); /* 初始化时间和日期 */
            printf("\r\n RTC Init finish\r\n");
            break;
        }
            
        delayms(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);
        delayms(1000);  /* 延时一秒 */
    }
    return 0;
}

http://www.kler.cn/a/533762.html

相关文章:

  • Java基础面试题50题
  • matlab小波交叉功率谱分析源代码
  • CNN的各种知识点(四): 非极大值抑制(Non-Maximum Suppression, NMS)
  • SQL进阶实战技巧:如何构建用户行为转移概率矩阵,深入洞察会话内活动流转?
  • 二维数组 C++ 蓝桥杯
  • 鼠标拖尾特效
  • 大模型RAG优化方案_融合bm25和语义检索
  • 【Kubernetes Pod间通信-第1篇】在单个子网中使用underlay网络实现Pod到Pod的通信
  • RK3588——解决Linux系统触摸屏坐标方向相反问题
  • Java 网络原理 ④-路由选择 || 网段划分
  • UE求职Demo开发日志#22 显示人物信息,完善装备的穿脱
  • 限流策略实战指南:从算法选择到阈值设置,打造高可用系统
  • 算法 贪心算法
  • 计算机网络笔记再战——理解几个经典的协议3
  • C#面试常考随笔13: 泛型的主要约束和次要约束是什么?
  • 前端框架中 HTML 的应用技巧:React、Vue、Angular 深度解析
  • ollama部署deepseek实操记录
  • 多项式曲线局部路径规划及实现(python)
  • 图像特征点提取与匹配
  • Docker技术相关学习三
  • 【HarmonyOS之旅】基于ArkTS开发(三) -> 兼容JS的类Web开发(四) -> 常见组件(一)
  • 因果推断与机器学习—因果表征学习与泛化能力
  • 基于微信小程序的校园水电费管理平台设计与实现
  • 基础I/O
  • windows环境下安装Python3.13.2
  • 笔记day7