iT邦幫忙

0

STM32-5 NVIC Timer中斷

  • 分享至 

  • xImage
  •  

前面文章有介紹到Delay的用法,Delay雖然也可以做到延遲或控制時間的效果,但嚴格來說透過Delay去做控制並不準確,這時候就可以用到Timer來做時間上的控制!

定時器說明

STM32L476RG中一共有11個計時器,其中又分為基本、通用、高階三種計時器。
https://ithelp.ithome.com.tw/upload/images/20220311/201463251QqBct4N27.png
https://ithelp.ithome.com.tw/upload/images/20220311/20146325dl3tE0iffE.png
基本定時器大多用來作為基本的定時功能,較常用的是通用定時器除了基本定時功能外,可以做輸入捕捉、輸出比較等等用途,而高級定時器可以對於馬達做死區控制的互補信號輸出等等功能。
這篇主要介紹定時器的基本使用方法,先解釋DataSheet當中的幾項:

  • Counter resolution : 分為16 / 32 bit ,主要的差異在於計數的最大值,當定時器開始時會從0開始向上數到所設定的數值(依照計數模式選擇),當達到時會觸發中斷。16bit最大計數值為2^16 = 65536,而32 bit則為2^32 = 4294967295。
  • Counter type : 計數方式,分為向上、中心對齊、向下三種模式
    1. 向上計數: 計數器會從0開始往上數到自動裝載值,然後回到0重新開始。
    2. 向下計數: 計數器會從自動裝載值往下數到0,再次回到自動裝載值開始。
    3. 中心對齊: 計數器從0開始到自動裝載值-1,接著會在往下計數到0+1再往上。
      自動裝載值(ARR)也就是上方Counter resolution所設定的數值。
      https://ithelp.ithome.com.tw/upload/images/20220311/20146325HXBsGsRgnx.png
      Prescaler factor :預分頻係數,每一種定時器都可設置1-65536,可以控制計數器的快慢

定時器設置

以STM32L476RG來說當中11種定時器分別來自不同的匯流排(APB1與APB2),其中TIM2,3,4,5,6,7來自APB1,而TIM1,8,15,16,17則來自APB2,在對於預分頻係數做設定的時候要注意一下。
https://ithelp.ithome.com.tw/upload/images/20220311/20146325FtNf3OUQhE.png
在IDE當中的.ioc檔中點選上方的Clock Configuration,可以去對Prescaler(預分頻係數)做設定。
分為兩個匯流排去做設定,當中有1,2,4,8,16可以依照需求做選擇。舉例來說如果下方預分頻係數選擇16,則分頻後會變成1Mhz。(如果從這邊去做設定的話會影響到所有連接在該匯流排的時脈)
https://ithelp.ithome.com.tw/upload/images/20220311/20146325Cd90XdRRV4.png
在使用時大多會從左側選單當中的Timer去做Prescaler的設定,這邊先選擇通用定時器Timer2去做設置。

  1. 左側點選Timers→TIM2可以看到出現下方畫面
    https://ithelp.ithome.com.tw/upload/images/20220311/20146325RTe6CPd1V0.png
  2. 點選Clock Source 選擇時鐘來源,這邊選內部時鐘
    https://ithelp.ithome.com.tw/upload/images/20220311/20146325s8QaFvJ4HS.png
  3. 接下來就可以去對於TIM2做詳細的設置,可以看到下方Configuration當中前三個選項,也就是剛剛前面所提到的分頻係數、計數模式與自動裝載值。
    https://ithelp.ithome.com.tw/upload/images/20220311/20146325nXTHUdvYBP.png
  • Prescaler (PSC):這邊可以設置0-65536,假設像上方Clock Configuration那張圖當中系統時鐘為16Mhz ,那在這邊設置15經過分頻後,則會等於16/(15+1)也就是1Mhz。
  • Counter Mode: 這邊計數方式選擇向上(詳細說明可以看上方的介紹)
  • Counter Period (ARR/計數週期): 也就是上方的Counter resolution,由於TIM2屬於32bit的計時器,所以最大可計算到2^32,目的在決定計數器從0數到多少產生一個溢位。

如何去計算計時器的溢位時間?

https://chart.googleapis.com/chart?cht=tx&chl=Tout%20%3D%20(ARR%2B1)*(PSC%2B1)%20%2F%20Tclk
ARR : 自動裝載值(上方的Counter Period)
PSC : 預分頻係數
Tclk : APB時鐘,通常會等於系統時鐘
舉例:
假設TCLK = 80M , PSC = 7999 , ARR = 9999
⇒ (9999+1)*(7999+1) / 80000000hz = 1s

1Mhz = 1000khz =1000000hz
1Mhz = 1us 1khz = 1ms 1hz = 1s
那這樣定時器就是設定為1秒(計數週期從0數到9999需要1秒的時間)


函數介紹

