[STM32] 09-Timer-PWM

Morgan Ting
閱益如美
Published in
11 min readApr 16, 2022
Photo by Shayan Ghiasvand on Unsplash

Pulse-width modulation , PWM 脈波寬度調變常見於亮度調整與機械控制,其原理是利用數位方式模擬類比訊號。本文章將介紹 STM32 的 PWM 產生機制並以 STM32CubeIDE 開發環境搭配 Hardware Abstraction Layer, HAL 函式庫產生 PWM 訊號。

文章內容

  1. STM32 的 PWM
  2. HAL 函式庫
  3. 實作
  4. 成果展示

工具與材料

  1. STM32CubeIDE
  2. Blue Pill ( STM32F103C8T6 ) 開發板
  3. ST-LINK v2
  4. 示波器

STM32 的 PWM

在微處理器中一般都是調整訊號開關時間來產生 PWM 訊號,訊號的工作週期 duty cycle 是我們所關心的。

duty cycle

如圖所示,工作週期是訊號高態維持時間與週期比值,這有什麼重要呢 ?

假設我們想控制一個 LED 燈亮度,當施加於 LED 燈的偏壓越大則 LED 燈越亮反之越暗淡,可是數位電路沒有辦法直接輸出類比電壓於是便需要產生一個工作週期可控的數位訊號來達成目的,例如微處理器輸出電壓最高為 3 v ,如果產生一個 duty cycle 20% 的訊號施加於 LED 燈上那麼 LED 燈上的電壓就是 3 v * 20% = 0.6 v ,依此類推增加到 duty cycle 100% 那麼 LED 燈就會得到 3 v 電壓,藉由這個方式便可以控制 LED 燈亮度。其他常見的還有控制伺服馬達,藉由 PWM 控制伺服馬達的旋轉角度。

STM32 產生 PWM 的原理是利用計時器 Timer 來決定訊號頻率與工作週期,STM32 計數器的計數方式有上數、下數與中央對齊 ( 上數 + 下數 ),以上數模式為例在一個完整的計數週期當中計數器會從 0 計數到 TIMx_ARR 內容值後歸零完成一次計數週期,所以暫存器 TIMx_ARR 內容值決定了 PWM 訊號的頻率。只有 TIMx_ARR 只能控制頻率我們需要借助輸出比較 Output Compare 暫存器 TIMx_CCRy 來決定訊號轉態時機如此才能產生 duty cycle 與 frequency 可控的 PWM 訊號。

STM32 Timer

如圖,當計數器從 0 開始上數到比較值 TIMx_CCRy 時便輸出低態否則輸出高態,因此 TIMx_CCRy 與 TIMx_ARR 兩個暫存器內容之比值就是 duty cycle 了。設定時需要注意如果 TIMx_CCRy 大於 TIMx_ARR 那麼輸出會一直維持高態,如果 TIMx_CCRy 等於零時輸出會維持低態。

HAL 函式庫

STM32 關於 PWM 控制的 HAL 函式庫有以下常用的函式

  • HAL_TIM_PWM_Start( Timer 編號 , 輸出通道編號 ) 開啟計時器。
  • 範例: HAL_TIM_PWM_Start ( &htim2 , TIM_CHANNEL_2 ); 開啟 Timer 2 並且由通道 2 輸出訊號。
  • HAL_TIM_PWM_Stop( Timer 編號 , 輸出通道編號 ) 關閉計時器。
  • __HAL_TIM_SET_COMPARE ( Timer 編號 , 輸出通道編號 , 新比較值 ) 設定比較值。
  • 範例:__HAL_TIM_SET_COMPARE ( &htim2 , TIM_CHANNEL_2 , 500 ) ;
  • 指定 Timer 2 的通道 2 當中的比較值為 500,比較值也可以是變數。
  • __HAL_TIM_GET_COMPARE ( Timer 編號 , 輸出通道編號 ) 得到目前的比較值。
  • 範例: int value = __HAL_TIM_GET_COMPARE ( &htim2 , TIM_CHANNEL_2 ) ;
  • 取得 Timer 2 通道 2 目前設定的比較值並放在變數 value 裡。

實作

本次實驗使用 Timer 2 並以位於 PA1的通道 2 作為輸出腳位,藉由示波器來觀察輸出訊號。

相關參數設定如下:

  • Timer 計數模式為上數。
  • Timer 2 分頻 prescaler 設為 72 。
  • Counter period 設為 1000。
  • Pulse 設為 500 。

由於 Timer 2 時脈為 72 MHz ,經過 prescaler 分頻後得到 72 MHz / 72 = 1 MHz,而 Counter period 其實就是設定 TIM2_ARR 暫存器內容設為 1000 表示計數到 1000 就歸零完成一次計數週期,因此輸出訊號頻率為 72 Mhz / 72 / 1000 = 1 KHz。至於參數 pulse 便是比較值設為 500 表示計數到 500 時輸出低電位,所以我們預期會得到頻率 1 Khz 且 duty cycle = 500 / 1000 = 50% 的 PWM 訊號。

一、開啟 STM32CubeIDE 設定專案目錄後來到歡迎頁面,點選 Start New STM32 project 新增一個專案。

start new project

二、在 Target Selection 頁面中於搜尋列輸入 「F103C8 」便會出現 STM32F103C8T6 型號,選取該晶片型號後按下 Next。

target selection

