2020年12月25日 星期五

STM32CubeMX 外部中斷(EXIT)

這一章我們在前一章GPIO的工程修改。複製GPIO的工程,修改文件夾名。點擊STM32F746I.ioc打開STM32cubeMX的工程文件重新配置。PA0管腳重新配置為GPIO_EXIT0模式。
WAKEUP按鍵已經外部下拉,按下是PA0為高電平。在GPIO配置中配置PA0為上升沿觸發。內部既不上拉也不下拉,添加用戶標籤WAKEUP。

在NVIC (嵌套向量中斷控制器)中,勾選EXIT Line0 interrupt使能PA0中斷。右邊兩個選項設置搶占優先級和響應優先級。此處我們選擇默認的,不修改。

        在這裡簡單介紹一下NVIC(嵌套向量中斷控制器)。NVIC就是控制中斷響應的。主要由三個參數,一個是中斷使能,一個是搶占優先級,還有一個就是響應優先級。(優先級數值越小,優先級別越高)
        中斷使能很好理解,就是是否開啟中斷,如果開啟中斷,則滿足中斷觸發條件時程序會跳到中斷服務程序運行,否則不響應中斷主程序繼續運行。
        搶占優先級是用來判斷一個中斷是否可以打斷另外一個中斷的中斷服務程序搶先運行。例如A中斷觸發,正在運行A中斷的服務程序,此時B中斷也觸發,如果B中斷的搶占優先級比A的高,則程序會打斷A的中斷服務程序,去運行B的中斷服務程序,即中斷嵌套。等B的中斷服務程序運行完後繼續運行A的中斷服務程序。如果B的搶占優先級沒有高過A的搶占優先級,則程序不會打斷A的中斷服務程序,而是待定A的中斷服務程序運行完成後才運行B的中斷服務程序。
       響應優先級是用來判斷搶占優先級相同的幾個中斷那個中斷會優先響應。如果幾個搶占優先相同的中斷同時觸發,那麼響應優先級高的最先運行。
        判斷中斷的優先級,先看搶占優先級,搶占優先級高的中斷優先級別高。搶占優先級相同的情況下,響應優先高的中斷優先級別高。搶占優先級和響應優先級相同的情況下,更加中斷向量表確定。如下為部分中斷向量表,詳細的可以查看stm32F7的數據手冊。

       
        在這裡簡單講解一下優先級分組。STM32以4個比特位表示中斷的搶占優先級和響應優先級。中斷優先級分組是為了給搶占式優先級和響應優先級在中斷優先級寄叢器的四個比特位分配各個優先級數字所佔的位數。例如3位用於搶占優先級(優先級有2^3=8種優先級),1位用於響應優先級(優先級有2^1=2種優先級)。


在這裡我們就配置好stm32CubeMX工程,重新生成報告,以及重新生成代碼,編譯程序。
打開main.c文件。把main()函數里while循環上一章的代碼刪掉,while循環裡面為空。在main.c文件後面USER CODE BEGIN 4和 USER CODE END 4中間添加中斷回調函數。
01/* USER CODE BEGIN 4 */
02/**
03  * @brief EXTI line detection callbacks
04  * @param GPIO_Pin: Specifies the pins connected EXTI line
05  * @retval None
06  */
07void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
08{
09  if(GPIO_Pin == GPIO_PIN_0)
10  {
11    /* Toggle LED1 */
12    BSP_LED_Toggle(LED1);
13  
14}
15/* USER CODE END 4 */

中断函数里面先判断是否为EXIT线0中断,如果是则LED1的状态翻转。在STM32F7的数据手册中可以找到下面这张图。PA0~PK0为EXTI线0中断。

重新編譯程序,編譯通過後下載到Open746-C開發板。如果沒有錯誤,按下一下WAKEUP按鍵LED1的狀態改變一次。

下面簡單講解一下中斷程序的運行流程。首先main函數主程序中一直在while循環裡面執行。當按鍵(PA0引腳)按下時,邊沿檢測電路檢測到上升沿,觸發中斷,設置中斷標識位。NVIC中斷控制器判斷EXTI0中斷優先是否為最高,若為最高優先級則執行EXIT0中斷。

再執行中斷服務函數之前,Contex-M7內核先將現在使用到的寄存器和主程序中斷點的地址壓入堆棧(保護現場)。然後程序在中斷向量表中找到EXTI0中斷對應的地址(0x0000 0058)。這個地址存儲的為EXTI0中斷服務函數的口人地址。然後程序轉跳到中斷服務函數執行。
在startup_stm32f746xx.s啟動文件中,我們可以找到中斷向量表。

在上面這張表中我們可以看到地址0x0000 0000保存的為棧頂的地址。0x0000 0004地址保存復位中斷服務函數的地址。第22個中斷為EXTI0中斷,對應的地址為22x4,即0x0000 0058。
在stm32f7xx_it.c中斷服務函數文件中,我們可以找到EXTI0中斷的服務函數。

01/**
02* @brief This function handles EXTI line0 interrupt.
03*/
04void EXTI0_IRQHandler(void)
05{
06  /* USER CODE BEGIN EXTI0_IRQn 0 */
07  
08  /* USER CODE END EXTI0_IRQn 0 */
09  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
10  /* USER CODE BEGIN EXTI0_IRQn 1 */
11  
12  /* USER CODE END EXTI0_IRQn 1 */
13}


中斷服務函數里面就調用了GPIO外部中斷處理函數HAL_GPIO_EXTI_IRQHandler(),參數為GPIO_PIN_0,即EXTI0中斷。GPIO外部中斷處理函數主要就是清除中斷標識位,然後調用中斷回調函數HAL_GPIO_EXTI_Callback()。我們只需重構中斷回調函數,在函數里面添加我們的應用代碼即可(程序中為翻轉LED1狀態)。

01/**
02  * @brief  This function handles EXTI interrupt request.
03  * @param  GPIO_Pin: Specifies the pins connected EXTI line
04  * @retval None
05  */
06void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
07{
08  /* EXTI line interrupt detected */
09  if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
10  {
11    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
12    HAL_GPIO_EXTI_Callback(GPIO_Pin);
13  }
14}

        執行完中斷服務函數後。內核從堆棧去除壓入的寄存器數據恢復現場,取出主程序中斷點的地址,轉到到主程序中斷點的地址繼續運行主程序。
        OK,到這裡就完成一次中斷服務。簡單的來說,觸發中斷時,硬件先標識中斷標識位,然後NVIC中斷控制器判斷中斷優先級是否可以執行此中斷。執行中斷時,先保護現在程序的狀態,將數據保存進堆棧中,執行完中斷服務函數後,再在堆棧中取數據回復現場,跳回主程序繼續運行。如果在中斷服務函數中有更高優先級的中斷觸發,則也會將現在的數據保存去執行更高優先級的中斷服務函數,即中斷嵌套。高優先級的中斷執行完後,恢復現場,繼續執行低優先級的服務函數。

       通過本章的講解希望各位更加深入了解中斷的過程。 

資料來源: https://www.waveshare.net/study/article-641-1.html

沒有留言: