一、RTC简介
RTC(Real Time Clock)即实时时钟,它是一个可以为系统提供精确的时间基准的元器件,RTC一般采用精度较高的晶振作为时钟源,有些RTC为了在主电源掉电时还可以工作,需要外加电池供电
二、Exynos4412下的 RTC控制器
它支持BCD Number,以BDC码的格式存储年月日、星期、时分秒。
BCD码举例: 12 用二进制表示是 1100 用BCD码表示就是 0001 0010
支持闰年(能判断闰年)
支持闹铃功能
支持滴答计数器,
支持独立电源引脚(RTCVDD)
滴答定时器还支持实时操作系统工作
这些东西32用的比较多,4412一般只用它来获取时间
左边是一个32.768KHz的时钟,经过一个2^15的分频器(32768),就获得了一个1Hz的时钟,来给秒使用
三、RTC寄存器详解
INTP:中断挂起寄存器
RTCCON: RTC的控制寄存器,用于控制相关功能
RTCALM:用于控制闹铃相关的功能
以BCD开头的寄存器:是用于存储RTC产生的时间的(以BCD码的形式进行存储的),读取和修改时,也必须以BCD码的形式进行读写。
以ALM开头的寄存器:用于设置一个预期时间,当预期时间与实际时间(以BCD开头的寄存器存储的)相等时,RTC寄存器就会产生一个中断信号,实现闹铃功能。
BCDTEAR寄存器有三块,用BCD码分别表示百位十位和个位,千位默认是2。
例如:2023年用BCD码表示就是000000100011
BCDMON寄存器:月的十位只能是0和1,所以只占一位
BCDDATY寄存器:日的十位只能是0-3,最大的3的BCD码为11,所以只占2位。
BCDDATYWEEK寄存器:星期只能是是0-7,最大的7的BCD码为111,所以只占3位。
对于RTCCON这个寄存器我们只需要关注他的第0位就行,其它位使用默认设置。
这其实就是一把锁,只有把锁打开了我们才能修改时间,这是为了防止有些野指针,把内容修改了。
四、RTC编程
由于板子并没有备用电源,所以我们要先校准一下时间:
C语言只支持八进制、十进制、十六进制,由于BCD转换成10进制看起来怪怪的,所以我们要写一个转化函数。或者我们可以把BCD码转换成十六进制,因为它们都是4位二进制对应1位。
如果要把年写成2023,即RTC.BCDYEAR = 0x023;
#include "exynos_4412.h"int main()
{/*使能RTC控制*/RTCCON = RTCCON | 1;/*校准时间信息*/RTC.BCDYEAR = 0x023;RTC.BCDMON = 0x12;RTC.BCDDAY = 0x7; //手册中BCDDAY和BCDWEEK地址写反了,所以这里需要反着写RTC.BCDWEEK = 0x31;RTC.BCDHOUR = 0x23;RTC.BCDMIN = 0x59;RTC.BCDSEC = 0x50;/*禁止RTC控制*/RTCCON = RTCCON & (~(1));while(1){printf("20%x-%x-%x %x %x:%x:%x\n", RTC.BCDYEAR, RTC.BCDMON, RTC.BCDWEEK, RTC.BCDDAY, RTC.BCDHOUR, RTC.BCDMIN, RTC.BCDSEC);}return 0;
}
但是程序跳动的太快了,所以我们改一下让他一秒钟打印一次
#include "exynos_4412.h"int main()
{unsigned int OldSec = 0, NewSec = 0;/*使能RTC控制*/RTCCON = RTCCON | 1;/*校准时间信息*/RTC.BCDYEAR = 0x023;RTC.BCDMON = 0x12;RTC.BCDDAY = 0x7; //手册中BCDDAY和BCDWEEK地址写反了,所以这里需要反着写RTC.BCDWEEK = 0x31;RTC.BCDHOUR = 0x23;RTC.BCDMIN = 0x59;RTC.BCDSEC = 0x50;/*禁止RTC控制*/RTCCON = RTCCON & (~(1));while(1){NewSec = RTC.BCDSEC;if(OldSec != NewSec){printf("20%x-%x-%x %x %x:%x:%x\n", RTC.BCDYEAR, RTC.BCDMON, RTC.BCDWEEK, RTC.BCDDAY, RTC.BCDHOUR, RTC.BCDMIN, RTC.BCDSEC);OldSec = NewSec;}}return 0;
}
五、作业
1.编程实现通过LED状态显示当前电压范围,并打印产生低压警报时的时间
注:
电压在1501mv~1800mv时,LED2、LED3、LED4、LED5点亮
电压在1001mv~1500mv时,LED2、LED3、LED4点亮
电压在501mv~1000mv时,LED2、LED3点亮
电压在0mv~500mv时,LED2闪烁,且每隔一秒钟向终端打印一次当前的电压值及当前的时间
#include "exynos_4412.h"void Delay(unsigned int Time)
{while(Time--);
}int main()
{unsigned int AdcValue;unsigned int OldSec = 0, NewSec = 0;/*LED2*/GPX2.CON = GPX2.CON | (0x1 << 28);/*LED3*/GPX1.CON = GPX1.CON | 0x1;/*LED4*/GPF3.CON = GPF3.CON | (0x1 << 16);/*LED5*/GPF3.CON = GPF3.CON | (0x1 << 20);/*将ADC的精度设置为12bit*/ADCCON = ADCCON | (1 << 16);/*使能ADC的分频器*/ADCCON = ADCCON | (1 << 14);/*设置ADC的分频 ADC时钟频率=PLCK/(19+1)=5MHz,ADC的转换频率=5MHz/5=1MHz*/ADCCON = ADCCON & (~(0xFF << 6)) | (19 << 6);/*关闭待机模式,使能正常模式*/ADCCON = ADCCON & (~(1 << 2));/*关闭通过读使能AD转换*/ADCCON = ADCCON & (~(1 << 1));/*选择转换通道 3通道*/ADCMUX = 3;/*使能RTC控制*/RTCCON = RTCCON | 1;/*校准时间信息*/RTC.BCDYEAR = 0x023;RTC.BCDMON = 0x12;RTC.BCDDAY = 0x7; //手册中BCDDAY和BCDWEEK地址写反了,所以这里需要反着写RTC.BCDWEEK = 0x31;RTC.BCDHOUR = 0x23;RTC.BCDMIN = 0x59;RTC.BCDSEC = 0x50;/*禁止RTC控制*/RTCCON = RTCCON & (~(1));while(1){/*开始转换*/ADCCON = ADCCON | 1;/*等待转换完成*/while(!(ADCCON & (1 << 15)));/*读取转换结果*/AdcValue = ADCDAT & 0xFFF;/*将结果转换成实际的电压值 =AdcValue*(15800/4096) mv*/AdcValue =AdcValue * 0.44;/*打印转换结果*/printf("AdcValue = %dmv\n", AdcValue);if(AdcValue > 1501){GPX2.DAT = GPX2.DAT | (1 << 7);GPX1.DAT = GPX1.DAT | 1;GPF3.DAT = GPF3.DAT | (1 << 4);GPF3.DAT = GPF3.DAT | (1 << 5);}else if(AdcValue > 1001){GPX2.DAT = GPX2.DAT | (1 << 7);GPX1.DAT = GPX1.DAT | 1;GPF3.DAT = GPF3.DAT | (1 << 4);}else if(AdcValue > 501){GPX2.DAT = GPX2.DAT | (1 << 7);GPX1.DAT = GPX1.DAT | 1;}else{NewSec = RTC.BCDSEC;GPX2.DAT = GPX2.DAT | (1 << 7);Delay(1000000);GPX2.DAT = GPX2.DAT & (~(1 << 7));if(OldSec != NewSec){printf("20%x-%x-%x %x %x:%x:%x\n", RTC.BCDYEAR, RTC.BCDMON, RTC.BCDWEEK, RTC.BCDDAY, RTC.BCDHOUR, RTC.BCDMIN, RTC.BCDSEC);OldSec = NewSec;}}}return 0;
}