四、GD32 MCU 常见外设介绍(14)

GD32 MCU内部提供了一个RTC(实时时钟)模块,通过RTC可以实现日历时钟、闹钟等功能。RTC也可以用于深度睡眠或待机模式的低功耗唤醒。不同系列的GD32 MCU在RTC设计和功能上有所区别,总体可以分为三大系列:

(1)F10x、F30x、E10x系列RTC功能基本相同,后文简称0x系列。

(2)F1x0、F3x0、E23x系列RTC功能基本相同,后文简称x0系列。

(3)F405、F407、F450系列RTC功能基本相同,后文简称4xx系列。后文会对0x系列、x0系列、4xx系列的RTC模块分别进行介绍,简单介绍RTC的工作原理、配置使用方法。

14.1.GD32 RTC 外设简介

0x 系列 RTC

0x系列RTC整体架构相对精简,主要依靠32位累加计数器配置分频实现时钟计数。日历功能可通过软件计算并写入备份域中实现;同时具有闹钟功能可用于定时产生中断和唤醒唤醒事件;RTC的核心计数部分在备份域中,可在VDD断电时VBAT供电的情况保持RTC的计数,正常上电工作时通过APB总线接口可对RTC寄存器进行配置。

0x系列RTC主要特点:

◼ 32位可编程计数器,用于计数运行时间

– 可编程的预分频器: 分频系数最高可达220

◼ 独立时钟域:

– PCLK1时钟域

– RTC时钟域(该时钟必须比PCLK1时钟至少慢4倍)

◼ RTC时钟源:

– HXTAL时钟分频

– LXTAL振荡电路时钟

– IRC40K振荡电路时钟

◼ 可屏蔽的中断源:

– 闹钟中断

– 秒中断

– 溢出中断

0x系列RTC框图介绍:

RTC由两个主要部分组成,如下图0x系列RTC结构框图所示,位于PCLK1时钟域的APB接口和位于RTC时钟域的RTC内核。

输入图片说明

第一部分APB接口用来和APB1总线相连。此单元还包含一组16位寄存器,可通过APB1总线对其进行读写操作。对RTC模块进行相关配置。

另一部分(RTC核心)由一组可编程计数器组成,分成两个主要模块。第一个模块是RTC的预分频模块,RTC时钟源输入后经过预分频模块,可编程产生RTC时间基准SC_CLK。RTC的预分频模块包含了一个20位的可编程分频器(RTC预分频器);如果在RTC_INTEN寄存器中设置了相应的允许位,则在每个SC_CLK周期中RTC产生一个中断(秒中断)。

第二个模块是一个32位的可编程计数器,可被初始化为当前的系统时间。系统时间按SC_CLK周期累加并与存储在RTC_ALRM寄存器中的可编程时间相比较,如果RTC_INTEN控制寄存器中设置了相应允许位,比较匹配时将产生一个闹钟中断。

x0 系列 RTC

0x系列RTC提供了一个包含日期(年/月/日)和时间(时/分/秒/亚秒)的日历功能。除亚秒用二进制码显示外,时间和日期都以BCD码的形式显示。RTC可以进行夏令时补偿。RTC可以工作在省电模式下,并通过软件配置来智能唤醒。RTC支持外接更高精度的低频时钟,用以达到更高的日历精度。

x0系列RTC主要特点:

◼ 通过软件设置来实现夏令时补偿。

◼ 参考时钟检测功能:通过外接更高精度的低频率时钟源(50Hz/60Hz)来提高日历精度。

◼ 数字校准功能:通过调整最小时间单位(最大可调精度0.95ppm)来进行日历校准。

◼ 通过移位功能进行亚秒级调整。

◼ 记录事件时间的时间戳功能。

◼ 两个模式可配置的独立的侵入检测。

◼ 可编程的日历和一个位域可屏蔽的闹钟。

◼ 5个32位(共20字节)通用备份寄存器,能够在省电模式下保存数据。当有外部事件侵入时,备份寄存器将会复位。

◼ 可屏蔽的中断源:

– 闹钟0;

– 时间戳检测;

– 侵入检测;

x0系列RTC框图介绍:

