STM32 核心內建的 SysTick 定時器是實現精準延遲的首選工具。本文詳細解析基於 SysTick 普通倒數模式的 delay_us、delay_ms 實現原理與代碼細節。
static uint8_t fac_us = 0; // us 延遲倍乘數
static uint16_t fac_ms = 0; // ms 延遲倍乘數
void delay_init(uint8_t SYSCLK) {
// SYSCLK: 系統時鐘頻率 (MHz),例如 72
SysTick->CTRL &= 0xFFFFFFFB; // bit2=0,選擇外部時鐘源 (HCLK/8)
fac_us = SYSCLK / 8; // 每個計數器脈衝週期 = 8/SYSCLK us
fac_ms = (uint16_t)fac_us * 1000; // 1ms 對應的計數值
}
關鍵點:設定 CTRL bit2=0 選擇 HCLK/8 作為時鐘源,此時 SysTick 頻率 = 系統頻率 / 8。fac_us 代表 1us 需要的計數個數。
delay_msvoid delay_ms(uint16_t nms) {
uint32_t temp;
SysTick->LOAD = (uint32_t)nms * fac_ms; // 載入重裝值
SysTick->VAL = 0x00; // 清空計數器
SysTick->CTRL = 0x01; // 啟用定時器,無中斷
do {
temp = SysTick->CTRL;
} while ((temp & 0x01) && !(temp & (1 << 16))); // 等待 COUNTFLAG (bit16) 置 1
SysTick->CTRL = 0x00; // 關閉計數器
SysTick->VAL = 0x00; // 清空計數器
}
LOAD:24 位寄存器,最大延遲受限。72MHz 下 nms <= 1864。CTRL 寄存器:bit 0 (ENABLE) = 1 表示正在計數。bit 16 (COUNTFLAG) = 1 表示計數歸零。讀取後自動清零。(temp & 0x01) && !(temp & (1<<16)):計數器仍啟用 且 尚未溢位時持續等待。delay_usvoid delay_us(uint32_t nus) {
uint32_t temp;
SysTick->LOAD = nus * fac_us; // 載入 us 計數值
SysTick->VAL = 0x00;
SysTick->CTRL = 0x01;
do {
temp = SysTick->CTRL;
} while ((temp & 0x01) && !(temp & (1 << 16)));
SysTick->CTRL = 0x00;
SysTick->VAL = 0x00;
}
LOAD 為 24 位,最大值 0xFFFFFF。若需更長延遲,請改用通用定時器(TIM)或迴圈調用。__disable_irq()。HCLK/8。若選 HCLK (bit2=1),fac_us = SYSCLK,精度更高但最大延遲縮短 8 倍。HAL_Delay() (ms) 和 HAL_SYSTICK_Callback(),但裸機開發常用上述獨立實現。SysTick 延遲函數核心在於:正確配置時鐘源 → 計算倍乘因子 → 載入 LOAD → 啟動 → 輪詢 COUNTFLAG → 關閉清理。掌握 CTRL 寄存器 bit0/bit16 交互邏輯,即可靈活移植到任何 Cortex-M 內核單片機。