STM32 第13讲 中断(NVIC/EXTI/如何使用中断/HAL库中断回调机制/实验)

文章目录

  • 中断简介
    • 中断的作用和意义
    • STM32 GPIO外部中断简图
  • NVIC
    • 基本概念
    • 中断向量表
    • NVIC相关寄存器介绍
    • NVIC工作原理
    • 中断优先级
    • NVIC的使用
      • 配置步骤
  • EXTI
    • 概念特性简介
    • 工作原理
    • EXTI与IO的映射关系
  • 使用中断
    • 使用中断流程
    • 外部中断配置步骤
    • HAL库中断回调机制简介
  • 实验
    • 硬件
    • 程序

中断简介

中断:打断CPU执行正常的程序,转而处理紧急程序,然后返回原暂停的程序继续执行

中断的作用和意义

  • 实时控制:在确定时间内对相应事件做出响应,如温度监控
  • 故障处理:检测到故障,需要第一时间处理,如电梯门夹人
  • 数据传输:不确定数据何时会来,如串口数据接收
    **意义:**高效处理紧急程序,不会一直占用CPU资源

STM32 GPIO外部中断简图

STM32 第13讲 中断(NVIC/EXTI/如何使用中断/HAL库中断回调机制/实验)_第1张图片

NVIC

基本概念

NVIC(Nested vectored interrupt controller, 嵌套向量中断控制器)
NVIC支持持 256 个中断,其中包含了 16 个系统中断和 240 个外部中断,并且具有 256 级的可编程中断设置。
STM32F407 的系统中断有 10 个,外部中断有82 个。
STM32 第13讲 中断(NVIC/EXTI/如何使用中断/HAL库中断回调机制/实验)_第2张图片

中断向量表

定义一块固定的内存,以4字节对齐,存放各个中断服务函数程序的首地址
中断向量表定义在启动文件,当发生中断,CPU会自动执行对应的终端服务函数。
STM32 第13讲 中断(NVIC/EXTI/如何使用中断/HAL库中断回调机制/实验)_第3张图片

NVIC相关寄存器介绍

typedef struct
{
	__IOM uint32_t ISER[8U]; /* 中断使能寄存器 */
	uint32_t RESERVED0[24U];
	__IOM uint32_t ICER[8U]; /* 中断清除使能寄存器 */
	uint32_t RSERVED1[24U];
	__IOM uint32_t ISPR[8U]; /* 中断使能挂起寄存器 */
	uint32_t RESERVED2[24U];
	__IOM uint32_t ICPR[8U]; /* 中断解挂寄存器 */
	uint32_t RESERVED3[24U];
	__IOM uint32_t IABR[8U]; /* 中断有效位寄存器 */
	uint32_t RESERVED4[56U];
	__IOM uint8_t IP[240U]; /* 中断优先级寄存器(8Bit 位宽) */
	uint32_t RESERVED5[644U];
	__OM uint32_t STIR; /* 中断触发中断寄存器 */
} NVIC_Type;
  • ISER[8]: Interrupt Set Enable Registers,中断使能寄存器组,CM4内核支持256个中断,8个32位寄存器控制,一个位控制一个中断,使能某个中断,必须设置相应的 ISER 位为 1。对于STM32F407来说,可屏蔽中断只有82个,有用的是ISER[0~3]中的一部分,其中:
    ISER[0]的 bit0 ~ 31 对应中断 0 ~ 31;
    ISER[1]的 bit0 ~ 31 对应中断 32 ~ 63;
    ISER[2]的 bit0 ~ 16 对应中断 64 ~ 81。
  • ICER[8]: Interrupt Clear Enable Registers,中断除能寄存器组。该寄存器组与 ISER 的作用恰好相反,是用来清除某个中断的使能的,其对应位的功能,也和 ISER 一样。这里要专门设置一个 ICER 来清除中断位,而不是向 ISER 写 0 来清除,是因为** NVIC 的这些寄存器都是写 1 有效的,写 0 是无效的。**
  • ISPR[8]: Interrupt Set Pending Registers,中断使能挂起控制寄存器组。通过置 1,可以将正在进行的中断挂起,而执行同级或更高级别的中断。写 0 是无效的。
  • ICPR[8]: Interrupt Clear Pending Registers,中断解挂控制寄存器组。其作用与 ISPR 相反,对应位也和 ISER 是一样的。通过设置 1,可以将挂起的中断解挂。写 0 无效。
  • IABR[8]: Interrupt Active Bit Registers,中断激活标志位寄存器组。对应位所代表的中断和 ISER 一样,如果为 1,则表示该位所对应的中断正在被执行。这是一个只读寄存器,通过它可以知道当前在执行的中断是哪一个。在中断执行完了由硬件自动清零。
  • IP[8]: Interrupt Priority Registers,中断优先级控制的寄存器组。IP 寄存器组由 240 个 8bit的寄存器组成,每个可屏蔽中断占用 8bit,这样总共可以表示 240 个可屏蔽中断。而 STM32F407只用到了其中的 82 个。
    其中IP[81] ~ IP[0]分别对应中断 81 ~ 0。而每个可屏蔽中断占用的 8bit 并没有全部使用,而是只用了高 4 位。这 4 位,又分为抢占优先级和子优先级(响应优先级)。抢占优先级在前,子优先级在后。而这两个优先级各占几个位又要根据 SCB->AIRCR 中的中断分组设置来决定。

