在STM32 HAL库中使用 WFI 实现低功耗准确延时

内容

使用一个16位定时器TIM1,并结合"__WFI"指令让CPU进入睡眠模式,实现延时过程低功耗、延时时间准确的delay模块。

delay.h

cpp 复制代码
#ifndef __DELAY_H
#define __DELAY_H

#include "stm32f1xx_hal.h"
#include <stdint.h>

extern TIM_HandleTypeDef htim1; // TIM1 用于硬件延时

// WFI 阻塞延时,微秒级 (不占用 CPU 资源)
void DELAY_Hardware_Us(uint32_t us);

// WFI 阻塞延时,毫秒级 (不占用 CPU 资源)
void DELAY_Hardware_Ms(uint32_t ms);

#endif // __DELAY_H

delay.c

cpp 复制代码
#include "delay.h"
#include "stm32f1xx.h"

// 延时完成标志,中断服务函数设置
static volatile uint8_t g_hardware_delay_complete = 0; 

/**
 * @brief  微秒级硬件延时 (WFI/CPU 睡眠)
 * @param  us: 延时时长 (微秒, 1us 到约 65535us)
 * @retval None
 */
void DELAY_Hardware_Us(uint32_t us)
{
    if (us == 0) {
        return;
    }
    
    // TIM1 是 16 位定时器,最大周期是 65536 us (约 65.5 ms)
    if (us > 65535) {
        // 如果延时超过最大限制,递归调用毫秒延时函数处理
        DELAY_Hardware_Ms(us / 1000);
        
        // 处理剩余的微秒部分
        uint32_t remaining_us = us % 1000;
        if (remaining_us > 0) {
            DELAY_Hardware_Us(remaining_us);
        }
        return;
    }

    // 1. 设置自动重装载值 (ARR)
    // TIM1 时钟是 72 MHz, 预分频 72-1 后,计数频率是 1 MHz (1 tick = 1 us)
    // ARR = 目标时长 (us) - 1
    __HAL_TIM_SET_AUTORELOAD(&htim1, us - 1);

    // 2. 清除更新标志,并重置完成标志
    __HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_UPDATE);
    g_hardware_delay_complete = 0;

    // 3. 启动定时器中断
    HAL_TIM_Base_Start_IT(&htim1);

    // 4. 进入 WFI 等待中断 (CPU 睡眠)
    while (g_hardware_delay_complete == 0) {
        __WFI(); 
    }

    // 5. 中断到达,停止定时器
    HAL_TIM_Base_Stop_IT(&htim1);
}

/**
 * @brief  毫秒级硬件延时 (WFI/CPU 睡眠)
 * @param  ms: 延时时长 (毫秒)
 * @retval None
 */
void DELAY_Hardware_Ms(uint32_t ms)
{
    uint32_t i;
    // 每次延时 60ms,避免单次调用超过 65.535ms 的 16 位定时器限制
    for (i = 0; i < ms / 60; i++) {
        DELAY_Hardware_Us(60000);
    }
    // 处理剩余的毫秒部分
    uint32_t remaining_ms = ms % 60;
    if (remaining_ms > 0) {
        DELAY_Hardware_Us(remaining_ms * 1000);
    }
}

中断处理(在stm32f1xx_it.c中)

(如果在TIM中断回调函数执行的过程中,被更高优先级的函数打断了,有小概率CPU会睡不醒,也可以在函数开始前后使能/使能中断更周全些)

cpp 复制代码
// TIM1 全局中断服务程序
void TIM1_UP_IRQHandler(void) 
{
    HAL_TIM_IRQHandler(&htim1);
}

// HAL TIM 中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    // 检查是否是 TIM1 触发的更新事件
    if (htim->Instance == TIM1) {
        // 设置完成标志,唤醒等待 WFI 的 CPU
        g_hardware_delay_complete = 1;
    }
    // ... 其他定时器回调
}
相关推荐
@晓凡5 小时前
keil5找不到下载器
stm32
Devlab5 小时前
LVGL设计大师——网页版anyui来了!!
嵌入式硬件·物联网·低代码·ui·iot
Dr.kangder8 小时前
嵌入式面试总结(十五)——异常处理
单片机·面试·职场和发展·系统架构·嵌入式
zlinear数据采集卡10 小时前
D223上位机C#开发实战:从帧解析到波形显示的完整实现
开发语言·arm开发·嵌入式硬件·fpga开发·开源·c#
ACP广源盛1392462567310 小时前
蚂蚁百灵 Ling‑3.0‑flash 开源 + 昇腾 0‑Day 原生适配@ACP#GSV9001E 在国产算力矩阵中的机会与落地场景
大数据·人工智能·分布式·单片机·嵌入式硬件
Rocktech_ruixun12 小时前
机器人端侧大模型部署对主板有哪些要求?瑞迅主控板分级方案对比
人工智能·嵌入式硬件·机器人
振南的单片机世界12 小时前
SysTick硬核拆解:既是HAL_Delay的“心跳”,也是RTOS的“节拍器”
arm开发·stm32·单片机·嵌入式硬件
Lzh编程小栈13 小时前
【嵌入式底层】SPI超透彻详解:通信原理、四线结构、四种模式、寄存器底层 + 面试全集
c语言·stm32·单片机·面试·职场和发展
Rocktech_ruixun13 小时前
机器人数据采集能力取决于什么?瑞迅科技RK3588/3576核心板方案深度解析
人工智能·嵌入式硬件·机器人
水果里面有苹果13 小时前
28-DRV8825PWPR步进电机驱动器
单片机