1. RTC内置实时时钟
1.1 RTC时钟介绍
RTC是实时时钟(Real-Time Clock)的缩写。它是一种硬件模块或芯片,用于提供准确的日期和时间信息。
GD32F407上有RTC的外设,它提供了一个包含日期(年/月/日)和时间(时/分/秒/亚秒)的日历功能。除亚秒用二进制码显示外,时间和日期都以BCD码的形式显示。
RTC本质上就是一个1秒计数器,通过秒来换算出时间。因此需要我们提供一个1HZ频率的时钟。
1.2 RTC结构框图
1.3 RTC原理图
1.4 RTC时钟电源
RTC时钟分为两个电源域。RTC的核心部分位于备份域中。系统复位或从待机模式唤醒时,RTC的设置和时间都保持不变。另一部分包括APB接口及一组控制寄存器位于VDD电源域中。
1.5 RTC的配置流程
2. RTC时钟备份寄存器
RTC时钟有20个32位(共80字节)通用备份寄存器,能够在省电模式下保存数据。通过备份寄存器可以实现只配置一次时间即可。
注意:如果在尝试向RTC_BKP0写入数据之前或之后调用了rcu_bkp_reset_enable(),那么这确实会导致写入的数据在系统复位后丢失,因为RTC备份寄存器会被复位重置。因此,要确保在向RTC备份寄存器写入数据并期望这些数据在复位后仍然有效时,不能调用rcu_bkp_reset_enable()。
if( RTC_BKP0 == 0xf234 ){
//说明不是第一次初始化,可以不用重新设置时间
}else{//如果后备寄存器0 的值 不为 0XF234
//设置后备寄存器0的值为 0XF234,标记已经初始化过RTC
RTC_BKP0 = 0xf234;
//初始化RTC时间
RtcTimeConfig(0x23,0x07,0x12,0x03,0x12,0x59,0x50);
}
3. RTC闹钟Alarm
3.1 开发流程
-
配置RTC时钟
-
设置RTC闹钟
-
配置RTC闹钟中断
-
实现中断函数
3.1.1 RTC闹钟初始化
// RTC Alarm ---------------------------
rtc_alarm_struct rat;
// 设置忽略的选项 (让闹钟在每分钟的xx秒都执行)
rat.alarm_mask = RTC_ALARM_DATE_MASK | RTC_ALARM_HOUR_MASK | RTC_ALARM_MINUTE_MASK;
// 设置忽略的选项 (让闹钟在每小时的xx分钟xx秒都执行)
// rat.alarm_mask = RTC_ALARM_DATE_MASK | RTC_ALARM_HOUR_MASK;
// 设置忽略的选项 (让闹钟在每天的xx小时xx分钟xx秒都执行)
// rat.alarm_mask = RTC_ALARM_DATE_MASK;
// 设置选择日期/周 25日/周6 23:59:58
rat.weekday_or_date = RTC_ALARM_DATE_SELECTED;
rat.alarm_day = 0x25; // 日期
rat.alarm_hour = 0x23; // 小时
rat.alarm_minute = 0x59; // 分钟
rat.alarm_second = 0x58; // 秒
rat.am_pm = RTC_PM; // AM上午,PM下午
rtc_alarm_config(RTC_ALARM0, &rat);
// NVIC -----------------------------------
nvic_irq_enable(RTC_Alarm_IRQn, 2, 2);
// 启用alarm0中断
rtc_interrupt_enable(RTC_INT_ALARM0);
// 清理alarm0标记
rtc_flag_clear(RTC_FLAG_ALRM0);
/* 启用enable RTC alarm */
rtc_alarm_enable(RTC_ALARM0);
// EXTI --------------------------------------
// 先清理EXTI_17 ALARM0标记
exti_interrupt_flag_clear(RTC_EXTI_LINE);
exti_flag_clear(RTC_EXTI_LINE);
// 初始化EXTI_17 (必须初始化)
exti_init(RTC_EXTI_LINE,EXTI_INTERRUPT,EXTI_TRIG_RISING);
exti_interrupt_enable(RTC_EXTI_LINE);
3.2.2 中断函数
#define RTC_EXTI_LINE EXTI_17
// 闹铃中断
void RTC_Alarm_IRQHandler(void){
// 以下判断,二选其一即可
if(SET == rtc_flag_get(RTC_FLAG_ALRM0)){
rtc_flag_clear(RTC_FLAG_ALRM0);
exti_flag_clear(RTC_EXTI_LINE);
printf("Alarm_1!\n");
}
// if(SET == exti_interrupt_flag_get(RTC_EXTI_LINE)){
// exti_interrupt_flag_clear(RTC_EXTI_LINE);
// exti_flag_clear(RTC_EXTI_LINE);
// rtc_flag_clear(RTC_FLAG_ALRM0);
// printf("Alarm_2!\n");
// }
}
3.3.3 完整代码
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "Usart.h"
// 十位取出左移4位 + 个位 (得到BCD数)
#define WRITE_BCD(val) ((val / 10) << 4) + (val % 10)
// 将高4位乘以10 + 低四位 (得到10进制数)
#define READ_BCD(val) (val >> 4) * 10 + (val & 0x0F)
void Usart0_recv(uint8_t *data, uint32_t len) {
printf("%s\r\n", data);
}
static void RTC_config() {
// 电池管理加载
rcu_periph_clock_enable(RCU_PMU);
pmu_backup_write_enable();
// 重置备份域(不重置可能导致无法设置晶振,出现不走字情况)
/* reset backup domain */
rcu_bkp_reset_enable();
rcu_bkp_reset_disable();
// 晶振加载
rcu_osci_on(RCU_LXTAL);
rcu_osci_stab_wait(RCU_LXTAL);
rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);
// RTC功能加载
rcu_periph_clock_enable(RCU_RTC);
rtc_register_sync_wait();
rtc_parameter_struct rps;
rps.year = WRITE_BCD(23);
rps.month = WRITE_BCD(04);
rps.date = WRITE_BCD(20);
rps.day_of_week = WRITE_BCD(04);
rps.hour = WRITE_BCD(23);
rps.minute = WRITE_BCD(59);
rps.second = WRITE_BCD(55);
rps.display_format = RTC_24HOUR;
rps.am_pm = RTC_AM;
rps.factor_asyn = 0x7F;
rps.factor_syn = 0xFF;
rtc_init(&rps);
}
static void RTC_read() {
rtc_parameter_struct rps;
rtc_current_time_get(&rps);
uint16_t year = READ_BCD(rps.year) + 2000;
uint8_t month = READ_BCD(rps.month);
uint8_t date = READ_BCD(rps.date);
uint8_t week = READ_BCD(rps.day_of_week);
uint8_t hour = READ_BCD(rps.hour);
uint8_t minute = READ_BCD(rps.minute);
uint8_t second = READ_BCD(rps.second);
printf("%d-%d-%d %d %d:%d:%d\r\n", year, month, date, week, hour, minute, second);
}
static void ALARM_config() {
// 闹钟外部中断
exti_flag_clear(EXTI_17);
exti_init(EXTI_17,EXTI_INTERRUPT,EXTI_TRIG_RISING);
// 重置闹钟
rtc_alarm_disable(RTC_ALARM0);
rtc_alarm_struct ras;
ras.alarm_mask = RTC_ALARM_HOUR_MASK | RTC_ALARM_MINUTE_MASK | RTC_ALARM_SECOND_MASK;
ras.weekday_or_date = RTC_ALARM_DATE_SELECTED;
ras.alarm_day = 0x21;
ras.alarm_hour = WRITE_BCD(23);
ras.alarm_minute = WRITE_BCD(59);
ras.alarm_second = WRITE_BCD(59);
ras.am_pm = RTC_AM;
rtc_alarm_config(RTC_ALARM0, &ras);
// 中断配置
nvic_irq_enable(RTC_Alarm_IRQn, 2, 2);
rtc_interrupt_enable(RTC_INT_ALARM0);
rtc_flag_clear(RTC_FLAG_ALRM0);
rtc_alarm_enable(RTC_ALARM0);
}
void RTC_Alarm_IRQHandler() {
if (SET == rtc_flag_get(RTC_FLAG_ALRM0)) {
// 处理RTC闹钟中断
printf("alarm \r\n");
}
rtc_flag_clear(RTC_FLAG_ALRM0);
exti_flag_clear(EXTI_17);
}
int main(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
systick_config();
Usart0_init();
RTC_config();
ALARM_config();
while(1) {
RTC_read();
delay_1ms(1000);
}
}
3.2 闹钟Bug处理
如果Alarm闹钟未正确触发,或是频繁触发,先检查编译环境配置,尝试把Optimization等级分别调整为-O2
,-O3
,-Ofast
后再编译烧录。