一、S3C2440上的RTC

1.1 概述

RTC,英文全称Real Time Clock,中文就是实时时钟,是一个可以为系统提供精确的时间基准的元器件,RTC一般采用精度较高的晶振作为时钟源,有些RTC为了在主电源掉电时还可以工作,需要外加电池供电。 

RTC可以通过使用STRB/LDRB ARM操作发送8位二-十进制交换码(BCD)值数据给CPU。这些数据包括年、月、日、星期、时、分和秒的时间信息。RTC单元工作在外部32.768kHz晶振并且可以执行闹钟功能。

1.2 特性

  • 时钟数据采用BCD编码,时钟数据包括秒、分、时、日、月、年、星期;
  • 32.768K 的晶振提供时钟输入;
  • 可以提供毫秒级的时钟节拍中断,该中断可用于作为嵌入式操作系统的内核时钟;
  • 闹钟功能:闹钟中断或从掉电模式唤醒;
  • 独立电源引脚(RTCVDD);

1.3 RTC方框图

1.3.1 中断

从上图可以看到RTC模块提供了2个中断:

  • INT_RTC:RTC alarm interrupt,即RTC闹钟中断;
  • INT_TICK: RTC Time tick interrupt,即RTC时钟节拍中断;
1.3.2 瑞年发生器

闰年发生器能够基于 BCDDATE、BCDMON 和 BCDYEAR 的数据,从 28、29、30 或 31 中决定哪个是每月的最后日,此模块决定每月最后日时会考虑闰年因素。

1.3.3 读写寄存器

为了写RTC模块中的BCD寄存器,RTCCON寄存器的位[0]必须设置为高。

为了显示,年、月、日、时、分和秒,CPU应该分别读取RTC模块中的 BCDSEC、BCDMIN、BCDHOUR、BCDDAY、BCDDATE、BCDMON和 BCDYEAR 寄存器中的数据。然而可能存在1秒的偏差,因为读取了多个寄存器。

例如,当用户从 BCDYEAR到BCDMIN读取寄存器,其结果假定为2059(年)、12(月)、31(日)、23(时)和 59(分)。当用户读取 BCDSEC寄存器并且值的范围是从 1 到 59(秒),这没有问题,但是如果该值为 0 秒。则年、月、日、时和分可能要变为 2060(年)、1(月)、1(日)、0(时)和 0(分),因为存在着 1 秒的偏差。在这种情况中,如果 BCDSEC为0则用户应该重新读取 BCDYEAR 到 BCDSEC。

1.3.4 备用电池

RTC逻辑可以由备用电池驱动,即使如果系统电源关闭了,则由 RTCVDD引脚供电给RTC模块。当关闭了电源则应该阻塞掉CPU和RTC逻辑的接口,并且备用电池只驱动振荡电路和BCD计数器来最小化功耗。

1.3.5 闹钟功能

RTC在掉电模式中或正常工作模式中的指定时间产生一个闹钟信号;

  • 在正常工作模式中,只激活闹钟中断(INT_RTC)信号;
  • 在掉电模式中,除了INT_RTC 之外还激活电源管理唤醒(PMWKUP)信号;

RTC闹钟寄存器(RTCALM)决定了闹钟使能/禁止状态和闹钟时间设置的条件。

1.3.6 时钟节拍中断

RTC时钟节拍是用于中断请求。TICNT寄存器有一个中断使能位和中断的计数值。当时钟节拍中断发生时计数值达到'0'。中断周期如下:$周期=(n+1)/128秒$

其中:n为时钟节拍计数值,1到127。

1.4 BCD码

BCD(Binary Coded Decimal)码,即二进制编码的十进制数,这种方法是用4位二进制码的组合代表十进制数的0,1,2,3,4,5,6 ,7,8,9 十个数符。

4位二进制数码有16种组合,原则上可任选其中的10种作为代码,分别代表十进制中的0,1,2,3,4,5,6,7,8,9这十个数符。最常用的BCD码称为8421BCD码,8.4.2.1 分别是4位二进数的位取值。

下图为十进制数和8421BCD编码的对应关系表:

