# 外部中断

## 中断的几个概念

### 中断是由内核管理的

> 中断是由（NVIC寄存器）Nested vectored interrupt controller，嵌套向量中断控制器管理的，这是内核级别的寄存器，在F103的数据参考手册找不到资料，需要在Coretx-M3权威指南查看。比如设置中断优先级分组就是在设置SCB->AIRCR寄存器，可以在==Coretx-M3权威指南==第七章查看。

### 共有多少个中断

> NVIC 共256个中断源，其中支持 1 至 240 个外部中断输入（其实就是外设的中断，通常写作 IRQs），也就是内核中断16个，但实际只用到10个内核中断，具体的中断类型可以在==Coretx-M3权威指南==查看。
>
> 内核级别的中断：
>
> <img src="https://s2.loli.net/2023/03/13/TRQ5h3ap8xsmZFr.png" alt="" data-size="original">
>
> 外设的中断没有具体
>
> <img src="https://s2.loli.net/2023/03/13/OLoXe28Bz3PsyZh.png" alt="" data-size="original">

## 内核是怎么知道什么中断执行什么函数的

> 查看本笔记的第6章节

## 如何查看中断名与对应的中断服务函数

> 在启动文件中设置了中断向量表，这里其实就是中断服务函数名，并且在启动文件中进行了弱定义，因此我们只需在.c文件对对应的中断服务函数进行重定义即可。
>
> ```
> __Vectors       DCD     __initial_sp               ; Top of Stack
>                 DCD     Reset_Handler              ; Reset Handler
>                 DCD     NMI_Handler                ; NMI Handler
>                 DCD     HardFault_Handler          ; Hard Fault Handler
>                 DCD     MemManage_Handler          ; MPU Fault Handler
>                 DCD     BusFault_Handler           ; Bus Fault Handler
>                 DCD     UsageFault_Handler         ; Usage Fault Handler
>                 DCD     0                          ; Reserved
>                 DCD     0                          ; Reserved
>                 DCD     0                          ; Reserved
>                 DCD     0                          ; Reserved
>                 DCD     SVC_Handler                ; SVCall Handler
>                 DCD     DebugMon_Handler           ; Debug Monitor Handler
>                 DCD     0                          ; Reserved
>                 DCD     PendSV_Handler             ; PendSV Handler
>                 DCD     SysTick_Handler            ; SysTick Handler
>
>                 ; External Interrupts
>                 DCD     WWDG_IRQHandler            ; Window Watchdog
>                 DCD     PVD_IRQHandler             ; PVD through EXTI Line detect
>                 DCD     TAMPER_IRQHandler          ; Tamper
>                 DCD     RTC_IRQHandler             ; RTC
>                 DCD     FLASH_IRQHandler           ; Flash
>                 DCD     RCC_IRQHandler             ; RCC
>                 DCD     EXTI0_IRQHandler           ; EXTI Line 0
>                 DCD     EXTI1_IRQHandler           ; EXTI Line 1
>                 DCD     EXTI2_IRQHandler           ; EXTI Line 2
>                 DCD     EXTI3_IRQHandler           ; EXTI Line 3
>                 DCD     EXTI4_IRQHandler           ; EXTI Line 4
>                 DCD     DMA1_Channel1_IRQHandler   ; DMA1 Channel 1
>                 DCD     DMA1_Channel2_IRQHandler   ; DMA1 Channel 2
>                 DCD     DMA1_Channel3_IRQHandler   ; DMA1 Channel 3
>                 DCD     DMA1_Channel4_IRQHandler   ; DMA1 Channel 4
>                 DCD     DMA1_Channel5_IRQHandler   ; DMA1 Channel 5
>                 DCD     DMA1_Channel6_IRQHandler   ; DMA1 Channel 6
>                 DCD     DMA1_Channel7_IRQHandler   ; DMA1 Channel 7
>                 DCD     ADC1_2_IRQHandler          ; ADC1 & ADC2
>                 DCD     USB_HP_CAN1_TX_IRQHandler  ; USB High Priority or CAN1 TX
>                 DCD     USB_LP_CAN1_RX0_IRQHandler ; USB Low  Priority or CAN1 RX0
>                 DCD     CAN1_RX1_IRQHandler        ; CAN1 RX1
>                 DCD     CAN1_SCE_IRQHandler        ; CAN1 SCE
>                 DCD     EXTI9_5_IRQHandler         ; EXTI Line 9..5
>                 DCD     TIM1_BRK_IRQHandler        ; TIM1 Break
>                 DCD     TIM1_UP_IRQHandler         ; TIM1 Update
>                 DCD     TIM1_TRG_COM_IRQHandler    ; TIM1 Trigger and Commutation
>                 DCD     TIM1_CC_IRQHandler         ; TIM1 Capture Compare
>                 DCD     TIM2_IRQHandler            ; TIM2
>                 DCD     TIM3_IRQHandler            ; TIM3
>                 DCD     TIM4_IRQHandler            ; TIM4
>                 DCD     I2C1_EV_IRQHandler         ; I2C1 Event
>                 DCD     I2C1_ER_IRQHandler         ; I2C1 Error
>                 DCD     I2C2_EV_IRQHandler         ; I2C2 Event
>                 DCD     I2C2_ER_IRQHandler         ; I2C2 Error
>                 DCD     SPI1_IRQHandler            ; SPI1
>                 DCD     SPI2_IRQHandler            ; SPI2
>                 DCD     USART1_IRQHandler          ; USART1
>                 DCD     USART2_IRQHandler          ; USART2
>                 DCD     USART3_IRQHandler          ; USART3
>                 DCD     EXTI15_10_IRQHandler       ; EXTI Line 15..10
>                 DCD     RTC_Alarm_IRQHandler        ; RTC Alarm through EXTI Line
>                 DCD     USBWakeUp_IRQHandler       ; USB Wakeup from suspend
>                 DCD     TIM8_BRK_IRQHandler        ; TIM8 Break
>                 DCD     TIM8_UP_IRQHandler         ; TIM8 Update
>                 DCD     TIM8_TRG_COM_IRQHandler    ; TIM8 Trigger and Commutation
>                 DCD     TIM8_CC_IRQHandler         ; TIM8 Capture Compare
>                 DCD     ADC3_IRQHandler            ; ADC3
>                 DCD     FSMC_IRQHandler            ; FSMC
>                 DCD     SDIO_IRQHandler            ; SDIO
>                 DCD     TIM5_IRQHandler            ; TIM5
>                 DCD     SPI3_IRQHandler            ; SPI3
>                 DCD     UART4_IRQHandler           ; UART4
>                 DCD     UART5_IRQHandler           ; UART5
>                 DCD     TIM6_IRQHandler            ; TIM6
>                 DCD     TIM7_IRQHandler            ; TIM7
>                 DCD     DMA2_Channel1_IRQHandler   ; DMA2 Channel1
>                 DCD     DMA2_Channel2_IRQHandler   ; DMA2 Channel2
>                 DCD     DMA2_Channel3_IRQHandler   ; DMA2 Channel3
>                 DCD     DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5
> __Vectors_End
> ```
>
> 在 stm32f103xe.h枚举了中断类型，HAL\_NVIC\_EnableIRQ函数的参数就可选以下的
>
> ```
>   NonMaskableInt_IRQn         = -14,    /*!< 2 Non Maskable Interrupt                             */
>   HardFault_IRQn              = -13,    /*!< 3 Cortex-M3 Hard Fault Interrupt                     */
>   MemoryManagement_IRQn       = -12,    /*!< 4 Cortex-M3 Memory Management Interrupt              */
>   BusFault_IRQn               = -11,    /*!< 5 Cortex-M3 Bus Fault Interrupt                      */
>   UsageFault_IRQn             = -10,    /*!< 6 Cortex-M3 Usage Fault Interrupt                    */
>   SVCall_IRQn                 = -5,     /*!< 11 Cortex-M3 SV Call Interrupt                       */
>   DebugMonitor_IRQn           = -4,     /*!< 12 Cortex-M3 Debug Monitor Interrupt                 */
>   PendSV_IRQn                 = -2,     /*!< 14 Cortex-M3 Pend SV Interrupt                       */
>   SysTick_IRQn                = -1,     /*!< 15 Cortex-M3 System Tick Interrupt                   */
>
> /******  STM32 specific Interrupt Numbers *********************************************************/
>   WWDG_IRQn                   = 0,      /*!< Window WatchDog Interrupt                            */
>   PVD_IRQn                    = 1,      /*!< PVD through EXTI Line detection Interrupt            */
>   TAMPER_IRQn                 = 2,      /*!< Tamper Interrupt                                     */
>   RTC_IRQn                    = 3,      /*!< RTC global Interrupt                                 */
>   FLASH_IRQn                  = 4,      /*!< FLASH global Interrupt                               */
>   RCC_IRQn                    = 5,      /*!< RCC global Interrupt                                 */
>   EXTI0_IRQn                  = 6,      /*!< EXTI Line0 Interrupt                                 */
>   EXTI1_IRQn                  = 7,      /*!< EXTI Line1 Interrupt                                 */
>   EXTI2_IRQn                  = 8,      /*!< EXTI Line2 Interrupt                                 */
>   EXTI3_IRQn                  = 9,      /*!< EXTI Line3 Interrupt                                 */
>   EXTI4_IRQn                  = 10,     /*!< EXTI Line4 Interrupt                                 */
>   DMA1_Channel1_IRQn          = 11,     /*!< DMA1 Channel 1 global Interrupt                      */
>   DMA1_Channel2_IRQn          = 12,     /*!< DMA1 Channel 2 global Interrupt                      */
>   DMA1_Channel3_IRQn          = 13,     /*!< DMA1 Channel 3 global Interrupt                      */
>   DMA1_Channel4_IRQn          = 14,     /*!< DMA1 Channel 4 global Interrupt                      */
>   DMA1_Channel5_IRQn          = 15,     /*!< DMA1 Channel 5 global Interrupt                      */
>   DMA1_Channel6_IRQn          = 16,     /*!< DMA1 Channel 6 global Interrupt                      */
>   DMA1_Channel7_IRQn          = 17,     /*!< DMA1 Channel 7 global Interrupt                      */
>   ADC1_2_IRQn                 = 18,     /*!< ADC1 and ADC2 global Interrupt                       */
>   USB_HP_CAN1_TX_IRQn         = 19,     /*!< USB Device High Priority or CAN1 TX Interrupts       */
>   USB_LP_CAN1_RX0_IRQn        = 20,     /*!< USB Device Low Priority or CAN1 RX0 Interrupts       */
>   CAN1_RX1_IRQn               = 21,     /*!< CAN1 RX1 Interrupt                                   */
>   CAN1_SCE_IRQn               = 22,     /*!< CAN1 SCE Interrupt                                   */
>   EXTI9_5_IRQn                = 23,     /*!< External Line[9:5] Interrupts                        */
>   TIM1_BRK_IRQn               = 24,     /*!< TIM1 Break Interrupt                                 */
>   TIM1_UP_IRQn                = 25,     /*!< TIM1 Update Interrupt                                */
>   TIM1_TRG_COM_IRQn           = 26,     /*!< TIM1 Trigger and Commutation Interrupt               */
>   TIM1_CC_IRQn                = 27,     /*!< TIM1 Capture Compare Interrupt                       */
>   TIM2_IRQn                   = 28,     /*!< TIM2 global Interrupt                                */
>   TIM3_IRQn                   = 29,     /*!< TIM3 global Interrupt                                */
>   TIM4_IRQn                   = 30,     /*!< TIM4 global Interrupt                                */
>   I2C1_EV_IRQn                = 31,     /*!< I2C1 Event Interrupt                                 */
>   I2C1_ER_IRQn                = 32,     /*!< I2C1 Error Interrupt                                 */
>   I2C2_EV_IRQn                = 33,     /*!< I2C2 Event Interrupt                                 */
>   I2C2_ER_IRQn                = 34,     /*!< I2C2 Error Interrupt                                 */
>   SPI1_IRQn                   = 35,     /*!< SPI1 global Interrupt                                */
>   SPI2_IRQn                   = 36,     /*!< SPI2 global Interrupt                                */
>   USART1_IRQn                 = 37,     /*!< USART1 global Interrupt                              */
>   USART2_IRQn                 = 38,     /*!< USART2 global Interrupt                              */
>   USART3_IRQn                 = 39,     /*!< USART3 global Interrupt                              */
>   EXTI15_10_IRQn              = 40,     /*!< External Line[15:10] Interrupts                      */
>   RTC_Alarm_IRQn              = 41,     /*!< RTC Alarm through EXTI Line Interrupt                */
>   USBWakeUp_IRQn              = 42,     /*!< USB Device WakeUp from suspend through EXTI Line Interrupt */
>   TIM8_BRK_IRQn               = 43,     /*!< TIM8 Break Interrupt                                 */
>   TIM8_UP_IRQn                = 44,     /*!< TIM8 Update Interrupt                                */
>   TIM8_TRG_COM_IRQn           = 45,     /*!< TIM8 Trigger and Commutation Interrupt               */
>   TIM8_CC_IRQn                = 46,     /*!< TIM8 Capture Compare Interrupt                       */
>   ADC3_IRQn                   = 47,     /*!< ADC3 global Interrupt                                */
>   FSMC_IRQn                   = 48,     /*!< FSMC global Interrupt                                */
>   SDIO_IRQn                   = 49,     /*!< SDIO global Interrupt                                */
>   TIM5_IRQn                   = 50,     /*!< TIM5 global Interrupt                                */
>   SPI3_IRQn                   = 51,     /*!< SPI3 global Interrupt                                */
>   UART4_IRQn                  = 52,     /*!< UART4 global Interrupt                               */
>   UART5_IRQn                  = 53,     /*!< UART5 global Interrupt                               */
>   TIM6_IRQn                   = 54,     /*!< TIM6 global Interrupt                                */
>   TIM7_IRQn                   = 55,     /*!< TIM7 global Interrupt                                */
>   DMA2_Channel1_IRQn          = 56,     /*!< DMA2 Channel 1 global Interrupt                      */
>   DMA2_Channel2_IRQn          = 57,     /*!< DMA2 Channel 2 global Interrupt                      */
>   DMA2_Channel3_IRQn          = 58,     /*!< DMA2 Channel 3 global Interrupt                      */
>   DMA2_Channel4_5_IRQn        = 59,     /*!< DMA2 Channel 4 and Channel 5 global Interrupt        */
> ```

