二十七、RTC实时时钟实验
1. RTC的配置流程
解锁备份域的写保护。我们RTC的核心寄存器是在备份域里的,要对备份域进行操作需要解除写保护。所以上电复位后的第一步是解锁备份域的写保护。
设置时钟来源。本案例的RTC时钟来源是使用外部低速时钟晶振32.768Khz提供的,所以我们在配置RTC的时钟时,需要选择开启外部低速时钟使能,选择它成为RTC的时钟来源。
开启RTC外设。就是开启RTC的时钟,等待它配置完成。
配置日历时间。包括年月日周时分秒。
2. 解除写保护
RTC的核心寄存器是在备份域里的,而备份域是归属于PMU电源管理时钟的控制下的。需要先开启PMU的时钟,再使能备份域中寄存器的写访问。
//打开振荡器 32.768 KHz
rcu_osci_on(RCU_LXTAL);
//等待振荡器32.768 KHz稳定的标志设置或振荡器启动超时
rcu_osci_stab_wait(RCU_LXTAL);
//配置RTC时钟源选择LXTAL=32.768 KHz晶振
rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);
2
3
4
5
6
7
8
3. 设置时钟来源
RTC的时钟来源有三种,内部IRC32K低速时钟、外部低速时钟32.768KHz和外部高速时钟2~31MHz。
- 如果使用内部时钟,则在系统VDD断电的情况下,RTC的时钟也跟着停止,导致RTC无法跑时;
- 如果使用外部高速时钟,则在VBAT接入有后备电池的情况下,可以让RTC在VDD断电的情况下继续跑时,但是使用外部高速时钟会有较高的功耗;
- 如果使用外部低速时钟,则可以满足断电跑时,并且功耗较低;
本案例的时钟来源选择使用外部低速时钟晶振32.768Khz提供。
//打开振荡器 32.768 KHz
rcu_osci_on(RCU_LXTAL);
//等待振荡器32.768 KHz稳定的标志设置或振荡器启动超时
rcu_osci_stab_wait(RCU_LXTAL);
//配置RTC时钟源选择LXTAL=32.768 KHz晶振
rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);
2
3
4
5
6
7
8
4. 开启RTC外设
rcu_periph_clock_enable(RCU_RTC)
函数用于启用RTC模块的时钟。RTC模块是一个独立的实时时钟模块外设,它可以提供高精度的时间计数和时钟功能。通过启用RTC时钟,我们可以激活RTC模块并使用它的功能。接下来,
rtc_register_sync_wait()
函数是等待RTC寄存器更新同步的一个过程。在配置RTC之前,一般需要等待RTC寄存器更新的同步。这是因为RTC设置只有在寄存器更新期间才能生效。通过等待RTC寄存器更新同步,我们确保前一个RTC操作的设置已经完成并生效。
//开启RTC时钟
rcu_periph_clock_enable(RCU_RTC);
//等到时钟同步后,再进行寄存器更新
rtc_register_sync_wait();
//获取RTC时钟项选择
//获取备份域控制寄存器(RCU_BDCTL)
//的第8第9位状态
//RTCSRC_FLAG = GET_BITS(RCU_BDCTL, 8, 9);
2
3
4
5
6
7
8
9
5. 配置日历时间
结构体rtc_parameter_struct可以一步到位的配置RTC外设的日历时间,相关参数如下:
配置完成之后,通过调用 rtc_init 函数将配置的参数设置到RTC外设寄存器中。需要注意的是传入的参数必须按照BCD码的格式传入,例如要设置时间为 2023年11月27日周一 16:12:10。则传入的参数如下:
void RtcTimeConfig(uint8_t year, uint8_t month, uint8_t date, \
uint8_t week, uint8_t hour, uint8_t minute, uint8_t second)
{
rtc_parameter_struct rtc_initpara;
rtc_initpara.rtc_factor_asyn = 0x7F; //RTC异步预分频值:0x0 ~ 0x7F
rtc_initpara.rtc_factor_syn = 0xFF; //RTC同步预分频值:0x0 - 0x7FFF
rtc_initpara.rtc_year = year; //设置年份
rtc_initpara.rtc_month = month; //设置月份
rtc_initpara.rtc_date = date; //设置日期
rtc_initpara.rtc_day_of_week = week; //设置星期
rtc_initpara.rtc_hour = hour; //设置时
rtc_initpara.rtc_minute = minute; //设置分钟
rtc_initpara.rtc_second = second; //设置秒
rtc_initpara.rtc_display_format = RTC_24HOUR;//24小时制
// rtc_initpara.am_pm = RTC_PM;//午后 //12小时制才使用到
// RTC当前时间配置
rtc_init(&rtc_initpara);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
6. 读取RTC时间
通过函数 rtc_current_time_get 获取RTC寄存器中的时间信息。
/**********************************************************
* 函 数 名 称:BcdToDecimal
* 函 数 功 能:BCD转10进制
* 传 入 参 数:bcd = BCD码
* 函 数 返 回:转换完成的10进制
* 作 者:LCKFB
* 备 注:无
**********************************************************/
int BcdToDecimal(int bcd)
{
int decimal = 0;
int temp = 1;
int number = 0;
while(bcd > 0)
{
number = bcd % 16;
decimal += number * temp;
temp *= 10;
bcd /= 16;
}
return decimal;
}
void RtcShowTime(void)
{
rtc_parameter_struct rtc_initpara_time;
//获取RTC时间信息
rtc_current_time_get(&rtc_initpara_time);
//将获取到的RTC时间从BCD码,转为10进制再通过串口输出
printf("Current time: %d:%d:%d\n\r", \
BcdToDecimal(rtc_initpara_time.hour),
BcdToDecimal(rtc_initpara_time.minute),
BcdToDecimal(rtc_initpara_time.second));
printf("%d-%d-%d\n\r", \
BcdToDecimal(rtc_initpara_time.year),
BcdToDecimal(rtc_initpara_time.month),
BcdToDecimal(rtc_initpara_time.date));
}
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
7. RTC完整代码
bsp_rtc.c
#include "bsp_rtc.h"
#include "bsp_usart.h"
#include "stdio.h"
void RtcTimeConfig(uint8_t year, uint8_t month, uint8_t date, uint8_t week, uint8_t hour, uint8_t minute, uint8_t second)
{
rtc_parameter_struct rtc_initpara;
rtc_initpara.rtc_factor_asyn = 0x7F; //RTC异步预分频值:0x0 ~ 0x7F
rtc_initpara.rtc_factor_syn = 0xFF; //RTC同步预分频值:0x0 - 0x7FFF
rtc_initpara.rtc_year = year; //设置年份
rtc_initpara.rtc_month = month; //设置月份
rtc_initpara.rtc_date = date; //设置日期
rtc_initpara.rtc_day_of_week = week; //设置星期
rtc_initpara.rtc_hour = hour; //设置时
rtc_initpara.rtc_minute = minute; //设置分钟
rtc_initpara.rtc_second = second; //设置秒
rtc_initpara.rtc_display_format = RTC_24HOUR;//24小时制
// rtc_initpara.am_pm = RTC_PM;//午后 //12小时制才使用到
// RTC当前时间配置
rtc_init(&rtc_initpara);
}
void rtc_config(void)
{
//使能电源管理时钟
rcu_periph_clock_enable(RCU_PMU);
//启用对备份域中寄存器的写访问
pmu_backup_write_enable();
//打开振荡器 32.768 KHz
rcu_osci_on(RCU_LXTAL);
//等待振荡器32.768 KHz稳定的标志设置或振荡器启动超时
rcu_osci_stab_wait(RCU_LXTAL);
//配置RTC时钟源选择LXTAL=32.768 KHz晶振
rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);
//开启RTC时钟
rcu_periph_clock_enable(RCU_RTC);
//等到时钟同步后,再进行寄存器更新
rtc_register_sync_wait();
//获取RTC时钟项选择
//获取备份域控制寄存器(RCU_BDCTL)
//的第8第9位状态
//RTCSRC_FLAG = GET_BITS(RCU_BDCTL, 8, 9);
//判断是否是第一次上电
//如果之前没有操作过RTC_BKP0 ,则RTC_BKP0 默认为0.
//如果RTC_BKP0 值为 0xf234,说明已经上电过
if( RTC_BKP0 == 0xf234 )
{
}
else //备份寄存器0 如果不为 0XF234 说明是第一次上电
{
RTC_BKP0 = 0xf234;//设置为0XF234,表示上电过了
//配置RTC时间
RtcTimeConfig(0x23,0x11,0x27,0x01,0x16,0x12,0x10);
}
}
/**********************************************************
* 函 数 名 称:BcdToDecimal
* 函 数 功 能:BCD转10进制
* 传 入 参 数:bcd = BCD码
* 函 数 返 回:转换完成的10进制
* 作 者:LCKFB
* 备 注:无
**********************************************************/
int BcdToDecimal(int bcd)
{
int decimal = 0;
int temp = 1;
int number = 0;
while(bcd > 0)
{
number = bcd % 16;
decimal += number * temp;
temp *= 10;
bcd /= 16;
}
return decimal;
}
/**********************************************************
* 函 数 名 称:RtcShowTime
* 函 数 功 能:获取RTC时间并显示
* 传 入 参 数:无
* 函 数 返 回:无
* 作 者:LCKFB
* 备 注:该函数是通过串口输出时间的方式显示,可以更改为其他方式
**********************************************************/
void RtcShowTime(void)
{
rtc_parameter_struct rtc_initpara_time;
rtc_current_time_get(&rtc_initpara_time);
printf("Current time: %d:%d:%d\n\r",
BcdToDecimal(rtc_initpara_time.rtc_hour),
BcdToDecimal(rtc_initpara_time.rtc_minute),
BcdToDecimal(rtc_initpara_time.rtc_second));
printf("%d-%d-%d\n\r",
BcdToDecimal(rtc_initpara_time.rtc_year),
BcdToDecimal(rtc_initpara_time.rtc_month),
BcdToDecimal(rtc_initpara_time.rtc_date));
}
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
bsp_rtc.h
#ifndef _BSP_RTC_H__
#define _BSP_RTC_H__
#include "gd32e23x.h"
void rtc_config(void);
void RtcShowTime(void);
#endif
2
3
4
5
6
7
8
9
8. 实验验证
在main.c中编写如下:
#include "gd32e23x.h"
#include "systick.h"
#include <stdio.h>
#include "bsp_usart.h"
#include "bsp_rtc.h"
int main(void)
{
//滴答定时器初始化
systick_config();
//串口初始化
usart_gpio_config(9600U);
//RTC初始化
rtc_config();
while(1)
{
//获取RTC时间并通过串口输出
RtcShowTime();
delay_1ms(1000);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
实验现象:
完整代码工程文件:
通过网盘分享的文件:013RTC时钟代码.zip
链接: https://pan.baidu.com/s/12uzWxmm5Nm87ZbkI-xA15w?pwd=lckf 提取码: lckf