一、RTC

RTC(Real-Time Clock) 实时时钟。

RTC是集成电路,通常称为时钟芯片。在一个嵌入式系统中,通常采用RTC来提供可靠的系统时间,包括时分秒和年月日等,而且要求在系统处于关机状态下它也能正常工作(通常采用后备电池供电)。它的外围也不需要太多的辅助电路,典型的就是只需要一个高精度的32.768kHz 晶体和电阻电容等,并且具有闹钟的功能。。

二、Exynos 4412 RTC

本篇主要以Cortex-A9 soc为例讲解RTC的使用方法。

1. 特性

实时时钟(RTC)单元可以通过备用电池供电,因此,即使系统电源关闭,它也可以继续工作。RTC可以通过STRB/LDRB 指令将8位BCD码数据送至CPU。这些BCD数据包括秒、分、时、日期、星期、月和年。RTC单元通过一个外部的32.768kHz 晶振提供时钟。RTC具有定时报警的功能。

其功能说明如下:

1 -- 时钟数据采用BCD编码。

2 -- 能够对闰年的年月日进行自动处理。

3 -- 具有告警功能,当系统处于关机状态时,能产生警告中断。

4 -- 具有独立的电源输入。

5 -- 提供毫秒级时钟中断,该中断可以用于作为嵌入式操作系统的内核时钟。

2. RTC Block

RTC在Linux中主要实现两种功能,分别是系统掉电后的时间日期维持和时间日期报警(类似定时器)。

1)时间日期维持功能:

主要是由RTC实时时钟控制寄存器RTCCON进行功能的使能控制,由节拍时间计数寄存器TICNT来产生节拍时间中断来实现实时操作系统功能相关的时间和实时同步。其中对时间日期的操作实际上是对BCD码操作,而BCD码则是由一系列的寄存器组成(BCD秒寄存器BCDSEC、BCD分寄存器BCDMIN、BCD小时寄存器BCDHOUR、BCD日期寄存器BCDDATE、BCD日寄存器BCDDAY、BCD月寄存器BCDMON、BCD年寄存器BCDYEAR)。

2)报警功能:

主要由RTC报警控制寄存器RTC ALM进行功能使能控制,并产生报警中断。报警时间日期的设置也是对一系列的寄存器进行操作(报警秒数据寄存器ALMSEC、报警分钟数据寄存器ALMMIN、报警小时数据寄存器ALMHOUR、报警日期数据寄存器ALMDATE、报警月数据寄存器ALMMON、报警年数据寄存器ALMYEAR)。

3)闰年发生器

可以根据BCDDAY、BCDMON和BCDEEAR的值自动计算闰年。

3. 备用电池

备用电池可以驱动RTC逻辑。备用电池通过RTCVDD引脚向RTC块,即使系统电源关闭。如果系统关闭,您应该阻止CPU和RTC逻辑。为了减少功耗,备用电池单独驱动振荡电路和BCD计数器。

4. Alarm【报警】 功能

RTC在断电模式或正常运行模式都可以在执行的时间产生一个ALARM_INT 和ALARM_WK信号。在正常工作模式下,它会产生ALARM_INT。在断电模式下,它会ALARM_WK以及ALARM_INT信号。RTC报警寄存器(RTCALM)确定报警启用/禁用状态和报警时间设置的条件。

5. 晶振

32.768 kHz X-Tal Connection Example

晶振时钟频率 32.768 kHz。

  1. XT_RTC_I 32.768 kHz RTC振荡器时钟输入
  2. XT_RTC_O 32.768 kHz RTC振荡器时钟输出
  3. XRTCCLKO 32.768 kHz RTC振荡器时钟输出,此信号默认关闭。可以通过设置寄存器RTCCON的CLKOUTEN字段为1来启用它。

引脚连接图:

由电路图可知,只连接了RTC振荡器时钟输入引脚XT_RTC_I 。

三、寄存器

1. RTC寄存器组:

2. INTP

设置对应的bit为1就可以清除中断。

3. RTCCON