## 外部中断

STM32F103 的 19 个外部中断为：\
EXTI 线 0~~15：对应外部 IO 口的输入中断。~~\
~~EXTI 线 16：连接到 PVD 输出。~~\
~~EXTI 线 17：连接到 RTC 闹钟事件。~~\
~~EXTI 线 18：连接到 USB 唤醒事件。~~\
~~EXTI 线 19：连接到以太网唤醒事件。~~\
~~从上面可以看出， STM32F1 供 IO 口使用的中断线只有 16 个，但是 STM32F1 的 IO 口却 远远不止 16 个，那么 STM32F1 是怎么把 16 个中断线和 IO 口一一对应起来的呢？GPIO 的管脚GPIOx.0~~GPIOx.15(x=A,B,C,D,E， F,G,H,I)分别对应中断线 0\~15。这\
样每个中断线对应了最多 9 个 IO 口，以线 0 为例：它对应了 GPIOA.0、 GPIOB.0、 GPIOC.0、\
GPIOD.0、 GPIOE.0、 GPIOF.0、 GPIOG.0。而中断线每次只能连接到 1 个 IO 口上，这样就需要 通过配置相应的寄存器，即外部中断配置寄存器AFIO\_EXTICRx,来决定对应的中断线配置到哪个 GPIO 上了。