x0系列RTC工作在备份域,可在低功耗模式下保持工作,通过APB总线可对RTC寄存器进行读取和配置。如下图x0系列RTC结构框图所示,RTC时钟源可配置通过数字平滑校准或直接输入到7位异步预分频器输出ck_apre时钟用于RTC_SS亚秒寄存器自减计数,ck_apre时钟又经过15位同步预分频器后输出1HZ的ck_spre时钟提供日历寄存器使用;基于日历寄存器还实现了闹钟和时间戳功能;RTC还具有闹钟、时钟输出功能,对RTC_TS、RTC_TAMP0、RTC_TAMP1引脚的有效输入可触发时间戳和侵入事件并产生中断。侵入事件会将备份域复位。

输入图片说明

◼ 闹钟

RTC闹钟功能被划分为多个位域并且每一个位域有一个该域的可屏蔽位。屏蔽某些位域后可固定周期产生闹钟事件。

◼ 侵入事件

RTC_TAMPx管脚可以作为侵入事件检测功能输入管脚,检测模式有两种可供用户选择:边沿检测模式或者是带可配置滤波功能的电平检测模式。侵入事件会将备份域复位,可产生一个中断。

◼ 可选的RTC输出功能:

  • 512Hz( 默认预分频值): RTC_OUT;

  • 1Hz( 默认预分频值): RTC_ OUT;

  • 闹钟事件( 极性可配置): RTC_ OUT。

◼ 可选的RTC输入功能:

  • 时间戳事件检测( RTC_TS);

  • 侵入事件检测 0( RTC_TAMP0);

  • 侵入事件检测 1( RTC_TAMP1);

  • 参考时钟输入 RTC_REFIN( 50 或 60Hz)。

◼ RTC中断

所有的RTC中断(闹钟、时间戳、侵入事件)都被连接到EXTI控制器。

4xx 系列 RTC 4xx系列RTC在x0系列RTC的基础上做了部分功能的升级。提供了一个包含日期(年/月/日)和时间(时/分/秒/亚秒)的日历功能。除亚秒用二进制码显示外,时间和日期都以BCD码的形式显示。

RTC可以进行夏令时补偿。RTC可以工作在省电模式下,并通过软件配置来智能唤醒。RTC支持外接更高精度的低频时钟,用以达到更高的日历精度。

  1. 4xx系列RTC主要特点:

◼ 通过软件设置来实现夏令时补偿。

◼ 参考时钟检测功能:通过外接更高精度的低频率时钟源(50Hz或60Hz)来提高日历精度。

◼ 数字校准功能:通过调整最小时间单位(最大可调精度0.95ppm)来进行日历校准。

◼ 通过移位功能进行亚秒级调整。

◼ 记录事件时间的时间戳功能。

◼ 两个模式可配置的独立的侵入检测。

◼ 可编程的日历和一个位域可屏蔽的闹钟。

◼ 20个32位(共80字节)通用备份寄存器,能够在省电模式下保存数据。当有外部事件侵入时,备份寄存器将会复位。

◼ 可屏蔽的中断源:

– 闹钟0和闹钟1;

– 时间戳检测;

– 自动唤醒事件;

– 侵入检测;

◼ 可配置周期的自动唤醒定时器

4xx系列RTC框图介绍:

4xx系列RTC工作在备份域,可在低功耗模式下保持工作,通过APB总线可对RTC寄存器进行读取和配置。如下图4xx系列RTC结构框图所示,RTC时钟源可配置通过数字平滑校准或直接输入到7位异步预分频器输出ck_apre时钟用于RTC_SS亚秒寄存器自减计数,ck_apre时钟又可通过数字粗平滑校准或直接输入15位同步预分频器后输出1HZ的ck_spre时钟提供日历寄存器使用;基于日历寄存器还实现了闹钟和时间戳功能;RTC还具有闹钟、时钟输出功能,对RTC_TS、RTC_TAMP0、RTC_TAMP1引脚的有效输入可触发时间戳和侵入事件并产生中断。侵入事件会将备份域复位。4xx系列RTC有一个独立的自动重加载唤醒定时器可用于产生唤醒事件和中断。