RTCCON 描述 复位值
保留 [31:10] 保留 0
CLKOUTEN [9] 使能RTC通过XRTCCLKO输出 0 disable 1 enbale 0
TICEN [8] 嘀嗒计时器 0 = 禁止 1 = 使能 0
TICCKSEL [7:4] 嘀嗒计时器子时钟源选择 4'b0000 = 32768 Hz 4'b0001 = 16384 Hz 4'b0010 = 8192 Hz 4'b0011 = 4096 Hz 4'b0100 = 2048 Hz 4'b0101 =1024 Hz 4'b0110 =512 Hz 4'b0111 =256 Hz 4'b1000 =128 Hz 4'b1001 =64 Hz 4'b1010 =32 Hz 4'b1011 =16 Hz 4'b1100 =8 Hz 4'b1101 =4 Hz 4'b1110 =2 Hz 4'b1111 =1 Hz 4'b0000
CLKRST [3] RTC时钟计数复位 0 = 不复位 1 = 复位 0
CNTSEL [2] BCD计数选择 0 = 分配 BCD 计数 1 = 保留 0
CLKSEL [1] BCD 时钟选择 0 = XTAL 1/2 divided clock 1 = 保留(XTAL 供频) 0
RTCEN [0] RTC控制使能 0 = 禁止 1 = 使能 0
  1. RTCCON寄存器由10位组成,如控制BCD SEL读/写启用的CTLEN,

    CNTSEL、CLKRST、TICKSEL、TICEN用于测试,CLKOUTEN用于RTC时钟输出控制。
  2. CTLEN位控制CPU和RTC之间的所有接口。因此,您应该在RTC控件中将其设置为“1”,在系统重置后启用数据写入的例程。为了防止无意中写入BCD计数器寄存器,应该关闭电源前将CTLEN位清除为0。
  3. CLKRST是215时钟分频器的计数器复位。在设置RTC时钟之前,应重置215时钟分频器以获得精确的RTC操作。

四、RTC的操作

1. 设置时间

举例:

我们要将当前时间设置为 2020年11月11日, 15:24:50

1) 先将RTC控制使能开启,即RTCCON[0]置为1;

2)然后将时间对应的BCD格式数值,设置到应对的寄存器,BCDYEAR 、BCDMON 、BCDDAY 、BCDHOUR 、BCDMIN 、BCDSEC;

3) 将RTCCON[0]置为0,防止误操作修改了时间;

4)如果我们要访问当前时间,可以直接读取寄存器BCDYEAR 、BCDMON 、BCDDAY 、BCDHOUR 、BCDMIN 、BCDSEC。

void rtc_init(void)
{
RTCCON = 1;//使能RTC控制写功能
RTC.BCDYEAR = 0x20;// 2020年11月11日, 15:24:50.以BCD码格式写入
RTC.BCDMON = 0x11;
RTC.BCDDAY = 0x11;
RTC.BCDHOUR = 0x15;
RTC.BCDMIN = 0x24;
RTC.BCDSEC = 0x50;
RTCCON = 0;//关闭RTC控制写功能
}

2. 操作滴答定时器

TICNT

RTC计时器是一个递增计数器,并引发计时中断。TICNT寄存器包含32位目标计数值,并且CURTICCNT寄存器包含32位当前计时计数。如果当前滴答数达到TICNT中指定的目标值时,计时中断发生。

一秒钟计数的次数,由RTCCON[7:4]即TICCKSEL位决定:

因为我们的晶振频率也是32768,为方便计数,所以我们设置RTCCON[7:4]为0,开启滴答计时器需要设置RTCCON[8]位1:

代码如下:

	RTCCON = RTCCON & (~(0xf << 4)) | (1 << 8);
TICCNT = 32768;

3. 操作ALARM闹钟

RTCALM

RTCALM寄存器控制报警功能的启用和报警时间。请注意,RTCALM寄存器在断电模式下将同时生成ALARM_INT和ALARM_WK信号,但在正常模式下仅生成ALARM_INT信号。设置ALMEN[6]为1以产生ALARM_INT和ALARM_WK信号。

举例:

比如我们想每个小时的25分58秒产生一个中断信号,那我们需要设置RTCALM[1]、RTCALM[0]为1,同时设置RTCALM[6]位1以开启alarm功能,然后将BCD格式的时间设置到寄存器ALMSEC、ALMMIN。

代码如下:

	RTCALM.ALM = (1 << 6)|(1 << 0)|(1 << 1);//使能bite:MINEN、SECEN
RTCALM.SEC = 0x58;
RTCALM.MIN = 0x25; //每小时25:58产生一次中断

alarm功能设置闹钟时间寄存器如下:

寄存器操作,采用BCD格式。

五、完整代码实现

滴答计时器和alarm闹钟会产生内部中断信号,所以我们必须给这两个中断信号进行中断相关的初始化,并在中断处理函数中增加相应的处理代码。

中断号

参考datasheet 9.2.2 GIC Interrupt Table

关于中断的初始化的寄存器配置,我们可以参考《11. 从0开始学ARM-基于Exynos4412中断详解、key程序编写》

区别是,key连接在了第一级中断控制器,而rtc的这两个中断则没有。

清中断需要设置的寄存器如下:

滴答计时器清中断:

