前言
本文是本人备赛物联网赛项的学习笔记,主要供本人学习、复习,不是经验分享或教学,若有错误,大佬轻喷。
一、RTC 基础概念扫盲
1.1 什么是 RTC?
RTC(Real-Time Clock,实时时钟)是 STM32 芯片内置的独立外设,核心作用是:
- 精准记录年、月、日、星期、时、分、秒;
- 系统掉电后,可通过备用电池(如 CR2032 纽扣电池)持续走时,重新上电无需重新设置时间。
注意 :RTC 是芯片内部功能,没有和任何 GPIO 引脚相连,无需配置 GPIO 即可使用!
二、CubeMX 全流程配置(保姆级步骤)
2.1 新建基础工程
- 打开 STM32CubeMX,点击ACCESS TO MCU SELECTOR;
- 搜索框输入
STM32WLE5CCU6,选中芯片后点击Start Project。
2.2 配置 RCC(外部晶振,RTC 精准走时的关键)
RTC 的精准走时依赖外部低速晶振(LSE),必须配置:
- 左侧Pinout & Configuration → System Core → RCC;
- Low Speed Clock (LSE) 选择
Crystal/Ceramic Resonator(外部晶振模式); - High Speed Clock (HSE) 根据开发板硬件选择对应模式即可

2.3 配置 USART2(用于串口打印验证)
为了后续能通过串口查看 RTC 时间,先配置基础串口:
- Connectivity → USART2;
- Mode 选择
Asynchronous(异步通信); - Parameter Settings 保持默认:波特率 115200 Bits/s,数据位 8,停止位 1,无校验。

2.4 RTC 核心功能配置(重点!)
这是 RTC 正常工作的核心步骤,一步都不能错:
- Timers → RTC;
- Mode 模式配置 :
- 勾选
Activate Clock Source(激活 RTC 时钟源); - 勾选
Activate Calendar(激活日历功能);
- 勾选
- Parameter Settings 参数配置 :
- Hour Format :选择
Hour Format 24(24 小时制,新手友好); - Asynchronous/Synchronous Predivider:保持默认值(异步 127、同步 255,32768kHz 晶振分频后刚好 1Hz 秒计数);
- Data Format :选择
BIN(二进制格式,方便代码读取); - Calendar Time :Hours/Minutes/Seconds 为上电默认时间,直接修改这里的数值即可设置初始时间;
- Calendar Date :Year/Month/Date/WeekDay 为默认日期,新手先保持默认,实际项目按需修改。

- Hour Format :选择
2.5 时钟树配置
- 顶部Clock Configuration;
- 确认 RTC 时钟源选择为LSE(外部低速晶振,确保走时精准);
- 其他参数保持默认,点击空白处自动完成时钟树计算。

