一、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;
}