输入图片说明

◼ 闹钟

RTC闹钟功能被划分为多个位域并且每一个位域有一个该域的可屏蔽位。屏蔽某些位域后可固定周期产生闹钟事件。

◼ 侵入事件

RTC_TAMPx管脚可以作为侵入事件检测功能输入管脚,检测模式有两种可供用户选择:边沿检测模式或者是带可配置滤波功能的电平检测模式。侵入事件会将备份域复位,可产生一个中断。

◼ 可选的RTC输出功能:

  • 512Hz(默认预分频值):(RTC_OUT)PC13

  • 1Hz(默认预分频值):(RTC_OUT)PC13

  • 闹钟事件(极性可配置):(RTC_OUT)PC13

  • 自动唤醒事件(极性可配置):(RTC_OUT)PC13

◼ 可选的RTC输入功能:

  • 时间戳事件检测(RTC_TS):RTC_AF0、RTC_AF1;

  • 侵入事件检测 0(RTC_TAMP0):RTC_AF0、RTC_AF1;

  • 侵入事件检测 1(RTC_TAMP1):RTC_AF1;

  • 参考时钟输入 RTC_REFIN(50或60Hz)。

◼ RTC中断

所有的RTC中断(闹钟0、闹钟1、唤醒、时间戳、侵入0、侵入1)都被连接到EXTI控制器。

各系列 RTC 模块功能对比

x0系列中的E23x系列均没有VBAT引脚,不支持VDD掉电保持RTC工作。

0x系列备份域不同于x0、4xx系列,为单独的一个外设模块,所以使用RTC时,0x系列相比x0系列和4xx系列还需使能BKP的时钟。

各系列 RTC 模块功能对比

输入图片说明

14.2.GD32 RTC 硬件设计

◼ Vbat电源设计

Vbat可以连接至外部电池,在Vdd掉电时可以保证备份域不掉电、RTC继续运行。VBAT引脚需要对GND连接0.1uF电容,如果没有外部电池需要将VBAT和一个0.1uF电容连接至Vdd电源上。

输入图片说明

◼ RTC_TAMPx引脚

入侵事件会导致备份域复位,如使用该功能需注意检测引脚的滤波,可在RTC_TAMPx引脚上并联0.1uf电容。

14.3.GD32 RTC 软件配置

GD32MCU_Example下的RTC_Example例程配置了日历和闹钟功能,并开启了闹钟中断。本小节讲解RTC_Example例程中RTC模块的配置说明,主要包括时钟及分频配置、日历配置、闹钟配置、主函数说明。本例程主要介绍GD32 MCU各系列RTC模块的时间、闹钟配置,有关RTC其他功能例程可参考各系列固件库例程。

时钟及分频配置

◼ 由于RTC工作在备份域,所以使用RTC时需要使能备份域写功能,而控制备份域写功能的寄存器位于PMU中,所以操作RTC还需要将PMU_CTL寄存中的BKPWEN置位,从而还需使能PMU的时钟。而对于0x系列备份域为单独的外设,还需额外使能BKP备份域时钟。

◼ 为了实现准确的日历功能,配置RTC前需要准备好需要的时钟源,在选择合适的时钟源后RTC还需要进行预分频的配置。

0x系列时钟及分频配置如代码清单 RTC 0x系列时钟及分频配置所示,RTC使能后需要等待RTC寄存器和APB1时钟同步,执行rtc_register_sync_wait()函数;此外0x系列RTC在RTC寄存器配 置时需要等待上一次配置结束才能继续新的配置,所以在每个寄存器配置前需要执行rtc_lwoff_wait()函数,等待LWOFF置位。

代码清单 RTC 0x 系列时钟及分频配置