在stm32xxxx_hal_tim當中可以找到相關的定時器函數
https://ithelp.ithome.com.tw/upload/images/20220311/20146325vCAW0xsG8z.png

  1. 開啟定時器
HAL_StatusTypeDef HAL_TIM_Base_Start(TIM_HandleTypeDef *htim)
{
	..........
}
//用法 htimx -> x 看是哪個TIM就填哪個
HAL_TIM_Base_Start(&htim2);
  1. 關閉定時器
HAL_StatusTypeDef HAL_TIM_Base_Stop(TIM_HandleTypeDef *htim)
{
	..........
}
//用法 htimx -> x 看是哪個TIM就填哪個
HAL_TIM_Base_Stop(&htim2);
  1. 開啟定時器中斷
HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim)
{
	...........
}
//用法 htimx -> x 看是哪個TIM就填哪個
HAL_TIM_Base_Start_IT(&htim2);
  1. 關閉定時器中斷
HAL_StatusTypeDef HAL_TIM_Base_Stop_IT(TIM_HandleTypeDef *htim);
{
	...........
}
//用法 htimx -> x 看是哪個TIM就填哪個
HAL_TIM_Base_Stop_IT(&htim2);
  1. 定時器中斷回調函數,將觸發中斷後要做的事情寫在這裡
__weak void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(htim);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_TIM_PeriodElapsedCallback could be implemented in the user file
   */
}
//用法 htimx -> x 看是哪個TIM就填哪個
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	 if (htim->Instance == htim2.Instance) //假如tim實例等於tim2才進迴圈
	 {
			.....................
	 }
}

程式範例-來做一個定時閃爍燈吧!

  1. 第一步先確定系統時鐘是多少,以我的為例是16Mhz
    https://ithelp.ithome.com.tw/upload/images/20220311/20146325CRPRa9YYPw.png
  2. 接著點選左側的選單設定TIM2
    https://ithelp.ithome.com.tw/upload/images/20220311/20146325WNo0vtEJUS.png
    https://ithelp.ithome.com.tw/upload/images/20220311/20146325Wy8gz0wXpY.png
    在這邊可以看到我將PSC設定為1599,計數方式為向上計數,計數週期(ARR)設定為9999。按照上方溢出時間計算方式可以得到(1599+1)*(9999+1) / 16000000 hz = 1s,也就是說每一次是一秒鐘。
  3. 記得開啟定時器中斷!
    https://ithelp.ithome.com.tw/upload/images/20220311/20146325HM9peOKALO.png
  4. 回到main.c當中首先在main()當中的/* USER CODE BEGIN 2 / / USER CODE END 2 */ 啟動TIM2中斷。
/* USER CODE BEGIN 2 */
  HAL_TIM_Base_Start_IT(&htim2);
 /* USER CODE END 2 */

接著找到下方的/* USER CODE BEGIN 4 / / USER CODE END 4 */,在當中寫入中斷回調函數,同樣的記得宣告全域變數i。

/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim){
	if(htim->Instance == htim2.Instance){
		i++;
		if(i==3)
		{
			i = 0;
			HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
		}
	}
}
/* USER CODE END 4 */

在這邊因為前面設定溢出時間為1秒,可以知道的是他觸發一次時間中斷是1秒一次,而i實際上也就是秒數的變化!接下來利用前面提到的Toggle函數可以達到每3秒切換一次燈號,可以看看板載上的LED燈是否有按照設定的時間去做變化!

前面介紹過有關Timer的使用方式,但使用的是中斷方式。結合其他中斷時需要考慮到優先權的問題,那如何利用定時器達到延時效果而不進入中斷呢?下方介紹如何去做設定。

  1. 首先設置.ioc 以TIM3為例,將PSC設為15999(系統時鐘為16Mhz) ARR設為1999,經過計算可以得知這是一個兩秒的定時器。

https://ithelp.ithome.com.tw/upload/images/20220313/20146325ZJ6t0nxdqn.png

  1. 透過下方函數可以獲得定時器Count的數值
__HAL_TIM_GET_COUNTER(&htimx); //x填入對應的TIM

這時我們就可以利用CNT的數值來做到延時效果,首先先啟動定時器。

HAL_TIM_Base_Start(&htim3);

接著可以在while(1)當中寫入下方程式碼,記得宣告timerct變數。

剛剛已知定時器為兩秒也就是說從0數至1999需要兩秒,那這時將當前的計數值減去後續的計數值大於1000時即為一秒。因為16Mhz/(15999+1) = 1khz,而1khz=1ms 接著 *1000 = 1s,可以看一下板載LED有沒有每一秒切換一次。

if (__HAL_TIM_GET_COUNTER(&htim3) - timerct >= 1000)
{
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
	  timerct = __HAL_TIM_GET_COUNTER(&htim3);
}

以上內容如果有誤的話,麻煩各位通知我。感謝~


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言