STM32F7学习笔记13:中断详解

Dr.Guo
发布于 2024-02-18 / 63 阅读
0
0

STM32F7学习笔记13:中断详解

STM32F7学习笔记13:中断详解

1. 中断类型

F767在内核水平上搭载了一个异常响应系统, 支持为数众多的系统异常和外部中断。 其中系统异常有10个,F767外部中断有110个,其余F7系列外部中断有150个。除了个别异常的优先级被定死外,其它异常的优先级都是可编程的。

有关具体的系统异常和外部中断可在标准库文件举例为F767的stm32f767xx.h这个头文件查询到,在IRQn_Type这个结构体里面包含了F7系列全部的异常声明。

系统异常如下表所示

编号 优先级 优先级类型 名称 说明 地址
保留(实际存的是 MSP地址) 0X0000 0000
-3 固定 Reset 复位 0X0000 0004
-2 固定 NMI 不可屏蔽中断。RCC 时钟安全系统(CSS) 连接到 NMI 向量 0X0000 0008
-1 固定 HardFaul t 所有类型的错误 0X0000 000C
0 可编程 MemManag e 存储器管理 0X0000 0010
1 可编程 BusFault 预取指失败,存储 器访问失败 0X0000 0014
2 可编程 UsageFau lt 未定义的指令或非 法状态 0X0000 0018
保留 0X0000 001C-0X0000 002B
3 可编程 SVCall 通过 SWI 指令调用的系统服 务 0X0000 002C
4 可编程 Debug Monitor 调试监控器 0X0000 0030
保留 0X0000 0034
5 可编程 PendSV 可挂起的系统服务 0X0000 0038
6 可编程 SysTick 系统嘀嗒定时器 0X0000 003C

外部中断如下表:

编号 优先级 优先级类型 名称 说明 地址
0 7 可编程 窗口看门狗中断 0X0000 0040
1 8 可编程 PVD 连接EXTI 线的可编程电压检 测中断 0X0000 0044
2 9 可编程 TAMP_STA MP 连接EXTI 线的入侵和时间戳 中断 0X0000 0048

2. NVIC

NVIC(Nested Vectored Interrupt Controller)是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对Cortex-M7内核里面的NVIC进行裁剪,把不需要的部分去掉,所以说STM32的NVIC是Cortex-M7的NVIC的一个子集。

NVIC结构体定义,在 core_cM7.h文件中定义如下:

typedef struct {
    __IO uint32_t ISER[8U];       // 中断使能寄存器
    uint32_t RESERVED0[24U];
    __IO uint32_t ICER[8U];       // 中断清除寄存器
    uint32_t RSERVED1[24U];
    __IO uint32_t ISPR[8U];       // 中断使能悬起寄存器
    uint32_t RESERVED2[24U];
    __IO uint32_t ICPR[8U];       // 中断清除悬起寄存器
    uint32_t RESERVED3[24U];
    __IO uint32_t IABR[8U];       // 中断有效位寄存器
    uint32_t RESERVED4[56U];
    __IO uint8_t  IP[240U];       // 中断优先级寄存器(8Bit wide)
    uint32_t RESERVED5[644U];
    __O  uint32_t STIR;          // 软件触发中断寄存器
}  NVIC_Type;

在配置中断的时候我们一般只用ISER、ICER和IP这三个寄存器,ISER用来使能中断,ICER用来失能中断,IP用来设置中断优先级。

3. 优先级

3.1优先级定义

在NVIC 有一个专门的寄存器:中断优先级寄存器NVIC_IPRx(在F7中,x=0…100)用来配置外部中断的优先级,IPR宽度为8bit,原则上每个外部中断可配置的优先级为0~255,数值越小,优先级越高。但是绝大多数CM7芯片都会精简设计,以致实际上支持的优先级数减少,在F767中,只使用了高4bit,

用于表达优先级的这4bit,又被分组成抢占优先级和子优先级。如果有多个中断同时响应,抢占优先级高的就会抢占,抢占优先级低的优先得到执行,如果抢占优先级相同,就比较子优先级。如果抢占优先级和子优先级都相同的话,就比较他们的硬件中断编号,编号越小,优先级越高。

3.2 优先级分组

