13. 从0学ARM-Cortex-A9 RTC裸机程序编写
一、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。
- XT_RTC_I 32.768 kHz RTC振荡器时钟输入
- XT_RTC_O 32.768 kHz RTC振荡器时钟输出
- 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 |
- RTCCON寄存器由10位组成,如控制BCD SEL读/写启用的CTLEN,
CNTSEL、CLKRST、TICKSEL、TICEN用于测试,CLKOUTEN用于RTC时钟输出控制。 - CTLEN位控制CPU和RTC之间的所有接口。因此,您应该在RTC控件中将其设置为“1”,在系统重置后启用数据写入的例程。为了防止无意中写入BCD计数器寄存器,应该关闭电源前将CTLEN位清除为0。
- 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裸机程序编写的更多相关文章
- 14. 从0学ARM-exynos4412-看门狗裸机程序编写
看门狗 一.概念 看门狗的简称是WDT(Watch Dog Timer),exynos4412scp中的看门狗定时器(WDT)是一种定时装置. 1. 工作原理 由(一般需要客户编写)软件读写定时器相关 ...
- 灵动微电子ARM Cortex M0 MM32F0010 UART1和UART2中断接收数据
灵动微电子ARM Cortex M0 MM32F0010 UART1和UART2中断接收数据 目录: 1.MM32F0010UART简介 2.MM32F0010UART特性 3.MM32F0010使用 ...
- 灵动微电子ARM Cortex M0 MM32F0010 Timer定时器中断定时功能的配置
灵动微电子ARM Cortex M0 MM32F0010 Timer定时器中断定时功能的配置 目录: 1.Timer1高级定时器Timer3通用定时器Timer14基本定时器简介 2.Timer1高级 ...
- 灵动微电子ARM Cortex M0 MM32F0010 GPIO 的配置驱动LED灯
灵动微电子ARM Cortex M0 MM32F0010 GPIO的配置 目录: 1.前言 2.学习方法简要说明 3.要点提示 4.注意事项 5.MM32F0010系统时钟的配置 6.MM32F001 ...
- swddude -- A SWD programmer for ARM Cortex microcontrollers.
Introducing swddude I love the ARM Cortex-M series of microcontrollers. The sheer computational po ...
- 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 ...
- cAdvisor0.24.1+InfluxDB0.13+Grafana4.0.2搭建Docker1.12.3 Swarm集群性能监控平台
目录 [TOC] 1.基本概念 既然是对Docker的容器进行监控,我们就不自己单独搭建cAdvisor.InfluxDB.Grarana了,本文中这三个实例,主要以Docker容器方式运行. 本 ...
- Ubuntu14.04 64位机上安装OpenCV2.4.13(CUDA8.0)版操作步骤
Ubuntu14.04 64位机上安装CUDA8.0的操作步骤可以参考http://blog.csdn.net/fengbingchun/article/details/53840684,这里是在已经 ...
- 【ARM-Linux开发】ARM7 ARM9 ARM Cortex M3 M4 有什么区别
ARM7 ARM9 ARM Cortex M3 M4 区别 arm7 arm9 可以类比386和奔腾, 不同代,arm9相比arm7指令集和性能都有所增强,arm7和arm9都有带mmu和无mmu的版 ...
随机推荐
- MySQL select 子查询的使用
### 子查询 >where 这个值是计算出来的 >本质:`在 where 语句中嵌套一个子查询语句` ```sql /*============== 子查询 ============== ...
- 在IDEA中通过Module管理多个项目
你身边有没有这种顽固的Eclipse忠实用户:IDEA不能一个窗口管理多个项目!太不方便了! 对于一个窗口同时管理多个项目的需求,在我们日常开发时候是经常需要的.尤其当我们在分布式环境下,在一个窗口中 ...
- thinkpad8平板安装win10系统
ThinkPad8 因为是平板电脑,只有一个micro USB接口,常规安装没法使用鼠标或键盘进行输入,所以难倒很多人. 幸好前段时间买了根otg线和3.0usb hub,安装方法记录如下: 准备:U ...
- uni-app开发经验分享十九: uni-app对接微信小程序直播
uni-app对接微信小程序直播 1.登录微信小程序后台-点击>设置->第三方设置->添加直播插件 2.添加直播组件后->点击<详情> 记录这两个参数直播 ...
- 配置Charles 设置手机代理并允许https请求
前言: 在h5开发调试时,为实现手机app访问localhost地址,可以使用ip地址的方式,但一般公司app出于安全考虑,会限制只能访问其自有域名.因此,使用charles代理的方式 步骤 用手机代 ...
- feign使用okHttpClient,调用原理
最近项目中 spring cloud 用到http请求,使用feign,配置okhttp,打算配置一下就直接使用,不过在压测与调优过程中遇到一些没有预测到的问题,附上排查与解析结 yml.pom配置 ...
- (01)-Python3之--字符串操作
1.字符串切片取值 字符串的取值通过索引来读取,从0开始. 取区间值如下:字符串变量名[起始索引:结束索引].包含起始,但不包含结束.例如: str_my = "hello,python!我 ...
- Windows server 2008常用优化设置
1. 如何取消开机按 CTRL+ALT+DEL登录? 控制面板→管理工具→本地安全策略→本地策略→安全选项→交互式登录:无须按CTRL+ALT+DEL→启用. 2. 如何取消关机时出现的关机理由选择项 ...
- Cannot assign requested address问题总结
Cannot assign requested address问题总结 - 简书 https://www.jianshu.com/p/51a953b789a4 python3 server.pyE07 ...
- 从零开始学Java (一)环境配置
1.下载jdk Oracle官网 2.配置环境变量 JAVA_HOME:指明JDK安装路径,如C:\Program Files\Java\jdk1.7.0Path:变量中添加 %JAVA_HOME%\ ...