32HAL——RTC时钟
一、RTC简介
RTC(Real Time Clock),即实时时钟,类似于钟表一般,能够持续记录时间,为程序提供精确的日期和时间信息,即使在断电期间也能确保准确运行。
二、 原理和特点
- 在STM32中,存在两个时钟源:高速时钟(8 MHz)和低速时钟(32.768 kHz)。高速时钟用于驱动CPU、外设和定时器等核心组件,而低速时钟则负责管理看门狗和RTC等功能。
- RTC依赖低速时钟运行。
- RTC模块内部包含了一个独立的32位寄存器来保存当前的时间戳信息。
- 低速时钟以极低的功耗运行,即使在断电情况下,通过备用电源(如纽扣电池),RTC也能持续运行以确保时间准确性。
三、cubeMx相关配置
1. 开启外部晶振
低速时钟选用外部晶振

2. 配置RTC时钟频率
选用外部低速晶振(LSE)

3. 激活RTC

四、重写RTC库
myRTC.c
#include "myRTC.h"/*** @brief Enters the RTC Initialization mode.* @param hrtc pointer to a RTC_HandleTypeDef structure that contains* the configuration information for RTC.* @retval HAL status*/ static HAL_StatusTypeDef RTC_EnterInitMode(RTC_HandleTypeDef *hrtc) {uint32_t tickstart = 0U;tickstart = HAL_GetTick();/* Wait till RTC is in INIT state and if Time out is reached exit */while ((hrtc->Instance->CRL & RTC_CRL_RTOFF) == (uint32_t)RESET){if ((HAL_GetTick() - tickstart) > RTC_TIMEOUT_VALUE){return HAL_TIMEOUT;}}/* Disable the write protection for RTC registers */__HAL_RTC_WRITEPROTECTION_DISABLE(hrtc);return HAL_OK; }/*** @brief Exit the RTC Initialization mode.* @param hrtc pointer to a RTC_HandleTypeDef structure that contains* the configuration information for RTC.* @retval HAL status*/ static HAL_StatusTypeDef RTC_ExitInitMode(RTC_HandleTypeDef *hrtc) {uint32_t tickstart = 0U;/* Disable the write protection for RTC registers */__HAL_RTC_WRITEPROTECTION_ENABLE(hrtc);tickstart = HAL_GetTick();/* Wait till RTC is in INIT state and if Time out is reached exit */while ((hrtc->Instance->CRL & RTC_CRL_RTOFF) == (uint32_t)RESET){if ((HAL_GetTick() - tickstart) > RTC_TIMEOUT_VALUE){return HAL_TIMEOUT;}}return HAL_OK; }/*** @brief Read the time counter available in RTC_CNT registers.* @param hrtc pointer to a RTC_HandleTypeDef structure that contains* the configuration information for RTC.* @retval Time counter*/ static uint32_t RTC_ReadTimeCounter(RTC_HandleTypeDef *hrtc) {uint16_t high1 = 0U, high2 = 0U, low = 0U;uint32_t timecounter = 0U;high1 = READ_REG(hrtc->Instance->CNTH & RTC_CNTH_RTC_CNT);low = READ_REG(hrtc->Instance->CNTL & RTC_CNTL_RTC_CNT);high2 = READ_REG(hrtc->Instance->CNTH & RTC_CNTH_RTC_CNT);if (high1 != high2){/* In this case the counter roll over during reading of CNTL and CNTH registers,read again CNTL register then return the counter value */timecounter = (((uint32_t) high2 << 16U) | READ_REG(hrtc->Instance->CNTL & RTC_CNTL_RTC_CNT));}else{/* No counter roll over during reading of CNTL and CNTH registers, countervalue is equal to first value of CNTL and CNTH */timecounter = (((uint32_t) high1 << 16U) | low);}return timecounter; }/*** @brief Write the time counter in RTC_CNT registers.* @param hrtc pointer to a RTC_HandleTypeDef structure that contains* the configuration information for RTC.* @param TimeCounter: Counter to write in RTC_CNT registers* @retval HAL status*/ static HAL_StatusTypeDef RTC_WriteTimeCounter(RTC_HandleTypeDef *hrtc, uint32_t TimeCounter) {HAL_StatusTypeDef status = HAL_OK;/* Set Initialization mode */if (RTC_EnterInitMode(hrtc) != HAL_OK){status = HAL_ERROR;}else{/* Set RTC COUNTER MSB word */WRITE_REG(hrtc->Instance->CNTH, (TimeCounter >> 16U));/* Set RTC COUNTER LSB word */WRITE_REG(hrtc->Instance->CNTL, (TimeCounter & RTC_CNTL_RTC_CNT));/* Wait for synchro */if (RTC_ExitInitMode(hrtc) != HAL_OK){status = HAL_ERROR;}}return status; }//设置RTC时间 HAL_StatusTypeDef myRTC_SetTime(struct tm *time) {uint32_t unixTime = mktime(time); // 将实际时间转换为时间戳return RTC_WriteTimeCounter(&hrtc, unixTime); }//获取RTC时间 struct tm *myRTC_GetTime(void) {time_t unixTime = RTC_ReadTimeCounter(&hrtc); //将时间戳转换为实际时间return gmtime(&unixTime); }void myRTC_Init(void) {// 先获取初始化标志位uint32_t initFlag = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1);// 如果初始化标志位是我们设定的值,说明已经初始化过一次了,则直接跳出初始化if (initFlag == RTC_INIT_FLAG) return;// cubeMx生成的RTC初始化代码if (HAL_RTC_Init(&hrtc) != HAL_OK){Error_Handler();}// 如果第一次初始化则给时间赋初始值, 这里使用结构体, 跟time.h一样struct tm time = {.tm_year = 2025 - 1900, // 注意这里的年是当前年与1900的差值.tm_mon = 11 - 1, // 这里的月是0-11,所以实际月份要减1.tm_mday = 7,.tm_hour = 16,.tm_min = 53,.tm_sec = 55,};//将时间存入RTCmyRTC_SetTime(&time);//运行到这里说明是第一次初始化,所以要把设定好的初始化标志位存进BKP, 防止下次时间被重新赋值HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, RTC_INIT_FLAG); }
myRTC.h
// // Created by Mood on 2025/11/7. //#ifndef RTC_DEMO_MYRTC_H #define RTC_DEMO_MYRTC_H#include "stm32f1xx_hal.h" #include "rtc.h" #include <time.h>#define RTC_INIT_FLAG 0x2333HAL_StatusTypeDef myRTC_SetTime(struct tm *time); struct tm *myRTC_GetTime(void); void myRTC_Init(void);#endif //RTC_DEMO_MYRTC_H
注:每次复位时代码均要进行一次初始化,cubeMx生成的RTC初始化代码中有一部分每次都会消耗一定时间,导致实时时间会偏慢,所以我们只在第一次初始化,后面复位均跳过此初始化。
void MX_RTC_Init(void)
{/* USER CODE BEGIN RTC_Init 0 */hrtc.Instance = RTC;hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;hrtc.Init.OutPut = RTC_OUTPUTSOURCE_ALARM;myRTC_Init();return;/* USER CODE END RTC_Init 0 *//* USER CODE BEGIN RTC_Init 1 *//* USER CODE END RTC_Init 1 *//** Initialize RTC Only*/hrtc.Instance = RTC;hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;hrtc.Init.OutPut = RTC_OUTPUTSOURCE_ALARM;// 该初始化代码,每次复位时均会消耗一段时间,导致时间偏慢,所以我们在自己的初始化函数中,将其设置为只有第一次初始化才会调用此函数,后面每次复位// 均会跳过if (HAL_RTC_Init(&hrtc) != HAL_OK){Error_Handler();}/* USER CODE BEGIN RTC_Init 2 *//* USER CODE END RTC_Init 2 */}
五、示例程序
该代码实现了实时时钟,并且具有掉电不丢失功能,当然初始时间需自己在myRTC_Init()函数中设置。
/* USER CODE BEGIN Header */
/********************************************************************************* @file : main.c* @brief : Main program body******************************************************************************* @attention** Copyright (c) 2025 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "rtc.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "oled.h"
#include <stdio.h>
#include "myRTC.h"
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_I2C2_Init();MX_RTC_Init();/* USER CODE BEGIN 2 */HAL_Delay(20);OLED_Init();char message[50];struct tm *now;/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){OLED_NewFrame();now = myRTC_GetTime();sprintf(message, "%d-%d-%d %d:%d:%d", now->tm_year, now->tm_mon, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec);OLED_PrintASCIIString(0, 0, message, &afont12x6, OLED_COLOR_NORMAL);OLED_ShowFrame();/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
注:上述相关资料均来自ksysking
