STM32 RTC 唤醒中断功能实现低功耗功能

目录

概述

[1 工程配置](#1 工程配置)

[1.1 参数配置和说明](#1.1 参数配置和说明)

[1.2 代码架构](#1.2 代码架构)

[2 代码实现](#2 代码实现)

[2.1 初始化PWR中断唤醒](#2.1 初始化PWR中断唤醒)

[2.2 配置唤醒中断函数](#2.2 配置唤醒中断函数)

[2.3 进入stop模式函数](#2.3 进入stop模式函数)

[2.4 进入待机模式函数](#2.4 进入待机模式函数)

[2.5 WakeUp回调函数](#2.5 WakeUp回调函数)

[3 功能测试](#3 功能测试)

[4 不同场景的配置示例](#4 不同场景的配置示例)


概述

STM32的RTC唤醒中断是实现超低功耗、电池长期续航应用 的基石。它的核心是一个与低功耗模式深度绑定的、独立的硬件定时器。配置时,请务必:根据需求(精度、周期)选择合适的唤醒时钟源; 正确计算并设置唤醒计数值 。在中断服务程序中务必清除标志 。深刻理解在不同低功耗模式下被唤醒后的系统行为差异(尤其是待机模式下的复位)。

1 工程配置

1.1 参数配置和说明

1) RCC配置

  • 启用LSE(低速外部时钟)

  • RTC选项卡中,使能Activate Clock SourceActivate Calendar

  • 时钟源选择:LSE

使能时钟源/日历,和Alarm类型

2) RTC参数配置 :通过预分频计算最终得到1Hz时钟用于日历

复制代码
Hour Format: 24小时制

Asynchronous Predivider:  127    (32768/(127+1)=256Hz)
Synchronous Predivider:   255    (256/(255+1)=1Hz)

3) NVIC配置

1.2 代码架构

使用STM32 Cube 生成代码架构

屏蔽如下代码,这些代码在用户程序中重新实现

2 代码实现

2.1 初始化PWR中断唤醒

源代码如下:

cpp 复制代码
void lowpwr_init( void )
{
    /* 检查是否为从待机模式唤醒 */
    if (__HAL_PWR_GET_FLAG(PWR_FLAG_SB) != RESET)
    {
        log_a("从待机模式唤醒!\r\n");
        __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);
        
        /* 检查唤醒源是否为RTC */
        if (__HAL_PWR_GET_FLAG(PWR_FLAG_WU) != RESET)
        {
            log_a("唤醒源:RTC唤醒定时器\r\n");
            __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
        }
    }
}

2.2 配置唤醒中断函数

源代码如下:

cpp 复制代码
/* 配置唤醒中断函数 */
void RTC_Wakeup_Config(uint32_t interval_ms)
{
    uint32_t wakeup_counter = 0;
    uint32_t clock_freq = 0;
    
    /* 1. 停止唤醒定时器(配置前必须先停止) */
    HAL_RTCEx_DeactivateWakeUpTimer(&hrtc);
    
    /* 2. 计算唤醒计数值(以RTCCLK/16=2048Hz为例) */
    // 公式:WakeupCounter = (interval_ms * clock_freq) / 1000 - 1
    // 选择RTC_WAKEUPCLOCK_RTCCLK_DIV16 (32768/16 = 2048Hz)
    // 每个计数周期 = 1/2048 ≈ 0.488ms
    
    clock_freq = 2048;          // Hz
    wakeup_counter = (interval_ms * clock_freq) / 1000 - 1;
    
    /* 3. 限制计数值范围(WUT是16位寄存器) */
    if (wakeup_counter > 0xFFFF)
        wakeup_counter = 0xFFFF;

    if (wakeup_counter < 1)
        wakeup_counter = 1;
    
    /* 4. 初始化并启动唤醒中断 */
    // 参数:RTC句柄,唤醒计数值,时钟源
    HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, wakeup_counter, 
                                RTC_WAKEUPCLOCK_RTCCLK_DIV16);
    
    /* 5. 清除可能存在的旧标志 */
    __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&hrtc, RTC_FLAG_WUTF);
    __HAL_RTC_WAKEUPTIMER_EXTI_CLEAR_FLAG();
    
    log_a("唤醒中断配置完成!\r\n");
    log_a("唤醒间隔:     %lu ms\r\n", interval_ms);
    log_a("唤醒计数值:   %lu\r\n", wakeup_counter);

}

2.3 进入stop模式函数

源代码:

cpp 复制代码
/* 进入停止模式函数 */
void Enter_Stop_Mode(void)
{
    log_a("\r\n....准备进入停止模式...\r\n");
    
    /* 1. 确保唤醒中断已正确配置 */
    if (__HAL_RTC_WAKEUPTIMER_GET_IT_SOURCE(&hrtc, RTC_IT_WUT) == RESET)
    {
        log_a("错误:唤醒中断未使能!\r\n");
        return;
    }
    
    /* 2. 清除所有唤醒标志 */
    __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
    
    /* 3. 配置唤醒引脚(如果需要外部唤醒) */
    // HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
    
    /* 4. 进入停止模式(根据需求选择调节器模式) */
    // 参数1:调节器模式(PWR_MAINREGULATOR_ON 或 PWR_LOWPOWERREGULATOR_ON)
    // 参数2:进入模式方式(PWR_STOPENTRY_WFI 或 PWR_STOPENTRY_WFE)
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
    
    /* 5. 唤醒后执行的代码(系统会从这里继续运行) */
    SystemClock_Config();  // 重新配置系统时钟(重要!)
    
    log_a("\r\n.....从停止模式唤醒!\r\n");
}

2.4 进入待机模式函数

cpp 复制代码
/* 进入待机模式函数(更低的功耗,唤醒后会复位) */
void Enter_Standby_Mode(void)
{
    log_a("准备进入待机模式...\r\n");

    /* 1. 清除所有唤醒标志 */
    __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);

    /* 2. 使能唤醒引脚(可选) */
    // HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);

    /* 3. 确保RTC唤醒中断已使能 */
    __HAL_RTC_WAKEUPTIMER_ENABLE_IT(&hrtc, RTC_IT_WUT);

    /* 4. 进入待机模式 */
    HAL_PWR_EnterSTANDBYMode();

    /* 注意:从待机模式唤醒后,MCU会完全复位,程序从main()重新开始执行 */
}

2.5 WakeUp回调函数

cpp 复制代码
void  HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
    /* 1. 检查是否是唤醒中断触发 */
    {
        /* 2. 清除RTC唤醒中断标志(必须的步骤!) */

        /* 3. 清除EXTI线路22的中断标志 */
        __HAL_RTC_WAKEUPTIMER_EXTI_CLEAR_FLAG();

        /* 4. 执行用户代码(尽量简短) */
        wakeup_flag = 1;  // 设置软件标志

        /* 5. 可选的LED指示(如果有LED连接) */
        //  HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
        TRIGGLE_LED;

        /* 6. 重新使能唤醒中断(某些情况下需要) */
        // HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, wakeup_counter, RTC_WAKEUPCLOCK_RTCCLK_DIV16);
    }    
}

3 功能测试

1) 初始化LR POWER 功能

2) 运行函数:

3) 性能优化建议

  1. 最小化中断服务时间:ISR中只设置标志,复杂操作放在主循环

  2. 合理选择低功耗模式

    • 短期空闲 → 睡眠模式

    • 长时间等待 → 停止模式

    • 超低功耗需求 → 待机模式

  3. 关闭未用外设时钟:进入低功耗前关闭所有不必要的外设时钟

  4. 使用RTC备份寄存器:保存关键数据,防止待机模式唤醒后丢失

4) 核心源代码如下:

cpp 复制代码
#define WAKE_UP_TIME     4000      // 400000 ms = 400 seconds  

extern RTC_HandleTypeDef hrtc;

extern void SystemClock_Config(void);

static uint8_t wakeup_flag = 0;

void lowpwr_init( void )
{
    /* 检查是否为从待机模式唤醒 */
    if (__HAL_PWR_GET_FLAG(PWR_FLAG_SB) != RESET)
    {
        log_a("从待机模式唤醒!\r\n");
        __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);
        
        /* 检查唤醒源是否为RTC */
        if (__HAL_PWR_GET_FLAG(PWR_FLAG_WU) != RESET)
        {
            log_a("唤醒源:RTC唤醒定时器\r\n");
            __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
        }
    }
}

/*******************************************************************************
 *   WAKE UP FUNCTION 
 *******************************************************************************
 */
static  int lwr_cnt = 0,wk_cnt =  0;
bool lwr_sate = 0;

/* 配置唤醒中断函数 */
void RTC_Wakeup_Config(uint32_t interval_ms)
{
    uint32_t wakeup_counter = 0;
    uint32_t clock_freq = 0;
    
    /* 1. 停止唤醒定时器(配置前必须先停止) */
    HAL_RTCEx_DeactivateWakeUpTimer(&hrtc);
    
    /* 2. 计算唤醒计数值(以RTCCLK/16=2048Hz为例) */
    // 公式:WakeupCounter = (interval_ms * clock_freq) / 1000 - 1
    // 选择RTC_WAKEUPCLOCK_RTCCLK_DIV16 (32768/16 = 2048Hz)
    // 每个计数周期 = 1/2048 ≈ 0.488ms
    
    clock_freq = 2048;          // Hz
    wakeup_counter = (interval_ms * clock_freq) / 1000 - 1;
    
    /* 3. 限制计数值范围(WUT是16位寄存器) */
    if (wakeup_counter > 0xFFFF)
        wakeup_counter = 0xFFFF;

    if (wakeup_counter < 1)
        wakeup_counter = 1;
    
    /* 4. 初始化并启动唤醒中断 */
    // 参数:RTC句柄,唤醒计数值,时钟源
    HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, wakeup_counter, 
                                RTC_WAKEUPCLOCK_RTCCLK_DIV16);
    
    /* 5. 清除可能存在的旧标志 */
    __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&hrtc, RTC_FLAG_WUTF);
    __HAL_RTC_WAKEUPTIMER_EXTI_CLEAR_FLAG();
    
    log_a("唤醒中断配置完成!\r\n");
    log_a("唤醒间隔:     %lu ms\r\n", interval_ms);
    log_a("唤醒计数值:   %lu\r\n", wakeup_counter);

}