优先级的分组由内核外设SCB的应用程序中断及复位控制寄存器AIRCR的PRIGROUP[10:8]位决定,F7分为了5组,具体如下:主优先级=抢占优先级

4. 中断编程

在配置每个中断的时候一般有3个编程要点:

  1. 使用 HAL\_NVIC\_SetPriorityGrouping(uint32\_t PriorityGroup)函数配置中断优先级分组。一般默认是 NVIC\_PRIORITYGROUP\_4分组4。
  2. 使用 HAL\_NVIC\_SetPriority(IRQn\_Type IRQn, uint32\_t PreemptPriority, uint32\_t SubPriority)函数配置具体外设中断通道的抢占优先级和子优先级。
  3. 使用 HAL\_NVIC\_EnableIRQ函数使能中断请求。

IRQn\_Type中断源结构体代码如下:

typedef enum IRQn {
    //Cortex-M7 处理器异常编号
    NonMaskableInt_IRQn      = -14,
    MemoryManagement_IRQn    = -12,
    BusFault_IRQn            = -11,
    UsageFault_IRQn          = -10,
    SVCall_IRQn              = -5,
    DebugMonitor_IRQn        = -4,
    PendSV_IRQn              = -2,
    SysTick_IRQn             = -1,
    //STM32 外部中断编号
    WWDG_IRQn                = 0,
    PVD_IRQn                 = 1,
    TAMP_STAMP_IRQn          = 2,

    // 限于篇幅,中间部分代码省略,具体的可查看库文件

} IRQn_Type;
  • PreemptPriority:抢占优先级,具体的值要根据优先级分组来确定
  • SubPriority:子优先级,具体的值要根据优先级分组来确定

5. 外部中断

外部中断/事件控制器(EXTI)管理了控制器的25个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。

EXTI功能框图如下:

EXTI可分为两大部分功能,一个是产生中断,另一个是产生事件,这两个功能从硬件上就有所不同。

5.1 产生中断流程

红色虚线指示的电路流程。它是一个产生中断的线路,最终信号流入到NVIC控制器内。

编号1是输入线,EXTI控制器有25个中断/事件输入线,这些输入线可以通过寄存器设置为任意一个GPIO,也可以是一些外设的事件。输入线一般是存在电平变化的信号。

编号2是一个边沿检测电路,它会根据上升沿触发选择寄存器(EXTI_RTSR)和下降沿触发选择寄存器(EXTI_FTSR)对应位的设置来控制信号触发。边沿检测电路以输入线作为信号输入端,如果检测到有边沿跳变就输出有效信号1给编号3电路,否则输出无效信号0。

编号3电路实际就是一个或门电路,它一个输入来自编号2电路,另外一输入来自软件中断事件寄存器(EXTI_SWIER)。EXTI_SWIER允许通过程序控制就可以启动中断/事件线,这在某些地方非常有用。

编号4电路是一个与门电路,它一个输入编号3电路,另外一个输入来自中断屏蔽寄存器(EXTI_IMR)。与门电路要求输入都为1才输出1。

编号5是将EXTI_PR寄存器内容输出到NVIC内,从而实现系统中断事件控制。

5.2 产生事件流程

绿色虚线指示的电路流程。它是一个产生事件的线路,最终输出一个脉冲信号。

产生事件线路是在编号3电路之后与中断线路有所不同,之前电路都是共用的。

编号6电路是一个与门,它一个输入编号3电路,另外一个输入来自事件屏蔽寄存器(EXTI_EMR)。

编号7是一个脉冲发生器电路,当它的输入端,即编号6电路的输出端,是一个有效信号1时就会产生一个脉冲;如果输入端是无效信号就不会输出脉冲。

编号8是一个脉冲信号,就是产生事件的线路最终的产物,这个脉冲信号可以给其他外设电路使用,比如定时器TIM、模拟数字转换器ADC等等,这样的脉冲信号一般用来触发TIM或者ADC开始转换。

5.3 区别

  • 产生中断线路目的是把输入信号输入到NVIC,进一步会运行中断服务函数,实现功能,这是软件级的。
  • 产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输,这是硬件级的。

6. 中断/事件线

EXTI有25个中断/事件线,每个GPIO都可以被设置为输入线,占用EXTI0至EXTI15,还有另外七根用于特定的外设事件。

EXTI中断/事件线如下表所示:

中断/事件线 输入源
EXTI0 PX0(X可为A,B,C,D,E,F,G,H,I)
EXTI1 PX1(X可为A,B,C,D,E,F,G,H,I)
EXTI2 PX2(X可为A,B,C,D,E,F,G,H,I)
EXTI3 PX3(X可为A,B,C,D,E,F,G,H,I)
EXTI4 PX4(X可为A,B,C,D,E,F,G,H,I)
EXTI5 PX5(X可为A,B,C,D,E,F,G,H,I)
EXTI6 PX6(X可为A,B,C,D,E,F,G,H,I)
EXTI7 PX7(X可为A,B,C,D,E,F,G,H,I)
EXTI8 PX8(X可为A,B,C,D,E,F,G,H,I)
EXTI9 PX9(X可为A,B,C,D,E,F,G,H,I)
EXTI10 PX10(X可为A,B,C,D,E,F,G,H,I)
EXTI11 PX11(X可为A,B,C,D,E,F,G,H,I)
EXTI12 PX12(X可为A,B,C,D,E,F,G,H,I)
EXTI13 PX13(X可为A,B,C,D,E,F,G,H,I)
EXTI14 PX14(X可为A,B,C,D,E,F,G,H,I)
EXTI15 PX15(X可为A,B,C,D,E,F,G,H)

EXTI0至EXTI15用于GPIO,通过编程控制可以实现任意一个GPIO作为EXTI的输入源。由EXTI有25个中断/事件线,每个GPIO都可以被设置为输入线, 占用EXTI0至EXTI15,还有另外七根用于特定的外设事件。

EXTI0可以通过SYSCFG外部中断配置寄存器1(SYSCFG_EXTICR1)的EXTI0[3:0]位选择配置为PA0、PB0、PC0、PD0、 PE0、PF0、PG0、PH0或者PI0

7. HAL中断编程

7.1 EXTI初始化详解

HAL库函数的EXIT初始化非常简单,只需配置好IO口的模式,然后配置中断源、中断优先级、使能中断。

  1. HAL_NVIC_SetPriority:该函数负责EXTI中断/事件线选择,可选EXTI0至EXTI25,可参考表 17-1选择,配置优先级。
  2. HAL_NVIC_EnableIRQ:该函数负责控制使能中断。

7.2 中断编程示例

使用外部按键1作为中断源

其他配置不再赘述

1. 使能外部中断

2. 使能中断

然后生成代码。

3. 添加中断代码

stm32f7xx_it.c中断设置代码如下:

/**
  * @brief This function handles EXTI line[15:10] interrupts.
  */
void EXTI15_10_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI15_10_IRQn 0 */

  /* USER CODE END EXTI15_10_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(KEY2_PIN_Pin);
  /* USER CODE BEGIN EXTI15_10_IRQn 1 */

  /* USER CODE END EXTI15_10_IRQn 1 */
}

在该代码下添加Callback方法:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
  if (GPIO_Pin== KEY2_PIN_Pin) {
    HAL_GPIO_TogglePin(LED_G_GPIO_Port, LED_G_Pin);
  }
}

该回调函数的原型为:


/**
  * @brief  EXTI line detection callbacks.
  * @param  GPIO_Pin Specifies the pins connected EXTI line
  * @retval None
  */
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(GPIO_Pin);
  
  /* NOTE: This function Should not be modified, when the callback is needed,
           the HAL_GPIO_EXTI_Callback could be implemented in the user file
   */
}

#if defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) /* ARM Compiler V6 */
  #ifndef __weak
    #define __weak  __attribute__((weak))
  #endif
  #ifndef __packed
    #define __packed  __attribute__((packed))
  #endif
#elif defined ( __GNUC__ ) && !defined (__CC_ARM) /* GNU Compiler */
  #ifndef __weak
    #define __weak   __attribute__((weak))
  #endif /* __weak */
  #ifndef __packed
    #define __packed __attribute__((__packed__))
  #endif /* __packed */
#endif /* __GNUC__ */

使用 __attribute__((weak)) 修饰函数,告诉编译器此函数为同名函数的备选,如果定义了其他同名函数,让编译器优先使用其他同名函数,如果未定义其他同名函数,那么就使用此函数。

7.3 测试


评论