NVIC工作原理

STM32 第13讲 中断(NVIC/EXTI/如何使用中断/HAL库中断回调机制/实验)_第4张图片

中断优先级

  • 抢占优先级:抢占优先级高的中断可以打断正在执行的抢占优先级低的中断。

  • 响应优先级:抢占优先级相同,响应优先级高的中断不能打断响应优先级低的中断。

  • 自然优先级:还有一种情况就是当两个或者多个中断的抢占式优先级和响应优先级相同时,那么就遵循自然优先级,看中断向量表的中断排序,数值越小,优先级越高。

  • 在 NVIC 中由寄存器 NVIC_IPR0-NVIC_IPR59 共 60 个寄存器控制中断优先级,每个寄存器的每 8 位又分为一组,可以分 4 组,所以就有了 240 组宽度为 8bit 的中断优先级控制寄存器,原则上每个外部中断可配置的优先级为 0~255,数值越小,优先级越高。但是实际上 M3 /M4/M7 芯片为了精简设计,只使用了高四位[7:4],低四位取零,这样以至于最多只有 16 级中断嵌套,即 2^4=16。

  • 5种中断优先级的分组
    一个工程中一般只设置一次中断优先级分组。
    STM32 第13讲 中断(NVIC/EXTI/如何使用中断/HAL库中断回调机制/实验)_第5张图片
    以优先级分组1为例:1位抢占优先级可以有2 ^1 = 2个抢占优先级,3位响应优先级可以有2 ^3 = 8个响应优先级。

NVIC的使用

配置步骤

  • 设置中断分组
AIRCR[10:8]				/* 寄存器 */
HAL_NVIC_SetPriorityGrouping		/* 函数 */


/**
  * @brief  Sets the priority grouping field (preemption priority and subpriority)
  *         using the required unlock sequence.
  * @param  PriorityGroup The priority grouping bits length. 
  *         This parameter can be one of the following values:
  *         @arg NVIC_PRIORITYGROUP_0: 0 bits for preemption priority
  *                                    4 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_1: 1 bits for preemption priority
  *                                    3 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_2: 2 bits for preemption priority
  *                                    2 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_3: 3 bits for preemption priority
  *                                    1 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_4: 4 bits for preemption priority
  *                                    0 bits for subpriority
  * @note   When the NVIC_PriorityGroup_0 is selected, IRQ preemption is no more possible. 
  *         The pending IRQ priority will be managed only by the subpriority. 
  * @retval None
  */