三、輸入專案名稱後按下 Finish 。

project name

四、在 Categories 項目中找到 Timers ,展開後點選 TIM 2 。右邊設定項目中 Clock Source 選取 Internal Clock,Channel 2 選擇 PWM Generation CH2 表示將使用 Timer 2 的通道 2 作為輸出。

Timer setting

下方的參數設定如下:

  • Prescaler 輸入 72 -1 。
  • Counter period 輸入 1000 - 1。
  • PWM Generation CH 2 項目底下的 Pulse 輸入 500 。
Timer parameter

五、Categories 中的 SYS 項目,debug 選擇 Serial Wire。

六、 Catagories 中的 RCC 項目,High Speed Clock ( HSE ) 選擇 Crystal / Ceramic Resonator。

七、Clock Configuration 設定 TIMER 時脈來源,首先在 PLL Source Mux 選擇 HSE,PLL Mul 選擇 9 ,System Clock Mux 選擇 PLLCLK ,最後在 APB1 Prescaler 選擇 /2 。Timer 的時脈來源便設定完成。

clock setting

八、Project Manager 的部分確認一下專案目錄與名稱以及 Code Generator 的部分是否需要更改。

project manager

沒什麼問題的話上方命令列 Project ==> Generate Code 產生初始程式碼。

generate code

九、由於 STM32CubeIDE 已經自動產生相關初始化設定程式碼,剩下的只有啟動計時器。我們借助 HAL 函式庫來啟動 TIMER,在 /* USER CODE BEGIN 2*/ 區段寫下一段程式來啟動計時器。

HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);

其中,&htim2 是指啟動 TIMER 2 ,TIM_CHANNEL_2 指定 CHANNEL_2 為輸出。

code

十、完成程式碼的撰寫後,上方命令列 Project ==> Build All 或是榔頭圖示,建立相關檔案。

build code

十一、相關檔案建立後,按下 RUN 綠色播放鍵圖示。

run

此時會出現 Debug 設定,Main 項目一般不太需要更改設定。

點選 Debugger ,將 Interface 內的 ST_LINK 前方框框打勾並按下右方的 Scan 按鈕,如果此時ST-LINK v2 燒錄器有接在電腦上就會出現燒錄器 ID 號碼。

設定完成後按下 Run 按鈕就會將程式上傳到 Blue Pill 開發板上頭,以示波器量測 PA1 腳位可以觀察到輸出波形。

成果展示

result fequency
result duty cycle

總結

使用 STM32CubeIDE 開發環境並搭配 HAL 函式庫可以輕鬆的產生 PWM 訊號,關於本文總結如下:

  • STM32F103C8T6 每一個 Timer 有 4 個輸出通道可以各自產生不同 duty cycle 之訊號。
  • Timer 計數模式有上數、下數與中心對齊 ( 上數 + 下數 )。
  • TIMx_ARR 暫存器內容值決定輸出訊號的頻率。
  • TIMx_CCRy 暫存器內容值決定訊號的 duty cycle。
  • TIMx_CCRy 內容值高於 TIMx_ARR 輸出恆高態,TIMx_CCRy 內容值等於 0 輸出恆低態。
  • HAL_TIM_PWM_Start 啟動計時器產生 PWM 訊號。
  • HAL_TIM_PWM_Stop關閉計時器。
  • __HAL_TIM_SET_COMPARE 賦予新的比較值,即更新 TIMx_CCRy 暫存器內容。
  • 此函式可用於軟體控制程序中改變 TIMx_CCRy 暫存器內容以產生不同 duty cycle 的PWM 訊號,例如呼吸燈、馬達加減速。
  • __HAL_TIM_GET_COMPARE 讀取目前的比較值。

參考資料

  1. STM32F103 手冊 [ 連結 ]
  2. STM32F1 HAL and Low-layer drivers [ 連結 ]
  3. STM32 PWM 網路影片教學 [ 連結 ]

感謝讀者

若文章有幫助到您可以拍手給我鼓勵,免費支持我。

相關文章

  • [STM32] 00-Install STM32CubeIDE [連結]
  • [STM32] 01-ST-LINK [連結]
  • [STM32] 02-STM32F103C8T6 [連結]
  • [STM32] 03-GPIO-Output [連結]
  • [STM32] 04-GPIO-Input [連結]
  • [STM32] 05-Ext-Interrupt [連結]
  • [STM32] 06-Timer-Basic [連結]
  • [STM32] 07-Timer-Interrupt [連結]
  • [STM32] 08-Timer-Output_Compare [連結]
  • [STM32] 09-Timer-PWM [連結]
  • [STM32] 10-Timer-Input_Capture [連結]
  • [STM32] 11-RTC-Second-Interrupt [連結]
  • [STM32] 12-RTC-Alarm_Interrupt [連結]
  • [STM32] 13-Independent_Watch_Dog [連結]
  • [STM32] 14-Windows_Watch_Dog [連結]
  • [STM32] 15-ADC_Conversion [連結]
  • [STM32] 16-ADC_Conversion_Temperature_Sensor [連結]
  • [STM32] 17-ADC_Convversion_DMA [連結]
  • [STM32] 18-SPI [連結]
  • [STM32] 19-UART [連結]
  • [STM32] 20-I2C [連結]

--

--

Morgan Ting
閱益如美

用好奇心探索世界。喜愛學習樂於分享。