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

ARM 实时时钟 RTC

一、何为实时时钟

(1) real time clock,真实时间,就是所谓的xx年x月x日x时x分x秒星期x.
(2) RTC是 SoC 中一个内部外设,RTC 有自己独立的晶振提供 RTC 时钟源(32.768KHz),内部有一些寄存器用来记录时间(年月日时分秒星期)。一般情况下为了在系统关机时时间仍然在走,还会给 RTC 提供一个电池供电。


二、S5PV210 实时时钟的结构框图

(1) 时间寄存器7个
(2) 闹钟发生器
在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


三、闹钟发生器

(1) 可以定闹钟时间,到时间会产生 RTC alarm interrupt,通知系统闹钟定时到了。
(2) 闹钟定时是定的时间点,而 timer 定时是定的时间段。


四、S5PV210 实时时钟的主要寄存器

在这里插入图片描述

(1) INTP 中断挂起寄存器
在这里插入图片描述


(2) RTCCON RTC控制寄存器
在这里插入图片描述

在这里插入图片描述


(3) RTCALM ALMxxx 闹钟功能有关的寄存器

在这里插入图片描述


在这里插入图片描述


(4) BCDxxx 时间寄存器
在这里插入图片描述


在这里插入图片描述


五、BCD码

(1 )RTC 中所有的时间(年月日时分秒星期,包括闹钟)都是用 BCD 码编码的。
(2) BCD 码本质上是对数字的一种编码。用来解决这种问题:由 56 得到 0x56(或者反过来)。也就是说我们希望十进制的56,可以被编码成 56(这里的 56 不是十进制 56,而是两个数字 5 和 6 ).
(3) BCD 码的作用在于可以将十进制数拆成组成这个十进制数的各个数字的编码,变成编码后就没有位数的限制了。譬如我有一个很大的数123456789123456789,如果这个数纯粹当数字肯定超出了 int 的范围,计算机无法直接处理。要想让计算机处理这个数,计算机首先得能表达这个数,表达的方式就是先把这个数转成对应的 BCD 码(123456789123456789)
(4) BCD 码在计算机中可以用十六进制的形式来表示。也就是说十进制的56转成BCD码后是56,在计算机中用0x56来表达(暂时存储与运算)。
(5 ) 需要写 2 个函数,一个是 bcd 转十进制,一个是十进制转 bcd。当我们要设置时间时(譬如要设置为 23 分),我们需要将这个 23 转成 0x23 然后再赋值给相应的寄存器 BCDMIN;当我们从寄存器 BCDMIN 中读取一个时间时(譬如读取到的是 0x59),需要将之当作 BCD 码转成十进制再去显示(0x59 当作BCD码就是 59,转成十进制就是 59,所以显示就是 59 分)。


六、RTC编程实战1

1、设置时间与读取显示时间

(1) 为了安全,默认情况下 RTC 读写是禁止的,此时读写 RTC 的时间是不允许的;当我们要更改 RTC 时间时,应该先打开 RTC 的读写开关,然后再进行读写操作,操作完了后立即关闭读写开关。
(2) 读写 RTC 寄存器时,一定要注意 BCD 码和十进制之间的转换。
(3) 年的问题。S5PV210 中做了个设定,BCDYEAR 寄存器存的并不是完整的年数(譬如 2015 年),而是基于 2000 年的偏移量来存储的,譬如 2015 年实际存的就是 15(2015-2000).还有些 RTC 芯片是以 1970 年(貌似)为基点来记录的。

#include "rtc.h"

