[TOC] # STM中斷運作方式 ## 中斷流程 1. 中斷事件的<font color="red">觸發</font>(接腳狀態變化、計時器溢出、數據接收等等) 2. 對應的中斷源暫存器的<font color="red">中斷Flag</font>會被立起,當這類Flag立起,表示這個中斷正在等待處理。以外部中斷來說,外部中斷控制器(EXTI)的Pending Rigister對應的bit立起,表示對應的外部中斷待處理。 3. NVIC控制器接收到立起的flag消息會再<font color="red">判斷NVIC內部暫存器是否有啟用這個中斷,以及中斷的優先級處理</font> 4. 確認要處理的中斷後,NVIC向CPU送出<font color="red">中斷請求(IRQ)</font>信號給處理器 5. NVIC查詢中斷向量表(interrupt vector table)讓處理器<font color="red">進入中斷服務例程(ISR)</font> 6. <font color="red">清除第2步的flag</font> ## 從程式看中斷 1. 上述流程第1~3部分是電路上的動作。 2. 從專案資料夾的startup_stm32f407xx.s檔來看,內部有對應的中斷名稱與中斷處理函式(IRQ_Handler),這個檔案可視為中斷向量表,會燒錄進記憶體 3. 假設今天知道發生的中斷源是EXTI Line0,從向量表可知對應的處理函式是 EXTI0_IRQHandler,在stm32f4xx_it.c可以找到函式定義。函式內有一個HAL_GPIO_EXTI_IRQHandler,在stm32f4xx_hal_gpio.c可以找到函式定義。 ``` c++= @ startup_stm32f407xx.s ... ... .word FLASH_IRQHandler /* FLASH */ .word RCC_IRQHandler /* RCC */ .word EXTI0_IRQHandler /* EXTI Line0 */ .word EXTI1_IRQHandler /* EXTI Line1 */ .word EXTI2_IRQHandler /* EXTI Line2 */ ... ... ``` ``` c++= @ stm32f4xx_it.c void EXTI0_IRQHandler(void) { /* USER CODE BEGIN EXTI0_IRQn 0 */ /* USER CODE END EXTI0_IRQn 0 */ HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); /* USER CODE BEGIN EXTI0_IRQn 1 */ /* USER CODE END EXTI0_IRQn 1 */ } ``` ``` c++= @ stm32f4xx_hal_gpio.c void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin) { /* EXTI line interrupt detected */ // 判斷中斷旗標是否立起 if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET) { // 清除中斷旗標 __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin); // 回調函式 HAL_GPIO_EXTI_Callback(GPIO_Pin); } } ``` 4. 本篇例子中把在ISR想做的事情放在 HAL_GPIO_EXTI_Callback。注意這時候已經清除中斷旗標 # NVIC設定 - 上面已經說明NVIC在一般中斷流程中的角色,具體NVIC內部的暫存器結構及其他功能可參閱野火-HAL開發指南,或看看stm32f4xx_hal_cortex.c或core_cm4.h內的程式說明 - 針對CubeMX設定來看  1. 只要在其他功能設定有選擇要產生中斷,Interrupt Table就會顯示對應中斷名稱 2. <font color="red">要使用中斷就得勾選Enable</font>,因為在中斷流程中會藉由確認這個Enable決定可不可以觸發中斷 3. 左上角的Priority Group和右邊兩欄決定各中斷的[優先級](https://blog.csdn.net/qq_53918631/article/details/125245342)。Preemption和Sub Priority總共佔4bit,Priority Group可決定這4個bit的分配。 4. 優先級先看Preemption,再看Sub,兩個都一樣就看rm0090的 Table 62.。 # 外部中斷(EXTI) - 野火-HAL實戰開發 第17章 - [STM EXTI](https://blog.csdn.net/iamyangbei/article/details/128158326) - rm0090 12.2節 ## 簡介 - 23種 interrupt/event request,野火 : *产生中断线路目的是把输入信号输入到NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号* 传输,属于硬件级的 - 以GPIO外部中斷來說,多個Port的同個Pin是共用同一條EXTI Line的。所以不可能有PA0和PD0都做為外部中斷的情況,CubeMX也不讓選   - 不同暫存器的功能與介紹可參閱rm0090,以中斷旗標來說,控制所有EXTI Line的中斷旗標都在EXTI_PR暫存器 ## 程式使用 - 以GPIO中斷為例,根據對應的腳位,Callback只需添加如下範例 ``` c++= void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == GPIO_PIN_X) // 中斷處理 else { __NOP(); } } ``` - 其他外部中斷種類可至HAL庫相關的.c檔尋找 # 外部中斷實例 - 按鈕觸發外部中斷控制LED - 板上的藍色按鈕已經接到PA0腳位,我們用它實現外部中斷 - 在UM1472手冊中有按鈕的電路圖  ## CubeMX配置 - 一開始一樣初始化 - PA0腳位做設置  - NVIC 中斷開啟  - 同樣設置板上四顆LED對應的腳位為輸出 ## 程式編寫1 - 在 USER CODE BEGIN 4區域加入以下程式 ``` c++= void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == GPIO_PIN_0) // or if (GPIO_Pin == **User Label Pin Name**) // 翻轉LED4腳位的輸出 else { __NOP(); } } ``` - 在 主迴圈while(1)中加入程式如 ``` c++= /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1){ /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ HAL_GPIO_WritePin(GPIOD, LD3_Pin, GPIO_PIN_RESET); HAL_Delay(3000); HAL_GPIO_WritePin(GPIOD, LD3_Pin, GPIO_PIN_SET); HAL_Delay(3000); } /* USER CODE END 3 */ ``` ## 程式編寫2 - 修改程式,把HAL_GPIO_EXTI_Callback都註解掉 - 改在主迴圈內讀取PA0狀態並做判斷翻轉LED4 ``` c++= while(1){ if(PA0讀取到HIGH) LED亮; else LED暗; HAL_GPIO_WritePin(GPIOD, LD3_Pin, GPIO_PIN_RESET); HAL_Delay(3000); HAL_GPIO_WritePin(GPIOD, LD3_Pin, GPIO_PIN_SET); HAL_Delay(3000); } ``` ## 小結 - 程式1的結果讓按鈕偵測可以隨時進行,反觀程式2要等到延遲結束才能進行按鈕的判讀 - 在控制或其他應用上有實時需求或不容許太多程式阻塞(blocking)的話,試著用中斷處理你的程式 - 注意中斷執行的程式應該輕薄短小,避免過多的運算。任何可能造成阻塞的程式都很不建議放在中斷內,譬如<font color="red">while、HAL_Delay</font>等等
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up