【STM32】HAL库中的实现(四):RTC (实时时钟)

🕒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,稳定性较好
获取时间 每秒打印当前时间
handlebars 复制代码
+------------------+
| RTC 每秒更新     |
+--------+---------+
         |
         v
+-------------------------+
| RTC_IRQn 中断触发      |
+-------------------------+
         |
         v
+-------------------------+
| HAL_RTCEx_RTCIRQHandler |
+-------------------------+
         |
         v
+-----------------------------+
| HAL_RTCEx_RTCEventCallback |
| (打印函数)           |
+-----------------------------+
完整代码(中断的方式获取时间)

📄 stm32f1xx_it.c

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

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 */

🧩 代码中的核心细节说明
  1. __HAL_RTC_SECOND_ENABLE_IT() 说明

这个宏开启了 秒中断(SECIE)

c 复制代码
#define __HAL_RTC_SECOND_ENABLE_IT(__HANDLE__, __INTERRUPT__) \
  ((__HANDLE__)->Instance->CRH |= (__INTERRUPT__))

配合:

c 复制代码
#define RTC_IT_SEC ((uint32_t)RTC_CRH_SECIE)

它控制的是 RTC->CRH.SECIE 位,每秒触发一次 SECF 标志位,由 HAL 库内部清除。

  1. HAL_RTCEx_RTCEventCallback() 是 HAL 提供的专用 RTC 秒中断回调函数:
c 复制代码
void HAL_RTCEx_RTCEventCallback(RTC_HandleTypeDef *hrtc)
{
    // 每秒触发
}

它在 HAL_RTCEx_RTCIRQHandler() 中被调用:

c 复制代码
if(__HAL_RTC_GET_IT(hrtc, RTC_IT_SEC) != RESET)
{
    __HAL_RTC_CLEAR_FLAG(hrtc, RTC_FLAG_SEC);
    HAL_RTCEx_RTCEventCallback(hrtc);
}

打印时钟:

c 复制代码
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() 不太安全,可能引发系统异常或丢中断。也可以替代为:

c 复制代码
// 替代 HAL_Delay(100) 或者不加延时,使用定时器节奏控制打印频率。
for (volatile uint32_t i = 0; i < 100000; ++i);

还有,使用 RTC_FORMAT_BIN 更直观,这是因为BCD 格式读取后要自己解码,而使用 BIN 格式更便于直接打印和逻辑判断。

c 复制代码
HAL_RTC_GetTime(hrtc, &sTime, RTC_FORMAT_BIN);
HAL_RTC_GetDate(hrtc, &sDate, RTC_FORMAT_BIN);

最后,RTC 时钟源也可以用 LSE(32.768kHz),目前这里使用的是:

c 复制代码
PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;

LSI 精度较低(±10%,温漂大),建议若有外部 32.768kHz 晶振,则改为:

c 复制代码
PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;

打印中也可以添加LED闪烁:

c 复制代码
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 每秒闪烁一次LED

串口输出验证:(使用串口工具(如 SSCOM、PuTTY)查看打印内容是否每秒刷新。如图所示,是我打印出来的时钟:)

以上。 这便是 STM32 HAL库 + RTC 每秒中断功能 的实现。

以上,欢迎有从事同行业的电子信息工程、互联网通信、嵌入式开发的朋友共同探讨与提问,我可以提供实战演示或模板库。希望内容能够对你产生帮助!

相关推荐
许野平35 分钟前
Rust:如何开发Windows 动态链接库 DLL
windows·单片机·rust·dll·动态链接库
嵌入式×边缘AI:打怪升级日志1 小时前
韦东山STM32_HAl库入门教程(SPI)学习笔记[09]内容
stm32·嵌入式硬件·microsoft
yiqiqukanhaiba3 小时前
江协科技STM32学习笔记1
科技·stm32·学习
码小文5 小时前
Altium Designer 22使用笔记(4)---添加封装、ERC检查、PDF文档与BOM生成
笔记·嵌入式硬件·硬件工程·学习方法·硬件经验
DogDaoDao5 小时前
WebRTC音视频编码模块深度解析:从编解码器到自适应码率控制(2025技术实践)
音视频·webrtc·实时音视频·视频编解码·h264·vp9·svc编码
努力做小白6 小时前
Linux驱动25 --- RkMedia音频API使用&&增加 USB 音视频设备
linux·驱动开发·单片机·嵌入式硬件·音视频
_smart_boy__6 小时前
基于铁头山羊STM32的平衡车电机转速开环闭环matlab仿真
stm32·嵌入式硬件·matlab
文火冰糖的硅基工坊7 小时前
[硬件电路-140]:模拟电路 - 信号处理电路 - 锁定放大器概述、工作原理、常见芯片、管脚定义
嵌入式硬件·架构·信号处理·电路·跨学科融合
机器视觉知识推荐、就业指导7 小时前
STM32 外设驱动模块一:LED 模块
stm32·单片机·嵌入式硬件