2.6 工程生成配置
- 顶部Project Manager :
- Project 选项卡 :设置工程名称、存放路径(重点:路径绝对不能有中文、空格、特殊字符! ),Toolchain/IDE 选择
MDK-ARM V5; - Code Generator 选项卡 :勾选
Generate peripheral initialization as a pair of '.c/.h' files per peripheral(每个外设生成独立文件,代码更清晰)。
- Project 选项卡 :设置工程名称、存放路径(重点:路径绝对不能有中文、空格、特殊字符! ),Toolchain/IDE 选择
- 点击右上角GENERATE CODE 生成代码,弹出提示后点击Open Project打开 Keil 工程。
三、逐行注释代码实现(仅聚焦 RTC)
我们创建两个独立文件app.h和app.c(将原讲义的zsdz统一替换为通用的app),完全解耦 CubeMX 生成的代码,重新生成 CubeMX 代码不会被覆盖。
3.1 编写 app.h 头文件
头文件核心作用是定义 RTC 时间结构体 、声明全局变量与函数,防止头文件重复包含。
cpp
#ifndef __APP_H // 防止头文件被重复包含的宏定义
#define __APP_H
#include "string.h" // 引入字符串操作头文件(基础依赖)
#include "stdio.h" // 引入标准输入输出头文件(printf串口打印依赖)
#include "gpio.h" // 引入GPIO驱动头文件(基础依赖)
// 定义RTC时间存储结构体:统一封装年月日周、时分秒
typedef struct
{
unsigned char Year; // 存储RTC读取的年份值
unsigned char Month; // 存储RTC读取的月份值(范围1-12)
unsigned char WeekDay; // 存储RTC读取的星期值(范围1-7)
unsigned char Date; // 存储RTC读取的日期值(范围1-31)
unsigned char Hours; // 存储RTC读取的小时值(范围0-23)
unsigned char Minutes; // 存储RTC读取的分钟值(范围0-59)
unsigned char Seconds; // 存储RTC读取的秒数值(范围0-59)
}RTC_APP;
extern RTC_APP rtc_value; // 声明全局RTC结构体变量,供其他文件(如main.c)直接调用
void app_read_RTC(void); // 声明RTC时间读取核心函数
#endif // 头文件结束标记
3.2 编写 app.c 源文件
源文件实现头文件中声明的函数,核心是RTC 时间的读取逻辑。
cpp
#include "app.h" // 包含自定义头文件,获取RTC结构体和函数声明
RTC_APP rtc_value; // 定义全局RTC结构体变量,实际存储读取到的RTC时间
// 函数功能:从RTC外设读取当前时间日期,存入全局结构体rtc_value中
// 入口参数:无
// 返回值:无
// 备注:必须先读时间,再读日期,顺序不能反!
void app_read_RTC(void)
{
extern RTC_HandleTypeDef hrtc; // 声明外部RTC句柄(由CubeMX生成,定义在rtc.c中)
RTC_DateTypeDef data_value; // 定义HAL库标准日期结构体,临时存储日期数据
RTC_TimeTypeDef time_value; // 定义HAL库标准时间结构体,临时存储时间数据
// 【新手必记坑点】必须先调用HAL_RTC_GetTime读取时间
// 参数1:RTC句柄;参数2:时间存储结构体;参数3:数据格式(RTC_FORMAT_BIN=二进制)
HAL_RTC_GetTime(&hrtc, &time_value, RTC_FORMAT_BIN);
// 【新手必记坑点】必须再调用HAL_RTC_GetDate读取日期,两个函数必须成对调用
HAL_RTC_GetDate(&hrtc, &data_value, RTC_FORMAT_BIN);
// 将读取到的日期数据赋值给全局RTC结构体
rtc_value.Year = data_value.Year; // 年份赋值
rtc_value.Month = data_value.Month; // 月份赋值
rtc_value.WeekDay = data_value.WeekDay; // 星期赋值
rtc_value.Date = data_value.Date; // 日期赋值
// 将读取到的时间数据赋值给全局RTC结构体
rtc_value.Hours = time_value.Hours; // 小时赋值
rtc_value.Minutes = time_value.Minutes; // 分钟赋值
rtc_value.Seconds = time_value.Seconds; // 秒数赋值
}
3.3 修改 main.c 主函数
重点提醒 :所有用户代码必须写在USER CODE BEGIN X和USER CODE END X之间,否则重新生成 CubeMX 代码时会被覆盖!
步骤 1:引入头文件
找到USER CODE BEGIN Includes代码段,添加如下代码:
cpp
/* USER CODE BEGIN Includes */
#include "app.h" // 引入我们自定义的RTC头文件
#include "stdio.h" // 引入标准输入输出头文件(printf依赖)
/* USER CODE END Includes */
步骤 2:添加 printf 重定向代码
找到USER CODE BEGIN 0代码段,添加串口 printf 重定向代码,否则无法通过串口打印时间:
cpp
/* USER CODE BEGIN 0 */
// printf串口重定向函数:将printf的输出重定向到USART2
int fputc(int ch, FILE *f)
{
extern UART_HandleTypeDef huart2; // 声明外部USART2句柄(由CubeMX生成)
// 通过USART2发送一个字符,参数1:USART2句柄;参数2:字符指针;参数3:发送长度;参数4:超时时间
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);
return ch; // 返回发送的字符
}
/* USER CODE END 0 */
步骤 3:主循环添加 RTC 读取与打印
找到 while (1) 主循环中的USER CODE BEGIN WHILE代码段,添加核心逻辑:
cpp
/* USER CODE BEGIN WHILE */
while (1)
{
app_read_RTC(); // 调用自定义函数,读取RTC当前时间到全局rtc_value
// 串口格式化打印RTC时间,%02d表示两位数显示,不足补0,格式更规范
printf("RTC实时时间:%02d-%02d-%02d 星期%d %02d:%02d:%02d\r\n",
rtc_value.Year, // 打印年份
rtc_value.Month, // 打印月份
rtc_value.Date, // 打印日期
rtc_value.WeekDay, // 打印星期
rtc_value.Hours, // 打印小时
rtc_value.Minutes, // 打印分钟
rtc_value.Seconds);// 打印秒数
HAL_Delay(500); // 延时500ms,控制打印频率,避免串口刷屏
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
四、编译下载与效果验证
- 编译工程 :点击 Keil 左侧Rebuild 按钮,全量编译,出现
0 Error(s), 0 Warning(s)即为成功; - 下载程序 :连接 ST-Link 仿真器,点击Load按钮下载程序,下载完成后按下开发板复位键;
- 串口验证:打开串口助手,选择对应串口号,配置波特率 115200、数据位 8、停止位 1、无校验,打开串口即可看到 RTC 实时时间每秒自动更新!