---
# System prepended metadata

title: STM32 Note Week3

---

# 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)
這樣就差不多可以進行程式的編寫了