十进制 BCD(8421)码
0 0000
1 0001
2 0010
3 0011
4 0100
5 0101
6 0110
7 0111
8 1000
9 1001

二、RTC相关寄存器

2.1 RTC控制寄存器RTCCON

RTCCON寄存器由4 位组成,如控制BCD寄存器读/写使能的RTCEN、CLKSEL、CNTSEL和测试用的CLKRST。
RTCEN位可以控制所有CPU与RTC之间的接口,因此在系统复位后在RTC控制程序中必须设置为1来使能数据的读/写。同样的在掉电前,RTCEN位应该清除为0来预防误写入RTC寄存器中。

寄存器 地址 R/W 描述 复位值
RTCCON

0x57000040(L 小端)

0x57000043(B 大端)

R/W字节 RTC控制寄存器 0x0

寄存器位信息:

RTCCON 描述 初始状态
CLKRST [3]

RTC时钟计数复位

0:不复位   1:复位

0
CNTSEL [2]

BCD计数选择

0:融入BCD计数器

1:保留(分离BCD计数器)

0
CLKSEL [1]

BCD时钟选择

0:XTAL 1/215分频时钟

1:保留(XTAL时钟只用于测试)

0
RTCEN [0]

RTC控制使能

0:禁止    1:使能

注意:只能执行BCD时间计数和读操作

0

注意:所有RTC寄存器必须按自己为单位使用STRB或LDRB指令或char型指针访问;

2.2 时钟节拍计数寄存器TICNT

寄存器 地址 R/W 描述 复位值
TICNT

0x57000044(L 小端)

0x57000047(B 大端)

R/W字节 时钟节拍计数寄存器 0x0

寄存器位信息:

TICNT 描述 初始状态
TICK INT使能 [7]

INT_TICK中断使能

0:禁止  1:使能

0
TICK时钟计数 [6:0]

时钟节拍计数值,1到127

此计数器值内部递减并且用户不能在工作中读取此计数器的值

000000

2.3 RTC闹钟控制寄存器RTCALM

RTCALM寄存器决定了闹钟使能和闹钟时间。请注意RTCALM寄存器在掉电模式中同时通过INT_RTC和PMWKUP产生闹钟信号,但是在正常工作模式中只产生INT_RTC。

寄存器 地址 R/W 描述 复位值
RTCALM

0x57000050(L 小端)

0x57000053(B 大端)

R/W字节 RTC闹钟控制寄存器 0x0

寄存器位信息:

RTCALM 描述 初始状态
保留 [7]

-

0
ALMEN [6]

全局闹钟使能

0:禁止  1:使能

0
YAEREN [5]

年闹钟使能

0:禁止  1:使能

0
MONREN [4]

月闹钟使能

0:禁止  1:使能

0
DATAEN [3] 日闹钟使能

0:禁止  1:使能

 0
HOUREN [2] 时闹钟使能

0:禁止  1:使能

 0
MINEN [1] 分闹钟使能

0:禁止  1:使能

 0
SECEN [0] 秒闹钟使能

0:禁止  1:使能

 0
2.3.1 闹钟秒数据寄存器
寄存器 地址 R/W 描述 复位值
ALMSEC

0x57000054(L 小端)

0x57000057(B 大端)

R/W字节 闹钟秒数据寄存器 0x0

寄存器位信息:

ALMSEC 描述 初始状态
保留 [7]

-

0
SECDATA [6:4]

闹钟秒BCD值

0至5

000
[3:0] 0至9 0000
2.3.2 闹钟分数据寄存器
寄存器 地址 R/W 描述 复位值
ALMMIN

0x57000058(L 小端)

0x5700005B(B 大端)

R/W字节 闹钟分数据寄存器 0x0

寄存器位信息:

ALMMIN 描述 初始状态
保留 [7]

-

0
MINDATA [6:4]

闹钟分BCD值

0至5

000
[3:0] 0至9 0000
2.3.3 闹钟时数据据寄存器
寄存器 地址 R/W 描述 复位值
ALMHOUR

0x5700005C(L 小端)

0x5700005F(B 大端)

R/W字节 闹钟时数据寄存器 0x0

寄存器位信息:

ALMHOUR 描述 初始状态
保留 [7:6]

-

0
HOURDATA [5:4]

闹钟时BCD值

0至2

00
[3:0] 0至9 0000
2.3.4 闹钟日数据寄存器
寄存器 地址 R/W 描述 复位值
ALMDATE

0x57000060(L 小端)

0x57000063(B 大端)

R/W字节 闹钟日数据寄存器 0x01

寄存器位信息:

ALMDATE 描述 初始状态
保留 [7:6]

-

0
DATEDATA [5:4]

闹钟日BCD值

0至3

00
[3:0] 0至9 0001
2.3.5 闹钟月数据寄存器
寄存器 地址 R/W 描述 复位值
ALMMON

0x57000064(L 小端)

0x57000067(B 大端)

R/W字节 闹钟月数据寄存器 0x01

寄存器位信息:

ALMMON 描述 初始状态
保留 [7:5]

-

0
MONDATA [4]

闹钟月BCD值

0至1

00
[3:0] 0至9 0001
2.3.6 闹钟年数据寄存器
寄存器 地址 R/W 描述 复位值
ALMYEAR

0x57000068(L 小端)

0x5700006B(B 大端)

R/W字节 闹钟年数据寄存器 0x0

寄存器位信息:

ALMYEAR 描述 初始状态
YEARDATA [7:0]

闹钟年BCD值

00至99

0x0

2.4 BCD寄存器

2.4.1 BCD秒寄存器
寄存器 地址 R/W 描述 复位值
BCDSEC

0x57000070(L 小端)

0x57000073(B 大端)

R/W字节 BCD秒寄存器 未定义

寄存器位信息:

BCDSEC 描述 初始状态
保留 [7]

-

-
SECDATA [6:4]

秒BCD值

0至5

-
[3:0] 0至9 -
 2.4.2 BCD分寄存器
寄存器 地址 R/W 描述 复位值
BCDMIN

0x57000074(L 小端)

0x57000077(B 大端)

R/W字节 BCD分寄存器 未定义

 寄存器位信息:

BCDMIN 描述 初始状态
保留 [7]

-

-
MINDATA [6:4]

分BCD值

0至5

-
[3:0] 0至9 -

 

2.4.3 BCD时寄存器
寄存器 地址 R/W 描述 复位值
BCDHOUR

0x57000078(L 小端)

0x5700007B(B 大端)

R/W字节 BCD时寄存器 未定义

 寄存器位信息:

BCDHOUR 描述 初始状态
保留 [7:6]

-

-
HOURDATA [5:4]

时BCD值

0至2

-
[3:0] 0至9 -
 2.4.4 BCD日寄存器
寄存器 地址 R/W 描述 复位值
BCDDATE

0x5700007C(L 小端)

0x5700007F(B 大端)

R/W字节 BCD日寄存器 未定义

 寄存器位信息:

BCDDATE 描述 初始状态
保留 [7:6]

-

-
DATEDATA [5:4]

日BCD值

0至3

-
[3:0] 0至9 -
2.4.5 BCD星期寄存器
寄存器 地址 R/W 描述 复位值
BCDDAY

0x57000080(L 小端)

0x57000083(B 大端)

R/W字节 BCD星期寄存器 未定义

寄存器位信息:

BCDDAY 描述 初始状态
保留 [7:3]

-

-
DAYDATA [2:0]

星期BCD值

1至7

-
2.4.6 BCD月寄存器
寄存器 地址 R/W 描述 复位值
BCDMON

0x57000084(L 小端)

0x57000087(B 大端)

R/W字节 BCD月寄存器 未定义

 寄存器位信息:

BCDMON 描述 初始状态
保留 [7:5]

-

-
MONDATA [4]

月BCD值

0至1

-
[3:0] 0至9 -
2.4.7 BCD年寄存器
寄存器 地址 R/W 描述 复位值
BCDYEAR

0x57000088(L 小端)

0x5700008B(B 大端)

R/W字节 BCD年寄存器 未定义

 寄存器位信息:

BCDYEAR 描述 初始状态
YEARDATA [7:0]

年BCD值

00至99

-

三、RTC代码

