二十八、RTC 实时时钟实验
28.1. RTC 的配置流程
- 解锁备份域的写保护。我们 RTC 的核心寄存器是在备份域里的,要对备份域进行操作需要解除写保护。所以上电复位后的第一步是解锁备份域的写保护。
- 设置时钟来源。 本案例的 RTC 时钟来源是使用外部低速时钟晶振 32.768Khz 提供的,所以我们在配置 RTC 的时钟时,需要选择开启外部低速时钟使能,选择它成为 RTC 的时钟来源。
- 开启 RTC 外设。 就是开启 RTC 的时钟,等待它配置完成。
- 配置日历时间。 包括年月日周时分秒。
28.2. 解除写保护
RTC 的核心寄存器是在备份域里的,而备份域是归属于 PMU 电源管理时钟的控制下的。需要先开启 PMU 的时钟,再使能备份域中寄存器的写访问。
//使能电源管理时钟
rcu_periph_clock_enable(RCU_PMU);
//启用对备份域中寄存器的写访问
pmu_backup_write_enable();
2
3
4
5
28.3. 设置时钟来源
RTC 的时钟来源有三种,内部 IRC32K 低速时钟、外部低速时钟 32.768KHz 和外部高速时钟 2~31MHz。
- 如果使用内部时钟,则在系统 VDD 断电的情况下,RTC 的时钟也跟着停止,导致 RTC 无法跑时;
- 如果使用外部高速时钟,则在 VBAT 接入有后备电池的情况下,可以让 RTC 在 VDD 断电的情况下继续跑时,但是使用外部高速时钟会有较高的功耗;
- 如果使用外部低速时钟,则可以满足断电跑时,并且功耗较低;
本案例的时钟来源选择使用外部低速时钟晶振 32.768Khz 提供,我们立创·梁山派开发板上也板载了外部低速时钟 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
28.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
28.5. 配置日历时间
结构体** rtc_parameter_struct** 可以一步到位的配置 RTC 外设的日历时间,相关参数如下:
配置完成之后,通过调用** rtc_init** 函数将配置的参数设置到 RTC 外设寄存器中。需要注意的是传入的参数必须按照 BCD 码的格式传入,例如要设置时间为 2023 年 4 月 24 日周一 12:24:59。则传入的参数如下:
RtcTimeConfig(0x23, 0x04, 0x24, 0x01, 0x12, 0x24, 0x59);
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.factor_asyn = 0x7F; //RTC异步预分频值:0x0 ~ 0x7F
rtc_initpara.factor_syn = 0xFF; //RTC同步预分频值:0x0 - 0x7FFF
rtc_initpara.year = year; //设置年份
rtc_initpara.month = month; //设置月份
rtc_initpara.date = date; //设置日期
rtc_initpara.day_of_week = week; //设置星期
rtc_initpara.hour = hour; //设置时
rtc_initpara.minute = minute; //设置分钟
rtc_initpara.second = second; //设置秒
rtc_initpara.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
28.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
28.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.factor_asyn = 0x7F; //RTC异步预分频值:0x0 ~ 0x7F
rtc_initpara.factor_syn = 0xFF; //RTC同步预分频值:0x0 - 0x7FFF
rtc_initpara.year = year; //设置年份
rtc_initpara.month = month; //设置月份
rtc_initpara.date = date; //设置日期
rtc_initpara.day_of_week = week; //设置星期
rtc_initpara.hour = hour; //设置时
rtc_initpara.minute = minute; //设置分钟
rtc_initpara.second = second; //设置秒
rtc_initpara.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,0x07,0x12,0x03,0x12,0x59,0x50);
}
}
/**********************************************************
* 函 数 名 称: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.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
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
bsp_rtc.h
#ifndef _BSP_RTC_H__
#define _BSP_RTC_H__
#include "gd32f4xx.h"
void rtc_config(void);
void RtcShowTime(void);
#endif
2
3
4
5
6
7
8
28.8 实验验证
在 main.c 中编写如下:
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "bsp_led.h"
#include "sys.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
22
23
24
实验现象:
关于这一章节的代码,在资源包/04软件资料/代码例程/里面的RTC。