[STM32] 06-Timer-Basic

Morgan Ting
閱益如美
Published in
11 min readJul 22, 2021
Photo by Alexandre Lion on Unsplash

Timer 計時器是微處理器重要的功能之一,延遲、時間計數是常運用在專案中。本文章介紹STM32F103C8T6 內含的 Timer 種類並搭配 Hardware Abstraction Layer, HAL 函式庫做基礎的計時功能演示。

文章內容

  1. TIMER 簡介
  2. 計數原理
  3. HAL 函式庫
  4. 實作
  5. 總結

材料

  1. Blue Pill ( STM32F103C8T6) 開發板
  2. ST-LINK v2 燒錄器
  3. LED *1
  4. 220 Ω電阻*1

TIMER 簡介

STM32F103C8T6 有不同類型的封裝型態因此同型號不同包裝有些許差異,本文章使用的 Blue Pill 開發板是搭載 LQFP48 封裝的晶片,其內含一個進階 Timer 與四個通用 Timer。

Timer for Blue Pill

計數原理

所有 Timer 的計時原理大致上都是利用時脈訊號作為觸發去 tick 踢計時器,計時器內部有計數器 counter 計算被踢了幾下,由於觸發訊號具有週期性因此可以知道每經過多久會踢一下計時器,如此就可以知道經過多少時間。

用來踢計時器的觸發訊號可以是外部訊號例如利用訊號產生器做實驗或是一個可以產生週期訊號的震盪電路,也可以是晶片內部的時脈。

Ref. STM32F103 datasheet

從 Timer 的系統圖可以了解無論是外部或內部時脈進入計數器前會先經過一個 Prescaler 分頻器,Prescaler 的功能是把訊號頻率降低,由於計數器有資料長度限制如果踢太快那麼計數器很快就計數溢位這對於長時間延遲需求是個困擾。

計數器的計數方式有上數、下數、中心對齊 ( 上數 + 下數 ),等 3 種方式。

以上數方式為例,計數器從 0 開始往上累加每一次加 1 ,累加的速度由觸發時脈決定,當累加到指定數值就歸零,此時觸發溢位中斷事件告訴系統計數完一趟了。該指定數值是儲存在名為 TIMx_ARR 的 16 位元暫存器中,最大值為 65535 。

下數方式是從 TIMx_ARR 內含值往下計數一直到 0 後重新將 TIMx_ARR 值載入,觸發溢位中斷事件,如此完成一個計數週期。

中心對齊是從 0 開始上數到 ( TIMx_ARR — 1 ) 此時觸發溢位中斷事件,然後往下數到 1 觸發溢位中斷事件,最後再從 0 開始新的計數週期。

STM32 的 Timer 並不是直接從系統時脈獲得而是先經過 APB1 這個 CPU 與周邊裝置的橋接器,系統時脈經過APB 1 的分頻器後才會進入 Timer 內部時脈 CK_INT。

假設 APB 1 不做分頻那麼 Timer 時脈頻率最大 72 MHz ,分頻器可以讓使用者設定分頻係數 ,經過分頻器的週期訊號就會以固定時間踢計數器,因此計算時間就是這三項要素:Timer 時脈頻率、分頻係數 、計數上限 。

例如要得到 1 秒鐘我們可以這樣設定:分頻係數 36000,計數上限 2000,

72 MHz / 36000 = 2000 Hz , 2000 Hz 頻率換算成週期就是 1 / 2000 = 0.5 ms。

每 0.5 ms 踢一下計數器那麼踢 2000 下就是 0.5m * 2000 = 1000 ms = 1 s。踢完 2000 下耗時 1 秒。

HAL 函式庫

利用 HAL 函式庫可以省下設定暫存器的功夫,以下介紹 本次使用 Timer 2 相關的幾個 HAL 函數。

  • HAL_TIM_Base_Start(&htim2) 啟動計時器
  • HAL_TIM_Base_Stop(&htim2) 停止計時器
  • __HAL_TIM_GET_COUNTER (&htim2) 讀取計數器內容值
  • __HAL_TIM_SET_COUNTER (&htim2 , value) 設定計數器內容值

實作

實作練習目標是利用 Timer 2 每隔一秒鐘改變位於 PA1 的 LED 燈狀態,使用輪詢法藉由讀取計數器內容值判斷是否達到需求時間。

一、新增一個專案,在歡迎頁面點擊 Start new STM32 project。

Start a new project

二、Target Selection 頁面在搜尋欄上輸入 F103C8 找到 STM32F103C8T6 ,選取後按 Next。

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