void rtc_config(void)
{
uint32_t prescaler=0;
/* enable PMU and BKPI clocks */
 rcu_periph_clock_enable(RCU_BKPI);
 rcu_periph_clock_enable(RCU_PMU);
 /* allow access to BKP domain */
 pmu_backup_write_enable();
#if RTC_CLOCK_SOURCE_IRC40K 
prescaler=40000;
/* enable LXTAL */
 rcu_osci_on(RCU_IRC40K);
 /* wait till LXTAL is ready */
 rcu_osci_stab_wait(RCU_IRC40K);
 /* select RCU_LXTAL as RTC clock source */
 rcu_rtc_clock_config(RCU_RTCSRC_IRC40K);
#elif RTC_CLOCK_SOURCE_LXTAL
prescaler=32767;
/* enable LXTAL */
 rcu_osci_on(RCU_LXTAL);
 /* wait till LXTAL is ready */
 rcu_osci_stab_wait(RCU_LXTAL);
 /* select RCU_LXTAL as RTC clock source */
 rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);
#else
#error RTC clock source should be defined.
#endif /* RTC_CLOCK_SOURCE_IRC40K */
 /* enable RTC Clock */
 rcu_periph_clock_enable(RCU_RTC);
 /* wait for RTC registers synchronization */
 rtc_register_sync_wait();
 /* wait until last write operation on RTC registers has finished */
rtc_lwoff_wait();
 /* set RTC prescaler: set RTC period to 1s */
 rtc_prescaler_set(prescaler);
 rtc_lwoff_wait();
 rtc_interrupt_enable(RTC_INT_ALARM);
 rtc_lwoff_wait();
}

x0系列时钟及分频配置如代码清单 RTC x0系列时钟及分频配置所示,RTC使能后需要等待RTC寄存器和APB1时钟同步,执行rtc_register_sync_wait()函数;和0x系列不同,后续配置过程x0和4xx系列均不需要执行rtc_lwoff_wait()函数。

代码清单 RTC x0 系列时钟及分频配置

void rtc_config(void)
{
 /* enable PMU clock */
 rcu_periph_clock_enable(RCU_PMU);
 /* enable the access of the RTC registers */
 pmu_backup_write_enable();
 #if (RTC_CLOCK_SOURCE_IRC40K) 
 rcu_osci_on(RCU_IRC40K);
 rcu_osci_stab_wait(RCU_IRC40K);
 rcu_rtc_clock_config(RCU_RTCSRC_IRC40K);

 prescaler_s = 0x18F;
 prescaler_a = 0x63;
 #elif (RTC_CLOCK_SOURCE_LXTAL)
 rcu_osci_on(RCU_LXTAL);
 rcu_osci_stab_wait(RCU_LXTAL);
 rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);
 prescaler_s = 0xFF;
 prescaler_a = 0x7F;
 #else
 #error RTC clock source should be defined.
 #endif /* RTC_CLOCK_SOURCE_IRC40K */
 rcu_periph_clock_enable(RCU_RTC);
 rtc_register_sync_wait();
rtc_interrupt_enable(RTC_INT_ALARM); 
}

4xx系列时钟及分频配置如代码清单 RTC 4xx系列时钟及分频配置所示,和x0系列基本相同,但由于4xx系列内部低速时钟为32K,所以分频系数和x0系列有所不同。

代码清单 RTC 4xx 系列时钟及分频配置

void rtc_config(void)
{
 /* enable PMU clock */
 rcu_periph_clock_enable(RCU_PMU);
 /* enable the access of the RTC registers */
 pmu_backup_write_enable();
 #if (RTC_CLOCK_SOURCE_IRC32K) 
 rcu_osci_on(RCU_IRC32K);
 rcu_osci_stab_wait(RCU_IRC32K);
rcu_rtc_clock_config(RCU_RTCSRC_IRC32K);

 prescaler_s = 0x13F;
 prescaler_a = 0x63;
 #elif (RTC_CLOCK_SOURCE_LXTAL)
 rcu_osci_on(RCU_LXTAL);
 rcu_osci_stab_wait(RCU_LXTAL);
 rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);

 prescaler_s = 0xFF;
 prescaler_a = 0x7F;
 #else
 #error RTC clock source should be defined.
 #endif /* RTC_CLOCK_SOURCE_IRC32K */
 rcu_periph_clock_enable(RCU_RTC);
 rtc_register_sync_wait();
 rtc_interrupt_enable(RTC_INT_ALARM0); 
}

日历配置