## 外部中断的初始化

以正点原子的HAL库外部中断例程为例

```
//外部中断初始化
void EXTI_Init(void)
{
    GPIO_InitTypeDef GPIO_Initure;

    __HAL_RCC_GPIOA_CLK_ENABLE();               //开启GPIOA时钟
    __HAL_RCC_GPIOE_CLK_ENABLE();               //开启GPIOE时钟

    GPIO_Initure.Pin=GPIO_PIN_0;                //PA0
    GPIO_Initure.Mode=GPIO_MODE_IT_RISING;      //上升沿触发
    GPIO_Initure.Pull=GPIO_PULLDOWN;
    HAL_GPIO_Init(GPIOA,&GPIO_Initure);

    GPIO_Initure.Pin=GPIO_PIN_3|GPIO_PIN_4;     //PE3,4
    GPIO_Initure.Mode=GPIO_MODE_IT_FALLING;     //下降沿触发
    GPIO_Initure.Pull=GPIO_PULLUP;
    HAL_GPIO_Init(GPIOE,&GPIO_Initure);

    //中断线0-PA0
    HAL_NVIC_SetPriority(EXTI0_IRQn,2,0);       //抢占优先级为2，子优先级为0
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);             //使能中断线0

    //中断线3-PE3
    HAL_NVIC_SetPriority(EXTI3_IRQn,2,2);       //抢占优先级为2，子优先级为2
    HAL_NVIC_EnableIRQ(EXTI3_IRQn);             //使能中断线2

    //中断线4-PE4
    HAL_NVIC_SetPriority(EXTI4_IRQn,2,3);       //抢占优先级为2，子优先级为3
    HAL_NVIC_EnableIRQ(EXTI4_IRQn);             //使能中断线4
}


//中断服务函数
void EXTI0_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);        //调用中断处理公用函数
}

void EXTI3_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3);        //调用中断处理公用函数
}

void EXTI4_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);        //调用中断处理公用函数
}
```

