STM32-外部中断

1,211 阅读12分钟

文章参考资料:STM32GPIO外部中断的详细解析和总结7.中断STM32中断(外部中断和定时器中断)stm32学习笔记---中断与事件的区别以及外部中断STM32单片机中断管理(stm32f103学习总结)—stm32外部中断事件与中断STM32 中断与事件关系的解剖STM32的“外部中断”和“事件”区别和理解

硬件:STM32F107VC( Cortex-M3)

软件:Keil μVision4,JLINK

一、EXTI

1. 跟中断相关的基本概念

1) 数据传输方式

2) 中断全过程

       比如一老师在教室里给学生们上课。课堂上的学生可能做出各种行为动作,比方做笔记、打哈气、翻书包、讲小话等,我们把这些行为统称为事件,其中有些行为老师往往只是视而不见,继续他的上课;而有些行为可能导致老师的上课中止,比方讲小话,并对学生的相关行为予以警告、批评或纠正等,然后继续上课。我们把老师因为学生的某些行为而中止授课,并产生后续动作,之后接着上课的这个过程理解为中断或中断响应。我们把可能导致老师上课中断的学生行为理解为中断事件

      中断事件是一种可以导致中断发生的事件,中断则是因为中断事件的发生而导致的后续行为过程。事件与中断事件是包含关系,即事件可分为中断事件或非中断事件。而中断事件与中断之间属于前后关联的因果关系,虽有关联,但二者在时序上、行为上并不一样。

3) 中断的作用

4) 中断优先级

       这个处理原则,我们也叫中断嵌套。当主程序响应低级中断请求,进行低级中断处理时,如果出现高级中断请求,将暂停低级中断,去响应高级中断请求,执行高级中断服务程序,执行完后,才会返回低级中断程序,进行低级中断处理,这个过程就叫中断嵌套

5) 中断向量

     在启动文件Libraries\CMSIS\Core\CM3\startup\arm\startup_stm32f10x_cl.s(77)

6)查找中断向量的过程和中断响应过程

2. EXTI简介

      STM32F10x外部中断/事件控制器(EXTI)包含多达 20 个用于产生事 件/中断请求的边沿检测器。EXTI的每根输入线都可单独进行配置,以选择类型(中断或事件)和相应的触发事件(上升沿触发、下降沿触发或 边沿触发),还可独立地被屏蔽。stm32f107上的EXTI控制器的主要功能如下:

  • 每个中断/事件线上的独立触发和屏蔽 
  • 每个中断线的专用状态位
  • 生成多达20个软件事件/中断请求 
  • 检测外部信号的脉冲宽度小于APB2时钟周期。

3. EXTI的结构框图

         从左到右,EXTI的结构框图里的寄存器和电路介绍:

  • 输入线,通过寄存器配置和I/O引脚相连 
  • 边沿检测电路,根据用户的配置,检测边沿变化,当检测到有效的边沿变化时输出信号“1” 
  • 软件中断事件寄存器,主要提供软件中断的功能,用来仿真外部中断
  • 中断掩码寄存器,主要用于中断的屏蔽,当寄存器输出0时,与门封锁,无法响应外部中断,当寄存器输出1时,与门开放,此时才可响应外部中断 
  • 挂起请求寄存器,用来置位中断标志 
  • 最后将中断申请送至NVIC分配的中断通道

4. 外部中断线或外部事件线的示意图

       图中信号线上划有一条斜线,旁边标志19字样的注释,表示这样的线路共有19套。(在stm32f107上应该有20套)。

       图中的蓝色虚线箭头,标出了外部中断信号的传输路径,首先外部信号从编号1的芯片管脚进入,经过编号2的边沿检测电路,通过编号3的或门进入中断“挂起请求寄存器”,最后经过编号4的与门输出到NVIC中断控制器。

       在这个通道上有4个控制选项,外部的信号首先经过边沿检测电路,这个边沿检测电路受上升沿或下降沿选择寄存器控制,用户可以使用这两个寄存器控制需要哪一个边沿产生中断,因为选择上升沿或下降沿是分别受2个平行的寄存器控制,所以用户可以同时选择上升沿或下降沿,而如果只有一个寄存器控制,那么只能选择一个边沿了。接下来是编号3的或门,这个或门的另一个输入是“软件中断/事件寄存器”,从这里可以看出,软件可以优先于外部信号请求一个中断或事件,既当“软件中断/事件寄存器”的对应位为“1”时,不管外部信号如何,编号3的或门都会输出有效信号。一个中断或事件请求信号经过编号3的或门后,进入挂起请求寄存器,到此之前,中断和事件的信号传输通路都是一致的,也就是说,挂起请求寄存器中记录了外部信号的电平变化。 外部请求信号最后经过编号4的与门,向NVIC中断控制器发出一个中断请求,如果中断屏蔽寄存器的对应位为“0”,则该请求信号不能传输到与门的另一端,实现了中断的屏蔽

       图中红色虚线箭头,标出了_外部事件信号的传输路径_,外部请求信号经过编号3的或门后,进入编号5的与门,这个5号的与门,用于引入事件屏蔽寄存器的控制;最后脉冲发生器把一个跳变的信号转变为一个单脉冲,输出到芯片中的其它功能模块。

      从这张图上我们也可以知道,从外部激励信号来看,中断和事件的产生源都可以是一样的。之所以分成2个部分,由于中断是需要CPU参与的,需要软件的中断服务函数才能完成中断后产生的结果,但是事件,是靠脉冲发生器产生一个脉冲,进而由硬件自动完成这个事件产生的结果,当然相应的联动部件需要先设置好,比如引起DMA操作,AD转换等。

       总之,事件机制提供了一个完全有硬件自动完成的触发到产生结果的通道,不要软件的参与,降低了CPU的负荷,节省了中断资源,提高了响应速度(硬件总快于软件),是利用硬件来提升CPU芯片处理事件能力的一个有效方法;

       例如外部I/O触发AD转换,来测量外部物品的重量,如果使用传统的中断通道,需要I/O触发产生外部中断,外部中断服务程序启动AD转换,AD转换完成中断服务程序提交最后结果。要是使用事件通道,I/O触发产生事件,然后联动触发AD转换,AD转换完成中断服务程序提交最后结果。相比之下,后者不要软件参与AD触发,并且响应速度也更块。要是使用事件触发DMA操作,就完全不用软件参与就可以完成某些联动任务了。

5. EXTI管理的中断线

      STM32F107的中断控制器支持 20 个外部中断/事件请求。每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置。

STM32F107的20 个外部中断为:

  • 线 0~15:对应GPIO 口的输入中断。
  • 线 16:连接到 PVD 输出。
  • 线 17:连接到 RTC 闹钟事件。
  • 线 18:连接到 USB 唤醒事件。
  • 线 19:连接到 以太网 唤醒事件。

       STM32 的每一个GPIO都能配置成一个外部中断触发源,这点也是 STM32 的强大之处。当对应的GPIO引脚与外部中断线连接后,GPIO引脚才具备外部中断的功能,可以设置外部中断的触发方式。

6. EXTI的初始化

      在stm32f10x_exti.h里定义了EXTI_InitTypeDef结构体变量。

typedef struct
{
  uint32_t EXTI_Line;               /*!< Specifies the EXTI lines to be enabled or disabled.
                                         This parameter can be any combination of @ref EXTI_Lines */

  EXTIMode_TypeDef EXTI_Mode;       /*!< Specifies the mode for the EXTI lines.
                                         This parameter can be a value of @ref EXTIMode_TypeDef */

  EXTITrigger_TypeDef EXTI_Trigger; /*!< Specifies the trigger signal active edge for the EXTI lines.
                                         This parameter can be a value of @ref EXTIMode_TypeDef */

  FunctionalState EXTI_LineCmd;     /*!< Specifies the new state of the selected EXTI lines.
                                         This parameter can be set either to ENABLE or DISABLE */ 
}EXTI_InitTypeDef;

       其中这个EXTI_InitTypeDef结构体第一个变量名为EXTI_Line,EXTI支持 20 个外部中断/事件请求,所以有20条中断线。

#define EXTI_Line0       ((uint32_t)0x00001)  /*!< External interrupt line 0 */
#define EXTI_Line1       ((uint32_t)0x00002)  /*!< External interrupt line 1 */
#define EXTI_Line2       ((uint32_t)0x00004)  /*!< External interrupt line 2 */
#define EXTI_Line3       ((uint32_t)0x00008)  /*!< External interrupt line 3 */
#define EXTI_Line4       ((uint32_t)0x00010)  /*!< External interrupt line 4 */
#define EXTI_Line5       ((uint32_t)0x00020)  /*!< External interrupt line 5 */
#define EXTI_Line6       ((uint32_t)0x00040)  /*!< External interrupt line 6 */
#define EXTI_Line7       ((uint32_t)0x00080)  /*!< External interrupt line 7 */
#define EXTI_Line8       ((uint32_t)0x00100)  /*!< External interrupt line 8 */
#define EXTI_Line9       ((uint32_t)0x00200)  /*!< External interrupt line 9 */
#define EXTI_Line10      ((uint32_t)0x00400)  /*!< External interrupt line 10 */
#define EXTI_Line11      ((uint32_t)0x00800)  /*!< External interrupt line 11 */
#define EXTI_Line12      ((uint32_t)0x01000)  /*!< External interrupt line 12 */
#define EXTI_Line13      ((uint32_t)0x02000)  /*!< External interrupt line 13 */
#define EXTI_Line14      ((uint32_t)0x04000)  /*!< External interrupt line 14 */
#define EXTI_Line15      ((uint32_t)0x08000)  /*!< External interrupt line 15 */
#define EXTI_Line16      ((uint32_t)0x10000)  /*!< External interrupt line 16 Connected to the PVD Output */
#define EXTI_Line17      ((uint32_t)0x20000)  /*!< External interrupt line 17 Connected to the RTC Alarm event */
#define EXTI_Line18      ((uint32_t)0x40000)  /*!< External interrupt line 18 Connected to the USB Device/USB OTG FS
                                                   Wakeup from suspend event */
#define EXTI_Line19      ((uint32_t)0x80000)  /*!< External interrupt line 19 Connected to the Ethernet Wakeup event */ 

    EXTI_InitTypeDef结构体第二个变量名为EXTI_Mode,是用枚举定义的,EXTI模式有2种:中断和事件。

typedef enum
{
  EXTI_Mode_Interrupt = 0x00,
  EXTI_Mode_Event = 0x04
}EXTIMode_TypeDef;

    EXTI_InitTypeDef结构体第三个变量名为EXTI_Trigger,是用枚举定义的,外部中断触发方式有3种:上升沿触发、下降沿触发、双边沿触发。

typedef enum
{
  EXTI_Trigger_Rising = 0x08,
  EXTI_Trigger_Falling = 0x0C,  
  EXTI_Trigger_Rising_Falling = 0x10
}EXTITrigger_TypeDef;

  EXTI_LineCmd是中断线使能设置,有使能ENABLE 和失能DISABLE两种。

     例如将引脚PB9设置为外部中断,需先将引脚(PB9)进行GPIO初始化,设置为浮空输入模式,50MHz工作速度,开启AFIO时钟,设置IO口与中断线的映射关系,再初始化EXTI,PB9对应的中断线是EXTI_Line9,模式是中断,触发方式是下降沿触发,中断线使能。

GPIO_InitTypeDef  GPIO_InitStructure;//定义初始化结构体
EXTI_InitTypeDef EXTI_InitStructure; //定义初始化结构体 
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);//使用GPIO使能时钟和AFIO复用时钟

 /* Configure Button pin as input floating */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//设置浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO

 /* Connect Button EXTI Line to Button GPIO Pin */
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource9);//将GPIO口与中断线映射起来

/* Configure Button EXTI line */
EXTI_InitStructure.EXTI_Line = EXTI_Line9;//选择中断线
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//选择中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //设置为下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;//中断线使能
EXTI_Init(&EXTI_InitStructure);//初始化中断

二、NVIC

1. NVIC简介

      NVIC 是Nested vectored interrupt controller的缩写,即嵌套向量中断控制器,用于为中断分组,从而分配抢占优先级和响应优先级。 它的特征是:

    1)68个外部可屏蔽中断(不包括十六个Cortex®-M3中断线)

    2)16个可编程优先级(使用4位中断优先级)

    3)低延迟异常和中断处理

    4)电源管理控制 

    5)系统控制寄存器的实施

     其中,中断是由内核外部产生的,一般由硬件引起,比如外设中断和外部中断等。

  异常通常是内核自身产生的,大多是软件引起的,比如除法除错异常、预取值失败等。