void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
{
  /* Check the parameters */
  assert_param(IS_NVIC_PRIORITY_GROUP(PriorityGroup));
  
  /* Set the PRIGROUP[10:8] bits according to the PriorityGroup parameter value */
  NVIC_SetPriorityGrouping(PriorityGroup);
}
  • 设置中断优先级
IPRx bit[7:4]				/* 寄存器 */
HAL_NVIC_SetPriority		/* 函数 */

/**
  * @brief  Sets the priority of an interrupt.
  * @param  IRQn External interrupt number.
  *         This parameter can be an enumerator of IRQn_Type enumeration
  *         (For the complete STM32 Devices IRQ Channels list, please refer to the appropriate CMSIS device file (stm32f4xxxx.h))
  * @param  PreemptPriority The preemption priority for the IRQn channel.
  *         This parameter can be a value between 0 and 15
  *         A lower priority value indicates a higher priority 
  * @param  SubPriority the subpriority level for the IRQ channel.
  *         This parameter can be a value between 0 and 15
  *         A lower priority value indicates a higher priority.          
  * @retval None
  */
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
{ 
  uint32_t prioritygroup = 0x00U;
  
  /* Check the parameters */
  assert_param(IS_NVIC_SUB_PRIORITY(SubPriority));
  assert_param(IS_NVIC_PREEMPTION_PRIORITY(PreemptPriority));
  
  prioritygroup = NVIC_GetPriorityGrouping();
  
  NVIC_SetPriority(IRQn, NVIC_EncodePriority(prioritygroup, PreemptPriority, SubPriority));
}
  • 使能中断
ISERx				/* 寄存器 */
HAL_NVIC_EnableIRQ		/* 函数 */

/**
  * @brief  Enables a device specific interrupt in the NVIC interrupt controller.
  * @note   To configure interrupts priority correctly, the NVIC_PriorityGroupConfig()
  *         function should be called before. 
  * @param  IRQn External interrupt number.
  *         This parameter can be an enumerator of IRQn_Type enumeration
  *         (For the complete STM32 Devices IRQ Channels list, please refer to the appropriate CMSIS device file (stm32f4xxxx.h))
  * @retval None
  */
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn)
{
  /* Check the parameters */
  assert_param(IS_NVIC_DEVICE_IRQ(IRQn));
  
  /* Enable interrupt */
  NVIC_EnableIRQ(IRQn);
}

EXTI

概念特性简介

STM32 第13讲 中断(NVIC/EXTI/如何使用中断/HAL库中断回调机制/实验)_第6张图片

  • External interruput/event Controller, 外部中断事件控制器

  • 包含20个产生事件/中断请求的边沿检测器,即共20条EXIT线(对于F1系列)
    中断:要进入NVIC,有相应的中断服务函数,需要CPU处理
    事件:不进入NVIC,仅用于内部硬件自动控制的,如:TIM、DMA、ADC

  • 主要特性
    每条EXTI线都可以单独配置:选择类型(中断或者事件)、触发方式(上升沿,下降沿或者双边沿触发)、支持软件触发、开启/屏蔽、有挂起状态位

工作原理