3.1 RTC初始化步骤

首先我们需要进行校准RTC,通俗的说,就是时间校准:

  • 使能进入RTC模块的PCLK,配置CLKCON时钟控制寄存器位[14]为1,该位默认为1,因此该步骤可以忽略;
  • RTC控制使能,配置RTCCON位[0]为1;
  • 根据当前时间配置BCD寄存器,包括BCDYEAR、BCDMON、BCDDATE、BCDDAY、BCDHOUR、BCDMIN、BCDSEC;
  • RTC控制禁止,配置RTCCON位[0]为0;

接下来,我们就可以通过读取BCD寄存器的值,获取当前时间:

  • RTC控制使能,配置RTCCON位[0]为1;
  • 读取BCDYEAR、BCDMON、BCDDATE、BCDDAY、BCDHOUR、BCDMIN、BCDSEC;
  • RTC控制禁止,配置RTCCON位[0]为0;

3.2 rtc.h

/*****************************************************************************************************************
 *
 *  FileName  : rtc.h
 *  Function  : RTC
 *  Author    : zy
 *
 ****************************************************************************************************************/

#ifndef  __RTC_H__
#define  __RTC_H__


#include  "type.h"
#include  "gpio.h"


/******************************************************************************************************************/

#ifdef  __BIG_ENDIAN
#define     RTCCON          (*(volatile u8 *) 0x57000043)
#define     TICNT           (*(volatile u8 *) 0x57000047)
#define     RTCALM          (*(volatile u8 *) 0x57000053)
#define     ALMSEC          (*(volatile u8 *) 0x57000057)
#define     ALMMIN          (*(volatile u8 *) 0x5700005B)
#define     ALMHOUR         (*(volatile u8 *) 0x5700005F)
#define     ALMDATE         (*(volatile u8 *) 0x57000063)
#define     ALMMON          (*(volatile u8 *) 0x57000067)
#define     ALMYEAR         (*(volatile u8 *) 0x5700006B)
#define     RTCRST          (*(volatile u8 *) 0x5700006F)
#define     BCDSEC          (*(volatile u8 *) 0x57000073)
#define     BCDMIN          (*(volatile u8 *) 0x57000077)
#define     BCDHOUR         (*(volatile u8 *) 0x5700007B)
#define     BCDDATE         (*(volatile u8 *) 0x5700007F)
#define     BCDDAY          (*(volatile u8 *) 0x57000083)
#define     BCDMON          (*(volatile u8 *) 0x57000087)
#define     BCDYEAR         (*(volatile u8 *) 0x5700008B)

#else   /* Little Endian */

/*  RTC控制寄存器 */
#define         RTCCON             (*(volatile u8 *)0x57000040)
/*  时钟节拍计数寄存器 */
#define     TICNT             (*(volatile u8 *)0x57000044)
/*  RTC闹钟控制寄存器 */
#define     RTCALM             (*(volatile u8 *)0x57000050)
/* 闹钟数据寄存器 */
#define        ALMSEC            (*(volatile u8 *)0x57000054)
#define        ALMMIN            (*(volatile u8 *)0x57000058)
#define        ALMHOUR            (*(volatile u8 *)0x5700005C)
#define        ALMDATE            (*(volatile u8 *)0x57000060)
#define        ALMMON            (*(volatile u8 *)0x57000064)
#define        ALMYEAR            (*(volatile u8 *)0x57000068)
/* BCD寄存器 */
#define        BCDSEC            (*(volatile u8 *)0x57000070)
#define        BCDMIN            (*(volatile u8 *)0x57000074)
#define        BCDHOUR            (*(volatile u8 *)0x57000078)
#define        BCDDATE            (*(volatile u8 *)0x5700007C)
#define        BCDDAY            (*(volatile u8 *)0x57000080)
#define        BCDMON            (*(volatile u8 *)0x57000084)
#define        BCDYEAR            (*(volatile u8 *)0x57000088)
#endif


/* 星期 */
typedef enum
{
   Sunday,                  /* 星期日 */
   Monday,                  /* 星期一 */
   Tuesday,                  /* 星期一二 */
   Wednesday,              /* 星期三 */
   Thursday,              /* 星期四 */
   Friday,                  /* 星期五 */
   Saturday                  /* 星期六 */
}WEEK;