2. 中断通道

       微控制器内集成了很多外设,对于单个外设而言,它通常具备若干个可以引起中断的中断源,而该外设的所有中断源只能通过指定的中断通道向内核申请中断。

     stm32f107上有68个中断通道,已经固定分配给相应的片内外设。由于中断源数量较多,而中断通道有限,会出现多个中断源共享同一个中断的情况。

     STM32 通过根据引脚的序号不同将众多中断触发源分成不同的组,比如:PA0,PB0,PC0,PD0,PE0,PF0,PG0为第一组,那么依此类推,我们能得出一共有16 组,STM32 规定,每一组中同时只能有一个中断触发源工作,那么,最多工作的也就是16个外部中断。

3. 中断优先级

        NVIC中有一个8位中断优先级寄存器NVIC_IPR,理论上可以配置0-255共256级中断,但是STM32只使用了其中的高4位,并分成为抢占优先级和子优先级两组。( 子优先级还可以叫响应优先级)。

       多个中断同时提出中断申请时,先比较抢占优先级,抢占优先级高的中断先执行。也就是说有较高的抢占优先级的中断可以打断抢占优先级较低的中断。这是实现中断嵌套的基础。如果抢占优先级相同,则比较子优先级。抢占优先级相同的中断,高子优先级不可以打断低子优先级的中断。所以子优先级不会造成中断嵌套。二者都相同时,比较中断编号,编号越小,优先级越高。中断编号位于芯片头文件中,优先执行位于中断向量表中位置较高的中断。如果两个中断的抢占优先级和子优先级都是一样的话,则看哪个中断先发生就先执行。

       分组配置是由SCB->AIRCR寄存器的bit10-8来定义的。SCB->AIRCR是CM3内核定义的。其中AIRCR寄存器来确定是用哪种分组,IP寄存器是相对应于那种分组抢占优先级和子优先级的分配比例。例如组设置成2,那么此时所有的60个中断优先寄存器高4位中的最高2位是抢占优先级,低2位为子优先级。

       例如,设置中断优先级组为2,然后设置中断3(RTC中断)的抢占优先级为2,子优先级为1。 中断6(外部中断0)的抢占优先级为3,子优先级为0。中断7(外部中断1)的抢占优先级为2,子优先级为0。那么这3个中断的优先级顺序为:**中断****7>**中断3>中断6

4. NVIC初始化

 在misc.h里定义了NVIC_InitTypeDef结构体变量。

typedef struct
{
  uint8_t NVIC_IRQChannel;                    /*!< Specifies the IRQ channel to be enabled or disabled.
                                                   This parameter can be a value of @ref IRQn_Type 
                                                   (For the complete STM32 Devices IRQ Channels list, please
                                                    refer to stm32f10x.h file) */

  uint8_t NVIC_IRQChannelPreemptionPriority;  /*!< Specifies the pre-emption priority for the IRQ channel
                                                   specified in NVIC_IRQChannel. This parameter can be a value
                                                   between 0 and 15 as described in the table @ref NVIC_Priority_Table */

  uint8_t NVIC_IRQChannelSubPriority;         /*!< Specifies the subpriority level for the IRQ channel specified
                                                   in NVIC_IRQChannel. This parameter can be a value
                                                   between 0 and 15 as described in the table @ref NVIC_Priority_Table */

  FunctionalState NVIC_IRQChannelCmd;         /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannel
                                                   will be enabled or disabled. 
                                                   This parameter can be set either to ENABLE or DISABLE */   
} NVIC_InitTypeDef;

     NVIC_InitTypeDef的结构体的第一个变量是NVIC_IRQChannel,使能外部中断所在的中断通道,常用的GPIO中断有:EXTI0_IRQnEXTI1_IRQnEXTI2_IRQnEXTI3_IRQnEXTI4_IRQnEXTI9_5_IRQnEXTI15_10_IRQn。在stm32f10x.h中定义了中断通道如下:

typedef enum IRQn
{
/******  Cortex-M3 Processor Exceptions Numbers ***************************************************/
  NonMaskableInt_IRQn         = -14,    /*!< 2 Non Maskable 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                       */

#ifdef STM32F10X_CL
  CAN1_TX_IRQn                = 19,     /*!< USB Device High Priority or CAN1 TX Interrupts       */
  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                      */
  RTCAlarm_IRQn               = 41,     /*!< RTC Alarm through EXTI Line Interrupt                */
  OTG_FS_WKUP_IRQn            = 42,     /*!< USB OTG FS WakeUp from suspend through EXTI Line 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_IRQn          = 59,     /*!< DMA2 Channel 4 global Interrupt                      */
  DMA2_Channel5_IRQn          = 60,     /*!< DMA2 Channel 5 global Interrupt                      */
  ETH_IRQn                    = 61,     /*!< Ethernet global Interrupt                            */
  ETH_WKUP_IRQn               = 62,     /*!< Ethernet Wakeup through EXTI line Interrupt          */
  CAN2_TX_IRQn                = 63,     /*!< CAN2 TX Interrupt                                    */
  CAN2_RX0_IRQn               = 64,     /*!< CAN2 RX0 Interrupt                                   */
  CAN2_RX1_IRQn               = 65,     /*!< CAN2 RX1 Interrupt                                   */
  CAN2_SCE_IRQn               = 66,     /*!< CAN2 SCE Interrupt                                   */
  OTG_FS_IRQn                 = 67      /*!< USB OTG FS global Interrupt                          */
#endif /* STM32F10X_CL */     
} IRQn_Type;

     NVIC_InitTypeDef的结构体的第二个、第三个变量是抢占优先级NVIC_IRQChannelPreemptionPriority和子优先级NVIC_IRQChannelSubPriority。有时会在程序中加入一个优先级分组函数void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)

    NVIC_InitTypeDef的结构体的第四个变量是NVIC_IRQChannelCmd,是外部中断通道使能,有使能ENABLE 和失能DISABLE两种。

     例如引脚PB9的NVIC初始化设置,PB9对应的外部中断通道是EXTI9_5_IRQn,抢占优先级和子优先级随便设置,使能外部中断通道。代码如下:

    NVIC_InitTypeDef NVIC_InitStructure;//定义结构体
    NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;//使能外部中断所在的中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道

    NVIC_Init(&NVIC_InitStructure); 

三、中断设置

MDK工程中与中断相关的编程文件

       放在启动文件Libraries\CMSIS\Core\CM3\startup\arm\startup_stm32f10x_cl.s,在该文件中,预先为每个中断编写了一个中断服务程序,只是这些中断服务程序都是死循环,目的只是初始化中断向量表。

       中断服务程序的属性定义为“weak”。weak属性的函数表示:如果该函数没有在其他文件中定义,则使用该函数;如果用户定义了该函数,则使用用户定义的函数。

       中断服务程序写在stm32f10x_it.c,该文件放在User组中。外部中断所对应的中断服务程序如下:

四、实验

1. 实现的功能

    每次按下User键(PB9),LED1(PD7)亮灭会发生翻转,LCD显示“External INT occur!”

    设置引脚PD7为GPIO功能,PB9为外部中断功能

2. 硬件连接

上拉式按键:按键按下,引脚PB9读到低电平;按键释放,引脚PB9读到高电平

触发方式:按键按下瞬间,形成下降沿;按键释放瞬间,形成上升沿

这里可以看出,触发方式是下降沿。

3. 软件设计

   实验步骤如下:

  • PD7的GPIO初始化(设定为推挽输出);
  • PB9的GPIO初始化(设定为悬空输入)
  • 设置IO 口与中断线的映射关系
  • 配置EXIT
  • 配置NVIC
  • 编写中断服务函数(在中断服务程序中完成中断标志的判断和清除)
  • 产生一个软件中断

  接下来是外部中断处理流程:

  • 中断跳转,跳转到该中断所对应的中断服务程序
  • 执行中断服务程序,执行在stm32f10x_it.c中对应的中断服务程序,并完成中断标志的判断和清除。

4. 工程的建立

新建工程添加的文件夹和子文件

       User : 存放用户自己写的源代码,其中main.c是主函数文件,stm32f10x_it.c是外设中断函数文件。

      RVMDK : 存放启动文件(汇编文件),用的硬件是stm32f107VC,是互联型产品,startup_stm32f10x_cl.s放的是MDK ARM编译器启动文件。

      CMSIS : 存放CMSIS接口文件,其中 cor_cm3.c是Cortex-M3内核及其设备文件,访问Cortex-M3的CPU寄存器和内核外设的函数,system_stm32f10x.c是微控制器专用系统文件,里面含有SystemInit等函数。

     STM32_EVAL:存放评估板文件stm32_eval.c和stm3210c_eval_lcd.c,其中stm3210c_eval_lcd.c是跟lcd有关的设置文件。

      StdPeriph_Driver : 存放ST标准库文件,其中misc.c是跟中断相关的固件库,主函数main.c用到GPIO、RCC、EXTI外设驱动函数文件,stm3210c_eval_lcd.c用到USART、SPI、DMA、I2C外设驱动函数文件。