#define 	RTC_BASE	 (0xE2800000)
#define		rINTP      	 (*((volatile unsigned long *)(RTC_BASE + 0x30)))
#define		rRTCCON    	 (*((volatile unsigned long *)(RTC_BASE + 0x40)))
#define		rTICCNT    	 (*((volatile unsigned long *)(RTC_BASE + 0x44)))
#define		rRTCALM    	 (*((volatile unsigned long *)(RTC_BASE + 0x50)))
#define		rALMSEC    	 (*((volatile unsigned long *)(RTC_BASE + 0x54)))
#define		rALMMIN    	 (*((volatile unsigned long *)(RTC_BASE + 0x58)))
#define		rALMHOUR  	 (*((volatile unsigned long *)(RTC_BASE + 0x5c)))
#define		rALMDATE         (*((volatile unsigned long *)(RTC_BASE + 0x60)))
#define		rALMMON    	 (*((volatile unsigned long *)(RTC_BASE + 0x64)))
#define		rALMYEAR  	 (*((volatile unsigned long *)(RTC_BASE + 0x68)))
#define		rRTCRST          (*((volatile unsigned long *)(RTC_BASE + 0x6c)))
#define		rBCDSEC    	 (*((volatile unsigned long *)(RTC_BASE + 0x70)))
#define		rBCDMIN   	 (*((volatile unsigned long *)(RTC_BASE + 0x74)))
#define		rBCDHOUR         (*((volatile unsigned long *)(RTC_BASE + 0x78)))
#define		rBCDDATE         (*((volatile unsigned long *)(RTC_BASE + 0x7c)))
#define		rBCDDAY          (*((volatile unsigned long *)(RTC_BASE + 0x80)))
#define		rBCDMON          (*((volatile unsigned long *)(RTC_BASE + 0x84)))
#define		rBCDYEAR         (*((volatile unsigned long *)(RTC_BASE + 0x88)))
#define		rCURTICCNT       (*((volatile unsigned long *)(RTC_BASE + 0x90)))
#define		rRTCLVD    	 (*((volatile unsigned long *)(RTC_BASE + 0x94)))

/**********************************************************************************/
#define         BIT_LOCATION_RTCCON_RTCEN_CONTROL       (0b1 << 0)
#define         RTCCON_RTC_CONTROL_ENABLE               (0b1 << 0)
#define         RTCCON_RTC_CONTROL_DISABLE              (0b0 << 0)

//函数功能:把十进制 num 转成 bcd 码,譬如把 56 转成 0x56
static unsigned int num_2_bcd(unsigned int num)
{
    //第一步,把56拆分成 5 和 6
    //第二步,把 5 和 6组合成 0x56
    return  ( ((num / 10) << 4) | (num % 10));
}

//函数功能:把 bcd 码bcd,转成十进制,譬如把 0x56 转成 56
static unsigned int bcd_2_num(unsigned int bcd)
{
    //第一步,把 0x56 拆分成 5 和 6
    //第二步,把 5 和 6 组合成56
    return  ( ((bcd & 0xf0) >> 4) * 10 + (bcd & 0xf));
}

void rtc_set_time(const struct rtc_time *p)
{
   //第一步,打开 RTC 读写开关 
   rRTCCON &= ~(BIT_LOCATION_RTCCON_RTCEN_CONTROL);
   rRTCCON |= (RTCCON_RTC_CONTROL_ENABLE);

   //第二步,写 RTC 时间寄存器
   rBCDYEAR = num_2_bcd(p->year - 2000);
   rBCDMON =  num_2_bcd(p->month);
   rBCDDATE=  num_2_bcd(p->date);
   rBCDHOUR = num_2_bcd(p->hour);
   rBCDMIN =  num_2_bcd(p->minute);
   rBCDSEC =  num_2_bcd(p->second);
   rBCDDAY =  num_2_bcd(p->day);

   //最后一个,关上 RTC 的读写开关
   rRTCCON &= ~(BIT_LOCATION_RTCCON_RTCEN_CONTROL);
   rRTCCON |= (RTCCON_RTC_CONTROL_DISABLE);

}

void rtc_get_time(struct rtc_time *p)
{
   //第一步,打开 RTC 读写开关 
   rRTCCON &= ~(BIT_LOCATION_RTCCON_RTCEN_CONTROL);
   rRTCCON |= (RTCCON_RTC_CONTROL_ENABLE);

   //第二步,读 RTC 时间寄存器
   p->year    = bcd_2_num(rBCDYEAR) + 2000;  
   p->month   = bcd_2_num(rBCDMON);  
   p->date    = bcd_2_num(rBCDDATE);  
   p->hour    = bcd_2_num(rBCDHOUR); 
   p->minute  = bcd_2_num(rBCDMIN); 
   p->second  = bcd_2_num(rBCDSEC); 
   p->day     = bcd_2_num(rBCDDAY); 

   //最后一个,关上 RTC 的读写开关
   rRTCCON &= ~(BIT_LOCATION_RTCCON_RTCEN_CONTROL);
   rRTCCON |= (RTCCON_RTC_CONTROL_DISABLE);
    
}

现象截图:
在这里插入图片描述


2、闹钟实验

#include "rtc.h"
#include "init.h"
#include "stdio.h"

