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 秒的时候,进入中断.

在这里插入图片描述


源自朱友鹏老师.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值