ARM 实时时钟 RTC

news2024/11/26 0:46:22

一、何为实时时钟

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

在这里插入图片描述


源自朱友鹏老师.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/167485.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序登陆,后端接口实现 - springboot

登录流程 1、通过调用wx.login获取登录凭证&#xff08;code&#xff09; uni-app通过调用uni.login 2、前端将code提交给服务器&#xff0c;springboot访问 auth.code2Session&#xff0c;使用 code 换取 openid、unionid、session_key 等信息。 3、完成登录操作&#xff0…

数学计算-C语言实现

任务描述 计算如下公式的值: 其中π=3.1415926 本关知识 C语言常用数学函数及其用法 在使用C语言数学函数时候,应该在该源文件中使用以下命令行包含库文件: #include <math.h> 或 #include "math.h" 本题中用到的C语言数学函数如下: abs函数: 求整型…

Pytorch模型自定义数据集训练流程

文章目录Pytorch模型自定义数据集训练流程1、任务描述2、导入各种需要用到的包3、分割数据集4、将数据转成pytorch标准的DataLoader输入格式5、导入预训练模型&#xff0c;并修改分类层6、开始模型训练7、利用训好的模型做预测Pytorch模型自定义数据集训练流程 我们以kaggle竞…

交互与前端20 APIFunc.DataBase监控

说明 APIFunc.DataBase的第一版有一个监控一直在做agg,造成数据库的无谓消耗,所以一定得修补。在修补的同时,做了一些主要的修改: 1 【自增ID】给Mongo的In和Out增加了数据的自动编号和随机数生成。2 【使用缓存】通过Redis缓存,极大的的减轻了Mongo(主库)的负担这样,使得…

Kruskal重构树学习笔记(C++)

Kruskal重构树学习笔记 提示&#xff1a; 学习Kruskal重构树之前建议先了解一下Kruskal算法&#xff0c;虽然不了解这个影响不会很大 但一定要了解一下并查集的算法 接下来如果想要应用Kruskal重构树&#xff0c;一定要了解一下LCA算法 什么是Kruskal重构树 这里先简单说…

exec函数族详解

文章目录exec介绍exec族execl函数execlp函数execv函数exec介绍 通过命令查看帮助&#xff1a;man 3 exec exec 函数族的作用是根据指定的文件名找到可执行文件&#xff0c;并用它来取代调用进程的内容&#xff0c;换句话说&#xff0c;就是在调用进程内部执行一个可执行文件。…

基于多线程版本的定时器

定时器 1)咱们前面学习过的阻塞队列&#xff0c;相比于普通的队列线程安全&#xff0c;相比于普通的队列起到一个更好的阻塞效果 2)虽然使用阻塞队列&#xff0c;可以达到销峰填谷这样的一个效果&#xff0c;但是峰值中有大量的数据涌入到队列中&#xff0c;如果后续的服务器消…

教程:Flutter 和 Rust混合编程,使用flutter_rust_bridge自动生成ffi代码

实践环境&#xff1a;Arch Linuxflutter_rust_bridge官方文档Flutter环境配置教程 | Rust环境配置教程记录使用flutter_rust_bridge遇到的一些坑。假设已经我们配置了Fluuter与Rust环境现在直接使用flutter_rust_bridge模板创建自己的项目运行&#xff1a;git clone https://gi…

W13Scan 扫描器挖掘漏洞实践

一、背景 这段时间总想捣鼓扫描器&#xff0c;发现自己的一些想法很多前辈已经做了东西&#xff0c;让我有点小沮丧同时也有点小兴奋&#xff0c;说明思路是对的&#xff0c;我准备站在巨人的肩膀去二次开发&#xff0c;加入一些自己的想法&#xff0c;从freebuf中看到W13Scan…

进程调度模块

目录 1.进程介绍 2.进程调度 2.1.进程状态 2.2.进程调度函数 ---schedule 2.3.进程切换函数 ---switch_to&#xff08;&#xff09; 1.进程介绍 在进程模块里面&#xff0c;我们知道了进程就是一个task_struct的结构体&#xff0c;里面含有进程的各种信息。进程存放在进程…

