今天我們來使用Timer的中斷功能吧!
設定與昨天大致相同,只是我們現在需要開啟中斷。
另外要注意的是什麼時候會進中斷
在不同的模式下進中斷的時機不同,上數與下數都是在一個周期的最後進入,而中心對齊則是數到頭尾都會進入中斷。
我們來自己實作一個counter來計時,利用中斷的方式,設置PSC=15,ARR=1000,當這個counter數到1000的時候就會進入中斷,稍微計算一下就可以知道每1ms會進入一次中斷(PSC將時鐘訊號降低1/16,變成1MHz,1000次會進入中斷,又變為1kHz)。我們宣告兩個變數,ms與sec,分別為毫秒與秒。當每次進入中斷就把ms++,只要ms=1000代表經過1s了,我們就將ms歸零,sec加一。實際程式碼如下
HAL_TIM_Base_Start_IT(&htim2);
這一行程式碼是不是與上次很相似啊,只是這次尾端加上了"IT",意思為interrupt,
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim){
if(htim->Instance == TIM2){
ms++;
if(ms==1000){
ms = 0;
sec++;
}
}
}
這一段函式其實在tim.c當中已經被定義過了,而我們這邊是重新定義這個函式,可以在以下的地方打開這個檔案直接搜尋這個函式
函式內容如下
函式的開頭有__weak,用在函式前,代表這個函式是弱定義,當你的程式碼其他地方有對這個函式就其他地方做定義,就會以新的定義為主,那有人可能會問,這樣他定義這個有什麼用呢?
就是給你複製的...通常不熟悉的人很難記住函式的API,照著打出來,因此你可以先到tim.c當中找到這些程式碼,直接複製到main.c當中,就可以直接使用了。
由於上述兩段程式碼分別在程式的不同地方,直接附上整段程式碼看起來會非常的亂,讀者可能也難以理解,因此以下會直接說明這兩段程式碼的位置
第一段程式碼放在main裡面不需要放在迴圈當中,他不需要被重複的執行,只要start一次就可以了。
第二段程式碼就是放在一般函式定義的地方,因此在main以外的任何地方都可以,我通常習慣放在底下的Begin和End之間,接近程式碼尾端的位置。
記得要新增sec與ms兩個全域變數,才能使用現場表達式監看喔
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
了解程式碼的原理就寫好完整的程式碼執行看吧,應該可以利用現場表達式的功能看到ms與sec兩個變數,看會不會如預期的一樣sec會每秒增加1囉。
第二個小程式我們要來實作微秒級的delay,函式庫裡的HAL_Delay()只能實現毫秒級的delay。
這個實作不會用到中斷,只會需要再介紹一個新的函式!
昨天我們講過底下這個函式可以獲得現在counter數到的值,
__HAL_TIM_GET_COUNTER()
另一個函式可以設定counter的值,這個函式一樣是用#define的方式定義的,我們可以在tim.h的文件中找到。
__HAL_TIM_SET_COUNTER()
實作delay的思路是,每次呼叫這個函式我們就將counter的值歸零,然後讓程式進到一個while的迴圈當中,只要counter的值小於傳進來的參數,就繼續在迴圈執行,大於等於時則跳出迴圈,這樣就可以讓程式卡在這個地方不繼續持行下去,達到delay的效果。底下為這個函式的設計:
void microDelay(int t){
__HAL_TIM_SET_COUNTER(&htim2,0);
while(__HAL_TIM_GET_COUNTER(&htim2) < t){
}
}
我們再另外設計一個小程式來檢測我們的microDelay(),到底有沒有成功delay
在while迴圈當中,每次延遲1000微秒,再把ms加一,而當ms等於1000,sec加1,這部分的思路與第一個實作很類似。
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
microDelay(1000);
ms++;
if(ms==1000){
sec++;
ms=0;
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
一樣記得在全域宣告ms與sec兩個變數
執行後的結果應該要與小實作一相同,不過我們一個是利用中斷,一個是利用實作微秒級的delay來達成。
這兩天介紹了TIM最最最基本的功能以及幾個函式。我們再來複習一次吧!程式開始都一定要讓TIMER啟用,而要啟用中斷的函式與一般的啟動函式略有不同