在 STM32 微控制器中,實現精確的 ms(毫秒)和 us(微秒)延時函數通常依賴于系統時鐘(SysTick)或定時器。以下是基于主頻為 216 MHz 的實現方法:
1. 使用 SysTick 實現延時函數
SysTick 是 Cortex-M 內核提供的一個 24 位倒計時定時器,通常用于操作系統的任務調度或提供精確的延時。
(1) 配置 SysTick
在 HAL_Init() 中,SysTick 已經被初始化為 1ms 中斷。我們可以利用 SysTick 的計數器來實現延時。
(2) 實現 ms 延時函數
#include "stm32f4xx_hal.h" // 包含 HAL 庫頭文件void Delay_ms(uint32_t ms)
{uint32_t start_tick = HAL_GetTick(); // 獲取當前 SysTick 值while ((HAL_GetTick() - start_tick) < ms){// 等待}
}
(3) 實現 us 延時函數
由于 SysTick 的分辨率是 1ms,無法直接用于微秒級延時。可以通過循環空操作來實現微秒級延時。
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 周期}
}
2. 使用定時器實現延時函數
如果需要更高精度的延時(尤其是微秒級延時),可以使用硬件定時器(如 TIM2、TIM3 等)。
(1) 配置定時器
假設使用 TIM2 定時器,配置為主頻 216 MHz 下的 1us 分辨率。
#include "stm32f4xx_hal.h"void TIM2_Init(void)
{__HAL_RCC_TIM2_CLK_ENABLE(); // 使能 TIM2 時鐘TIM_HandleTypeDef htim2;htim2.Instance = TIM2;htim2.Init.Prescaler = (SystemCoreClock / 1000000) - 1; // 分頻為 1MHz(1us)htim2.Init.CounterMode = TIM_COUNTERMODE_UP;htim2.Init.Period = 0xFFFFFFFF; // 最大計數值htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;HAL_TIM_Base_Init(&htim2);HAL_TIM_Base_Start(&htim2); // 啟動定時器
}
(2) 實現 us 延時函數
void Delay_us(uint32_t us)
{uint32_t start_tick = __HAL_TIM_GET_COUNTER(&htim2); // 獲取當前定時器值while ((__HAL_TIM_GET_COUNTER(&htim2) - start_tick) < us){// 等待}
}
(3) 實現 ms 延時函數
void Delay_ms(uint32_t ms)
{Delay_us(ms * 1000); // 調用微秒延時函數
}
3. 綜合示例
以下是一個完整的示例,結合 SysTick 和定時器實現 ms 和 us 延時函數:
#include "stm32f4xx_hal.h"// SysTick 延時函數
void Delay_ms(uint32_t ms)
{uint32_t start_tick = HAL_GetTick();while ((HAL_GetTick() - start_tick) < ms){// 等待}
}// 定時器延時函數
TIM_HandleTypeDef htim2;void TIM2_Init(void)
{__HAL_RCC_TIM2_CLK_ENABLE();htim2.Instance = TIM2;htim2.Init.Prescaler = (SystemCoreClock / 1000000) - 1;htim2.Init.CounterMode = TIM_COUNTERMODE_UP;htim2.Init.Period = 0xFFFFFFFF;htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;HAL_TIM_Base_Init(&htim2);HAL_TIM_Base_Start(&htim2);
}void Delay_us(uint32_t us)
{uint32_t start_tick = __HAL_TIM_GET_COUNTER(&htim2);while ((__HAL_TIM_GET_COUNTER(&htim2) - start_tick) < us){// 等待}
}int main(void)
{// 初始化 HAL 庫HAL_Init();// 配置系統時鐘SystemClock_Config();// 初始化定時器TIM2_Init();// 初始化 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}
}
4. 注意事項
精度問題:
SysTick 的 ms 延時精度較高,但 us 延時依賴于空操作循環,可能受編譯器優化影響。
定時器的 us 延時精度較高,但需要占用一個硬件定時器資源。
編譯器優化:
如果使用空操作循環實現 us 延時,建議禁用編譯器優化(如 -O0),或使用 volatile 關鍵字防止優化。
定時器資源:
如果系統中使用了多個定時器,請確保選擇的定時器未被占用。
通過以上方法,可以在 216 MHz 主頻的 STM32 微控制器上實現精確的 ms 和 us 延時函數。