5. 代码      

   main.c中的代码如下:

#include "stm32f10x.h"
#include "stm3210c_eval_lcd.h"
#include <stdio.h>

int main(void)
{  
  /* Setup the microcontroller system. Initialize the Embedded Flash Interface,  
     initialize the PLL and update the SystemFrequency variable. */
   SystemInit();

  /* Initialize the LCD */

  {
  /* Setups the LCD */
  LCD_Setup();
  LCD_SetFont(&LCD_DEFAULT_FONT);
}

 /* STM3210C_LCD_Init();  */
  /* Clear the LCD */ 
  LCD_Clear(White);
  /* Set the LCD Text Color */
  LCD_SetTextColor(Black);
  printf("   STM3210C-EVAL    \n");
  printf("  Example on how to use EXTI\n");

  /* Initialize LED1 and Key Button mounted on STM3210X-EVAL board */  

  GPIO_InitTypeDef  GPIO_InitStructure;

  /* Enable the GPIO_LED Clock */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);

  /* Configure the GPIO_LED pin */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init(GPIOD, &GPIO_InitStructure);	

 {
  GPIO_InitTypeDef GPIO_InitStructure;
  EXTI_InitTypeDef EXTI_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;

  /* Enable the BUTTON Clock */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);

  /* Configure Button pin as input floating */
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  GPIO_Init(GPIOB, &GPIO_InitStructure);

    /* Connect Button EXTI Line to Button GPIO Pin */
   GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource9);

    /* Configure Button EXTI line */
    EXTI_InitStructure.EXTI_Line = EXTI_Line9;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; 
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);

    /* Enable and set Button EXTI Interrupt to the lowest priority */
    NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&NVIC_InitStructure); 

}

  /* Generate software interrupt: simulate a falling edge applied on Key Button EXTI line */
  EXTI_GenerateSWInterrupt(EXTI_Line9);

  while (1)
  {
  }
}

  EXTI和NVIC的初始化也可以用板级函数来写,代码如下:

 STM_EVAL_LEDInit(LED1);
 STM_EVAL_PBInit(Button_KEY, Mode_EXTI);

在stm32f10x_it.c里的中断服务函数EXTI9_5_IRQHandler:

void EXTI9_5_IRQHandler(void)
{
  if(EXTI_GetITStatus(KEY_BUTTON_EXTI_LINE) != RESET)//判断某个线上的中断是否发生
  {
    /* Toggle LED1 */
     STM_EVAL_LEDToggle(LED1);//LED1灯的状态翻转
	 printf("External INT occur!\n");

    /* Clear the Key Button EXTI line pending bit */
    EXTI_ClearITPendingBit(KEY_BUTTON_EXTI_LINE); //清除中断线上的中断标志位
  }
}

6. 代码中用到的函数

  1. 将GPIO口与中断线映射起来

  1. 产生一个软件中断

  1. 检查指定外部中断线的状态是否有效

4) EXTI_GetFlagStatus和EXTI_GetITStatus的区别

       追踪一下这两个库函数的实现即可发现区别。可以看到区别在于 EXTI_GetITStatusEXTI_GetFlagStatus 多做了一个判断: 

enablestatus = EXTI->IMR & EXTI_Line; 
if (((EXTI->PR& EXTI_Line) != (uint32_t)RESET) && (enablestatus != (uint32_t)RESET)) 

EXTI->PR 是 STM32 的挂起寄存器(EXTI_PR),其中的各个位被称为挂起位(Pending bit)。当外部中断线上发生了选择的边沿事件时,EXTI_PR 中相应的 Pending 位被置‘1’,也就是上面提到的 Flag。而 EXTI->IMR 是中断屏蔽寄存器(EXTI_IMR),其中各个位表示相应中断线上的中断屏蔽。‘0’表示屏蔽来自线x上的中断请求,‘1’开放来自线x上的中断请求。所以,EXTI_GetFlagStatus 只是纯粹读取中断标志位的状态,但是不一定会响应中断(EXT_IMR 寄存器对该中断进行屏蔽);而 EXTI_GetITStatus 除了读取中断标志位,还查看 EXT_IMR 寄存器是否对该中断进行屏蔽,在中断挂起 & 没有屏蔽的情况下就会响应中断。