iT邦幫忙

0

STM32-4 NVIC 外部中斷EXTI

  • 分享至 

  • xImage
  •  

NVIC介紹

NVIC(Nested vectored interrupt controller)為嵌套向量中斷控制器,當中每個中斷的優先級都是用暫存器當中的8 bit來做設置,也就是2^8=256,可以支持到256個中斷。但實際上在使用時僅使用高四位[7:4]來做設定,低四位為0,表示支援2^4=16級中斷。
https://ithelp.ithome.com.tw/upload/images/20220310/20146325CO8fXD0lZo.png
在NVIC當中分為兩個優先級分組分別是Preemption Priority(主)與Sub Priority(子),字面上來看可以知道Preemption Priority比Sub Priority優先權來的高,在同一個優先級上數字越小越高。
有接觸過8051應該會比較好理解暫存器的設定,8051中也有中斷優先暫存器IP,可以依照bit去做中斷優先權的設定!

設備 Preemption Priority Sub Priority
A 1 2
B 2 1
上表可以看到當B遇到中斷時,由於A的主優先級較高,這時候B會立即停止。等待A執行完畢後再輪到B執行。
設備 Preemption Priority Sub Priority
------------- ------------- -------------
A 1 2
B 1 1
當兩設備Preemption Priority相同時,這時候就依照先後順序來決定誰先執行。
但當A與B同時到來時,這時候會先比較Preemption Priority發現兩者相同時,接著比較Sub Priority來決定何者先行執行。
優先級分組 Preemption Priority Sub Priority
------------- ------------- -------------
NVIC_PriorityGroup_0 0 0~15
NVIC_PriorityGroup_1 0,1 0~7
NVIC_PriorityGroup_2 0~3 0~3
NVIC_PriorityGroup_3 0~7 0,1
NVIC_PriorityGroup_4 0~15 0

如何設定ioc檔

進入到.ioc檔後左側選擇Sydtem Core 當中的NVIC可以看到中斷的設定~
https://ithelp.ithome.com.tw/upload/images/20220310/20146325ad7cvPlAIv.png
在上方的Priority Group 則是上方所提到的中斷分組,點開可以看到分成五組,每組須按照規定給予中斷分級。
https://ithelp.ithome.com.tw/upload/images/20220310/20146325GWqtz0yBuD.png
可以看到從最上方的是分組0最下方則是分組4,以分組0為例可以看到它是0 bits for pre-emption priority 4 bits for sub priority,意思也就是pre-emption priority只能設置為0,而sub priority可以設置0-15(2^4 = 16)。

  1. 要使用外部中斷首先要先將腳位設定為GPIO_EXTIx
    https://ithelp.ithome.com.tw/upload/images/20220310/20146325Pmh2AVOsu6.png
  2. 將NVIC當中剛剛所選的腳位中斷開啟
    https://ithelp.ithome.com.tw/upload/images/20220310/20146325aOlaYBk4t8.png
    接著可以點回GPIO的介面並點選剛剛所設定的中斷腳位,下方會出現可以配置的選項。
    https://ithelp.ithome.com.tw/upload/images/20220310/20146325kKrJ92X0xX.png
    第一個GPIO Mode可以設置中斷的觸發時機
    External Interrupt Mode with Rising edge trigger detection:上升緣觸發~低電位變為高電位時會出發。
    External Interrupt Mode with Falling edge trigger detection:下降緣觸發 ~高電位變為低電位時觸發
    External Interrupt Mode with Rising/Falling edge trigger detection:上升與下降時都會觸發
    下方三個當發生時會設置中斷旗標,但不會產生中斷
    External Event Mode with Rising edge trigger detection:上升緣觸發
    External Event Mode with Falling edge trigger detection:下降緣觸發
    External Event Mode with Rising/Falling edge trigger detection:上升與下降時都會觸發

事件: 當檢測到一個動作觸發事件發生時,由硬體自動完成觸發到解決的狀況 。
中斷: 當有某個事件發生並觸發中斷後,由CPU介入跳到中斷服務常式中執行。

(中斷有可能被優先權更高的中斷搶先,而事件不會。事件可以在不需要CPU介入時執行動作)
第二個GPIO Pull-up/Pull-down可以看看上一篇的介紹,默認設置為No pull-up and no pull-down
第三個User Label可以設定比較好區分的名稱


外部中斷暫存器介紹

STM32的EXTI有20個中斷/事件線,每個GPIO都可以設為一個中斷(GPIO_0 ~ GPIO_15),另外4個為特殊用途。
同樣也可以直接透過暫存器去對中斷做設定,步驟大上至上如下:

硬體中斷

  1. 設置20個中斷線的屏蔽位→ EXTI_IMR
  2. 設置選定中斷線的觸發條件 → EXTI_RTSR EXTI FTSR
  3. 設置對應外部中斷的NVIC的致能和屏蔽位。

硬體事件

  1. 設置20個事件線的屏蔽位 → EXTI_EMR
  2. 設置選定事件線的觸發條件 → EXTI_RTSR EXTI FTSR

軟體中斷/事件

  1. 設置20個中斷/事件線的屏蔽位 → EXTI_IMR EXTI_EMR
  2. 設置軟件中斷暫存器的請求位 EXTI_SWIER

這邊說一下硬體中斷與軟體中斷的區別:

  1. 硬體中斷 : 由連接的外部設備產生的,當發生時可以直接觸發中斷,其餘正在執行程式的暫時停止,直到中斷結束。(會因為優先權而決定誰先執行) EX:按鈕中斷、定時器中斷
  2. 軟體中斷: 由執行中斷指令去產生的(不會有搶佔的問題) EX: 除數為0、常用的逐行執行程式

AFIO_EXTICRx : 控制20條中斷輸入線,一共有4個暫存器分別控制0-3、4-7、8-11、12-15。
https://ithelp.ithome.com.tw/upload/images/20220310/20146325cIb6IQLJXu.png
EXTI_IMR : 只有當對應的位設定為1時才會產生中斷,0-19共20條中斷線。
https://ithelp.ithome.com.tw/upload/images/20220310/201463253cKtC2fKEx.png
EXTI_EMR :同上,只有設置為1才會產生事件
https://ithelp.ithome.com.tw/upload/images/20220310/20146325AaATKqflNW.png
EXTI_RTSR : 觸發中斷條件,這邊是選擇觸發時是由低電位轉為高電位時觸發。
https://ithelp.ithome.com.tw/upload/images/20220310/20146325i826WCIqX0.png
EXTI_FTSR : 同上為觸發條件,選擇觸發時是由高電位轉為低電位時觸發
https://ithelp.ithome.com.tw/upload/images/20220310/20146325nBXrRw8p2B.png
資料來源:STM32中文技術手冊


函數介紹

下方是STM32xxxx_HAL_Driver中GPIO的定義,可以看到外部中斷的回調函數。前面開頭關鍵字是弱定義的意思,假如其他地方定義過了會優先選擇。
__weak的介紹可以看連結這篇
https://ithelp.ithome.com.tw/upload/images/20220310/20146325P984IYdOEN.png
函數庫當中所定義的回調函數,用在當觸發了外部中斷後要做什麼事情,像是8051當中的中斷服務常式~ 兩者的目的是一樣的。

//8051 中斷服務常式
void EXIT_ISR(void) interrupt 2 //外部中斷向量為 2
{
   .......
}
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(GPIO_Pin);

  /* NOTE: This function should not be modified, when the callback is needed,
           the HAL_GPIO_EXTI_Callback could be implemented in the user file
   */
}

HAL庫當中還有個函數是用來檢查中斷旗標有沒有被設定,如果有被設定了會清除中斷旗標,之後繼續呼叫上方的回調函數執行要做的事情!

void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
  /* EXTI line interrupt detected */
  if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
  {
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
    HAL_GPIO_EXTI_Callback(GPIO_Pin);
  }
}
//使用方法
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_x);

板載LED燈外部中斷

我的R476LG開發版上有顆藍色的按鈕已經接好線了,不用在外接線去做外部中斷,可以直接利用這顆按鈕做測試~一樣查找DataSheet可以找到這顆按鈕的連接腳位(B1 USER)。
https://ithelp.ithome.com.tw/upload/images/20220310/20146325clDZ3UDJqz.png
結合上一篇所提到的板載LED燈(LD2)可以去做LED燈模式的變化,LD2的腳位是PA5。
首先先找到main.c檔下方的/* USER CODE BEGIN 4 / / USER CODE END 4 */ 將中斷回調函數寫在這,記得先在最上放宣告全域變數 i。

/* USER CODE BEGIN 4 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == GPIO_PIN_13)
	{
		i++;
	}
}
/* USER CODE END 4 */

接著可以到while(1)當中去寫燈的模式變化,透過按下按鈕對i+1,當i/2的餘數為0時做第一種燈的變化,不為0時則做第二種變化。也可以在現場表達式中加入 i 去觀察變數的變化,是不是跟程式所寫的一樣!

while (1)
{
	 if(i%2==0)
	 {
		  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, SET);
	  	HAL_Delay(1000);
	  	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, RESET);
	  	HAL_Delay(500);
	 }
	 else
	 {
	  	HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
	  	HAL_Delay(50);
	 }
 }

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


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言