内容
使用一个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;
}
// ... 其他定时器回调
}