#define 	RTC_BASE	 (0xE2800000)
#define		rINTP      	 (*((volatile unsigned long *)(RTC_BASE + 0x30)))
#define		rRTCCON    	 (*((volatile unsigned long *)(RTC_BASE + 0x40)))
#define		rTICCNT    	 (*((volatile unsigned long *)(RTC_BASE + 0x44)))
#define		rRTCALM    	 (*((volatile unsigned long *)(RTC_BASE + 0x50)))
#define		rALMSEC    	 (*((volatile unsigned long *)(RTC_BASE + 0x54)))
#define		rALMMIN    	 (*((volatile unsigned long *)(RTC_BASE + 0x58)))
#define		rALMHOUR  	 (*((volatile unsigned long *)(RTC_BASE + 0x5c)))
#define		rALMDATE         (*((volatile unsigned long *)(RTC_BASE + 0x60)))
#define		rALMMON    	 (*((volatile unsigned long *)(RTC_BASE + 0x64)))
#define		rALMYEAR  	 (*((volatile unsigned long *)(RTC_BASE + 0x68)))
#define		rRTCRST          (*((volatile unsigned long *)(RTC_BASE + 0x6c)))
#define		rBCDSEC    	 (*((volatile unsigned long *)(RTC_BASE + 0x70)))
#define		rBCDMIN   	 (*((volatile unsigned long *)(RTC_BASE + 0x74)))
#define		rBCDHOUR         (*((volatile unsigned long *)(RTC_BASE + 0x78)))
#define		rBCDDATE         (*((volatile unsigned long *)(RTC_BASE + 0x7c)))
#define		rBCDDAY          (*((volatile unsigned long *)(RTC_BASE + 0x80)))
#define		rBCDMON          (*((volatile unsigned long *)(RTC_BASE + 0x84)))
#define		rBCDYEAR         (*((volatile unsigned long *)(RTC_BASE + 0x88)))
#define		rCURTICCNT       (*((volatile unsigned long *)(RTC_BASE + 0x90)))
#define		rRTCLVD    	 (*((volatile unsigned long *)(RTC_BASE + 0x94)))

/**********************************************************************************/
#define         BIT_LOCATION_RTCCON_RTCEN_CONTROL       (0b1 << 0)
#define         RTCCON_FUNC_RTC_CONTROL_ENABLE          (0b1 << 0)
#define         RTCCON_FUNC_RTC_CONTROL_DISABLE         (0b0 << 0)

/**********************************************************************************/
#define         BIT_LOCATION_RTCALM_ALMEN               (0b1 << 6)
#define         RTCALM_FUNC_ALARM_GLOBAL_DISABLE        (0b0 << 6)
#define         RTCALM_FUNC_ALARM_GLOBAL_ENABLE         (0b1 << 6)

#define         RTCALM_FUNC_YEAR_ALARM_ENABLE           (0b1 << 5)
#define         RTCALM_FUNC_YEAR_ALARM_DISABLE          (0b0 << 5)

#define         RTCALM_FUNC_MONTH_ALARM_ENABLE          (0b1 << 4)
#define         RTCALM_FUNC_MONTH_ALARM_DISABLE         (0b0 << 4)

#define         RTCALM_FUNC_DAY_ALARM_ENABLE            (0b1 << 3)
#define         RTCALM_FUNC_DAY_ALARM_DISABLE           (0b0 << 3)

#define         RTCALM_FUNC_HOUR_ALARM_ENABLE           (0b1 << 2)
#define         RTCALM_FUNC_HOUR_ALARM_DISABLE          (0b0 << 2)

#define         RTCALM_FUNC_MINUTE_ALARM_ENABLE         (0b1 << 1)
#define         RTCALM_FUNC_MINUTE_ALARM_DISABLE        (0b0 << 1)

#define         RTCALM_FUNC_SEC_ALARM_ENABLE            (0b1 << 0)
#define         RTCALM_FUNC_SEC_ALARM_DISABLE           (0b0 << 0)

//You can clear specific bits of INTP register by writing 1’s to the bits that you want to clear regardless of RTCEN value.
#define         BIT_LOCATION_INTP_ALARM_INTERRUPT       (0b1 << 1)
#define         INTP_FUNC_ALARM_INTERRUPT_OCCURRED      (0b1 << 1)

//函数功能:把十进制 num 转成 bcd 码,譬如把 56 转成 0x56
static unsigned int num_2_bcd(unsigned int num)
{
    //第一步,把56拆分成 5 和 6
    //第二步,把 5 和 6组合成 0x56
    return  ( ((num / 10) << 4) | (num % 10));
}