/* 进入停止模式函数 */
void Enter_Stop_Mode(void)
{
    log_a("\r\n....准备进入停止模式...\r\n");
    
    /* 1. 确保唤醒中断已正确配置 */
    if (__HAL_RTC_WAKEUPTIMER_GET_IT_SOURCE(&hrtc, RTC_IT_WUT) == RESET)
    {
        log_a("错误:唤醒中断未使能!\r\n");
        return;
    }
    
    /* 2. 清除所有唤醒标志 */
    __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
    
    /* 3. 配置唤醒引脚(如果需要外部唤醒) */
    // HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
    
    /* 4. 进入停止模式(根据需求选择调节器模式) */
    // 参数1:调节器模式(PWR_MAINREGULATOR_ON 或 PWR_LOWPOWERREGULATOR_ON)
    // 参数2:进入模式方式(PWR_STOPENTRY_WFI 或 PWR_STOPENTRY_WFE)
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
    
    /* 5. 唤醒后执行的代码(系统会从这里继续运行) */
    SystemClock_Config();  // 重新配置系统时钟(重要!)
    
    log_a("\r\n.....从停止模式唤醒!\r\n");
}

/* 进入待机模式函数(更低的功耗,唤醒后会复位) */
void Enter_Standby_Mode(void)
{
    log_a("准备进入待机模式...\r\n");

    /* 1. 清除所有唤醒标志 */
    __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);

    /* 2. 使能唤醒引脚(可选) */
    // HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);

    /* 3. 确保RTC唤醒中断已使能 */
    __HAL_RTC_WAKEUPTIMER_ENABLE_IT(&hrtc, RTC_IT_WUT);

    /* 4. 进入待机模式 */
    HAL_PWR_EnterSTANDBYMode();

    /* 注意:从待机模式唤醒后,MCU会完全复位,程序从main()重新开始执行 */
}


void  HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
    /* 1. 检查是否是唤醒中断触发 */
    {
        /* 2. 清除RTC唤醒中断标志(必须的步骤!) */

        /* 3. 清除EXTI线路22的中断标志 */
        __HAL_RTC_WAKEUPTIMER_EXTI_CLEAR_FLAG();

        /* 4. 执行用户代码(尽量简短) */
        wakeup_flag = 1;  // 设置软件标志

        /* 5. 可选的LED指示(如果有LED连接) */
        //  HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
        TRIGGLE_LED;

        /* 6. 重新使能唤醒中断(某些情况下需要) */
        // HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, wakeup_counter, RTC_WAKEUPCLOCK_RTCCLK_DIV16);
    }    
}