0x系列由于没有硬件日历功能,所以需要读取计数器通过软件计算出日历;而x0系列和4xx系列具有硬件日历功能,日期信息均是BCD码,所以在日历配置的格式和方式均有差别。

0x系列RTC的日历配置如代码清单 RTC 0x系列日历配置所示,该函数提供了日历配置的入口参数,先将需要配置的日历信息转换成秒单位,再写入RTC计数器即可。入口参数使用十进制写入。

代码清单 RTC 0x 系列日历配置

uint32_t rtc_time_set(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second)
{
 uint16_t t;
 uint32_t seccount = 0;

 if(year < 1970 || year > 2099)
 return ERROR;
 for(t = 1970; t < year; t++){
 if(is_leap_year(t)){
 seccount += 31622400;
 }else{
 seccount += 31536000;
 }
 }
 month -= 1;

 for(t=0; t < month; t++){
 seccount += (uint32_t)month_table[t] * 86400; 
 if(is_leap_year(year) && t==1){
 seccount+=86400;
 }
 }

 seccount += (uint32_t)(day-1) * 86400;
 seccount += (uint32_t)hour * 3600;
seccount += second;
rtc_lwoff_wait();
 rtc_counter_set(seccount);
rtc_lwoff_wait();
 return SUCCESS;
}

x0系列RTC的日历配置如代码清单 RTC x0系列日历配置所示,该函数提供了日历配置的入口参数,参数为BCD码格式,写入后对RTC日历结构体赋初值,日历结构体还可以配置星期、时间格式等,这里默认配置了24小时制。函数入口参数使用16进制写入。

代码清单 RTC x0 系列日历配置

uint32_t rtc_time_set(uint16_t year,uint8_t month,uint8_t day,uint32_t tmp_hh,uint32_t tmp_mm,uint32_t tmp_ss)
{
 rtc_initpara.rtc_factor_asyn = prescaler_a;
 rtc_initpara.rtc_factor_syn = prescaler_s;
 rtc_initpara.rtc_year = year&0x00ff;
 rtc_initpara.rtc_day_of_week = RTC_SATURDAY;
 rtc_initpara.rtc_month = month;
 rtc_initpara.rtc_date = day;
 rtc_initpara.rtc_display_format = RTC_24HOUR;
 rtc_initpara.rtc_am_pm = RTC_AM;
 rtc_initpara.rtc_hour = tmp_hh;
rtc_initpara.rtc_minute = tmp_mm;

 rtc_initpara.rtc_second = tmp_ss;
 if(ERROR == rtc_init(&rtc_initpara)){
 return ERROR;
}
return SUCCESS;
}

4xx系列RTC的日历配置如代码清单 0-52 RTC 4xx系列日历配置所示,和x0系列基本相同, 唯一区别只是日历结构体成员名字少了“RTC_”的前缀。

代码清单 RTC 4xx 系列日历配置

uint32_t rtc_time_set(uint16_t year,uint8_t month,uint8_t day,uint32_t tmp_hh,uint32_t tmp_mm,uint32_t tmp_ss)
{
 rtc_initpara.factor_asyn = prescaler_a;
 rtc_initpara.factor_syn = prescaler_s;
 rtc_initpara.year = year&0x00ff;
 rtc_initpara.day_of_week = RTC_SATURDAY;
 rtc_initpara.month = month;
 rtc_initpara.date = day;
 rtc_initpara.display_format = RTC_24HOUR;
 rtc_initpara.am_pm = RTC_AM;
 rtc_initpara.hour = tmp_hh;
rtc_initpara.minute = tmp_mm;

 rtc_initpara.second = tmp_ss;
 if(ERROR == rtc_init(&rtc_initpara)){
return ERROR;
}
return SUCCESS;
}

闹钟配置

0x系列的RTC通过32位计数器运行时间,所以其闹钟也是32位数据,当计数器和闹钟值匹配时会产生闹钟事件或中断,所需配置的闹钟值为闹钟剩余倒计时加上当前计数器值;而x0系列和4xx系列是硬件BCD日历功能,所以其闹钟也是BDC格式,且具有位域屏蔽功能,根据需要直接配置具体的时间即可。

