Cortex-M3-STM32F1 开发:(十六)HAL 库开发 ➤ 超详细的中断讲解(主要讲解外部中断),包括 NVIC、EXTI、配置方法和示例

上一篇下一篇
GPIO


中断(⭐️)

提前说一下:中断服务函数内执行的程序不宜太复杂,中断执行时间也不宜太长,

定义:打断 CPU 执行正常的程序,转而处理紧急程序,然后返回原程序继续运行,就叫中断。

作用及意义:实时控制、故障处理、数据传输。高效处理紧急程序,不会一直占用 CPU 资源。

两个关键名词(模块):

  • NVIC:Nested Vectored Interrupt Controller (嵌套向量中断控制器)
  • EXTI:External Interrupt/Event Controller (外部中断/事件控制器)

中断的基本流程

在这里插入图片描述

注意事项:

中断服务函数(ISR)执行时间应尽可能短,通常建议在微秒(μs)级别完成,一般不超过几十到几百微秒,绝对不应达到“秒”级。不然会影响系统实时性、可能导致中断丢失、破坏系统稳定性。也就是说:ISR 中只做紧急、简单的操作,如读取寄存器、置标志位、发信号,避免在 ISR 中使用 delay() 延时,进行大量计算或复杂逻辑,复杂的逻辑可以通过标志位识别,在 main 函数中执行。


① NVIC

简介:Nested Vectored Interrupt Controller,嵌套向量中断控制器,属于内核(M3/4/7)。NVIC 是内核的一部分:它是 ARM Cortex-M 处理器内核的一个标准组件,直接集成在 CPU 内核中,负责管理所有中断源(包括来自 EXTI 的请求),可以说是 “ 中断的管理者 ” 或 “ 调度中心 ”

NVIC 支持 256 个中断(16内核 + 240外部),支持 256 个优先级(允许裁剪),ST 公司就将其裁剪成了 16 个( 16个中断优先级 )。STM 的所有型号的内核中断数、外部中断数、中断优先级个数如下表所示:

STM32型号内核中断外部中断中断优先级
STM32F103xx106016
STM32F407xx108216
STM32F429xx109116
STM32F750xx109816
STM32F767xx1011016
STM32H743xx1015016
STM32H750xx1015016

NVIC 在处理优先级时,并不区分该中断是内部还是外部产生的。统一标准:所有中断都使用相同的 16 级优先级(4 位有效)进行配置和比较。统一调度:最终的响应顺序完全由抢占优先级、响应优先级和中断号共同决定,内部中断和外部中断在这个体系下是平等竞争的

当中断发生之后,CPU 会自动执行相应的中断服务函数,而中断服务函数的入口地址被定义在了中断向量表里面。

1. 中断向量表

简介:ARM Cortex-M 系列 MCU 的中断向量表是一个存储在内存起始地址(通常是 Flash 的 0x00000000 或经过 ST 等厂商重定向的起始地址)的数组。这个表的每一项(向量)都存储了一个函数指针(以 4 字节对齐),指向特定中断或异常处理程序的入口地址,也就是存放各个中断服务函数程序的首地址。

中断向量表定义在 .s 启动文件中,STM32F1 的表内容具体如下,每个中断向量都以 Handler 结尾,0 表示保留,前面是 10 个内核中断,下面是 60 个外部中断(MCU参考手册里会有详细的表格描述):

__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

汇编伪指令 DCD:为程序分配连续的 4 字节存储单元,并用指定的值进行初始化。

2. NVIC 相关寄存器

三个外部中断寄存器,一个共用的优先级分组寄存器。