RTCINTP	 = RTCINTP | (1 << 0);
//清GIC中断标志位
ICDICPR.ICDICPR2 = ICDICPR.ICDICPR2 | (0x1 << 13);
//清cpu中断标志位
CPU0.ICCEOIR = CPU0.ICCEOIR&(~(0x3ff))|irq_num;

alarm计时器清中断:

RTCINTP	 = RTCINTP | (1 << 1);
//清GIC中断标志位
ICDICPR.ICDICPR2 = ICDICPR.ICDICPR2 | (0x1 << 12);
//清cpu中断标志位
CPU0.ICCEOIR = CPU0.ICCEOIR&(~(0x3ff))|irq_num;

滴答计时器中断初始化:

void rtc_tic(void)
{
RTCCON = RTCCON & (~(0xf << 4)) | (1 << 8);
TICCNT = 32768;
ICDDCR = 1; //使能分配器
ICDISER.ICDISER2 = ICDISER.ICDISER2 | (0x1 << 13); //使能相应中断到分配器
ICDIPTR.ICDIPTR19 = ICDIPTR.ICDIPTR19 & (~(0xff << 8))|(0x1 << 8); //选择CPU接口
CPU0.ICCPMR = 255; //中断屏蔽优先级
CPU0.ICCICR = 1; //使能中断到CPU
}

alarm初始化

void rtc_alarm(void)
{
RTCALM.ALM = (1 << 6)|(1 << 0)|(1 << 1);
RTCALM.SEC = 0x58;
RTCALM.MIN = 0x25; //每小时25:58产生一次中断
ICDDCR = 1; //使能分配器
//使能相应中断到分配器
ICDISER.ICDISER2 = ICDISER.ICDISER2 | (0x1 << 12);
//选择CPU接口
ICDIPTR.ICDIPTR19 = ICDIPTR.ICDIPTR19 & (~(0xff << 0))|(0x1 << 0);
CPU0.ICCPMR = 255; //中断屏蔽优先级
CPU0.ICCICR = 1; //使能中断到CPU
}

中断处理函数

void do_irq(void)
{
static int a = 1;
int irq_num;
irq_num = CPU0.ICCIAR&0x3ff; //获取中断号
switch(irq_num)
{
case 57: //按键key
printf("in the irq_handler\n");
//清GPIO中断标志位
EXT_INT41_PEND = EXT_INT41_PEND |((0x1 << 1));
//清GIC中断标志位
ICDICPR.ICDICPR1 = ICDICPR.ICDICPR1 | (0x1 << 25);
break;
case 76:
printf("in the alarm interrupt!\n");
RTCINTP = RTCINTP | (1 << 1);
//清GIC中断标志位
ICDICPR.ICDICPR2 = ICDICPR.ICDICPR2 | (0x1 << 12);
break;
case 77:
printf("in the tic interrupt!\n");
RTCINTP = RTCINTP | (1 << 0);
//清GIC中断标志位
ICDICPR.ICDICPR2 = ICDICPR.ICDICPR2 | (0x1 << 13);
break;
}
//清cpu中断标志位
CPU0.ICCEOIR = CPU0.ICCEOIR&(~(0x3ff))|irq_num;
}

其他代码:

void rtc_init(void)
{
RTCCON = 1;//使能RTC控制写功能
RTC.BCDYEAR = 0x20;// 2020年11月11日, 15:24:50.以BCD码格式写入
RTC.BCDMON = 0x11;
RTC.BCDDAY = 0x11;
RTC.BCDHOUR = 0x15;
RTC.BCDMIN = 0x24;
RTC.BCDSEC = 0x50;
RTCCON = 0;//关闭RTC控制写功能
}
int main (void)
{ rtc_init();
rtc_alarm();
rtc_tic();
//每隔一秒打印以下当前时间
while(1)
{
printf("%x-%x-%x %x:%x:%x\n",RTC.BCDYEAR,
RTC.BCDMON,
RTC.BCDDAY,
RTC.BCDHOUR,
RTC.BCDMIN,RTC.BCDSEC);
delay_ms(1000);
}
}

更多 ARM Linux干货,请关注 一口Linux