//函数功能:把 bcd 码bcd,转成十进制,譬如把 0x56 转成 56
static unsigned int bcd_2_num(unsigned int bcd)
{
    //第一步,把 0x56 拆分成 5 和 6
    //第二步,把 5 和 6 组合成56
    return  ( ((bcd & 0xf0) >> 4) * 10 + (bcd & 0xf));
}

void rtc_set_time(const struct rtc_time *p)
{
   //第一步,打开 RTC 读写开关 
   rRTCCON &= ~(BIT_LOCATION_RTCCON_RTCEN_CONTROL);
   rRTCCON |= (RTCCON_FUNC_RTC_CONTROL_ENABLE);

   //第二步,写 RTC 时间寄存器
   rBCDYEAR = num_2_bcd(p->year - 2000);
   rBCDMON =  num_2_bcd(p->month);
   rBCDDATE=  num_2_bcd(p->date);
   rBCDHOUR = num_2_bcd(p->hour);
   rBCDMIN =  num_2_bcd(p->minute);
   rBCDSEC =  num_2_bcd(p->second);
   rBCDDAY =  num_2_bcd(p->day);

   //最后一个,关上 RTC 的读写开关
   rRTCCON &= ~(BIT_LOCATION_RTCCON_RTCEN_CONTROL);
   rRTCCON |= (RTCCON_FUNC_RTC_CONTROL_DISABLE);

}

void rtc_get_time(struct rtc_time *p)
{
   //第一步,打开 RTC 读写开关 
   rRTCCON &= ~(BIT_LOCATION_RTCCON_RTCEN_CONTROL);
   rRTCCON |= (RTCCON_FUNC_RTC_CONTROL_ENABLE);

   //第二步,读 RTC 时间寄存器
   p->year    = bcd_2_num(rBCDYEAR) + 2000;  
   p->month   = bcd_2_num(rBCDMON);  
   p->date    = bcd_2_num(rBCDDATE);  
   p->hour    = bcd_2_num(rBCDHOUR); 
   p->minute  = bcd_2_num(rBCDMIN); 
   p->second  = bcd_2_num(rBCDSEC); 
   p->day     = bcd_2_num(rBCDDAY); 

   //最后一个,关上 RTC 的读写开关
   rRTCCON &= ~(BIT_LOCATION_RTCCON_RTCEN_CONTROL);
   rRTCCON |= (RTCCON_FUNC_RTC_CONTROL_DISABLE);
}

//函数功能:实现 秒级别的闹钟
void rtc_set_alarm(void)
{
    rALMSEC = num_2_bcd(31);

    rRTCALM &= ~(BIT_LOCATION_RTCALM_ALMEN);
    rRTCALM |= (RTCALM_FUNC_ALARM_GLOBAL_ENABLE);

    rRTCALM |= (RTCALM_FUNC_SEC_ALARM_ENABLE);
}

void isr_rtc_alarm(void)
{
    static int i = 0;
    printf("rtc alarm, i = %d...\r\n", i++);

    rINTP |= (INTP_FUNC_ALARM_INTERRUPT_OCCURRED);
    intc_clearVectaddr();
}

现象截图:可以看到,精确在 31 秒的时候,进入中断.

在这里插入图片描述


源自朱友鹏老师.

相关文章:

  • 顺义石家庄网站建设/网络seo是什么工作
  • 蔬菜基地做网站合适吗/网络服务器有哪些
  • wordpress iis支持/网络营销的特点主要包括什么
  • 英文站网站源码/口碑营销的优势有哪些
  • 标准物质网站建设模板/怎么优化网站排名
  • PHP网站开发实例教程电子书/查网站关键词工具
  • 1.16中断实验
  • 通讯录小练习:柔性数组和文件操作实现
  • React--》如何在React中创建TypeScript项目并使用?
  • 新入公司 git基本命令使用(二) 小乌龟版
  • ASP.NET Core 3.1系列(30)——Newtonsoft.Json实现JSON的序列化和反序列化
  • 快出数量级的性能是怎样炼成的
  • Linux的基本使用在Linux上部署程序
  • 信号的时域和频域特性的区别到底是什么?
  • svg绘(viewBox viewport preserveAspectRatio)代替png图片等
  • 2022正式结束全年总基调,向2023迈向新征程
  • Go语言并发编程及依赖管理
  • 十五天学会Autodesk Inventor,看完这一系列就够了(一),前言—介绍及区别