二十八、RTC 实时时钟实验
RTC 的配置流程
- 解锁备份域的写保护:我们 RTC 的核心寄存器是在备份域里的,要对备份域进行操作需要解除写保护。所以上电复位后的第一步是解锁备份域的写保护。
- 设置时钟来源:本案例的 RTC 时钟来源是使用外部低速时钟晶振 32.768Khz 提供的,所以我们在配置 RTC 的时钟时,需要选择开启外部低速时钟使能,选择它成为 RTC 的时钟来源。
- 开启 RTC 外设:就是开启 RTC 的时钟,等待它配置完成。
- 配置日历时间:包括年月日周时分秒。
解除写保护
RTC 的核心寄存器是在备份域里的,而备份域是归属于 PMU 电源管理时钟的控制下的。需要先开启 PMU 的时钟,再使能备份域中寄存器的写访问。
//使能电源管理时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
PWR_BackupAccessCmd(ENABLE);//使能备份寄存器操作
2
3
4
设置时钟来源
RTC 的时钟来源有三种,内部 IRC32K 低速时钟、外部低速时钟 32.768KHz 和外部高速时钟 2~31MHz。
- 如果使用内部时钟,则在系统 VDD 断电的情况下,RTC 的时钟也跟着停止,导致 RTC 无法跑时;
- 如果使用外部高速时钟,则在 VBAT 接入有后备电池的情况下,可以让 RTC 在 VDD 断电的情况下继续跑时,但是使用外部高速时钟会有较高的功耗;
- 如果使用外部低速时钟,则可以满足断电跑时,并且功耗较低;
本案例的时钟来源选择使用外部低速时钟晶振 32.768Khz 提供,我们立创·梁山派天空星开发板上也板载了外部低速时钟 32.768Khz(自己贴的)。
// 打开外部振荡器 32.768 KHz
// 注意:青春版没有贴低速晶振,如果是青春版使用低速晶振会直接卡死,所以青春版需要更换时钟源。
RCC_LSICmd(ENABLE); // 青春版本使用这个
// RCC_LSEConfig(RCC_LSE_ON); // 打开外部低速晶振
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); // 低配版使用
// RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); // 高配版本使用
2
3
4
5
6
7
开启 RTC 外设
RCC_RTCCLKCmd``()
函数用于启用 RTC 模块的时钟。RTC 模块是一个独立的实时时钟模块外设,它可以提供高精度的时间计数和时钟功能。通过启用 RTC 时钟,我们可以激活 RTC 模块并使用它的功能。接下来,RTC_WaitForSynchro()
函数是等待 RTC 寄存器更新同步的一个过程。在配置 RTC 之前,一般需要等待 RTC 寄存器更新的同步。这是因为 RTC 设置只有在寄存器更新期间才能生效。通过等待 RTC 寄存器更新同步,我们确保前一个 RTC 操作的设置已经完成并生效。
RCC_RTCCLKCmd(ENABLE); // 使能RTC时钟
RTC_WaitForSynchro();
2
配置日历时间
结构体 RTC_TimeTypeDef 和 RTC_DateTypeDef 可以一步到位的配置 RTC 外设的日历时间,相关参数如下:
配置完成之后,通过调用 RTC_SetTime 和 RTC_SetDate 函数将配置的参数设置到 RTC 外设寄存器中。需要注意的是传入的参数必须按照 BCD 码的格式传入,例如要设置时间为 2024 年 03 月 11 日周一 18:10:01。则传入的参数如下:
RtcTimeConfig(24,3,11,1,18,10,1, RTC_H12_AM);
/***********************************
* 函数名 :RtcTimeConfig
* 函数功能:设置日期时间
* @defgroup RTC_AM_PM_Definitions
*
* #define RTC_H12_AM ((uint8_t)0x00)
* #define RTC_H12_PM ((uint8_t)0x40)
***********************************/
void RtcTimeConfig(uint8_t year, uint8_t month, uint8_t date, uint8_t week, \
uint8_t hour, uint8_t minute, uint8_t second, uint8_t RTC_H12)
{
RTC_WriteProtectionCmd(DISABLE); // 打开写保护
RTC_InitTypeDef RTC_InitStructure;
RTC_EnterInitMode(); // 使能RTC编辑模式
RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24; // 设置为24小时制
RTC_InitStructure.RTC_AsynchPrediv = 0x7F;
RTC_InitStructure.RTC_SynchPrediv = 0xFF;
RTC_Init(&RTC_InitStructure);
// 时间设定
RTC_TimeTypeDef RTC_TimeStructure;
RTC_TimeStructure.RTC_Seconds = second;
RTC_TimeStructure.RTC_Minutes = minute;
RTC_TimeStructure.RTC_Hours = hour;
RTC_TimeStructure.RTC_H12 = RTC_H12;
RTC_SetTime(RTC_Format_BIN,&RTC_TimeStructure);
// 日期设定
RTC_DateTypeDef RTC_DateStructure;
RTC_DateStructure.RTC_Date = date;
RTC_DateStructure.RTC_Month = month;
RTC_DateStructure.RTC_WeekDay= week;
RTC_DateStructure.RTC_Year = year;
RTC_SetDate(RTC_Format_BIN,&RTC_DateStructure);
RTC_ExitInitMode(); // 关闭RTC编辑模式
RTC_WriteBackupRegister(RTC_BKP_DR0,0x2002);//初始化完成,设置标志
RTC_WriteProtectionCmd(ENABLE);
}
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
读取 RTC 时间
通过函数 RTC_GetTime 和 RTC_GetDate 获取 RTC 寄存器中的时间信息。
void RtcShowTime(void)
{
// 时间结构体
RTC_TimeTypeDef RTC_TimeStructure;
// 日期结构体
RTC_DateTypeDef RTC_DateStructure;
// 获取RTC日期
RTC_GetTime(RTC_Format_BIN, &RTC_TimeStructure);
// 获取RTC时间
RTC_GetDate(RTC_Format_BIN, &RTC_DateStructure);
//将获取到的RTC时间BIN二进制再通过串口输出
printf("Current time: %d:%d:%d ", \
RTC_TimeStructure.RTC_Hours,
RTC_TimeStructure.RTC_Minutes,
RTC_TimeStructure.RTC_Seconds);
printf("%d-%d-%d\n\r", \
RTC_DateStructure.RTC_Year,
RTC_DateStructure.RTC_Month,
RTC_DateStructure.RTC_Date);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
RTC 完整代码
rtc.c
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 立创论坛:https://oshwhub.com/forum
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*
Change Logs:
* Date Author Notes
* 2024-03-11 LCKFB-LP first version
*/
#include "rtc.h"
#include "bsp_uart.h"
#include "stdio.h"
void bsp_rtc_init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
PWR_BackupAccessCmd(ENABLE);//使能备份寄存器操作
RCC_LSICmd(ENABLE); // 打开外部低速晶振
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
RCC_RTCCLKCmd(ENABLE); // 使能RTC时钟
RTC_WaitForSynchro();
if(RTC_ReadBackupRegister(RTC_BKP_DR0) != 0x2002) //一个变量,看看RTC初始化没
{
printf("Set Time and Date.....\r\n");
RtcTimeConfig(24,3,11,1,18,10,1, RTC_H12_AM);
printf("Set End!!\r\n");
}
PWR_BackupAccessCmd(DISABLE); // 关闭备份寄存器
}
/***********************************
* 函数名 :RtcTimeConfig
* 函数功能:设置日期时间
* @defgroup RTC_AM_PM_Definitions
*
* #define RTC_H12_AM ((uint8_t)0x00)
* #define RTC_H12_PM ((uint8_t)0x40)
***********************************/
void RtcTimeConfig(uint8_t year, uint8_t month, uint8_t date, uint8_t week, \
uint8_t hour, uint8_t minute, uint8_t second, uint8_t RTC_H12)
{
RTC_WriteProtectionCmd(DISABLE); // 打开写保护
RTC_InitTypeDef RTC_InitStructure;
RTC_EnterInitMode(); // 使能RTC编辑模式
RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24; // 设置为24小时制
RTC_InitStructure.RTC_AsynchPrediv = 0x7F;
RTC_InitStructure.RTC_SynchPrediv = 0xFF;
RTC_Init(&RTC_InitStructure);
// 时间设定
RTC_TimeTypeDef RTC_TimeStructure;
RTC_TimeStructure.RTC_Seconds = second;
RTC_TimeStructure.RTC_Minutes = minute;
RTC_TimeStructure.RTC_Hours = hour;
RTC_TimeStructure.RTC_H12 = RTC_H12;
RTC_SetTime(RTC_Format_BIN,&RTC_TimeStructure);
// 日期设定
RTC_DateTypeDef RTC_DateStructure;
RTC_DateStructure.RTC_Date = date;
RTC_DateStructure.RTC_Month = month;
RTC_DateStructure.RTC_WeekDay= week;
RTC_DateStructure.RTC_Year = year;
RTC_SetDate(RTC_Format_BIN,&RTC_DateStructure);
RTC_ExitInitMode(); // 关闭RTC编辑模式
RTC_WriteBackupRegister(RTC_BKP_DR0,0x2002);//初始化完成,设置标志
RTC_WriteProtectionCmd(ENABLE);
}
/**********************************************************
* 函 数 名 称:BcdToDecimal
* 函 数 功 能:BCD转10进制
* 传 入 参 数:bcd = BCD码
* 函 数 返 回:转换完成的10进制
* 作 者:LCKFB
* 备 注:无
**********************************************************/
int BcdToDecimal(int bcd)
{
int decimal = 0;
int temp = 1;
int number = 0;
if( bcd >= 0x0A ) // 如果大于或等于10
{
while(bcd > 0)
{
number = bcd % 16;
decimal += number * temp;
temp *= 10;
bcd /= 16;
}
return decimal;
}
return bcd;
}
void RtcShowTime(void)
{
// 时间结构体
RTC_TimeTypeDef RTC_TimeStructure;
// 日期结构体
RTC_DateTypeDef RTC_DateStructure;
// 获取RTC日期
RTC_GetTime(RTC_Format_BIN, &RTC_TimeStructure);
// 获取RTC时间
RTC_GetDate(RTC_Format_BIN, &RTC_DateStructure);
//将获取到的RTC时间BIN二进制再通过串口输出
printf("Current time: %d:%d:%d ", \
RTC_TimeStructure.RTC_Hours,
RTC_TimeStructure.RTC_Minutes,
RTC_TimeStructure.RTC_Seconds);
printf("%d-%d-%d\n\r", \
RTC_DateStructure.RTC_Year,
RTC_DateStructure.RTC_Month,
RTC_DateStructure.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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
rtc.h
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 立创论坛:https://oshwhub.com/forum
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*
Change Logs:
* Date Author Notes
* 2024-03-11 LCKFB-LP first version
*/
#ifndef __RTC_H__
#define __RTC_H__
#include "stm32f4xx.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, uint8_t RTC_H12);
#endif
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
实验验证
在 main.c 中编写如下:
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 立创论坛:https://oshwhub.com/forum
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*
Change Logs:
* Date Author Notes
* 2024-03-11 LCKFB-LP first version
*/
#include "board.h"
#include "bsp_uart.h"
#include <stdio.h>
#include "rtc.h"
int main(void)
{
board_init();
uart1_init(115200U);
printf("RTC Init Start.....\r\n");
bsp_rtc_init(); // RTC 初始化
printf("RTC Init Successful!!\r\n");
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
28
29
30
31
32
33
34
实验现象:
进行了 9 秒的断电实验,发现拔掉 USB 之后 RTC 依靠电池继续进行计时。
注意:进行掉电实验要在开发板背面焊接纽扣电池底座,然后选择合适的纽扣电池安装。
这一章节的代码
在开发板介绍百度网盘链接中:立创·梁山派·天空星STM32F407VET6开发板资料/第03章软件资料/代码例程/015RTC时钟实验(可做掉电实验)。