# STM32 Note Week3 Author: KT LIU reference: Mastering_STM32-Second_Edtion ## Interrupts Management 所有的微控制器都提供一個名為中斷(interrupt)的功能。中斷是一種異步事件,它會根據優先級(中斷越重要,優先級越高,這會導致優先級較低的中斷被暫停)停止當前代碼的執行。處理中斷的代碼稱為中斷服務程式(ISR, Interrupt Service Routine)。 中斷可以由硬體和軟體本身觸發。ARM架構區分這兩種類型:中斷由硬體觸發,異常(exceptions)由軟體觸發(例如,訪問無效的內存位置)。在ARM術語中,中斷是一種異常。Cortex-M處理器提供了一個專門用於異常管理的單元,稱為嵌套向量中斷控制器(NVIC)。 ### NVIC Controller ![image](https://hackmd.io/_uploads/BJT57trXyx.png) #### MCU 內部外設: 例如:定時器、UART 等 #### MCU 外部外設: 來自這些外設的中斷來源是 MCU 的 I/O ### MCU I/O 的配置方式: #### 通用 I/O: 例如,一個連接到配置為輸入的引腳的觸摸開關 #### 驅動外部高級外設: 例如,配置為通過 RMII 接口與以太網 PHY 交換數據的 I/O ### 外部中斷/事件控制器 (EXTI) EXTI(External Interrupt/Event Controller) 是一個專用的可編程控制器 #### 功能: 負責外部 I/O 信號與 NVIC 控制器之間的互連 ,中斷管理,NVIC 單元處理異常和中斷 #### 中斷來源: 來自 MCU 外部外設,通过 I/O 接收中斷信號 EXTI 負責將外部 I/O 信號連接到 NVIC 控制器 ### Cortex M 內建中斷 #### Reset 說明:此異常在CPU重置後立即觸發,是運行中的韌體的真正入口點。 功能:處理程序包含一些用於初始化執行環境(如主堆棧、.bss 區域等)的匯編代碼。 詳細介紹:第22章將深入討論啟動過程。 #### NMI(Non-Maskable Interrupt) 說明:這是一種特殊的異常,優先級僅次於Reset異常,不能被屏蔽,與關鍵和不可延遲的活動相關聯。 功能:在所有STM32微控制器中,NMI與時鐘安全系統(CSS)相關聯。CSS是一種自診斷外設,檢測外部時鐘(HSE)的故障,並在故障時自動禁用HSE、啟用內部HSI,並觸發NMI中斷以通知軟體HSE出現問題。 詳細介紹:第10章將進一步介紹。 #### Hard Fault 說明:這是一種通用的故障異常,與軟體中斷相關。 功能:當其他故障異常被禁用時,Hard Fault會收集所有類型的異常(例如,如果總線故障異常未啟用,訪問無效內存位置會觸發Hard Fault異常)。 #### Memory Management Fault¹ 說明:當執行代碼嘗試訪問非法位置或違反內存保護單元(MPU)的規則時觸發。 詳細介紹:第20章將進一步探討。 #### Bus Fault¹ 說明:當AHB接口從總線從設備收到錯誤響應時觸發(指令預取失敗稱為預取中止,數據訪問失敗稱為數據中止)。 功能:也可能由其他非法訪問引起(例如,訪問不存在的SRAM內存位置)。 #### Usage Fault¹ 說明:當程序出現錯誤時觸發,例如非法指令、對齊問題或嘗試訪問不存在的協處理器。 #### SVCall 說明:這不是一種故障條件,當Supervisor Call(SVC)指令被調用時觸發。 功能:RTOS使用它在特權狀態下執行指令(需要執行特權操作的任務執行SVC指令,操作系統執行請求的操作,這與其他操作系統中的系統調用行為相同)。 #### Debug Monitor¹ 說明:當處理器核心處於監視器調試模式時發生軟體調試事件時觸發。 功能:也用於軟體調試解決方案中的斷點和觀察點等調試事件。 #### PendSV 說明:這是與RTOS相關的另一個異常。 功能:與SVCall異常不同,SVCall異常在執行SVC指令後立即執行,而PendSV可以延遲執行,允許RTOS完成更高優先級的任務。 #### SysTick 說明:此異常通常與RTOS活動相關。 功能:每個RTOS需要一個定時器定期中斷當前代碼的執行並切換到另一個任務。所有STM32微控制器都提供Cortex-M核心內部的SysTick定時器,即使其他定時器可以用來調度系統活動,專用定時器的存在確保了在所有STM32系列中的可移植性。此外,即使在我們的韌體中未使用RTOS,ST CubeHAL也使用SysTick定時器執行內部與時間相關的活動(假設SysTick定時器配置為每1ms產生一次中斷)。 下圖中可以看到各自的優先級與功能 ![image](https://hackmd.io/_uploads/Sy6zjtHXJl.png) ### 關於interrupt在記憶體中的執行 定義:向量表是一個陣列,其每個條目包含異常或中斷服務程序 (ISR) 的地址。 首項:向量表的第一個條目是主堆棧指針 (Main Stack Pointer, MSP) 在SRAM中的地址。 後續項:從第二個條目開始,是各異常和中斷的服務程序地址。 向量表的長度 Cortex-M0/0+:向量表長度為48。 Cortex-M3/4/7:向量表長度為256。 關鍵點 異常處理程序名稱 靈活性:異常處理程序的名稱只是慣例,可以根據需要進行更改,它們只是符號(像變數和函數一樣)。 注意事項:CubeMX軟件設計時使用這些名稱來生成ISR,因此更改名稱時需同步更改ISR名稱。 向量表位置 位置要求:向量表必須位於閃存 (Flash) 的起始位置。 工具支持:GCC Linker將向量表放置在閃存數據開始處的任務是在生成絕對文件(二進制文件)的過程中完成的。 ![image](https://hackmd.io/_uploads/BJLYxqSm1g.png) ### Enabling Interrupts 以 STM32F4 為例 只有特定的GPIO腳位支援interrupt的特殊功能,但其實有114個GPIO腳位連接至16個EXIT line,但是因為共用EXIT的關係,只有7條interrupt是獨立作用的,可看下圖所示 ![image](https://hackmd.io/_uploads/BksxYcSmyg.png) ### 範例程式 腳位開關高低控制LED 以下是如何使用中斷每次按下用戶可編程按鈕(連接到PC13引腳)時切換LD2 LED的示例。首先,我們配置GPIO PC13,使其每次從低電平變為高電平時觸發中斷。 這是通過設置GPIO.Mode為GPIO_MODE_IT_RISING來實現的。 接下來,我們啟用與Px13引腳相關聯的EXTI線的中斷,EXTI15_10_IRQn。 ```cpp #include "stm32f4xx_hal.h" int main(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* MCU Configuration ----------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* Configure the system clock */ SystemClock_Config(); /* GPIOA and GPIOC Configuration ----------------------------------------------*/ /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /* Configure GPIO pin : PC13 */ GPIO_InitStruct.Pin = GPIO_PIN_13; GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; GPIO_InitStruct.Pull = GPIO_PULLDOWN; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); /* Configure GPIO pin : PA5 */ GPIO_InitStruct.Pin = GPIO_PIN_5; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* Configure GPIO pin Output Level */ HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); /* EXTI interrupt init */ HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); while (1) { // Main loop does nothing, waiting for interrupts } } void EXTI15_10_IRQHandler(void) { if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_13) != RESET) { __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_13); HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); } } ``` 這裡主要利用以下function來進行interrupt的判斷,可以將希望進行的判斷或是中斷後要執行的內容寫在此處,可由先前的架構圖判斷哪些腳位對應的interrupt。 ```cpp void EXTI15_10_IRQHandler(void) { if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_13) != RESET) { __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_13); HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); } } ``` ### Cubr Mx設定 上面提到除了主要的callback function,其他大部分都是由cube Mx設定的,主要要將須使用的腳位,調整成指定的EXIT模式 ![image](https://hackmd.io/_uploads/S1xi8jrQ1x.png) 並且要將interrupt的功能打開 ![image](https://hackmd.io/_uploads/SJE2UsrQye.png) 這樣就差不多可以進行程式的編寫了