/* 日历 */
typedef struct
{
  u16 year;
  u8 month;
  u8 day;
  u8 hour;
  u8 min;
  u8 sec;
  WEEK week;
}calendar;


/* 函数声明 */
extern  void rtc_set(calendar date);      /* 时间校准 */
extern  void rtc_get(calendar *date);     /* 获取RTC时间 */
u8* get_week(WEEK week);                  /* 将星期转换为字符串 */
extern void rtc_tick_init();              /* RTC滴答设置 */
extern void rtc_alarm_init();             /* RTC闹钟设置 */

#endif

3.3 rtc.c

/**************************************************************************
 *
 *  FileName  : rtc.c
 *  Function  : RTC
 *  Author    : zy
 *
 *************************************************************************/
#include "rtc.h"
#include "isr.h"
#include "stdio.h"

/*************************************************************
*
*  Function  : RTC16进制转为10进制
*  Input     : val  需要转换的BCD数
*  Return    :转换完成的10进制值
*
**************************************************************/
u8 rtc_bcd_to_ten(u8 val)
{
    u8 temp = 0;
    temp = (val>>4)*10;
    return (temp + (val&0x0f));
}


/*************************************************************
*
*  Function  : RTC10进制转为16进制
*  Input     : val  需要转换的10进制数
*  Return    :转换完成的BCD值
*
**************************************************************/
u8 rtc_ten_to_bcd(u8 val)
{
  u8 high = val / 10;
  u8 low = val %10 ;
  return (high<<4|low);
}


/*************************************************************
*
*  Function  : 中断源8  RTC 时钟滴答中断
*
**************************************************************/
void TICK_IRQHandler()
{
    calendar current_date;
    rtc_get(&current_date);
    printf("NOWTIME: %04d-%02d-%02d %s %02d:%02d:%02drn",current_date.year,
            current_date.month,
            current_date.day,
            get_week(current_date.week),
            current_date.hour,
            current_date.min,
            current_date.sec);

    /* 清除滴答中断位 */
    SRCPND |= BIT_TICK;
    INTPND |= BIT_TICK;
}


/*************************************************************
*
*  Function  : 中断源30  RTC RTC闹钟中断处理函数
*
**************************************************************/
void RTC_IRQHandler()
{
    printf("alarm intrn");

    /* 清除中断位 */
    SRCPND |= BIT_RTC;
    INTPND |= BIT_RTC;
}



/*************************************************************
*
*  Function  : 校准RTC实时时钟   以2000年1月1日为基址  星期六
*  Input     : date  当前时间
*
**************************************************************/
void rtc_set(calendar date)
{
    /* RTC读写使能,BCD时钟、计数器、无复位 */
    RTCCON = 0x01;

    BCDYEAR = rtc_ten_to_bcd(date.year - 2000);
    BCDMON  = rtc_ten_to_bcd(date.month);
    BCDDATE = rtc_ten_to_bcd(date.day);
    BCDDAY  = rtc_ten_to_bcd(date.week + 1);
    BCDHOUR = rtc_ten_to_bcd(date.hour);
    BCDMIN  = rtc_ten_to_bcd(date.min);
    BCDSEC  = rtc_ten_to_bcd(date.sec);

    RTCCON = 0x00;
}

/*************************************************************
*
*  Function  : RTC时间获取 以2000年1月1日为基址  星期六
*  Input    : date
*
**************************************************************/
void rtc_get(calendar *date)
{
       /* RTC读写使能,BCD时钟、计数器、无复位 */
       RTCCON = 0x01;

       date->year  = rtc_bcd_to_ten(BCDYEAR) + 2000;
       date->month = rtc_bcd_to_ten(BCDMON & 0x1F);
       date->day   = rtc_bcd_to_ten(BCDDATE & 0x3F);
       date->week  = rtc_bcd_to_ten(BCDDAY & 0x07) - 1;
       date->hour  = rtc_bcd_to_ten(BCDHOUR & 0x3F);
       date->min   = rtc_bcd_to_ten(BCDMIN  & 0x7F);
       date->sec   = rtc_bcd_to_ten(BCDSEC  & 0x7F);

       RTCCON = 0x00;
}

