2. RTC闹钟中断唤醒
任务定义
本案例使用STM32L0系列MCU(以STM32L071为例),完成以下闭环:配置RTC闹钟事件,使MCU进入STOP低功耗模式,闹钟触发后通过EXTI线17唤醒MCU,在RTC闹钟中断服务函数中翻转LED并记录唤醒次数。核心目标是验证RTC闹钟中断能够将MCU从STOP模式唤醒,并执行指定任务。
硬件连接
代码实现
#include "stm32l0xx_hal.h"
/* 闹钟中断唤醒次数 */
volatile uint32_t alarm_wakeup_count = 0;
/* RTC句柄 */
RTC_HandleTypeDef hrtc;
/* LED引脚定义 */
#define LED1_PIN GPIO_PIN_0
#define LED1_PORT GPIOB
#define LED2_PIN GPIO_PIN_1
#define LED2_PORT GPIOB
/* 闹钟中断服务函数:翻转LED1并计数 */
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
HAL_GPIO_TogglePin(LED1_PORT, LED1_PIN);
alarm_wakeup_count++;
}
/* RTC初始化:LSE 32.768kHz,闹钟A使能 */
static void MX_RTC_Init(void)
{
__HAL_RCC_RTC_ENABLE();
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
hrtc.Init.AsynchPrediv = 127; /* 异步分频 */
hrtc.Init.SynchPrediv = 255; /* 同步分频,得到1Hz */
hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
HAL_RTC_Init(&hrtc);
/* 使能闹钟A中断 */
HAL_RTC_AlarmA_EnableIt(&hrtc);
}
/* 设置闹钟:在当前时间基础上加5秒 */
static void Set_Alarm_After_5s(void)
{
RTC_AlarmTypeDef sAlarm = {0};
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
/* 读取当前时间 */
HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
/* 计算目标秒数 */
uint32_t target_sec = sTime.Seconds + 5;
if (target_sec >= 60) {
target_sec -= 60;
}
/* 配置闹钟A:每秒匹配 */
sAlarm.AlarmTime.Hours = 0;
sAlarm.AlarmTime.Minutes = 0;
sAlarm.AlarmTime.Seconds = target_sec;
sAlarm.AlarmTime.SubSeconds = 0;
sAlarm.AlarmTime.TimeFormat = RTC_HOURFORMAT_24;
sAlarm.AlarmMask = RTC_ALARMMASK_DATE_WEEKDAY | /* 不关心日期 */
RTC_ALARMMASK_HOURS | /* 不关心小时 */
RTC_ALARMMASK_MINUTES; /* 不关心分钟 */
/* 只匹配秒字段 */
sAlarm.Alarm = RTC_ALARM_A;
sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL;
HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN);
}
/* GPIO初始化 */
static void MX_GPIO_Init(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = LED1_PIN | LED2_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
/* 进入STOP模式 */
static void Enter_Stop_Mode(void)
{
HAL_SuspendTick();
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
/* 唤醒后重新配置系统时钟 */
SystemClock_Config();
HAL_ResumeTick();
}
/* 系统时钟配置:MSI 2.097MHz */
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE | RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.LSEState = RCC_LSE_ON;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_5; /* 2.097MHz */
RCC_OscInitStruct.MSICalibrationValue = 0;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK |
RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0);
}
/* 主函数 */
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_RTC_Init();
/* 初始状态:LED1灭,LED2亮表示运行 */
HAL_GPIO_WritePin(LED1_PORT, LED1_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(LED2_PORT, LED2_PIN, GPIO_PIN_SET);
while (1)
{
/* 设置5秒后的闹钟 */
Set_Alarm_After_5s();
/* 进入STOP模式,等待闹钟唤醒 */
Enter_Stop_Mode();
/* 唤醒后翻转LED2,表示主循环恢复 */
HAL_GPIO_TogglePin(LED2_PORT, LED2_PIN);
/* 延时1秒,避免频繁循环 */
HAL_Delay(1000);
}
}
关键配置说明
RTC使用LSE外部32.768kHz晶振,异步分频127、同步分频255,得到1Hz的秒时钟。闹钟A设置为仅匹配秒字段,因此每分钟内每个目标秒数都会触发一次。代码中每次唤醒后重新读取当前时间并设置“当前秒+5”的闹钟,实现周期性唤醒。EXTI线17由HAL库在HAL_RTC_AlarmA_EnableIt内部自动映射,无需手动配置NVIC以外的EXTI设置。
编译与烧录
# 使用STM32CubeIDE或Makefile工程编译
make clean && make
# 烧录(以ST-Link为例)
st-flash write build/rtc_alarm_wakeup.bin 0x08000000
验证方法与判定标准
将程序烧录至STM32L071开发板,连接3.3V电源和ST-Link调试器。观察以下现象:
- 上电后LED2常亮,LED1熄灭,表示程序进入主循环;
- 约5秒后LED1翻转一次(点亮或熄灭),同时LED2翻转一次,表示MCU被RTC闹钟从STOP模式唤醒并执行了中断回调;
- 之后每隔约5秒,LED1和LED2各翻转一次,形成连续周期;
- 使用调试器查看变量
alarm_wakeup_count,其值每唤醒一次递增1,与LED1翻转次数一致。
判定通过标准:LED1和LED2以约5秒周期同步翻转,且alarm_wakeup_count持续递增,证明RTC闹钟中断成功将MCU从STOP模式唤醒并执行了指定任务。