开发环境:
MDK:Keil 5.30
开发板:GD32F207I-EVAL
MCU:GD32F207IK
16.1 RTC工作原理
16.1.1 RTC简介
GD32 的 RTC 外设,实质是一个掉电后还继续运行的定时器。从定时器的角度来说,相对于通用定时器 TIMER 外设,它十分简单,只有很纯粹的计时功能(当然,可以触发中断);但从掉电还继续运行的角度来说,它却是 GD32中唯一一个具有如此强大功能的外设。所以 RTC 外设的复杂之处并不在于它的定时功能,而在于它掉电还继续运行的特性。
以上所说的掉电,是指主电源 VDD断开的情况,为了 RTC 外设掉电继续运行,必须给GD32芯片通过 VBAT引脚接上锂电池。当主电源 VDD有效时,由 VDD给 RTC 外设供电。当 VDD掉电后,由 VBAT给 RTC 外设供电。但无论由什么电源供电,RTC 中的数据都保存在属于 RTC 的备份域中,若主电源 VDD和 VBAT都掉电,那么备份域中保存的所有数据将丢失。备份域除了 RTC 模块的寄存器,还有 42 个 16 位的寄存器可以在 VDD掉电的情况下保存用户程序的数据,系统复位或电源复位时,这些数据也不会被复位。
从 RTC 的定时器特性来说,它是一个 32 位的计数器,只能向上计数。它使用的时钟源有三种,分别为高速外部时钟的 128 分频:HXTAL/128;低速内部时钟IRC40K;使 HXTAL分频时钟或IRC40K的话,在主电源 VDD掉电的情况下,这两个时钟来源都会受到影响,因此没法保证 RTC 正常工作。因此 RTC 一般使用低速外部时钟LXTAL,频率为实时时钟模块中常用的 32.768KHz,这是因为 32768 = 215,分频容易实现,所以它被广泛应用到 RTC 模块。在主电源 VDD有效的情况下(待机),RTC 还可以配置闹钟事件使 GD32退出待机模式。
RTC模块在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。RTC模块和时钟配置系统处于后备区域,即在系统复位或从待机模式唤醒后,RTC的设置和时间维持不变。
16.1.2主要特性
可编程的预分频系数:分频系数最高为2^20
32位的可编程计数器,可用于较长时间段的测量。
2个分离的时钟:用于APB1接口的PCLK1和RTC时钟(RTC时钟的频率必须小于PCLK1时钟频率的四分之一以上)。
可以选择以下三种RTC的时钟源:
A) HXTAL 时钟除以 128
B) LXTAL 振荡电路时钟
C) IRC40K 振荡电路时钟
2个独立的复位类型:
A) APB1接口由系统复位;
B) RTC核心(预分频器、闹钟、计数器和分频只能由后备域复位
3个专门的可屏蔽中断:
A) 闹钟中断,用来产生一个软件可编程的闹钟中断。
B) 秒中断,用来产生一个可编程的周期性中断信号 (最长可达1秒)。
C) 溢出中断,指示内部可编程计数器溢出并回转为的状态。
16.1.3 RTC架构
RTC的架构如下图所示。
RTC 由两个主要部分组成, 第一部分(APB1 接口)用来和 APB1 总线相连。此单元还包含一组 16 位寄存器,可通过 APB1 总线对其进行读写操作。 APB1 接口由 APB1 总线时钟驱动,用来与 APB1 总线连接。
另一部分(RTC 核心)由一组可编程计数器组成,RTC内核包含两个主要模块。一个是RTC预分频模块,用来产生RTC时间基准时钟SC_CLK。RTC预分频模块包含一个20位可编程分频器(RTC预分频器) ,该分频器可以通过对RTC时钟源分频产生SC_CLK。如果对RTC_INTEN寄存器中的秒中断标志位被使能, RTC会在每个SC_CLK上升沿产生一个秒中断。 另外一个模块是一个32 位可编程计数器,其数值可以被初始化为当前系统时间。如果对RTC_INTEN 寄存器的闹钟中断标志位被使能, RTC会在系统时间等于闹钟时间(存储于RTC_ALRMH/L 寄存器)时产生一个闹钟中断。
16.2 RTC寄存器分析
16.2.1 RTC寄存器描述
RTC 总共有 2 个控制寄存器RTC_INTEN和 RTC_CTL。
RTC_INTEN寄存器用来控制中断的,我们本章将要用到秒钟中断,所以在该寄存器必须设置最低位为 1,以允许秒钟中断。
RTC_CTL的第 0 位是秒钟标志位,我们在进入闹钟中断的时候,通过判断这位来决定是不是发生了秒钟中断。然后必须通过软件将该位清零(写0)。第 3 位为寄存器同步标志位,我们在修改控制寄存器之前,必须先判断该位,是否已经同步了,如果没有则等待同步,在没同步的情况下修改RTC_INTEN/RTC_CTL的值是不行的。第4位为配置标位,在软件修改 RTC_CNTx/RTC_ALRMx/RTC_PSCx的值的时候,必须先软件置位该位,以允许进入配置模式。第 5 位为 RTC 操作位,该位由硬件操作,软件只读。通过该位可以判断上次对 RTC 寄存器的操作是否完成,如果没有,我们必须等待上一次操作结束才能开始下一次操作。
【注意】
任何标志位都将保持挂起状态,直到适当的RTC_CTL请求位被软件复位,表示所请求的中断已经被接受。
在复位时禁止所有中断,无挂起的中断请求,可以对RTC寄存器进行写操作。
当APB1时钟不运行时,SCIF、ALRMIF、OVIF和RSYNF位不被更新。
SCIF、ALRMIF、OVIF和RSYNF位只能由硬件置位,由软件来清零。
若ALRMIF =1且ALRMIE =1,则允许产生RTC全局中断。如果在EXTI控制器中允许产生EXTI线 17中断,则允许产生RTC全局中断和RTC闹钟中断。
若ALRMIF =1,如果在EXTI控制器中设置了EXTI线 17的中断模式,则允许产生RTC闹钟中断;如果在EXTI控制器中设置了EXTI线 17的事件模式,则这条线上会产生一个脉冲(不会产生RTC闹钟中断)。
RTC 预分频装载寄存器,也有 2 个寄存器组成,RTC_PSCH和RTC_PSCL。这两个寄存器用来配置 RTC 时钟的分频数的,比如我们使用外部 32.768K 的晶振作为时钟的输入频率,那么我们要设置这两个寄存器的值为 32767,以得到一秒钟的计数频率。RTC_PSCH的各位描述如下图所示。
从上图可以看出,RTC_PSCH只有低四位有效,用来存储PSC的 19~16 位。而PSC的前 16 位,存放在RTC_PSCL里面,寄存器RTC_PSCL的各位描述如下图所示。
【注】如果输入时钟频率是32.768kHz(RTCCLK),这个寄存器中写入7FFFh可获得周期为1秒钟的信号。
RTC 预分频器寄存器也有 2 个寄存器组成 RTC_DIVH 和 RTC_DIVL,这两个寄存器的作用就是用来获得比秒钟更为准确的时钟,比如可以得到 0.1 秒,或者 0.01 秒等。该寄存器的值自减的,用于保存还需要多少时钟周期获得一个秒信号。在一次秒钟更新后,由硬件重新装载。这两个寄存器和 RTC 预分频装载寄存器的各位是一样的,这里我们就不列出来了。
接着要介绍的是 RTC 最重要的寄存器, RTC 计数器寄存器 RTC_CNT。该寄存器由 2 个 16位的寄存器组成 RTC_CNTH 和 RTC_CNTL,总共 32 位,用来记录秒钟值(一般情况下)。此两个计数器也比较简单,我们也不多说了。注意一点,在修改这个寄存器的时候要先进入配置模式。
最后我们介绍 RTC 部分的最后一个寄存器, RTC 闹钟寄存器,该寄存器也是由 2 个 16 为的寄存器组成 RTC_ALRH 和 RTC_ALRL。总共也是 32 位,用来标记闹钟产生的时间(以秒为单位),如果 RTC_CNT 的值与 RTC_ALR 的值相等,并使能了中断的话,会产生一个闹钟中断。该寄存器的修改也要进入配置模式才能进行。
因为我们使用到备份寄存器来存储 RTC 的相关信息(我们这里主要用来标记时钟是否已经经过了配置)。
16.2.2读RTC寄存器
RTC完全独立于RTC APB1接口。
软件通过APB1接口访问RTC的预分频值、计数器值和闹钟值。但是,相关的可读寄存器只在与RTC APB1时钟进行重新同步的RTC时钟的上升沿被更新。RTC标志也是如此的。
这意味着,如果APB1接口曾经被关闭,而读操作又是在刚刚重新开启APB1之后,则在第一次的内部寄存器更新之前,从APB1上读出的RTC寄存器数值可能被破坏了(通常读到0) 。下述几种情况下能够发生这种情形:
发生系统复位或电源复位
系统刚从待机模式唤醒
系统刚从停机模式唤醒
所有以上情况中,APB1接口被禁止时(复位、无时钟或断电)RTC核仍保持运行状态。
因此,若在读取RTC寄存器时,RTC 的APB1 接口曾经处于禁止状态,则软件首先必须等待RTC_CTL寄存器中的RSYNF位(寄存器同步标志)被硬件置’1’。
16.2.3配置RTC寄存器
必须设置RTC_CTL寄存器中的CMF位,使 RTC进入配置模式后,才能写入 RTC_PSC、RTC_CNT、RTC_ALRM寄存器。
另外,对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询RTC_CTL寄存器中的LWOFF状态位,判断RTC寄存器是否处于更新中。仅当LWOFF状态位是’1’时,才可以写入RTC寄存器。
配置过程:
1.查询LWOFF位,直到LWOFF的值变为’1’
2.置CMF值为1,进入配置模式
3.对一个或多个RTC寄存器进行写操作
4.清除CMF标志位,退出配置模式
5.查询LWOFF,直至LWOFF位变为’1’ 以确认写操作已经完成。
6.仅当CMF标志位被清除时,写操作才能进行,这个过程至少需要3个RTCCLK周期。
16.3 RTC具体代码实现
RTC 正常工作的一般配置步骤如下:
1)使能电源时钟和备份区域时钟。
前面已经介绍了,我们要访问 RTC 和备份区域就必须先使能电源时钟和备份区域时钟。
rcu_periph_clock_enable(RCU_BKPI);
rcu_periph_clock_enable(RCU_PMU);
1
2
2)取消备份区写保护。
要向备份区域写入数据,就要先取消备份区域写保护(写保护在每次硬复位之后被使能),否则是无法向备份区域写入数据的。我们需要用到向备份区域写入一个字节,来标记时钟已经配置过了,这样避免每次复位之后重新配置时钟。 取消备份区域写保护的库函数实现方法是:
pmu_backup_write_enable(); //使能 RTC 和后备寄存器访问
1
3)复位备份区域,开启外部低速振荡器。
在取消备份区域写保护之后,我们可以先对这个区域复位,以清除前面的设置,当然这个操作不要每次都执行,因为备份区域的复位将导致之前存在的数据丢失,所以要不要复位,要看情况而定。然后我们使能外部低速振荡器,注意这里一般要先判断 RCC_BDCR 的 LSERDY位来确定低速振荡器已经就绪了才开始下面的操作。
备份区域复位的函数是:
bkp_deinit();//复位备份区域
开启外部低速振荡器的函数是:
rcu_osci_on(RCU_LXTAL);// 开启外部低速振荡器
1
2
3
4)选择 RTC 时钟,并使能。
库函数中,选择 RTC 时钟的函数是:
rcu_rtc_clock_config(RCU_RTCSRC_LXTAL); //选择LXTAL作为 RTC 时钟
1
对于 RTC 时钟的选择使能 RTC 时钟的函数是:
rcu_periph_clock_enable(RCU_RTC); //使能 RTC 时钟
1
5)设置 RTC 的分频,以及配置 RTC 时钟。
在开启了 RTC 时钟之后,我们要做的就是设置 RTC 时钟的分频数,通过 RTC_PSCH 和RTC_PSCL 来设置,然后等待 RTC 寄存器操作完成,并同步之后,设置秒钟中断。然后设置RTC 的允许配置位( RTC_CTL的 CMF 位),设置时间(其实就是设置 RTC_CNTH 和 RTC_CNTL两个寄存器)。
下面我们一一这些步骤用到的库函数:
在进行 RTC 配置之前首先要打开允许配置位(CMF),库函数是:
rtc_configuration_mode_enter ();/// 允许配置
1
在配置完成之后,千万别忘记更新配置同时退出配置模式,函数是:
rtc_configuration_mode_exit ();//退出配置模式,更新配置
1
设置 RTC 时钟分频数, 库函数是:
void rtc_prescaler_set(uint32_t psc)
1
这个函数只有一个入口参数,就是 RTC 时钟的分频数,很好理解。
然后是设置秒中断允许,RTC 使能中断的函数是:
void rtc_interrupt_enable(uint32_t interrupt)
1
这个函数的第一个参数是设置秒中断类型,这些通过宏定义定义的。 对于使能秒中断方法是:
rtc_interrupt_enable(RTC_INT_SECOND); //使能 RTC 秒中断
1
下一步便是设置时间了,设置时间实际上就是设置 RTC 的计数值,时间与计数值之间是需要换算的,当然也可先不设置。库函数中设置 RTC 计数值的方法是:
void rtc_counter_set(uint32_t cnt)
1
通过这个函数直接设置 RTC 计数值。
6)更新配置,设置 RTC 中断分组。
在设置完时钟之后,我们将配置更新同时退出配置模式,这里还是通过 RTC_CTL的CMF来实现。库函数的方法是:
rtc_configuration_mode_exit ();//退出配置模式,更新配置
1
在退出配置模式更新配置之后我们在备份区域 BKP_DATA0中写入 0xA5A5代表我们已经初始化过时钟了,下次开机(或复位)的时候,先读取 BKP_DATA0的值,然后判断是否是 0xA5A5来决定是不是要配置。接着我们配置 RTC 的秒钟中断,并进行分组。
往备份区域写用户数据的函数是:
void bkp_data_write(bkp_data_register_enum register_number, uint16_t data)
1
这个函数的第一个参数就是寄存器的标号了,这个是通过宏定义定义的。 比如我们要往BKP_DATA0 写入 0xA5A5,方法是:
bkp_data_write(BKP_DATA_0, 0xA5A5);
1
同时,有写便有读,读取备份区域指定寄存器的用户数据的函数是:
uint16_t bkp_data_read(bkp_data_register_enum register_number)
1
这个函数就很好理解了,这里不做过多讲解。
设置中断分组的方法之前已经详细讲解过,这里不做重复讲解。
7)编写中断服务函数。
最后,我们要编写中断服务函数,在秒钟中断产生的时候,读取当前的时间值。
/**
* @brief This function handles RTC exception.
* @param None
* @retval None
*/
void RTC_IRQHandler(void)
{
if(rtc_flag_get(RTC_FLAG_SECOND)!=RESET)//读取中断标志
{
rtc_flag_clear(RTC_FLAG_SECOND);//清楚中断标志
tim_bz=1;//秒中断标志
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
完成的配如下:
/**
* @brief RTC配置
* @param None
* @retval None
*/
void rtc_configuration(void)
{
/* enable PMU and BKPI clocks 使能电源时钟和备份区域时钟*/
rcu_periph_clock_enable(RCU_BKPI);
rcu_periph_clock_enable(RCU_PMU);
/* allow access to BKP domain 允许访问BKP区域*/
pmu_backup_write_enable();
//复位备份区域,开启外部低速振荡器
/* reset backup domain */
bkp_deinit();
/* enable LXTAL 使能外部低速晶振 32.768K */
rcu_osci_on(RCU_LXTAL);
/* wait till LXTAL is ready */
rcu_osci_stab_wait(RCU_LXTAL);
//选择 RTC 时钟,并使能
/* select RCU_LXTAL as RTC clock source */
rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);
/* enable RTC Clock 使能RTC时钟 */
rcu_periph_clock_enable(RCU_RTC);
rtc_configuration_mode_enter();
/* wait for RTC registers synchronization */
rtc_register_sync_wait();
/* wait until last write operation on RTC registers has finished 等待写RTC寄存器完成*/
rtc_lwoff_wait();
/* enable the RTC second interrupt 使能RTC秒中断*/
rtc_interrupt_enable(RTC_INT_SECOND);
/* wait until last write operation on RTC registers has finished 等待写RTC寄存器完成*/
rtc_lwoff_wait();
/* set RTC prescaler: set RTC period to 1s 设置预分频*/
rtc_prescaler_set(32767);
/* wait until last write operation on RTC registers has finished 等待写RTC寄存器完成*/
rtc_lwoff_wait();
nvic_priority_group_set(NVIC_PRIGROUP_PRE1_SUB3);
nvic_irq_enable(RTC_IRQn, 1, 0);
}
/**
* @brief RTC时钟初始化
* @param None
* @retval None
*/
void clock_init(void)
{
if(0xA5A5 != bkp_data_read(BKP_DATA_0))
{
//第一次运行 初始化设置
rtc_configuration();//RTC初始化
/* wait until last write operation on RTC registers has finished */
rtc_lwoff_wait();
/* change the current time */
rtc_counter_set(0);
/* wait until last write operation on RTC registers has finished */
rtc_lwoff_wait();
rtc_lwoff_wait();//等待写RTC寄存器完成
rtc_lwoff_wait();//等待写RTC寄存器完成
bkp_data_write(BKP_DATA_0, 0xA5A5);//写配置标志
}
else
{
/* check if the power on reset flag is set */
if(rcu_flag_get(RCU_FLAG_PORRST) != RESET)
{
printf("\r\n\n Power On Reset occurred....");
}
else if(rcu_flag_get(RCU_FLAG_SWRST) != RESET)
{
/* check if the pin reset flag is set */
printf("\r\n\n External Reset occurred....");
}
printf("\r\n No need to configure RTC....");
rtc_register_sync_wait();//等待RTC寄存器同步
rtc_interrupt_enable(RTC_INT_SECOND);//使能RTC秒中断
rtc_lwoff_wait();//等待写RTC寄存器完成
}
rtc_configuration_mode_exit();//退出配置模式, 更新配置
rcu_all_reset_flag_clear();//清除复位标志;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
主函数代码如下:
/*
brief main function
param[in] none
param[out] none
retval none
*/
int main(void)
{
uint32_t TimeData=0,hh=0,mm=0,ss=0;
//usart init 115200 8-N-1
com_init(COM1);
rtc_configuration();
clock_init();
while(1)
{
if(tim_bz==1)
{
tim_bz=0;
TimeData=rtc_counter_get();
hh= TimeData/3600;
mm = (TimeData%3600)/60;
ss = TimeData%60;
printf("Time: %0.2d:%0.2d:%0.2d\r\n",hh,mm,ss);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
16.4实验现象
打开串口助手,打印信息如下:
————————————————
版权声明:本文为CSDN博主「Bruceoxl」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/bruceoxl/article/details/126358693
https://www.codetd.com/article/1445410
RTC是什么鬼?
RTC ---- REAL Time Clock----->实时时钟(年、月、日、星期、时、分、秒)
RTC的使用:
1)设置当前的时间
2)RTC可以自动的工作(嵌入式平台中,RTC也会不准)--->获取网络时间校正 --->GPS和北斗校时
3)可以设置闹钟时间,产生闹钟中断。
ARM中的RTC模块
ARM的电源原理图
既有电源供电,也有电池供电,但没电时就像电子表一样纽扣电池供电
S5PV210中RTC的特点
1、Supports BCD Number, that is Second, Minute, Hour, Day of the week, Day, Month, and Year.
BCD码(压缩型的BCD码)---> 32秒 --->second寄存器的内容 0x32
压缩型的BCD码是8位的,高4位是十位,第4位是各位。
2、Supports Leap Year Generator 支持闰年
3、Supports Alarm Function, that is ,Alarm-Interrupt or Wake-up from power down modes (idle, deep idle, stop,deep stop, and sleep) 支持闹钟功能,闹钟中断
4、Supports Tick Counter Function, that is Tick-Interrupt or Wake-up from power down modes (idle, deep idle,stop, deep stop, and sleep) 支持滴答功能,也就是每秒产生一次中断
5、Supports Independent Power Pin (RTCVDD) 支持独立电源,就是纽扣电池供电
6、Supports millisecond tick time interrupt for RTOS kernel time tick .
RTC的TICK时钟,可以提供给实时操作系统的内核,作为实时操作系统内核时钟。
RTC的寄存器
配置寄存器
闹钟的设置寄存器
RTC时间设置和读取的寄存器
1、中断判断寄存器---INTP
1、RTC的控制寄存器----RTCCON
1、滴答时钟计数器寄存器----TICCNT
例:设置tick timer的周期是500ms,则频率2Hz。
1)设置tick timer的clock频率:
RTCCON &= ~(0xf<<4); //tick timer的基准频率32768Hz
2)设置tick timer的计数值
TICNT = 32768/2; //tick timer的工作频率2Hz
基准频率/计数值 = 工作频率
基准频率/工作频率=计数值
4、RTC的闹钟寄存器---RTCALM
设置闹钟的比较时间。
四、RTC的程序
#include "main.h"
#include "stdio.h"
#include "int.h"
extern unsigned int m;
#define RTC_BASE (0xE2800000)
#define rINTP (*((volatile unsigned long *)(RTC_BASE + 0x30)))
#define rRTCCON (*((volatile unsigned long *)(RTC_BASE + 0x40)))
#define rTICCNT (*((volatile unsigned long *)(RTC_BASE + 0x44)))
#define rRTCALM (*((volatile unsigned long *)(RTC_BASE + 0x50)))
#define rALMSEC (*((volatile unsigned long *)(RTC_BASE + 0x54)))
#define rALMMIN (*((volatile unsigned long *)(RTC_BASE + 0x58)))
#define rALMHOUR (*((volatile unsigned long *)(RTC_BASE + 0x5c)))
#define rALMDATE (*((volatile unsigned long *)(RTC_BASE + 0x60)))
#define rALMMON (*((volatile unsigned long *)(RTC_BASE + 0x64)))
#define rALMYEAR (*((volatile unsigned long *)(RTC_BASE + 0x68)))
#define rRTCRST (*((volatile unsigned long *)(RTC_BASE + 0x6c)))
#define rBCDSEC (*((volatile unsigned long *)(RTC_BASE + 0x70)))
#define rBCDMIN (*((volatile unsigned long *)(RTC_BASE + 0x74)))
#define rBCDHOUR (*((volatile unsigned long *)(RTC_BASE + 0x78)))
#define rBCDDATE (*((volatile unsigned long *)(RTC_BASE + 0x7c)))
#define rBCDDAY (*((volatile unsigned long *)(RTC_BASE + 0x80)))
#define rBCDMON (*((volatile unsigned long *)(RTC_BASE + 0x84)))
#define rBCDYEAR (*((volatile unsigned long *)(RTC_BASE + 0x88)))
#define rCURTICCNT (*((volatile unsigned long *)(RTC_BASE + 0x90)))
#define rRTCLVD (*((volatile unsigned long *)(RTC_BASE + 0x94)))
unsigned int alarm_flag = 1; //1---alarm没有工作;
// 函数功能:把十进制num转成bcd码,譬如把56转成0x56,num-->dec
static unsigned int num_2_bcd(unsigned int num)
{
// 第一步,把56拆分成5和6
// 第二步,把5和6组合成0x56
return (((num / 10)<<4) | (num % 10));
}
#if 1
// 函数功能:把bcd码bcd转成十进制,譬如把0x56转成56
static unsigned int bcd_2_num(unsigned int bcd)
{
// 第一步,把0x56拆分成5和6
// 第二步,把5和6组合成56
return (((bcd & 0xf0)>>4)*10 + (bcd & (0x0f)));
}
#endif
void rtc_alarm_init(void)
{
rALMMIN = num_2_bcd(1); //闹钟的分是1
rRTCALM |= 1<<1;//分钟匹配成功,产生闹钟中断
rRTCALM |= 1<<6; //打开闹钟中断功能
}
void isr_rtc_alarm(void) //闹钟的ISR<--当RTC的分和ALARM分一致
{
static int i = -1;
printf("rtc alarm, i = %d...\r\n", i++); //debug
GPD0DAT |= (1<<0); //beep 响
alarm_flag = 0;
rINTP |= (1<<1); //清中断
intc_clearvectaddr();
}
void rtc_tick_init(void)
{
rRTCCON &= ~(0xf<<4); //clock = 32768
rTICCNT = 26214;//32768/(1.25);
printf("---rtc write time test 2016-9-13 2 15:59:03 ---\n\r");
struct rtc_time tWrite =
{
.year = 2016,
.month = 9,
.date = 13,
.hour = 15,
.minute = 59,
.second = 3,
.day = 2,
};
rtc_set_time(&tWrite);
rRTCCON |= (1<<8); //enable tick timer
}
void rtc_set_time(const struct rtc_time *p)
{
// 第一步,打开RTC读写开关
rRTCCON |= (1<<0);
// 第二步,写RTC时间寄存器
rBCDYEAR = num_2_bcd(p->year - 2000);
rBCDMON = num_2_bcd(p->month);
rBCDDATE = num_2_bcd(p->date);
rBCDHOUR = num_2_bcd(p->hour);
rBCDMIN = num_2_bcd(p->minute);
rBCDSEC = num_2_bcd(p->second);
rBCDDAY = num_2_bcd(p->day);
// 最后一步,关上RTC的读写开关
rRTCCON &= ~(1<<0);
}
void rtc_get_time(struct rtc_time *p)
{
// 第一步,打开RTC读写开关
rRTCCON |= (1<<0);
// 第二步,读RTC时间寄存器
p->year = bcd_2_num(rBCDYEAR) + 2000;
p->month = bcd_2_num(rBCDMON);
p->date = bcd_2_num(rBCDDATE);
p->hour = bcd_2_num(rBCDHOUR);
p->minute = bcd_2_num(rBCDMIN);
p->second = bcd_2_num(rBCDSEC);
p->day = bcd_2_num(rBCDDAY);
// 最后一步,关上RTC的读写开关
rRTCCON &= ~(1<<0);
}
void isr_rtc_tick(void) //Tick timer 的ISR (800ms)
{
if(alarm_flag == 0)
m++; //每800ms 加一次1
if(m == 12) //10s
{
GPD0DAT &= ~(1<<0); //beep不响
m = 0;
alarm_flag = 1;
}
struct rtc_time tRead;
rtc_get_time(&tRead);
printf("The time read is: %d:%d:%d:%d:%d:%d:%d.\r\n",
tRead.year, tRead.month, tRead.date, tRead.hour,
tRead.minute, tRead.second, tRead.day);
rINTP |= (1<<0); //清中断
intc_clearvectaddr();
}