AM437x——RTC裸机

版权声明:本文采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可,欢迎转载,但转载请注明来自hceng blog(www.hceng.cn),并保持转载后文章内容的完整。本人保留所有版权相关权利。 https://blog.csdn.net/hceng_linux/article/details/89839002

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:

  1. Enable the module clock domains (for details on which clock domain, see Section 19.4.2, Integration).
  2. Enable the RTC module using CTRL_REG.RTC_disable.
  3. Enable the 32K clock from PER PLL, if using the internal RTC oscillator.
  4. Write to the kick registers (KICK0R, KICK1R) in the RTC.
  5. Configure the timer in RTCSS for desired application (set time and date, alarm wakeup, and so on).
  6. 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()显示没有设置成功,这时按键复位重启一下,发现这时就设置成功了。 = = ?

源码——>GitHub

4.心得

RTC感觉还是比较重要,能够实现得到实时的时间。
整个过程,稍微需要注意点的就是BCD的转换,容易弄错。

猜你喜欢

转载自blog.csdn.net/hceng_linux/article/details/89839002
RTC