* 先初始化GPIO时钟
* 初始化AFIO时钟，这边没有体现是因为在初始化USART时已经初始化了
* 初始化GPIO，设置中断触发类型，并设置上下拉
* 配置NVIC，设置中断优先级，并开启中断
* 配置中断服务函数，EXTI0\_IRQHandler函数与启动文件的中断向量表设置一致，在该函数中又调用了HAL\_GPIO\_EXTI\_IRQHandler函数

  首先通过\_\_HAL\_GPIO\_EXTI\_GET\_IT函数判断是哪个中断被触发，然后通过\_\_HAL\_GPIO\_EXTI\_CLEAR\_IT函数清除中断标志位。最后调用HAL\_GPIO\_EXTI\_Callback函数，该函数有HAL库先进性弱定义，再由用户自己编写覆盖重定义，用于实现自己的业务逻辑。

  该函数具体如下

```
        /**
  * @brief  This function handles EXTI interrupt request.
  * @param  GPIO_Pin: Specifies the pins connected EXTI line
  * @retval None
  */
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\_Callback需要自行编写，里面是真正的自己需要让中断完成的业务逻辑
* 值得注意的是上述代码看似并没有对中断线进行设置，及设置中断线0映射到GPIOA0还是GPIOB0又或是GPIOE0,这是因为HAL\_GPIO\_Init函数里对此进行了处理，它对形如GPIO\_MODE\_IT\_RISING这样的中断模式进行了判断，当判断到模式为中断类型的话，就对AFIO->EXTICR寄存器进行设置

## 什么时候要开启AFIO时钟

> 是只要使用到重映射或者IO复用就要需要开启吗？不是的，只有需要对AFIO寄存器进行读写才需要开启，而什么时候需要用到呢？AFIO共有以下几个寄存器：
>
> &#x20;       1、 事件控制寄存器（AFIO\_EVCR）\
> 2、 复用重映射和调试I/O 配置寄存器（AFIO\_MAPR） ：配置JTAG/SWD调试接口的IO口功能。 　　3、 外部中断配置寄存器1（AFIO\_EXTICR1）\
> 4、 外部中断配置寄存器2（AFIO\_EXTICR2）\
> 5、 外部中断配置寄存器3（AFIO\_EXTICR3）\
> 6、 外部中断配置寄存器4（AFIO\_EXTICR4）
>
> 具体来说：1、使用重映射时需要。2、使用外部中断时需要

## 为什么使用JTAG/SWD调试时不需要配置AFIO，也不用开启时钟

> AFIO\_MAPR寄存器用以配置是否开启JTAG/SWD调试，但是查看官方的文件SystemInit里面并没有对其进行设置，因为查看参考手册，发现默认复位状态，就是开启JTAG和SWD接口调试的，
>
> <img src="https://s2.loli.net/2023/03/13/DMt7ZgRdCV8xPIa.png" alt="" data-size="original">
>
> 由于我们并没有对AFIO\_MAPR寄存器进行读写，自然也无需开启AFIO时钟