寄存器名位数寄存器个数备注
中断使能寄存器(ISER328每个位控制一个中断
只给外部中断使用,所以只用了 240 位,其余 16 位保留
中断除能寄存器(ICER328每个位控制一个中断
只给外部中断使用,所以只用了 240 位,其余 16 位保留
应用程序中断及复位控制寄存器(AIRCR321ST 公司只用了 3 个位( [10:8]),来控制优先级分组(5组)
外部中断优先级设置寄存器(IPR8240对应 240 个外部中断
每 8 位对应一个中断,而 STM32 只使用其中的高 4 位
在 CMSIS 底层库中,是用一个数组 IP[] 来索引的
系统异常优先级设置寄存器(SHPR给内核中断使用的,但详细情况不清楚

工作原理(给出了一个例子,优先级的顺序已知):

在这里插入图片描述

3. 中断优先级(抢占和响应、自然)

之所以会有这个抢占和响应的区分,就是因为 ST 公司将本来的 256 个一一对应的优先级裁剪成了 16 个,需要引入别的判别机制。

  1. 抢占优先级(pre):高抢占优先级可以打断 正在执行的低抢占优先级中断。
  2. 响应优先级(sub):当抢占优先级相同时,响应优先级高的先执行,但是不能互相打断
  3. 自然优先级(中断号)
    • 中断向量表的优先级,也就是在中断向量表中的物理位置(即中断号);
    • 手册里内核中断号是负数,外部中断的中断号是正的。
  4. 抢占和响应都相同的情况下,自然优先级越高的,先执行。
  5. 数值越小(越靠前),优先级越高,包括负数

总结起来就是首先,抢占优先级高的可以打断低的。其次,当两个中断同时发生,并且抢占优先级相同时,响应优先级高的先执行;但是当两个抢占优先级相同的中断不是同时发生的时候,那么就谁先来的就先执行。最后,当两个中断同时发生,并且抢占和响应优先级全都相同时,咱们就看它俩的中断号,谁中断号靠前(数值小)就先执行。

中断优先级分组

优先级分组AIRCR [10:8]IPRx 位 [7:4] 分配分配结果
0111none :[7:4]0 位抢占优先级(0个),4 位响应优先级(16个)
1110[7] :[6:4]1 位抢占优先级(2个),3 位响应优先级(8个)
2101[7:6] :[5:4]2 位抢占优先级(4个),2 位响应优先级(4个)
3100[7:5] :[4]3 位抢占优先级(8个),1 位响应优先级(2个)
4011[7:4] :none4 位抢占优先级(16个),0 位响应优先级(0个)

【注】一般情况下,只设置一次中断优先级分组,随意改变分组(改变抢占和响应的位数)会导致中断管理混乱。

当抢占优先级高的抢占了优先级低的,是不是这个低优先级的程序就中止了呢

  1. 不是中止,是暂停:高抢占优先级中断发生时,低优先级中断服务程序会被暂停,而非终止。
  2. 自动保存上下文:CPU硬件自动将低优先级中断的执行状态(如寄存器、PC等)压入堆栈。
  3. 优先执行高优先级中断:系统转而执行高优先级ISR。
  4. 恢复并继续执行:高优先级中断处理完成后,自动恢复低优先级中断的上下文,从中断点继续执行。
  5. 真正“中止”的情况:只有当中断标志被清除、事件消失或中断被禁用时,低优先级中断才可能被丢弃。

4. NVIC 的使用

  1. 设置中断分组

    寄存器:AIRCR[10:8],函数:HAL_NVIC_SetPriorityGrouping

    这个函数是在 HAL_Init() 中调用的,属于系统初始化的一步。函数调用示例:

    /* NVIC_PRIORITYGROUP_0 对应分组0
       NVIC_PRIORITYGROUP_1 对应分组1
       NVIC_PRIORITYGROUP_2 对应分组2
       NVIC_PRIORITYGROUP_3 对应分组3
       NVIC_PRIORITYGROUP_4 对应分组4 */
    HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);  //设置优先级分组2
    
  2. 设置中断优先级

    寄存器:IPRx 位[7:4],函数:HAL_NVIC_SetPriority

    这个函数是在某个外设的初始化函数里调用的,属于外设初始化的一步,调用示例:

    HAL_NVIC_SetPriority(USART1_IRQn,3,3);  
    // USART1中断优先级设置, USART1_IRQn为中断号, 第一个3为抢占优先级, 第二个3为响应优先级
    // 中断号要查定义, 抢占和响应优先级要在优先级分组的限制内进行选择
    

    中断号枚举类型定义:

    typedef enum
    {
    /******  Cortex-M3 Processor Exceptions Numbers ***************************************************/
      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        */
    } IRQn_Type;
    
  3. 使能中断

    寄存器:ISERx,函数:HAL_NVIC_EnableIRQ

    此函数是在某个外设的初始化函数里调用的,属于外设初始化的一步,调用示例:

    HAL_NVIC_EnableIRQ(USART1_IRQn);  //使能USART1中断通道
    // 其中 USART1_IRQn 也是中断号, 具体查看上面列出的所有中断号
    

在正点原子的例程中,中断分组已经在 HAL_Init() 函数中设置了分组 2 ,这是一个折中的方案。

② EXTI

简介:External interrupt/event Controller,外部中断事件控制器。通过检测外部或内部信号的边沿变化(上升沿、下降沿或双边沿),(根据配置)来产生中断或事件,其中:

  • 中断:要进入 NVIC ,有相应的中断服务函数,需要 CPU 处理。
  • 事件:外设在特定条件下产生一个硬件脉冲信号(Event),这个信号不进入 NVIC,而是直接通过内部事件总线 (Event Bus) 传递给其他外设。CPU 完全不知道这个事件的发生,不进入中断,不执行任何代码(例如:如 ADC 转换完成 → 触发 TIM 启动)。

F1 系列的单片机包含 20 个产生事件/中断请求的边沿检测器,即总共 20 条 EXTI 线。非互联型是 19 个(√)。

F1/F4/F7/H7 四个型号中 EXTI 支持的外部中断/事件请求(主要是 EXTI 线 0~15 ):

在这里插入图片描述

EXTI 的主要特性:

每条 EXTI 线都可以单独配置:选择类型(中断或者事件)、触发方式(上升沿,下降沿或者双边沿触发)、支持软件触发、开启/屏蔽、有挂起状态位 。。比如说:外部输入了一个上升沿,配置产生中断(或事件)

1. EXTI 的工作原理

在这里插入图片描述

在这里插入图片描述

框图如上所示,通过与或门来控制。对应寄存器,我们只需要掌握上满四个标出的即可,软件触发和事件基本不用。

2. EXTI 相关寄存器

寄存器名位数功能
上升沿触发选择寄存器 EXTI_RTSR32 位,非互联型只用了前 0~18 位19 位,每一位都对应一个 EXTI 线
写 1 允许对应线上的上升沿触发
写 0 禁止对应线上的上升沿触发
下降沿触发选择寄存器 EXTI_FTSR32 位,非互联型只用了前 0~18 位19 位,每一位都对应一个 EXTI 线
写 1 允许对应线上的下降沿触发
写 0 禁止对应线上的下降沿触发
中断屏蔽寄存器 EXTI_IMR32 位,非互联型只用了前 0~18 位19 位,每一位都对应一个 EXTI 线
写 1 开放来自对应线上的中断请求
写 0 屏蔽来自对应线上的中断请求
挂起寄存器 EXTI_PR32 位,非互联型只用了前 0~18 位19 位,每一位都对应一个 EXTI 线
为 0 表示没有发生触发请求
为 1 表示发生了选择的触发请求
在该位中写 1 可以清除挂起状态

如果某一条线上,想要设置双边沿触发,那么将 EXTI_RTSR 和 EXTI_FTSR 对应位均设为 1 即可。

前三个寄存器是 CPU 主动写入来设置的,第四个寄存器可读可写,通过读来判断是否有请求,通过写 1 来清除挂起状态。

3. EXTI 和 IO 的映射关系

这里就是之前表格说的 EXTI 线 0~15 对应 GPIO PIN 0~15。

简介 在 STM32 中,绝大多数外部中断 EXTI 是由 GPIO 引脚触发的。然而,每个 EXTI 线(如 EXTI0)在上电复位后并不会自动连接到某个特定的 GPIO 引脚。需要通过配置 AFIO 模块的 EXTICRx 寄存器,将目标 GPIO 引脚(如 PA0、PB0 等)的数字输入信号路由到对应的 EXTI 输入线。配置完成后,该引脚的电平变化(上升沿或下降沿)将被 EXTI 模块检测到,并可触发中断或事件。

AFIO

Alternate Function lO,即复用功能 IO,主要用于重映射和 外部中断映射配置 。其功能如下(这里主要是第三个):

  • 调试 IO 配置:不用管。
  • 重映射配置:不用管。
  • 外部中断配置:寄存器 AFIO_EXTICR1~4,配置 EXTI 中断线 0~15 对应到哪个具体 IO 口。

特别注意:配置 AFIO 寄存器之前要使能 AFIO 时钟,方法如下:HAL_RCC_AFIO_CLK_ENABLE(); 对应 RCC_APB2ENR 寄存器位 0 。

【注意】

  • 这个 EXTI 和 IO 的映射并不是 IO 的复用功能输入和输出,它使用的是 GPIO 的 “数字输入” 信号(来自 IDR),而非复用功能输出。并且必须将 GPIO 配置为某种输入模式(浮空、上拉、下拉)
  • IO 口的复用功能输出和输入是给 TIM、USART 这些外设使用的。

EXTI 和 IO 的对应关系(分时对应)

这俩的对应关系是靠 AFIO 模块的 EXTICRx 寄存器 配置的,EXTICR 寄存器总共有 4 个(AFIO_EXTICR1~4),每个寄存器控制 4 根 EXTI 线(共 16 根线)。并且其中一个引脚和某根 EXTI 线连接上之后,其他的几个引脚就不能再次与该 EXTI 线相连(就是说一个 EXTI 线每次只能连接一个引脚)。具体对应情况如下所示(具体要查看手册):

在这里插入图片描述

其中:PX0 映射到 EXTI0 ;PX1 映射到 EXTI1;…;PX14 映射到 EXTI14;PX15 映射到 EXTI15 。

③ 如何使用中断

整个中断的流程图,包括 GPIO EXTI 中断、其他外设 EXTI 中断、外设中断(但是这里主要讲 GPIO EXTI 中断)。

在这里插入图片描述

1. STM32 EXTI 的 HAL 库配置步骤(GPIO外部中断):

  1. 使能 GPIO 时钟

    • 使用宏 __HAL_RCC_GPIOx_CLK_ENABLE() 配置。
  2. 配置 GPIO / AFIO(SYSCFG) / EXTI ,可细分为:

    • 设置 GPIO 输入模式

    • 使能 AFIO 时钟

    • 设置 EXTI 和 IO 对应关系

    • 设置 EXTI 屏蔽,上/下沿

    • 【注意】这些都是在 HAL 库中是使用函数 HAL_GPIO_Init() 一步到位的。

  3. 配置 NVIC(前面说过)

    • 设置中断分组:HAL_NVIC_SetPriorityGrouping,此函数仅需设置一次!

      (这里给出但实际并不用设置,因为已经在 HAL_Init() 中设置过了)

    • 设置中断优先级:HAL_NVIC_SetPriority

    • 使能中断:HAL_NVIC_EnablelRQ

  4. 设计中断服务函数

    • 编写 EXTIx_IRQHandler 中断服务函数,同时清除中断标志
    • 中断服务函数中要调用中断处理公用函数(这里清除中断标志),然后公用函数中还要调用数据处理回调函数(见下方图解)。回调函数事先已经被定义了,这是一个弱定义函数,里面没有实际的函数体,我们通常会重定义该函数(名字不能变)。
    • 中断服务函数和中断回调函数都不需要自己再声明了!原来的官方库中已经声明过了!
    • 在单片机的 HAL 库开发中,!!!中断服务函数(Interrupt Service Routine, ISR)的名字必须与 .s 启动文件(startup file)中的中断向量表(Interrupt Vector Table)中定义的函数名完全一致!!!

【注意】STM32 仅有:EXTI0、EXTI1、EXTI2、EXTI3、EXTI4、EXTI9_5、EXTI15_10,7个外部中断服务函数

2. HAL库 中断回调处理机制介绍

比较复杂,和标准库不同,这里先给出简图:

在这里插入图片描述

④ 实例:按键触发外部中断控制LED

首先,分析按键 IO 应该设置成什么模式:

要看原理图中,按键连的是 VCC 还是 GND 。下方图中,当 KEY_UP 按键按下时, WK_UP 左边连接 VCC ,按键松开时应让此 IO 口处于低电平,那么 WK_UP 应该配置为输入下拉(默认低电平);当 KEY0 按键按下时(KEY1同理), KEY0 引脚左边连接接地,按键松开时应让此 IO 口处于高电平,那么 KEY0 应该配置为输入上拉(默认高电平)。

在这里插入图片描述

要求: 使用 KEY_UP 按键配置上升沿触发控制 LED0 的亮灭;使用 KEY0 按键配置下降沿触发控制 LED1 的亮灭;

部分核心代码如下:

exti.c 文件:

#include "exti.h"
#include "delay.h"
#include "led.h"
#include "key.h"

//外部中断初始化
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/PE4
    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
}


// EXTI0 中断服务函数
void EXTI0_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);       // 调用中断处理公用函数
}
// EXTI3 中断服务函数
void EXTI3_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3);       // 调用中断处理公用函数
}
// EXTI4 中断服务函数
void EXTI4_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);       // 调用中断处理公用函数
}

