🕒HAL庫中的實現:RTC(Real-Time Clock)實時時鐘
RTC 是 STM32 的低功耗實時時鐘模塊,常用于:
- 實時時間維護(年月日時分秒)
- 定時喚醒
- 日志時間戳
- 鬧鐘功能
RTC(實時時鐘)模塊 提供了 三個中斷源,用于在不同時間條件下觸發事件。這些中斷在 低功耗喚醒、定時任務、系統監控等場景下使用。
🕒 RTC 的三個中斷源
中斷類型 | 含義 | 用途 | HAL支持情況 |
---|---|---|---|
秒中斷(Second Interrupt) | 每秒觸發一次 | 定時刷新、節拍事件 | ? HAL 默認未封裝,需裸機配置 |
鬧鐘中斷(Alarm Interrupt) | 到指定時間觸發一次 | 定時喚醒、事件提醒 | ? HAL 封裝完整 |
溢出中斷(Overflow Interrupt) | RTC 計數器溢出時觸發 | 長時間周期事件(如每86400秒) | ? HAL 默認未封裝,需裸機配置 |
CubeMX配置:
RTC時鐘秒更新中斷流程:
🧾 業務代碼配置( RTC 秒中斷功能)
配置流程:
步驟 | 狀態 | 說明 |
---|---|---|
RTC 初始化 | ? | 使用 MX_RTC_Init() 初始化 |
啟用秒中斷 | ? | __HAL_RTC_SECOND_ENABLE_IT(&hrtc, RTC_IT_SEC); |
中斷向量啟用 | ? | RTC_IRQn 已在中斷向量表中啟用 |
回調函數實現 | ? | HAL_RTCEx_RTCEventCallback() 已實現 |
中斷函數轉發 | ? | RTC_IRQHandler() 中調用了 HAL_RTCEx_RTCIRQHandler() |
時鐘源配置 | ? | 使用 LSI,穩定性較好 |
獲取時間 | ? | 每秒打印當前時間 |
+------------------+
| RTC 每秒更新 |
+--------+---------+|v
+-------------------------+
| RTC_IRQn 中斷觸發 |
+-------------------------+|v
+-------------------------+
| HAL_RTCEx_RTCIRQHandler |
+-------------------------+|v
+-----------------------------+
| HAL_RTCEx_RTCEventCallback |
| (打印函數) |
+-----------------------------+
完整代碼(中斷的方式獲取時間)
📄 stm32f1xx_it.c
/* USER CODE BEGIN Header */
/********************************************************************************* @file stm32f1xx_it.c* @brief Interrupt Service Routines.******************************************************************************* @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 "stm32f1xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "usart.h"
#include "iwdg.h"
#include "rtc.h"
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD *//* USER CODE END TD *//* 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 -----------------------------------------------*/
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END 0 *//* External variables --------------------------------------------------------*/
extern RTC_HandleTypeDef hrtc;
extern TIM_HandleTypeDef htim3;
extern UART_HandleTypeDef huart1;
/* USER CODE BEGIN EV *//* USER CODE END EV *//******************************************************************************/
/* Cortex-M3 Processor Interruption and Exception Handlers */
/******************************************************************************/
/*** @brief This function handles Non maskable interrupt.*/
void NMI_Handler(void)
{/* USER CODE BEGIN NonMaskableInt_IRQn 0 *//* USER CODE END NonMaskableInt_IRQn 0 *//* USER CODE BEGIN NonMaskableInt_IRQn 1 */while (1){}/* USER CODE END NonMaskableInt_IRQn 1 */
}/*** @brief This function handles Hard fault interrupt.*/
void HardFault_Handler(void)
{/* USER CODE BEGIN HardFault_IRQn 0 *//* USER CODE END HardFault_IRQn 0 */while (1){/* USER CODE BEGIN W1_HardFault_IRQn 0 *//* USER CODE END W1_HardFault_IRQn 0 */}
}/*** @brief This function handles Memory management fault.*/
void MemManage_Handler(void)
{/* USER CODE BEGIN MemoryManagement_IRQn 0 *//* USER CODE END MemoryManagement_IRQn 0 */while (1){/* USER CODE BEGIN W1_MemoryManagement_IRQn 0 *//* USER CODE END W1_MemoryManagement_IRQn 0 */}
}/*** @brief This function handles Prefetch fault, memory access fault.*/
void BusFault_Handler(void)
{/* USER CODE BEGIN BusFault_IRQn 0 *//* USER CODE END BusFault_IRQn 0 */while (1){/* USER CODE BEGIN W1_BusFault_IRQn 0 *//* USER CODE END W1_BusFault_IRQn 0 */}
}/*** @brief This function handles Undefined instruction or illegal state.*/
void UsageFault_Handler(void)
{/* USER CODE BEGIN UsageFault_IRQn 0 *//* USER CODE END UsageFault_IRQn 0 */while (1){/* USER CODE BEGIN W1_UsageFault_IRQn 0 *//* USER CODE END W1_UsageFault_IRQn 0 */}
}/*** @brief This function handles System service call via SWI instruction.*/
void SVC_Handler(void)
{/* USER CODE BEGIN SVCall_IRQn 0 *//* USER CODE END SVCall_IRQn 0 *//* USER CODE BEGIN SVCall_IRQn 1 *//* USER CODE END SVCall_IRQn 1 */
}/*** @brief This function handles Debug monitor.*/
void DebugMon_Handler(void)
{/* USER CODE BEGIN DebugMonitor_IRQn 0 *//* USER CODE END DebugMonitor_IRQn 0 *//* USER CODE BEGIN DebugMonitor_IRQn 1 *//* USER CODE END DebugMonitor_IRQn 1 */
}/*** @brief This function handles Pendable request for system service.*/
void PendSV_Handler(void)
{/* USER CODE BEGIN PendSV_IRQn 0 *//* USER CODE END PendSV_IRQn 0 *//* USER CODE BEGIN PendSV_IRQn 1 *//* USER CODE END PendSV_IRQn 1 */
}/*** @brief This function handles System tick timer.*/
void SysTick_Handler(void)
{/* USER CODE BEGIN SysTick_IRQn 0 *//* USER CODE END SysTick_IRQn 0 */HAL_IncTick();/* USER CODE BEGIN SysTick_IRQn 1 *//* USER CODE END SysTick_IRQn 1 */
}/******************************************************************************/
/* STM32F1xx Peripheral Interrupt Handlers */
/* Add here the Interrupt Handlers for the used peripherals. */
/* For the available peripheral interrupt handler names, */
/* please refer to the startup file (startup_stm32f1xx.s). */
/******************************************************************************//*** @brief This function handles RTC global interrupt.*/
void RTC_IRQHandler(void)
{/* USER CODE BEGIN RTC_IRQn 0 *//* USER CODE END RTC_IRQn 0 */HAL_RTCEx_RTCIRQHandler(&hrtc);/* USER CODE BEGIN RTC_IRQn 1 *//* USER CODE END RTC_IRQn 1 */
}/*** @brief This function handles EXTI line0 interrupt.*/
void EXTI0_IRQHandler(void)
{/* USER CODE BEGIN EXTI0_IRQn 0 *//* USER CODE END EXTI0_IRQn 0 */HAL_GPIO_EXTI_IRQHandler(PA0_Key_Pin);/* USER CODE BEGIN EXTI0_IRQn 1 *//* USER CODE END EXTI0_IRQn 1 */
}/*** @brief This function handles TIM3 global interrupt.*/
void TIM3_IRQHandler(void)
{/* USER CODE BEGIN TIM3_IRQn 0 *//* USER CODE END TIM3_IRQn 0 */HAL_TIM_IRQHandler(&htim3);/* USER CODE BEGIN TIM3_IRQn 1 *//* USER CODE END TIM3_IRQn 1 */
}/*** @brief This function handles USART1 global interrupt.*/
void USART1_IRQHandler(void)
{/* USER CODE BEGIN USART1_IRQn 0 *//* USER CODE END USART1_IRQn 0 */HAL_UART_IRQHandler(&huart1);/* USER CODE BEGIN USART1_IRQn 1 *//* USER CODE END USART1_IRQn 1 */
}/* USER CODE BEGIN 1 */void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{if(GPIO_Pin == PA0_Key_Pin){if( HAL_GPIO_ReadPin(PA0_Key_GPIO_Port, PA0_Key_Pin) == GPIO_PIN_RESET){HAL_GPIO_TogglePin(GPIOC, LED_G_Pin); //紅燈的狀態翻轉
// HAL_IWDG_Refresh(&hiwdg); //進行喂狗
// printf("Iwdg Count = %d \r\n", Count++);}}
}void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{U1RxLen = Size;HAL_UARTEx_ReceiveToIdle_IT(&huart1, U1RxData, U1RxDataSize); //啟動串口空閑中斷的接收U1RxFlag = 1;
}//TIM的中斷回調函數 控制LED燈的閃爍
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
// static uint32_t Count = 0;if(htim->Instance == TIM3) //判斷產生中斷的哪一個中斷回調函數{HAL_GPIO_TogglePin(LED_G_GPIO_Port, LED_G_Pin); //綠燈的狀態翻轉HAL_IWDG_Refresh(&hiwdg); //進行喂狗
// printf("Iwdg Count = %d \r\n", Count++);}
}RTC_TimeTypeDef sTime;
RTC_DateTypeDef sDate;void HAL_RTCEx_RTCEventCallback(RTC_HandleTypeDef *hrtc)
{HAL_RTC_GetTime(hrtc, &sTime, RTC_FORMAT_BCD);HAL_RTC_GetDate(hrtc, &sDate, RTC_FORMAT_BCD);printf("TimeDate -> %d:%d:%d[%d] | %d:%d:%d \r\n", sDate.Year,sDate.Month,sDate.Date,sDate.WeekDay,sTime.Hours,sTime.Minutes,sTime.Seconds);HAL_Delay(100);
}/* USER CODE END 1 */
📄 main.c
/* 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 "iwdg.h"
#include "rtc.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "string.h"/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
uint8_t U1SendData[] = {"hello world!"};
/* 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 */uint16_t pwmVal = 0; //占空比/* 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_USART1_UART_Init();MX_IWDG_Init();MX_TIM3_Init();MX_RTC_Init();/* USER CODE BEGIN 2 */HAL_UARTEx_ReceiveToIdle_IT(&huart1, U1RxData, U1RxDataSize); //使能空閑中斷HAL_TIM_Base_Start_IT(&htim3);// 啟動定時器HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3); //PWM初始化完畢之后,找到對應PWM的啟動函數__HAL_RTC_SECOND_ENABLE_IT(&hrtc, RTC_IT_SEC); //開啟秒更新中斷/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */HAL_UART_Transmit(&huart1, U1SendData, sizeof(U1SendData), 0xff); //啟動串口空閑中斷的發送while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.LSIState = RCC_LSI_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK){Error_Handler();}PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC;PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK){Error_Handler();}
}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}/* USER CODE END Error_Handler_Debug */
}#ifdef USE_FULL_ASSERT
/*** @brief Reports the name of the source file and the source line number* where the assert_param error has occurred.* @param file: pointer to the source file name* @param line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
🧩 代碼中的核心細節說明
__HAL_RTC_SECOND_ENABLE_IT()
說明
這個宏開啟了 秒中斷(SECIE):
#define __HAL_RTC_SECOND_ENABLE_IT(__HANDLE__, __INTERRUPT__) \((__HANDLE__)->Instance->CRH |= (__INTERRUPT__))
配合:
#define RTC_IT_SEC ((uint32_t)RTC_CRH_SECIE)
它控制的是 RTC->CRH.SECIE
位,每秒觸發一次 SECF
標志位,由 HAL 庫內部清除。
HAL_RTCEx_RTCEventCallback()
是 HAL 提供的專用 RTC 秒中斷回調函數:
void HAL_RTCEx_RTCEventCallback(RTC_HandleTypeDef *hrtc)
{// 每秒觸發
}
它在 HAL_RTCEx_RTCIRQHandler()
中被調用:
if(__HAL_RTC_GET_IT(hrtc, RTC_IT_SEC) != RESET)
{__HAL_RTC_CLEAR_FLAG(hrtc, RTC_FLAG_SEC);HAL_RTCEx_RTCEventCallback(hrtc);
}
打印時鐘:
HAL_RTC_GetTime(hrtc, &sTime, RTC_FORMAT_BCD);
HAL_RTC_GetDate(hrtc, &sDate, RTC_FORMAT_BCD);
printf("TimeDate -> %d:%d:%d[%d] | %d:%d:%d\r\n",sDate.Year, sDate.Month, sDate.Date, sDate.WeekDay,sTime.Hours, sTime.Minutes, sTime.Seconds);
每秒中斷觸發后,會打印一次當前時間,非常適合作為 RTC 秒事件驗證。
另外,代碼中可以去除 HAL_Delay(100)
:這是因為在中斷中使用 HAL_Delay()
不太安全,可能引發系統異常或丟中斷。也可以替代為:
// 替代 HAL_Delay(100) 或者不加延時,使用定時器節奏控制打印頻率。
for (volatile uint32_t i = 0; i < 100000; ++i);
還有,使用 RTC_FORMAT_BIN
更直觀,這是因為BCD 格式讀取后要自己解碼,而使用 BIN 格式更便于直接打印和邏輯判斷。
HAL_RTC_GetTime(hrtc, &sTime, RTC_FORMAT_BIN);
HAL_RTC_GetDate(hrtc, &sDate, RTC_FORMAT_BIN);
最后,RTC 時鐘源也可以用 LSE(32.768kHz)
,目前這里使用的是:
PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
LSI 精度較低(±10%,溫漂大),建議若有外部 32.768kHz 晶振,則改為:
PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
打印中也可以添加LED閃爍:
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 每秒閃爍一次LED
串口輸出驗證:(使用串口工具(如 SSCOM、PuTTY)查看打印內容是否每秒刷新。如圖所示,是我打印出來的時鐘:)
以上。 這便是 STM32 HAL庫 + RTC 每秒中斷功能 的實現。
以上,歡迎有從事同行業的電子信息工程、互聯網通信、嵌入式開發的朋友共同探討與提問,我可以提供實戰演示或模板庫。希望內容能夠對你產生幫助!