0x系列RTC的闹钟配置如代码清单 RTC 0x系列闹钟配置所示,该函数提供了闹钟配置的入口参数,先将需要配置的时间信息转换成秒单位,再加上当前计数器的值写入闹钟寄存器即可。

0x系列配置的闹钟参数为闹钟中断的倒计时时间,写入参数为十进制。

代码清单 RTC 0x 系列闹钟配置

void rtc_alarm_set(uint8_t hour, uint8_t minute, uint8_t second)
{
 alarm_second = 3600 * hour + minute * 60 + second;
rtc_lwoff_wait();
 rtc_alarm_config(rtc_counter_get() + alarm_second);
rtc_lwoff_wait();
} 
 seccount += (uint32_t)(day-1) * 86400;
 seccount += (uint32_t)hour * 3600;
 seccount += (uint32_t)minute * 60;
 seccount += second;
rtc_lwoff_wait();
 rtc_counter_set(seccount);
rtc_lwoff_wait();
 return SUCCESS;
}

x0系列RTC的闹钟配置如代码清单 RTC x0系列闹钟配置所示,该函数提供了闹钟配置的入口参数,参数为BCD码格式,写入后对RTC闹钟结构体赋初值,闹钟结构体可以配置位域屏蔽、选择配置日期或星期、时间格式等,这里默认配置了屏蔽天、小时、分钟,所以配置最后实际生效的只有秒,所以配置后闹钟均是1分钟产生一次。闹钟配置前需失能闹钟,配置后再使能,4xx系列配置的闹钟参数为闹钟产生的日期时间,因BCD格式写入参数参数使用16进制。

代码清单 RTC x0 系列闹钟配置

void rtc_alarm_set(uint32_t tmp_hh,uint32_t tmp_mm,uint32_t tmp_ss)
{
rtc_alarm_struct rtc_alarm;
 rtc_alarm_disable();
 rtc_alarm.rtc_alarm_mask = RTC_ALARM_DATE_MASK|RTC_ALARM_HOUR_MASK|RTC_ALARM_MINUTE_MASK;
 rtc_alarm.rtc_weekday_or_date = RTC_ALARM_DATE_SELECTED;
 rtc_alarm.rtc_alarm_day = 0x31;
 rtc_alarm.rtc_am_pm = RTC_AM;
 rtc_alarm.rtc_alarm_hour = tmp_hh;

 rtc_alarm.rtc_alarm_minute = tmp_mm;
 rtc_alarm.rtc_alarm_second = tmp_ss;
 rtc_alarm_config(&rtc_alarm);

 rtc_alarm_enable(); 
}

4xx系列RTC的闹钟配置如代码清单 0-55 RTC 4xx系列闹钟配置所示,和x0系列基本相同, 唯一区别只是闹钟结构体成员名字少了“RTC_”的前缀。

代码清单 RTC 4xx 系列闹钟配置

uint32_t rtc_time_set(uint16_t year,uint8_t month,uint8_t day,uint32_t tmp_hh,uint32_t tmp_mm,uint32_t tmp_ss)
{
 rtc_initpara.factor_asyn = prescaler_a;
 rtc_initpara.factor_syn = prescaler_s;
 rtc_initpara.year = year&0x00ff;
 rtc_initpara.day_of_week = RTC_SATURDAY;
 rtc_initpara.month = month;
 rtc_initpara.date = day;
 rtc_initpara.display_format = RTC_24HOUR;
 rtc_initpara.am_pm = RTC_AM;
 rtc_initpara.hour = tmp_hh;
rtc_initpara.minute = tmp_mm;

 rtc_initpara.second = tmp_ss;
 if(ERROR == rtc_init(&rtc_initpara)){
 return ERROR;
}
return SUCCESS;
}

主函数说明 主函数如代码清单 RTC_Example主函数所示,主要包含了中断配置、RTC时钟源和预分频配置、日历设置以及闹钟设置,成功设置好日历和闹钟后在备份域数据寄存器写入一个标志位,下次非备份域复位的情况就可以不再重复配置RTC。While1中循环调用rtc_current_time_get()函数获取日历信息。4xx系列和x0系列中固件库底层已经提供了rtc_current_time_get()函数,直接更新至日历结构体;而0x系列固件库没有此函数,额外编写了相同接口的函数,通过读取32位计数器软件计算出日历信息更新至自定义的日历结构体中。通过仿真或串口打印可以看到结构体中的时间数据在更新。

