我在 2022 年 9 月重新寫了與本文內容相近的文章,建議可以觀看新文章:
前言
LibOpenCM3 是一個 Open-Source 的 ARM Cortex-M3 微控制器底層硬體函式庫,支援包含 STM32 在內的多種微控制器。
本文將以 STM32F103RB(Nucleo F103RB)作為示範,介紹如何使用 LibOpenCM3 寫出 STM32 的 PWM(Pulse-Width Modulation) 功能,並且可以控制頻率與 Duty Cycle(佔空比)。
正文
在 STM32 中,PWM 的功能是由 Timer 所實現的,並藉由設定 CCR(捕獲/比較暫存器) 的值來調整 Duty Cycle。
完整程式
程式分段說明
引入函式庫
除了基本的「RCC」及「GPIO」外,由於 PWM 是透過 Timer 所實現的,所以還要引入「Timer」。
PWM 數值宣告
宣告 PWM 的目標頻率及 Duty Cycle 以供後續計算。
PWM 頻率設定
這裡算是本文的重點之一,也就是如何設定 PWM 的頻率。
首先:
f_pwm = f_tim / [(PRS + 1) * (PER + 1)]
所以:
PER = {f_tim / [(PRS + 1) * f_pwm]} - 1
其中:
f_pwm
: PWM frequency,PWM 的頻率.
f_tim
: Timer frequency, Timer 的頻率.
PRS
: Timer prescaler,Timer 的預除頻器數值.
PER
: Timer period,Timer 的週期數值.
透過時鐘樹(Datasheet P.12, Figure 2. Clock tree)可以知道,我們使用的「Timer 3」的時鐘源是「APB 1」,而在本例中,我們會在主程式呼叫 rcc_clock_setup_in_hsi_out_48mhz()
以將系統時鐘設為 48 MHz,這樣將會一併讓「APB 1」的預除頻器(Prescaler)被設定為「除 2」,所以我們的「APB 1」時鐘頻率為 48 MHz / 2 = 24 MHz。
然而,當「APB 1」的預除頻器不等於「除 1」時,「APB 1」的時鐘會先乘 2 再給「Timer 3」,因此「Timer 3」的時鐘頻率 f_tim
為 24 MHz * 2 = 48 MHz。
最後我將 PRS
設定為 48 - 1
,將 PER
以上面的公式帶入。
rcc_apb1_frequency
的數值會在呼叫 rcc_clock_setup_in_hsi_out_48mhz()
時設定。
GPIO 設定
設定 GPIO 以輸出 PWM 訊號。注意,每個 Timer 的 Channel 能輸出的 GPIO 腳位都不同,像這裡「Timer 3」的「Channel 2」是對應「PA7」,在使用不同 Timer、Channel 時請參考 STM32 Datasheet。
PWM Timer 設定
這裡也是本文的重點之一,在這裡設定好要使用的 Timer 及 PWM Duty Cycle。
timer_set_oc_value()
就是改變 CCR 的值,以調整 Duty Cycle。因為我們使用的是「Channel 2」,所以 Output channel 是TIM_OC2
。
在 TIM_OCM_PWM1
模式下,想要輸出 X
% Duty Cycle 的 PWM,只要把 CCR 的數值也設定成 Timer 週期的 x
% 就可以了,也就是 CCR_Value = (PWM_TIMER_PERIOD + 1) * (PWM_DUTY_CYCLE / 100.0)
。
最後以 timer_enable_oc_output()
啟動 PWM 的輸出,timer_enable_counter()
啟動整個 Timer。
主程式
主程式先將系統時鐘設定為 48 MHz,並依序完成 GPIO 及 PWM 的初始化設定,在執行完 pwm_setup()
後就會開始輸出 PWM 了,最後就進入一個無限空迴圈。
成果
可以看到輸出的波形誤差很小(設定值為:Duty Cycle 72.5%,頻率 1 kHz)。
結語
本次的程式我一樣有放在 GitHub 上,可以使用 PlatformIO 開始專案。
若有什麼問題或錯誤歡迎留言討論。
相關連結
留言可能不會立即顯示。若過了幾天仍未出現,請 Email 聯繫:)