CSDN仅用于增加百度收录权重,排版未优化,日常不维护。请访问:www.hceng.cn 查看、评论。
本博文对应地址: https://hceng.cn/2017/08/23/AM437x——RTC裸机/#more
AM437x的RTC模块裸机程序。
0.为什么要RTC
在实际开发中,往往需要知道当前的时间,比如现在的2017年8月23日17点02分。
由于设备不使用的时候或者遇到故障时,会出现关机操作,而关机的这段时间长短,设备是不知道的,当再次开机时,时间要么被归零,要么显示为关机时的那个时间。
为了同步到现在的实时时间,我能想到两个解决方案。
1.利用网络,从网络中获取当前时间。
2.利用一个设备关机仍在计时工作的模块,开机后加上关机的这段时间。
第一个方案,需要网络,对于大多数单片机设备是不现实的,附加的成本太高。
第二个方案,就是现在大量使用的RTC,有的集成到SOC里面了,有些仍以外部芯片的形式出现。
在设备关机后,RTC在备用电池(一般是纽扣电池)的支持下,以超低功耗的方式继续默默的工作,直到开机,将时间同步。
这就是RTC存在的主要意义。
1.AM437x的RTC
AM437x的RTC介绍在参考手册19章Timers的第4部分:Real-Time Clock (RTC)
大致的特性有:
- 总计数时间为100年;
- 提供秒、分、小时、星期、日期、月份、年等;
- 数据以Binary-coded-decimal (BCD)表示;
- 时钟源可选择内部或外部;
- 有两个闹钟;
- 有两种中断:时间中断和闹钟中断;
很良心的有个Use Cases:
The following list includes high-level steps to start using the RTC:
- Enable the module clock domains (for details on which clock domain, see Section 19.4.2, Integration).
- Enable the RTC module using CTRL_REG.RTC_disable.
- Enable the 32K clock from PER PLL, if using the internal RTC oscillator.
- Write to the kick registers (KICK0R, KICK1R) in the RTC.
- Configure the timer in RTCSS for desired application (set time and date, alarm wakeup, and so on).
- Start the RTC (in CTRL_REG.STOP_RTC).
基本把配置步骤写得很清楚了。
-
关于时钟和中断:
时钟来自于外部CLKIN32的CLK_32K_RTC或者内部PRCM的CLK_32KHz;
可以产生两种中断:timer_intr_pend和alarm_intr_pend。 -
关于写保护
如图中的三个状态,向KICK0和KICK1写入指定的值,即可去写保护。
2.RTC编程
本次裸机的目的,是想实现以下两个功能:
- 设置和读取RTC的时间;
- 设置和读取RTC闹钟的时间,利用中断打印闹钟;
模块初始化:
包含时钟使能、模块使能、时钟源选择、中断注册、使能中断。
{% codeblock lang:c%}
void rtc_init(void)
{
//1.set performing the System Clock configuration for RTC.
PRCM_CM_RTC_CLKCTRL &= ~(0x3<<0);
PRCM_CM_RTC_CLKCTRL |= (0x01<<1);
PRCM_CM_RTC_CLKSTCTRL &= ~(0x3<<0);
//Wait write is completed.
while(!(PRCM_CM_RTC_CLKSTCTRL & (0x01<<9 | 0x01<<8)));
while(PRCM_CM_RTC_CLKCTRL & (0x03<<16));
//2.Enable the RTC module;
RTCSS->CTRL &= ~(0x01<<6);
//3.Write Project Disable;
RTCSS->KICK0R = (0x83E70B13);
RTCSS->KICK1R = (0x95A4F1E0);
//4.Set clock Source;
//mode1:Internal clock
RTCSS->OSC &= ~(0x01<<3);
RTCSS->OSC |= (0x01<<6);
//mode2:External clock
//RTCSS->OSC &= ~(0x01<<3);
//RTCSS->OSC |= (0x01<<6 | 0x01<<3);
register_irq(RTCALARMINT, alarm_irq);
interrupt_init(RTCALARMINT);
//5.Enable interrupt;
rtc_int_enalbe();
//6.Run.
//rtc_start();
}
{% endcodeblock %}
这里将中断的使能/去能、RTC的启动/停止,分别封装成了函数。
{% codeblock lang:c%}
void rtc_start(void)
{
RTCSS->CTRL |= (0x01<<0);
}
void rtc_stop(void)
{
RTCSS->CTRL &= ~(0x01<<0);
}
static void rtc_int_enalbe(void)
{
RTCSS->INTRS |= (0x01<<3);//RTC_ALARM1
}
static void rtc_int_clear(void)
{
RTCSS->STS |= (0x01<<6);;//RTC_ALARM1
}
{% endcodeblock %}
设置时间和获取时间:
将所有时间参数封装成一个结构体:
{% codeblock lang:c%}
struct rtc_struct {
int year;
int month;
int week;
int day;
int hour;
int minute;
int second;
};
extern struct rtc_struct rtc_time, rtc_alarm;
{% endcodeblock %}
设置时间主要是将值分解成BCD的格式:
{% codeblock lang:c%}
int set_time(struct rtc_struct rtc_time)
{
rtc_stop();
if((rtc_time.year-2000) > 99 || (rtc_time.year-2000) < 0)
goto err;
RTCSS->YEARS = (((rtc_time.year-2000)/10) << 4) | (((rtc_time.year-2000)%10) << 0);
if(rtc_time.month > 12 || rtc_time.month < 0)
goto err;
RTCSS->MONTHS = ((rtc_time.month/10) << 4) | ((rtc_time.month%10) << 0);
if(rtc_time.week > 7 || rtc_time.week < 0)
//goto err;
rtc_time.week = 1;
RTCSS->WEEKS = rtc_time.week;
if(rtc_time.day > 32 || rtc_time.day < 0)
goto err;
RTCSS->DAYS = ((rtc_time.day/10) << 4) | ((rtc_time.day%10) << 0);
if(rtc_time.hour > 23 || rtc_time.hour < 0)
goto err;
RTCSS->HOURS = ((rtc_time.hour/10) << 4) | ((rtc_time.hour%10) << 0);
if(rtc_time.minute > 59 || rtc_time.minute < 0)
goto err;
RTCSS->MINUTES = ((rtc_time.minute/10) << 4) | ((rtc_time.minute%10) << 0);
if(rtc_time.second > 59 || rtc_time.second < 0)
goto err;
RTCSS->SECONDS = ((rtc_time.second/10) << 4) | ((rtc_time.second%10) << 0);
rtc_start();
return 0;
err:
printf(“set_time err.\n\r”);
return 1;
}
struct rtc_struct get_time(void)
{
struct rtc_struct current_time;
current_time.year = (((RTCSS->YEARS & (0x03<<4))>>4)*10 + (RTCSS->YEARS & (0x0F<<0)));
current_time.month = (((RTCSS->MONTHS & (0x07<<4))>>4)*10 + (RTCSS->MONTHS & (0x0F<<0)));
current_time.week = ( RTCSS->WEEKS & (0x0F<<0));
current_time.day = (((RTCSS->DAYS & (0x07<<4))>>4)*10 + (RTCSS->DAYS & (0x0F<<0)));
current_time.hour = (((RTCSS->HOURS & (0x03<<4))>>4)*10 + (RTCSS->HOURS & (0x0F<<0)));
current_time.minute = (((RTCSS->MINUTES & (0x07<<4))>>4)*10 + (RTCSS->MINUTES & (0x0F<<0)));
current_time.second = (((RTCSS->SECONDS & (0x07<<4))>>4)*10 + (RTCSS->SECONDS & (0x0F<<0)));
printf("\n\rcurrent_time is:\n\r\%d-%d-%d %d:%d:%d\n\r",\
current_time.year+2000,current_time.month,current_time.day,current_time.hour,current_time.minute,current_time.second);
return current_time;
}
{% endcodeblock %}
设置闹钟和获取闹钟:
{% codeblock lang:c%}
int set_alarm(struct rtc_struct alarm_time)
{
if((alarm_time.year-2000) > 99 || (alarm_time.year-2000) < 0)
goto err;
RTCSS->ALARM_YEARS = (((alarm_time.year-2000)/10) << 4) | (((alarm_time.year-2000)%10) << 0);
if(alarm_time.month > 12 || alarm_time.month < 0)
goto err;
RTCSS->ALARM_MONTHS = ((alarm_time.month/10) << 4) | ((alarm_time.month%10) << 0);
if(alarm_time.day > 32 || alarm_time.day < 0)
goto err;
RTCSS->ALARM_DAYS = ((alarm_time.day/10) << 4) | ((alarm_time.day%10) << 0);
if(alarm_time.hour > 23 || alarm_time.hour < 0)
goto err;
RTCSS->ALARM_HOURS = ((alarm_time.hour/10) << 4) | ((alarm_time.hour%10) << 0);
if(alarm_time.minute > 59 || alarm_time.minute < 0)
goto err;
RTCSS->ALARM_MINUTES = ((alarm_time.minute/10) << 4) | ((alarm_time.minute%10) << 0);
if(alarm_time.second > 59 || alarm_time.second < 0)
goto err;
RTCSS->ALARM_SECONDS = ((alarm_time.second/10) << 4) | ((alarm_time.second%10) << 0);
return 0;
err:
printf("set_alarm err.\n\r");
return 1;
}
struct rtc_struct get_alarm(void)
{
struct rtc_struct alarm_time;
alarm_time.year = (((RTCSS->ALARM_YEARS & (0x03<<4))>>4)*10 + (RTCSS->ALARM_YEARS & (0x0F<<0)));
alarm_time.month = (((RTCSS->ALARM_MONTHS & (0x07<<4))>>4)*10 + (RTCSS->ALARM_MONTHS & (0x0F<<0)));
alarm_time.day = (((RTCSS->ALARM_DAYS & (0x07<<4))>>4)*10 + (RTCSS->ALARM_DAYS & (0x0F<<0)));
alarm_time.hour = (((RTCSS->ALARM_HOURS & (0x03<<4))>>4)*10 + (RTCSS->ALARM_HOURS & (0x0F<<0)));
alarm_time.minute = (((RTCSS->ALARM_MINUTES & (0x07<<4))>>4)*10 + (RTCSS->ALARM_MINUTES & (0x0F<<0)));
alarm_time.second = (((RTCSS->ALARM_SECONDS & (0x07<<4))>>4)*10 + (RTCSS->ALARM_SECONDS & (0x0F<<0)));
printf("\n\ralarm_time is:\n\r\%d-%d-%d %d:%d:%d\n\r",\
alarm_time.year+2000,alarm_time.month,alarm_time.day,alarm_time.hour,alarm_time.minute,alarm_time.second);
return alarm_time;
}
{% endcodeblock %}
中断服务函数
{% codeblock lang:c%}
void alarm_irq(void)
{
rtc_int_clear();
printf("\n\r----------------alarm_irq-----------------\n\r");
}
{% endcodeblock %}
主函数
{% codeblock lang:c%}
int main()
{
rtc_time.year = 2017;
rtc_time.month = 8;
//rtc_time.week = 1;
rtc_time.day = 24;
rtc_time.hour = 10;
rtc_time.minute = 42;
rtc_time.second = 15;
rtc_alarm.year = 2017;
rtc_alarm.month = 8;
rtc_alarm.day = 24;
rtc_alarm.hour = 10;
rtc_alarm.minute = 42;
rtc_alarm.second = 20;
uart_init();
printf("init ok.\n\r");
gic_init();
timer2_init();
rtc_init();
set_time(rtc_time);
set_alarm(rtc_alarm);
printf("=====set time and alarm is:=====\n\r");
get_time();
get_alarm();
printf("================================\n\r");
while(1)
{
delay_ms(1000);
get_time();
}
return 0;
}
{% endcodeblock %}
3.实测与源码
BUG:
上电后,set_alarm()是没问题的,set_time()显示没有设置成功,这时按键复位重启一下,发现这时就设置成功了。 = = ?
4.心得
RTC感觉还是比较重要,能够实现得到实时的时间。
整个过程,稍微需要注意点的就是BCD的转换,容易弄错。