13. 从0学ARM-Cortex-A9 RTC裸机程序编写的更多相关文章

  1. 14. 从0学ARM-exynos4412-看门狗裸机程序编写

    看门狗 一.概念 看门狗的简称是WDT(Watch Dog Timer),exynos4412scp中的看门狗定时器(WDT)是一种定时装置. 1. 工作原理 由(一般需要客户编写)软件读写定时器相关 ...

  2. 灵动微电子ARM Cortex M0 MM32F0010 UART1和UART2中断接收数据

    灵动微电子ARM Cortex M0 MM32F0010 UART1和UART2中断接收数据 目录: 1.MM32F0010UART简介 2.MM32F0010UART特性 3.MM32F0010使用 ...

  3. 灵动微电子ARM Cortex M0 MM32F0010 Timer定时器中断定时功能的配置

    灵动微电子ARM Cortex M0 MM32F0010 Timer定时器中断定时功能的配置 目录: 1.Timer1高级定时器Timer3通用定时器Timer14基本定时器简介 2.Timer1高级 ...

  4. 灵动微电子ARM Cortex M0 MM32F0010 GPIO 的配置驱动LED灯

    灵动微电子ARM Cortex M0 MM32F0010 GPIO的配置 目录: 1.前言 2.学习方法简要说明 3.要点提示 4.注意事项 5.MM32F0010系统时钟的配置 6.MM32F001 ...

  5. swddude -- A SWD programmer for ARM Cortex microcontrollers.

    Introducing swddude I love the ARM Cortex-M series of microcontrollers.   The sheer computational po ...

  6. Implementation of Serial Wire JTAG flash programming in ARM Cortex M3 Processors

    Implementation of Serial Wire JTAG flash programming in ARM Cortex M3 Processors The goal of the pro ...

  7. cAdvisor0.24.1+InfluxDB0.13+Grafana4.0.2搭建Docker1.12.3 Swarm集群性能监控平台

    目录 [TOC] 1.基本概念 ​ 既然是对Docker的容器进行监控,我们就不自己单独搭建cAdvisor.InfluxDB.Grarana了,本文中这三个实例,主要以Docker容器方式运行. 本 ...

  8. Ubuntu14.04 64位机上安装OpenCV2.4.13(CUDA8.0)版操作步骤

    Ubuntu14.04 64位机上安装CUDA8.0的操作步骤可以参考http://blog.csdn.net/fengbingchun/article/details/53840684,这里是在已经 ...

  9. 【ARM-Linux开发】ARM7 ARM9 ARM Cortex M3 M4 有什么区别

    ARM7 ARM9 ARM Cortex M3 M4 区别 arm7 arm9 可以类比386和奔腾, 不同代,arm9相比arm7指令集和性能都有所增强,arm7和arm9都有带mmu和无mmu的版 ...

随机推荐

  1. 【Linux】linux中通过date命令获取昨天或明天时间的方法

    date +"%F" 输出格式:2011-12-31 date +"%F %H:%M:%S" 输出格式:2011-12-31 16:29:50 这都是打印出系统 ...

  2. KeepAlive安装以及简单配置

    操作系统:Centos7.3 一.依赖安装 首先安装相关依赖: yum install -y gcc openssl-devel popt-devel yum -y install libnl lib ...

  3. 分布式 ID 生成算法 — SnowFlake

    一.概述 分布式 ID 生成算法的有很多种,Twitter 的 SnowFlake 就是其中经典的一种. SnowFlake 算法生成 ID 的结果是一个 64bit 大小的整数,它的结构如下图: 1 ...

  4. v-model语法糖

    其实v-model就是一个结合了v-bind和v-on的语法糖,实现了双向数据绑定. 举个(栗子):

  5. 与图论的邂逅05:最近公共祖先LCA

    什么是LCA? 祖先链 对于一棵树T,若它的根节点是r,对于任意一个树上的节点x,从r走到x的路径是唯一的(显然),那么这条路径上的点都是并且只有这些点是x的祖先.这些点组成的链(或者说路径)就是x的 ...

  6. torch.optim.SGD()各参数的解释

    看pytorch中文文档摘抄的笔记. class torch.optim.SGD(params, lr=, momentum=0, dampening=0, weight_decay=0, neste ...

  7. 如何创建一个验证请求的API框架

    ​开发一款成功软件的关键是良好的架构设计.优秀的设计不仅允许开发人员轻松地编写新功能,而且还能丝滑的适应各种变化. 好的设计应该关注应用程序的核心,即领域. 不幸的是,这很容易将领域与不属于这一层的职 ...

  8. python 使用函数名的字符串调用函数(4种方法)_black-heart的专栏-CSDN博客 https://blog.csdn.net/mrqingyu/article/details/84403924

    funcs = ['fetch_data_' + i for i in ( 'activities', 'banners', 'server_list')]# from operator import ...

  9. YAMLLoadWarning: calling yaml.load() without Loader=... is deprecated, as the default Loader is unsafe. Please read https://msg.pyyaml.org/load for full details. data = yaml.load(file_data)

    YAMLLoadWarning: calling yaml.load() without Loader=... is deprecated, as the default Loader is unsa ...

  10. Convert a string into an ArrayBuffer

    https://github.com/mdn/dom-examples/blob/master/web-crypto/import-key/spki.js How to convert ArrayBu ...