/*************************************************************
*
*  Function  : 将星期转换为字符串
*  Input    : week 星期
*
**************************************************************/
u8* get_week(WEEK week)
{
  const u8 *week_str[7] = {"SUN","MON","TUES","WED","THURS","FRI","SAT"};
  return week_str[week];
}



/*************************************************************
*
*  Function  : RTC滴答设置
*
**************************************************************/
void rtc_tick_init()
{
    /* 时钟节拍周期 = (时间计数+1)/128 = (127+1)/128 = 1s */
    TICNT = ((1<<7) | (0x7f<<0));  /*  使能中断,时间计数设置为127  */


    /* 清除时钟节拍中断位 */
    SRCPND |= BIT_TICK;
    INTPND |= BIT_TICK;

    /* 使能时钟节拍中断 */
    INTMSK &= ~BIT_TICK;
}

/*************************************************************
*
*  Function  : RTC闹钟设置
*
**************************************************************/
void rtc_alarm_init()
{
    /* RTC读写使能,BCD时钟、计数器、无复位 */
    RTCCON = 1;

    /* 秒闹钟设置为0,当秒数为0时,触发中断 */
    ALMSEC = 0;

    /*  使能全局闹钟,使能秒闹钟 */
    RTCALM |= ((1<<6) | (1<<0));

    /* RTC读写禁止,BCD时钟、计数器、无复位 */
    RTCCON = 0;

    /*  清除闹钟中断位 */
    SRCPND |= BIT_RTC;
    INTPND |= BIT_RTC;

    /* 使能闹钟中断 */
    INTMSK &= ~BIT_RTC;
}

3.4 测试

主函数代码如下:

#include "led.h"
#include "common.h"
#include "uart.h"
#include "rtc.h"

calendar current_date = {
    .year  = 2023,
    .month = 1,
    .day   = 1,
    .week  = Sunday,
    .hour  = 0,
    .min   = 0,
    .sec   = 0,
};

int main()
{
    u8 i = 0;
    vector_enable();

    led_init();
    uart_init();
    rtc_tick_init();
    rtc_alarm_init();
    delay_ms(1000);
    printf("rtc set 2023-01-01 00:00:00 rn");
    rtc_set(current_date);

    while(1)
    {
        led_turn(LED1);
        delay_ms(1000);
    }
    return 0;
}

将代码下载到Nand Flash,启动开发板运行输出如下:

rtc set 2023-01-01 00:00:00 
alarm int
NOWTIME: 2023-01-01 SUN 00:00:00
NOWTIME: 2023-01-01 SUN 00:00:01
NOWTIME: 2023-01-01 SUN 00:00:02
NOWTIME: 2023-01-01 SUN 00:00:03
NOWTIME: 2023-01-01 SUN 00:00:04
NOWTIME: 2023-01-01 SUN 00:00:05
NOWTIME: 2023-01-01 SUN 00:00:06
NOWTIME: 2023-01-01 SUN 00:00:07......
NOWTIME: 2023-01-01 SUN 00:00:52
NOWTIME: 2023-01-01 SUN 00:00:53
NOWTIME: 2023-01-01 SUN 00:00:54
NOWTIME: 2023-01-01 SUN 00:00:55
NOWTIME: 2023-01-01 SUN 00:00:56
NOWTIME: 2023-01-01 SUN 00:00:57
NOWTIME: 2023-01-01 SUN 00:00:58
NOWTIME: 2023-01-01 SUN 00:00:59
alarm int
NOWTIME: 2023-01-01 SUN 00:01:00

四、代码下载

Young / s3c2440_project【14.rtc】

参考文章

[1]S3C2440 RTC 裸机程序

[2]STM32---RTC(Real Time Clock)

[3]RTC实验

内容来源于网络如有侵权请私信删除

文章来源: 博客园

原文链接: https://www.cnblogs.com/zyly/p/15467460.html

你还没有登录,请先登录注册
  • 还没有人评论,欢迎说说您的想法!