代码清单 RTC_Example 主函数

int main(void)
{
 /* NVIC configure */
 nvic_config();
rtc_config();
alarm_second=5;
if (RTC_BKP0 != 0xA5A5){ 
 /* backup data register value is not correct or not yet programmed
 (when the first time the program is executed) */
#if defined GD32F10X_HD || GD32F30X_HD || GD32F20X_CL || GD32E10X
 if(SUCCESS==rtc_time_set(2019, 10, 14, 12, 0, 0)){
 rtc_alarm_set(12, 12, alarm_second);
#elif defined GD32F1X0 || GD32F4XX || GD32F3X0 || GD32E23X
if(SUCCESS==rtc_time_set(0x2019, 0x10, 0x14, 0x12, 0, 0)){
rtc_alarm_set(0x12, 0x12, alarm_second);
#endif
RTC_BKP0=0xA5A5;
 }
 }
 while (1){
/* updata time in infinite loop */
 rtc_current_time_get(&rtc_initpara);
}
}

代码清单 0x 系列自定义 rtc_current_time_get()主函数

void rtc_current_time_get(rtc_parameter_struct* RTC_Calend)
{
 static uint16_t daycnt = 0;
 uint32_t temp = 0,timevar=rtc_counter_get();
 uint16_t temp1 = 0;
 temp = timevar / 86400;
 if(daycnt != temp) {
 daycnt = temp;
 temp1 = 1970;

 while(temp >= 365){
 if(is_leap_year(temp1)){
 if(temp >= 366)
 temp-=366;
 else 
 break;
 }else 
 temp -= 365;
 temp1++;
 }

 RTC_Calend->years = temp1;
 temp1=0;

 while(temp >= 28)
 {
 if(is_leap_year(RTC_Calend->years) && temp1 == 1){
 if(temp >= 29)
 temp -= 29;
 else
 break;
 }else{
 if(temp >= month_table[temp1])
 temp -= month_table[temp1];
 else
 break;
 }
 temp1++;
 }
 RTC_Calend->months = temp1 + 1;
 RTC_Calend->days = temp + 1;
 }
 temp = timevar % 86400;
 RTC_Calend->hours = temp / 3600;
 RTC_Calend->minutes = (temp % 3600) / 60; 
 RTC_Calend->seconds = (temp % 3600) % 60; 
}

闹钟中断说明

例程中开启了闹钟中断,初始化调用rtc_alarm_set(uint8_t hour, uint8_t minute, uint8_t second)函数分别配置了“0时0分5秒”的闹钟。

◼ 对0x系列来说这个配置是倒计时“0时0分5秒”后产生闹钟,进入闹钟中断后再配置新的5s实现5s一次的闹钟周期;

◼ 对x0和4xx系列这个配置是时间在“0时0分5秒”时产生闹钟,但因为闹钟配置中已经屏蔽了闹钟的天、时、分位域,所以闹钟会在每分钟的5秒产生,闹钟周期为一分钟产生一次。

14.4.RTC 使用注意事项

1、 因为内部低速时钟是在VDDA电源域,所以VDD断电后VBAT供电情况下保持RTC运行需要使用外部低速时钟;此外使用LXTAL在非备份域复位时可以不用在初始化阶段配置,但使用内部低速时钟需要每次复位都进行内部时钟的初始化。

2、 内部低速时钟精度相对较差,如必须使用内部且对精度有一定要求,4xx和0x系列可以使用TIMER4、x0系列可以使用TIMER13捕获内部低速时钟,计算出实际的频率值来进行合适分频。

3、 日历信息意外被清零的话注意是否发生过备份域复位,是否有Vbat掉电或入侵事件发生。

4、 调试中如果修改了RTC代码的配置,请先擦除全片Flash,然后断电并上电后再下载新的代码。

更多GD32 MCU相关咨询:https://www.gd32bbs.com/ 

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值