// 中断服务程序中需要做的事情
// 在HAL库中所有的外部中断服务函数都会调用此函数
// GPIO_Pin:中断引脚号
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    delay_ms(100);      // 消抖
    switch(GPIO_Pin)
    {
        case GPIO_PIN_0:
            if(WK_UP==1) 
            {
                LED0=!LED0;     // 控制LED0反转
            }
            break;
        case GPIO_PIN_3:
            if(KEY1==0)         // 控制LED1反转
            {
                LED1=!LED1;
            }
            break;
        case GPIO_PIN_4:
            if(KEY0==0)  
            {
                LED0=!LED0;
                LED1=!LED1; 
            }
            break;
    }
}

其中 HAL_GPIO_Init() 函数配置中,引脚的边沿触发方式配置 GPIO_Initure.Mode 选择如下:

#define  GPIO_MODE_IT_RISING                    0x10110000u   // 上升沿触发
#define  GPIO_MODE_IT_FALLING                   0x10210000u   // 下降沿触发
#define  GPIO_MODE_IT_RISING_FALLING            0x10310000u   // 双边沿触发

exti.h 文件(中断服务函数和中断回调函数都不需要自己再声明了!原来的官方库中已经声明过了!):

#ifndef _EXTI_H
#define _EXTI_H
#include "sys.h"

void EXTI_Init(void);
#endif

main.c 文件:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "exti.h"

/*下面主函数是使用HAL库函数实现控制IO口输出*/

int main(void)
{
    HAL_Init();                         // 初始化HAL库    
    Stm32_Clock_Init(RCC_PLL_MUL9);     // 设置时钟,72M
    delay_init(72);                     // 初始化延时函数
    LED_Init();                         // 初始化LED
    KEY_Init();                         // 初始化按键
    EXTI_Init();                        // 初始化外部中断
    
    while(1)
    {
        // 正常执行程序
        HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5);  // LED1翻转
        delay_ms(1000);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值