學完本章你將能夠:
用 TIM PWM 產生可變佔空比,驅動 LED 亮度與直流風扇。
產生 50 Hz、1–2 ms 脈寬控制 SG90 伺服(開門/關門)。
解釋 ARR、PSC、CCR 三個暫存器如何決定頻率與佔空比。
把亮度與轉速限制在安全範圍,避免「口令說 1000%」寫爆比較值。
GPIO 只能全開全關。燈光調光、風扇轉速、舵機角度都需要脈衝寬度 調變(PWM):固定週期、改變高電平時間。STM32 的通用/進階計時器就是 做這件事的硬體。
ARR、PSC、CCR
計時器時鐘 \(f_{\mathrm{tim}}\) 先經預分頻器 PSC,再數到自動重載 ARR 就更新一次:
\[f_{\mathrm{pwm}} = \frac{f_{\mathrm{tim}}}{(PSC+1)(ARR+1)}\]
佔空比 \(\approx \mathrm{CCR}/(ARR+1)\)。實務設定:
LED/風扇:20 kHz 左右,避開人耳可聽範圍;ARR 取 999,CCR 0–999 對應 0–100%。
SG90:週期 20 ms(50 Hz)。0\(^\circ\) 約 0.5–1 ms, 180\(^\circ\) 約 2–2.5 ms(以你手上的舵機規格為準)。
CubeMX:把某根支援 TIM 的腳設為 TIM_CH1,參數填 PSC/ARR, 產生後 |HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1)|,再用 |__HAL_TIM_SET_COMPARE| 改 CCR。
直流風扇與 MOSFET
12 V 風扇由邏輯電平 N-MOSFET(例如 IRLZ44N 對 5 V 友善;3.3 V 請選 邏輯閘極型號)低端開關。PWM 打在閘極,源極接地,汲極接風扇負端,風扇 正端接 12 V。務必加飛輪二極體。
有些風扇在低佔空比時會 stalled(轉不動但耗電流)。軟體應設最低轉速 (例如 25%),或先全開 300 ms 再降到目標值(soft-start)。
伺服:角度對應脈寬
/* 1 MHz 計時 => 1 tick = 1 us,ARR = 19999 => 20 ms */ static uint16_t servo_us_to_ccr(uint16_t us) if (us < 500) us = 500; if (us > 2500) us = 2500; return us; /* 因為 1 tick = 1 us */
void dev_door_set_angle(uint8_t deg) if (deg > 180) deg = 180; uint16_t us = (uint16_t)(500U + ((uint32_t)deg * 2000U) / 180U); __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_2, servo_us_to_ccr(us));
開門/關門不要瞬間對打:在任務裡用 20–40 ms 步進,或限制指令頻率。 機構卡住時電流會飆,課程用 SG90 小負載即可,不要接真正的大門電機。
實驗:序列埠調光與開門
firmware/lab03_pwm_fan_motor/。協定: l 40 亮度 40%、f 70 風扇 70%、d 0/d 90 門角度、a 全關。
上層仍走 |dev_*|。PWM 細節關在 dev_pwm.c。若沒有 12 V 電源,把風扇腳改接 LED,用亮度模擬轉速,邏輯完全相同。
void dev_set_percent(TIM_HandleTypeDef *htim, uint32_t ch, uint16_t arr, uint8_t percent) if (percent > 100) percent = 100; uint32_t ccr = ((uint32_t)percent * arr) / 100U; __HAL_TIM_SET_COMPARE(htim, ch, ccr);
實作「亮一點/暗一點」:每次加減 15%,飽和在 0 與 100。這正好對應 語音指令 CMD_LIGHT_UP / DOWN。再量一次 PWM 腳的 頻率,確認是 20 kHz 而不是 20 Hz(PSC 填錯一個零是經典事故)。