AppScan被动手动探索扫描

系列文章 AppScan介绍和安装 AppScan 扫描web应用程序 第三节-AppScan被动手动探索扫描 被动式扫描&#xff1a;浏览器代理到AppScan&#xff0c;然后进行手工操作&#xff0c;探索产生出的流量给AppScan进行扫描。 他的优点是&#xff1a;扫描足够精准&#xff0c;覆盖率更…

注册中心和负载均衡(黑马SpringCloud笔记)

注册中心和负载均衡 目录注册中心和负载均衡一、服务远程调用1. RestTemplate2. 服务调用关系3. 远程调用的问题二、注册中心1. Eureka注册中心1.1 搭建Eureka注册中心1.2 服务注册1.3 服务拉取1.4 小结2. nacos注册中心2.1Nacos搭建2.2 服务注册2.3 服务拉取2.4 服务分级存储模…

虹科新闻 | 虹科与丹麦Eupry正式建立合作伙伴关系

近期&#xff0c;虹科与丹麦Eupry正式建立合作伙伴关系。未来&#xff0c;虹科与Eupry将共同关注最具创新性和稳定性的解决方案&#xff0c;为客户提供温度记录仪、温湿度记录仪、Mapping温度分布验证服务、以及基于云的温湿度自动监测系统。 虹科非常高兴欢迎并宣布我们的新合…

【Linux】基础:进程信号

【Linux】基础&#xff1a;进程信号 摘要&#xff1a;本文将会从生活实际出发&#xff0c;由此掌握进程信号的学习过程&#xff0c;分别为信号的产生、信号的传输、信号的保存和信号的处理&#xff0c;最后再补充学习信号后方便理解的其他概念。 文章目录【Linux】基础&#xf…

echarts柱状图值为0时不显示以及柱状图百分比展示

echarts柱状图值为0时不显示以及柱状图百分比展示 1.效果展示 2.代码 <template><div id"container"><div id"main"></div></div> </template> <script>import * as echarts from echarts import * as lodash…

(JVM)浅堆深堆与内存泄露

​浅堆深堆与内存泄露 1. 浅堆&#xff08;Shallow Heap&#xff09; 浅堆是指一个对象所消耗的内存。在 32 位系统中&#xff0c;一个对象引用会占据 4 个字节&#xff0c;一个 int 类型会占据 4 个字节&#xff0c;long 型变量会占据 8 个字节&#xff0c;每个对象头需要占用…

01.【Vue】Vue2基础操作

一、Vue Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是&#xff0c;Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff0c;不仅易于上手&#xff0c;还便于与第三方库或既有项目整合。另一方面&…

十五天学会Autodesk Inventor,看完这一系列就够了(七),工程图纸

众所周知&#xff0c;Autocad是一款用于二维绘图、详细绘制、设计文档和基本三维设计&#xff0c;现已经成为国际上广为流行的绘图工具。Autodesk Inventor软件也是美国AutoDesk公司推出的三维可视化实体模拟软件。因为很多人都熟悉Autocad&#xff0c;所以再学习Inventor&…

自动化测试 | 这些常用测试平台,你们公司在用的是哪些呢?

本文节选自霍格沃兹测试学院内部教材 测试管理平台是贯穿测试整个生命周期的工具集合&#xff0c;它主要解决的是测试过程中团队协作的问题。在整个测试过程中&#xff0c;需要对测试用例、Bug、代码、持续集成等等进行管理。下面分别从这四个方面介绍现在比较流行的管理平台。…

Spring入门-SpringAOP详解

文章目录SpringAOP详解1&#xff0c;AOP简介1.1 什么是AOP?1.2 AOP作用1.3 AOP核心概念2&#xff0c;AOP入门案例2.1 需求分析2.2 思路分析2.3 环境准备2.4 AOP实现步骤步骤1:添加依赖步骤2:定义接口与实现类步骤3:定义通知类和通知步骤4:定义切入点步骤5:制作切面步骤6:将通知…