STM32 第13讲 中断(NVIC/EXTI/如何使用中断/HAL库中断回调机制/实验)_第7张图片

  • ①是一个边沿检测电路,包括边沿检测电路,上升沿触发选择寄存器(EXTI_RTSR)和下降沿触发选择寄存器(EXTI_FTSR)。边沿检测电路以输入线作为信号输入端,如果检测到有边沿跳变就输出有效信号‘1’,就输出有效信号‘1’到标号②部分电路,否则输入无效信号‘0’。
  • ②是一个或门电路,它的两个信号输入端分别是软件中断事件寄存器(EXTI_SWIER)和边沿检测电路的输入信号。或门电路只要输入端有信号‘1’,就会输出‘1’,所以就会输出‘1’到标号③电路和标号④电路。通过对软件中断事件寄存器的读写操作就可以启动中断/事件线,即相当于输出有效信号‘1’到或门电路输入端。
  • ③是一个与门电路,它的两个信号输入端分别是中断屏蔽寄存器(EXTI_IMR)和标号②电路输出信号。与门电路要求输入都为‘1’才输出‘1’,这样子的情况下,如果中断屏蔽寄存器(EXTI_IMR)设置为 0 时,不管从标号②电路输出的信号特性如何,最终标号③电路输出的信号都是 0;假如中断屏蔽寄存器(EXTI_IMR)设置为 1 时,最终标号③电路输出的信号才由标号②电路输出信号决定,这样子就可以简单控制 EXTI_IMR 来实现中断的目的。标号④电路输出‘1’就会把请求挂起寄存器(EXTI_PR)对应位置 1。
  • ④电路输出有效信号 1 就会使脉冲发生器电路产生一个脉冲,而无效信号就不会使其产生脉冲信号。脉冲信号产生可以给其他外设电路使用,例如定时器,模拟数字转换器等,这样的脉冲信号一般用来触发 TIM 或者 ADC 开始转换。

EXTI与IO的映射关系

STM32 第13讲 中断(NVIC/EXTI/如何使用中断/HAL库中断回调机制/实验)_第8张图片

SYSCFG, System configuration controller, 系统配置控制器
STM32 第13讲 中断(NVIC/EXTI/如何使用中断/HAL库中断回调机制/实验)_第9张图片
配置SYSCFG寄存器之前要使能SYSCFG时钟

__HAL_RCC_SYSCFG_CLK_ENABLE(); 

使用中断

使用中断流程

STM32 第13讲 中断(NVIC/EXTI/如何使用中断/HAL库中断回调机制/实验)_第10张图片

外部中断配置步骤

  • 使能GPIO时钟
  • 设置GPIO输入模式
  • 使能SYSCFG时钟
  • 设置EXIT与IO的映射关系
  • 设置EXIT屏蔽
  • 设置NVIC
  • 设计中断服务函数
    使用HAL库中的HAL_GPIO_Init 2~5步一次性配置完成
    **STM32仅有:EXTI0~4、EXTI9_5、EXTI15_10,7个外部中断服务函数
    **

HAL库中断回调机制简介

STM32 第13讲 中断(NVIC/EXTI/如何使用中断/HAL库中断回调机制/实验)_第11张图片

实验

通过 KEY0 控制 LED0 的亮灭

硬件

STM32 第13讲 中断(NVIC/EXTI/如何使用中断/HAL库中断回调机制/实验)_第12张图片
PE4 IO初始化:输入上拉、下降沿触发

程序

-exti.h

#ifndef __EXIT_H
#define __EXIT_H
# include "./SYSTEM/sys/sys.h"

void exti_init(void);

#endif

-exti.c

#include "./BSP/EXTI/exti.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
void exti_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;
    __HAL_RCC_GPIOE_CLK_ENABLE();

    gpio_init_struct.Pin = GPIO_PIN_4;                  
    gpio_init_struct.Mode = GPIO_MODE_IT_FALLING;         
    gpio_init_struct.Pull = GPIO_PULLUP;                   
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;        
    HAL_GPIO_Init(GPIOE, &gpio_init_struct);  
    
    HAL_NVIC_SetPriority(EXTI4_IRQn,2,0);
    HAL_NVIC_EnableIRQ(EXTI4_IRQn);
}

void EXTI4_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    delay_ms(20);
    if(GPIO_Pin == GPIO_PIN_4)
    {
        if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4) == 0)
        {
            LED0_TOGGLE();
        }
    }
}

  • main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "/BSP/EXTI/exti.h"


int main(void)
{
    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);         /* 设置时钟,168Mhz */
    delay_init(168);                            /* 延时初始化 */
    led_init();                                 /* 初始化LED */
    exti_init();

    while(1)
    {                          
        LED1(1);                        
        delay_ms(500);
        LED1(0);                              
        delay_ms(500);
    }
}

你可能感兴趣的:(单片机学习笔记,单片机,stm32,嵌入式硬件)