如果希望在不使用硬件定时器的情况下实现软延时(即通过 CPU 空操作实现延时),可以通过计算 CPU 周期数来实现 ms 和 us 级别的延时。以下是基于 216 MHz 主频的实现方法:
1. 实现原理
CPU 周期计算:在 216 MHz 的主频下,1 秒可以执行 216,000,000 个 CPU 周期。
1ms = 216,000 个 CPU 周期。
1us = 216 个 CPU 周期。
空操作延时:通过执行空操作(NOP 指令)来消耗 CPU 周期,从而实现延时。
2. 实现 ms 延时函数
void Delay_ms(uint32_t ms)
{
uint32_t i;
for (i = 0; i < ms; i++)
{
Delay_us(1000); // 调用微秒延时函数
}
}
3. 实现 us 延时函数
void Delay_us(uint32_t us)
{
uint32_t delay_cycles = us * (SystemCoreClock / 1000000); // 计算需要的 CPU 周期数
volatile uint32_t count = 0;
for (count = 0; count < delay_cycles; count++)
{
__asm__("nop"); // 空操作,占用一个 CPU 周期
}
}
4. 完整示例
以下是一个完整的示例,展示如何使用软延时实现 ms 和 us 延时:
#include "stm32f4xx_hal.h" // 包含 HAL 库头文件
// 微秒延时函数
void Delay_us(uint32_t us)
{
uint32_t delay_cycles = us * (SystemCoreClock / 1000000); // 计算需要的 CPU 周期数
volatile uint32_t count = 0;
for (count = 0; count < delay_cycles; count++)
{
__asm__("nop"); // 空操作,占用一个 CPU 周期
}
}
// 毫秒延时函数
void Delay_ms(uint32_t ms)
{
uint32_t i;
for (i = 0; i < ms; i++)
{
Delay_us(1000); // 调用微秒延时函数
}
}
int main(void)
{
// 初始化 HAL 库
HAL_Init();
// 配置系统时钟
SystemClock_Config();
// 初始化 GPIO
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 主循环
while (1)
{
// 闪烁 LED(500ms 间隔)
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
Delay_ms(500);
// 微秒延时示例
Delay_us(1000); // 延时 1ms
}
}
5. 注意事项
编译器优化:
如果编译器优化级别较高(如 -O2 或 -O3),可能会将空操作循环优化掉,导致延时失效。
解决方法:
使用 volatile 关键字修饰循环变量(如 volatile uint32_t count)。
在编译时禁用优化(如 -O0)。
精度问题:
软延时的精度受 CPU 负载和其他中断的影响,可能不够精确。
如果需要高精度延时,建议使用硬件定时器。
主频变化:
如果系统主频发生变化(如动态调整时钟频率),需要重新计算 delay_cycles。
空操作指令:
asm(“nop”) 是 GCC 编译器的内联汇编语法,表示插入一个空操作指令。
如果使用其他编译器(如 IAR 或 Keil),可能需要使用对应的语法。