void lwr_mode_run( void )
{
   RTC_TimeTypeDef current_time;
    
    /* 检查唤醒标志 */
    if(wakeup_flag)
    {
        wakeup_flag = 0;
        
        /* 这里可以执行周期性任务 */
        // Perform_Periodic_Task();
        
        if( lwr_sate )
        {
            wk_cnt++;
            lwr_cnt = 0;
            log_a("RTC唤醒中断触发,Current time: ");
            /* 读取并显示当前RTC时间 */
            HAL_RTC_GetTime(&hrtc, &current_time, RTC_FORMAT_BIN);
                            log_a("%02d:%02d:%02d\r\n", 
                            current_time.Hours, 
                            current_time.Minutes, 
                            current_time.Seconds);
            

            if(wk_cnt > 10 )
            {
                lwr_sate = 0;
                wk_cnt = 0;
                RTC_Wakeup_Config(WAKE_UP_TIME);
            }
        }
        else
        {
            lwr_cnt++;
            wk_cnt = 0;
        }
    }
    
    
    /* 空闲时进入低功耗模式 */
    //if (!wakeup_flag)
    if( lwr_cnt > 50 && !wakeup_flag )
    {
        /* 按键检测示例:按下按键进入停止模式 */
        // if (HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET)
        // {
        //     HAL_Delay(50);  // 消抖
        //     if (HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET)
        //     {
        //         Enter_Stop_Mode();
        //     }
        // }
        
        
        /* 读取并显示当前RTC时间 */
        HAL_RTC_GetTime(&hrtc, &current_time, 
                               RTC_FORMAT_BIN);
        
        log_a("%02d:%02d:%02d\r\n", 
               current_time.Hours, 
               current_time.Minutes, 
               current_time.Seconds);
        
        log_a(" 系统进入低功耗模式 \r\n ");
        lwr_cnt = 0;
        Enter_Stop_Mode();
        lwr_sate = 1;
        
        /* 进入睡眠模式(最轻度低功耗) */
        // HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
    }
}

/***
 *    lwr test 
 */
void lwr_test( void )
{
    lowpwr_init();
    /* 
        配置RTC唤醒中断为2秒间隔 
        2000ms = 2秒
    */
    RTC_Wakeup_Config(WAKE_UP_TIME);
}

4 不同场景的配置示例

1) 每隔10分钟唤醒采集数据

cpp 复制代码
// 使用ck_spre(1Hz时钟源),最长可设置36小时
void Config_10min_Wakeup(void)
{
    // 600秒 = 10分钟,注意WUTR最大值为0xFFFF
    HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 600-1, RTC_WAKEUPCLOCK_CK_SPRE_16BITS);
}

2) 场景2:高精度每秒唤醒

cpp 复制代码
void Config_1s_Wakeup(void)
{
    // 使用RTCCLK/16 = 2048Hz,计数值2047
    HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 2047, RTC_WAKEUPCLOCK_RTCCLK_DIV16);
}

3) 场景3:动态调整唤醒间隔

cpp 复制代码
void Dynamic_Adjust_Wakeup(uint32_t new_interval_ms)
{
    // 先停止定时器
    HAL_RTCEx_DeactivateWakeUpTimer(&hrtc);
    
    // 重新计算并配置
    uint32_t counter = (new_interval_ms * 2048) / 1000 - 1;
    if (counter > 0xFFFF) counter = 0xFFFF;
    
    HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, counter, RTC_WAKEUPCLOCK_RTCCLK_DIV16);
    
    // 清除旧标志
    __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&hrtc, RTC_FLAG_WUTF);
}
相关推荐
CQ_YM11 小时前
ARM时钟与定时器
arm开发·单片机·嵌入式硬件·arm
哄娃睡觉11 小时前
stm32 mcu SWD和SPI下载模式有什么区别?
stm32
xiebs_11 小时前
0127TR
单片机·嵌入式硬件
A9better13 小时前
嵌入式开发学习日志50——任务调度与状态
stm32·嵌入式硬件·学习
草丛中的蝈蝈15 小时前
STM32向FLASH写入数据后,重新读出的数据和原写入数据不一致
stm32
DLGXY16 小时前
STM32——EXTI外部中断(六)
stm32·单片机·嵌入式硬件
LEEE@FPGA16 小时前
zynq 是不是有了设备树,再linux中不需要编写驱动也能控制
linux·运维·单片机
CQ_YM16 小时前
ARM之I2C与ADC
arm开发·嵌入式硬件·嵌入式·arm
同志啊为人民服务!16 小时前
RS485通信,无法进入中断处理程序,问题分析过程
单片机·编译器·rs485·中断处理程序