核心結論
C 語言標準未定義「機器周期」,這完全取決於:目標架構、編譯器、優化等級、時鐘頻率。單片機開發中常用「機器周期」估算延時,但現代優化編譯器會大幅改變指令數,實測為準。
經典 8051(12MHz)手工估算模型
| 指令類型 | 機器周期 | 備註 |
| NOP | 1 | 12 個時鐘周期 = 1μs |
| 單周期指令 | 1 | MOV, ADD, INC 等 |
| 雙周期指令 | 2 | MUL, DIV, 跳轉 |
| for 循環開銷 | 約 4-8 | 初始化、判斷、遞增、跳轉 |
經驗公式(12MHz 8051,Keil C51 無優化)
// 空 for 循環
for (i = 0; i < N; i++); // 約 8*N 個機器周期 = 8*N μs
// 1ms 延時函數
void delay_ms(uint16_t ms) {
while (ms--) {
for (uint8_t i = 0; i < 120; i++); // 120*8 = 960μs ≈ 1ms
}
}
關鍵變量:優化等級改變一切
| 優化 | for(i=0;i<100;i++); 指令數 | 實測時間 (12MHz) |
| O0 (無優化) | ~800 指令 | ~800 μs |
| O1 | 循環被完全刪除 (死代碼消除) | 0 μs |
| O2/O3 | 同上 | 0 μs |
| volatile 防優化 | ~800 指令 | ~800 μs |
防優化正確寫法
volatile uint8_t i; // 告訴編譯器:副作用不可刪除
for (i = 0; i < 100; i++);
現代單片機
| MCU | 核心 | 典型頻率 | 1條指令週期 | 延時推薦方式 |
| STM32F1 | Cortex-M3 | 72 MHz | 1/72 μs ≈ 14 ns | SysTick 定時器 / DWT_CYCCNT |
| STM32F4 | Cortex-M4 | 168 MHz | 6 ns | 硬件定時器 / HAL_Delay |
| ESP32 | Xtensa LX6 | 240 MHz | 4 ns | 硬件定時器 / esp_rom_delay_us |
| GD32 | Cortex-M3/4 | 108-168 MHz | 同主頻 | SysTick / Timer |
工程最佳實踐
- 拒絕軟件延時循環:不可移植、受優化影響、佔用 CPU、功耗高
- 使用硬件定時器:SysTick (Cortex-M 通用)、通用定時器、RTC、PWM 輸出
- 精確微秒延時:讀取
DWT->CYCCNT (Cortex-M3/4/7) 或定時器計數寄存器
- RTOS 環境:用
vTaskDelay() / osDelay() 釋放 CPU
- 必須用軟件延時時:
volatile + 校準迴路 + 註釋「僅用於極短延時/無定時器場景」
校準軟件延時的標準流程
// 1. 寫測試函數
void delay_calib(uint32_t cnt) {
volatile uint32_t i;
for (i = 0; i < cnt; i++);
}
// 2. 邏輯分析儀/示波器測 GPIO 翻轉週期
// 3. 計算 cnt_per_us = cnt / 實測微秒數
// 4. 運行時動態調用 delay_calib(cnt_per_us * 目標微秒)
總結
「for 循環多少機器周期」是教學/面試題,實際工程中請直接用硬件定時器。若面試被問,答出「取決於架構/編譯器/優化,現代開發用硬件定時器」即得滿分。
- 上一篇文章: C語言判斷完數(完全數):標準算法與優化版完整代碼
- 下一篇文章: C語言打印年曆:完整算法與代碼實現(含閏年、星期計算)
Copyright ©
程式碼之家 All Rights Reserved