四、在 Pinout 頁面上設定 I / O 腳位,因為 LED 燈接在 PA1 上所以把 PA1 設為輸出。

滑鼠點選 PA1 出現選單,選 「GPIO Output」。緊接著,我們給 PA1 取個別名比較有識別性,在 PA1 上按滑鼠右鍵選「 User Label 」後輸入 LED 作為 PA1 腳位的別名。

五、在 「catagories」項目下點選 Timers 設定計時器,可以看到有 RTC 與 TIM1 ~ TIM4,點選 TIM2 通用計時器。Clock Source 選擇 「 Internal Clock 」內部時脈。

底下 configuration 的部分設定分頻係數,因為Timer 上一層時脈源不做分頻因此會得到 72 MHz 計時器時脈頻率,我們把 prescaler 分頻係數設為 36000 這樣計時器時脈頻率就會變成 72 M / 36000 = 2000 Hz ,注意到設定分頻係數時該數字要減 1 ,所以 prescaler 項目填 ( 36000–1 ),其餘參數使用預設數值。

六、「categories 」當中的「SYS」設定「Debug」選擇「 Serial Wire 」。

「categories 」當中的「RCC」設定時脈來源,點選「High Speed Clock (HSE) 」選擇「Crystal / Ceramic Resonator」外部震盪器。

七、 「 Clock configuration 」設定時脈來源,因為 Blue Pill 有外接 8 MHz 震盪器所以可以選擇外接震盪器做為時脈來源。目標是 APB1 Timer Clocks 設為 72 MHz 。

  1. 「PLL Source Mux 」選擇「HSE」。
  2. 「PLL Mul 」選擇 9 倍。
  3. 「System Clock Mux 」選擇「PLLCLK」。
  4. 「APB1 Prescaler 」選擇 2 。

八、「 Project Manager 」當中「Project 」確認一下專案名稱與儲存位置,一般而言不需做任何設定。「Code Generator 」可以設定「 Copy only the necessary library files」複製必要的函式庫道專案目錄中。完成後可以按「Ctrl + S」存檔或上方命令列「Project」==> 「Generate Code」產生初始化程式碼。

程式一開始我們可以利用 HAL 函式庫啟動 Timer ,其函式為 HAL_TIM_Base_Start(htim2);

當中的參數 htim2 是指 TIM2 計時器。若忘記函式名稱可以在輸入 HAL_TIM 後按下「Ctrl + 空白鍵」出現相關函式可以選擇,相當方便。

start timer

接下來就可以來撰寫主要的程式邏輯了,我們的想法是一直讀取計數器內容值若等於 2000 代表時間過了 1 秒鐘,此時改變 LED 燈腳位狀態來點亮或關閉 LED 燈,並且將計數器內容清零。

if( __HAL_TIM_GetCounter(&htim2) >= 2000) // if counter >= 2000{HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); // toggle LED pin__HAL_TIM_SET_COUNTER(&htim2, 0);  // set counter as 0}

程式撰寫完畢後案上方命令列 「Project」==> 「Build All」或是榔頭圖案建立相關程式。

完成後接上 ST-LINK v2 ,上方命令列按下 「Run」==> 「Debug」或是臭蟲圖案進行除錯與燒錄步驟。

在除錯頁面確認專案所在目錄是否正確。

再來點選 「Debugger」勾選 interface 項目中「ST-LINK S/N」並按一下 Scan 此時會出現 St-LINK v2 的 ID。按下「Apply」與「OK」後就會把程式傳送到開發板上,完成後按一下開發板的「RESET」按鈕便可以看到結果。

電路圖

總結

Timer 計時器是微處理器不可或缺的功能之一,STM32 通用暫存器可以用來計時與產生脈波調變訊號。STM32CubeIDE 提供了圖形化的設定可以自動產生初始化程式碼,搭配 HAL 函式庫省去直接操作暫存器的麻煩,本文章利用通用暫存器計時並開關 LED 燈所需相關知識整理如下:

  • Timer 時脈由 APB1 或 APB2 提供,最高可以供給 Timer 72 MHz 時脈訊號。
  • Timer 計數方式有上數、下數及中心對齊 ( 上數 + 下數 )。
  • Prescaler 分頻係數可以是 1 ~ 65536 。
  • 計數上限由 TIM_ARR 暫存器內容決定,範圍是 1 ~ 65536 。

參考資料

  1. STM32F103 手冊 [ 連結 ]
  2. STM32F1 HAL and Low-layer drivers [ 連結 ]

感謝讀者

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

相關文章

  • [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
閱益如美

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