二十八、RTC实时时钟实验
WARNING
📌 注意:高低配版都没有贴电池座,需要自行焊接电池座并加入电池才能做掉电实验。
1. RTC的配置流程
- 解锁备份域的写保护。我们RTC的核心寄存器是在备份域里的,要对备份域进行操作需要解除写保护。所以上电复位后的第一步是解锁备份域的写保护。
- 设置时钟来源。本案例的RTC时钟来源是使用外部低速时钟晶振32.768Khz提供的,所以我们在配置RTC的时钟时,需要选择开启外部低速时钟使能,选择它成为RTC的时钟来源。
- 开启RTC外设。就是开启RTC的时钟,等待它配置完成。
- 配置日历时间。包括年月日周时分秒。
2. 解除写保护
RTC的核心寄存器是在备份域里的,而备份域是归属于PMU电源管理时钟的控制下的。需要先开启PMU的时钟,再使能备份域中寄存器的写访问。
//使能电源管理时钟
rcu_periph_clock_enable(RCU_PMU);
//启用对备份域中寄存器的写访问
pmu_backup_write_enable();
2
3
4
5
3. 设置时钟来源
RTC的时钟来源有三种,内部IRC32K低速时钟、外部低速时钟32.768KHz和外部高速时钟2~31MHz。
- 如果使用内部时钟,则在系统VDD断电的情况下,RTC的时钟也跟着停止,导致RTC无法跑时;
- 如果使用外部高速时钟,则在VBAT接入有后备电池的情况下,可以让RTC在VDD断电的情况下继续跑时,但是使用外部高速时钟会有较高的功耗;
- 如果使用外部低速时钟,则可以满足断电跑时,并且功耗较低; 本案例的时钟来源选择使用外部低速时钟晶振32.768Khz提供,我们立创·梁山派天空星开发板上也板载了外部低速时钟32.768Khz。
WARNING
📌 注意:青春版没有贴低速晶振,如果用户不贴低速晶振而使用他,程序会直接卡死,青春版使用外部低速晶振需要自行焊接。
//打开振荡器 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
9
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年12月1日周五 10:30:01。则传入的参数如下:
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
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完整代码
rtc.c
#include "rtc.h"
#include "bsp_uart.h"
void bsp_rtc_init(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 值为 0x1234,说明已经上电过
if( RTC_BKP0 == 0x1234 )
{
}
else //备份寄存器0 如果不为 0X1234 说明是第一次上电
{
RTC_BKP0 = 0x1234;//设置为0X1234,表示上电过了
//配置RTC时间
RtcTimeConfig(0x23,0x12,0x01,0x05,0x10,0x52,0x01);
}
}
/***********************************
* 函数名 :RtcTimeConfig
* 函数功能:设置日期时间
***********************************/
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_init(&rtc_initpara);// 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
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
rtc.h
#ifndef __RTC_H__
#define __RTC_H__
#include "gd32f4xx.h"
void bsp_rtc_init(void);
void RtcShowTime(void);
void RtcTimeConfig(uint8_t year, uint8_t month, uint8_t date, uint8_t week, \
uint8_t hour, uint8_t minute, uint8_t second);
#endif
2
3
4
5
6
7
8
9
10
11
12
13
8. 实验验证
在main.c中编写如下:
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 立创论坛:club.szlcsc.com
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
* Change Logs:
* Date Author Notes
* 2023-11-02 LCKFB-yzh first version
*/
#include "board.h"
#include "rtc.h"
int main(void)
{
board_init();
bsp_uart_init();
bsp_rtc_init();
while (1)
{
//获取RTC时间并通过串口输出
RtcShowTime();
delay_ms(1000);
}
}
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
实验现象:
完整代码工程文件:
下载
在下载中心
的入门手册资料(点击跳转🚀)百度网盘链接下载 软件资料
-> 代码例程
里面,可以下载需要的例程。