Skip to content

中断

image-20231121195841744

中断有优先级, 数字越小优先级越高

  • 系统异常: 内核水平
  • 外部中断: 外设水平

有10个系统异常, 60个外设中断, 对应一个中断向量表, 保存在启动文件里面

image-20230628180451294

image-20230628180512907

控制NVIC

NVIC: 嵌套向量中断控制器, 属于内核外设, 支持256个中断(16个内核中断, 240个外部中断), 最多支持256个优先级(可以剪裁), ST只使用了16个

image-20231121200314188

image-20231121201014795

还有一些中断挂起, 解挂, 激活标志位等不常用的功能寄存器

工作原理

image-20231121201124666

首先判断中断是否开启, 之后根据AIRCR寄存器里面的分组方式进行控制进一步操作, 判断运行的顺序

内核中断不通过外部寄存器的开关判断, 但是经过相同的评级

在文件STM32F10xxxx Cortex-M3编程手册中

内核的寄存器定义在core_cm3.h文件

c
typedef struct
{
  __IO uint32_t ISER[8];                      /*!< Offset: 0x000  Interrupt 使能寄存器           */
       uint32_t RESERVED0[24];                                   
  __IO uint32_t ICER[8];                      /*!< Offset: 0x080  Interrupt 清除         */
       uint32_t RSERVED1[24];                                    
  __IO uint32_t ISPR[8];                      /*!< Offset: 0x100  Interrupt 使能悬起          */
       uint32_t RESERVED2[24];                                   
  __IO uint32_t ICPR[8];                      /*!< Offset: 0x180  Interrupt 清除悬起        */
       uint32_t RESERVED3[24];                                   
  __IO uint32_t IABR[8];                      /*!< Offset: 0x200  Interrupt 有效位寄存器           */
       uint32_t RESERVED4[56];                                   
  __IO uint8_t  IP[240];                      /*!< Offset: 0x300  Interrupt 中断优先级寄存器,8位有效 */
       uint32_t RESERVED5[644];                                  
  __O  uint32_t STIR;                         /*!< Offset: 0xE00  软件触发中断寄存器     */
}  NVIC_Type;

经过裁剪之后STM公司把它放在misc.h文件中

优先级设定

实际操作的是NVIC->IPRx寄存器, 一个寄存器8位

使用STM只使用高四位共64种, 又将它分为主优先级, 子优先级, 数字越小优先级越高, 都相等比较硬件中断编号

优先级分为抢占优先级以及响应优先级

抢占优先级可以对低优先级的中断函数进行打断

响应优先级不能进行打断, 抢占优先级相同的时候响应优先级比较高的先执行

优先级相同的时候通过自然优先级进行排序

优先级还会进行分组, 设置为五个组, 控制寄存器表示的方法(主优先级和子优先级分别由几位进行控制)

image-20230628183435658

编程顺序

  • 请求使能中断

外设使能, 控制外设相关寄存器 + 总开关NVIC中断使能寄存器

  • 配置优先级分组 有专门的函数进行配置

  • 配置NVIC寄存器

c
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;
  • 编写中断服务函数

在中断向量表里面找到对应的函数名称

中断函数写错了会执行启动文件中的, 陷入循环, 函数全部在说stm32F10x_it.h文件中重新定义为空函数

实际使用

使用寄存器SCB->AIRCR:PRIGROUP[10:8](系统控制块(System Control Block))控制分组的模式

实现GPIO中断(外部中断)

EXTI

EXTI(External interupt/event Controller外部中断/事件控制器)控制, 对应的是上面的中断函数中的EXTI触发的中断

在H7里面为Extended interupt/event Controller扩展中断事件控制器

有二十个产生事件.中断请求的边沿检测器, 即有二十个EXTI线(F1)

中断和事件的理解:

中断: 进入NVIC, 有相应的中断处理函数, 需要CPU进行处理

事件: 不进入NVIC, 只用于内部硬件的自动控制, 比如TIM, DMA, ADC

image-20231121204307002

H7里面有89条, 主要是各种的唤醒事件, 为的是降低功耗

主要特性

  • F1/F4/F7

每一条线都可以单独的进行配置, 选择触发的是事件还是中断, 触发的方式(上升沿, 下降沿, 双边沿触发), 支持软件触发, 开启/屏蔽, 有挂起状态位(中断)

  • H7系列

对EXTI产生的事件分为可配置事件以及直接事件

可配置事件跟上面的类似(来自产生脉冲的I/O或者外设的信号)

直接事件(来自其他的外设中断唤醒源, 需要再外设的中断进行清除): 固定上升沿触发, 不支持软件触发, 无挂起状态位(由其他的外设提供)

工作原理

  • F1/F4/F7

image-20231123214517394

image-20230628193616469

中断为上面的一路, 最终的信号通入NVIC控制器

事件为下面的一路

①是输入线, EXTI有十九个中断/事件输入线, 输入线通过寄存器设置为任意一个GPIO, 也可以是一些外设的时间, 一般是存在电平变化的信号

②是一个检测电路, 根据上升沿或下降沿选择寄存器, 对应的位设置控制信号触发, 测到有边沿跳变就左侧输出有效信号 1 给编号 3 电路,否则输出无效信号 0。

③软件中断事件控制器EXTI_SWIER, 设置为1则与门设置为1, 之后会控制EXTI_PR寄存器对应的位为1设置为挂起, 之后会输出1

④EXIT_IMR中断屏蔽寄存器控制位, 设置为1的时候开放请求

⑤发送到内核产生中断

⑥EXTI_EMR事件屏蔽寄存器控制, 写1使能, 来到脉冲发生器, 产生脉冲, 之后会触发ADC产生信号, 或触发定时器等

image-20231123214653457

下降沿触发选择寄存器(EXTI_FTSR)和上面的类似

image-20231123214757504

image-20231123214919899

  • H7

image-20231123215123970

PWR: 电源管理模块

image-20231123215640457

EXTI和实际的GPIO的对应(AFIO-F1, SYSCFG-F4/F7/H7)

AFIO: 复用功能I/O和调试配置, 主要是用来进行重映射和外部中断的配置

  1. 调试IO的配置: AFIO_MAPR[26:24], 配置JTAG和SWD的开关状态
  2. 重映射配置: AFIO_MAPR, 部分外设IO的重映射配置
  3. 外部中断配置: AFIO_EXTICR1-4, EXTI的每根线具体对应的IO

配置之前需要使能时钟

SYSCFG: 系统时钟配置控制器, 用于外部中断的配置

使用的是SYSCFG_EXTICR1-4配置实际对应的具体IO

配置之前也需要使能时钟

EXTI可以控制20根外部的终端线, 每个 GPIO 都可以被设置为输入线,占用 EXTI0 至 EXTI15,还有另 外七根用于特定的外设事件,见表 EXTI 中断 _ 事件线 。有十六根外部中断线连接到EXTI的一根线上

image-20230628200901595

image-20231125195051787

固件库

使用EXIT_InitTypeDef函数进行实现

c
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;

这里控制的是上面的框图里面的寄存器

之后使用void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);函数进行设置实际使用的引脚

思路总理

首先设置GPIO的触发中断的引脚使用函数GPIO_EXTILineConfig, 之后产生的信号会进入EXTI, 由EXTI初始化函数确定中断的进一步选择是触发中断还是产生事件, 最后进入NVIC在配置优先级之后会触发中断

  1. 初始化引脚为输入, 打开时钟
  2. 选择引脚复用, 由于属于引脚复用打开AFIO时钟(对寄存器AFIO_EVCR,AFIO_MAPR和AFIO_EXTICRX进行读写操作前,应当首先打开AFIO 的时钟。参考第6.3.7节APB2外设时钟使能寄存器(RCC_APB2ENR)。)
  3. 初始化EXTI
  4. 初始化NVIC
c
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
    //配置优先级分组
  /* Check the parameters */
  assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
  
  /* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */
  SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}

在初始化NVIC的通道的时候前四个EXTI有单独的通道, 后面的会使用这种定义

image-20230630154013370

区分不同的中断源需要进行相应的判断,通过取读EXTI->PR寄存器来判

  1. 中断服务函数

首先检查中断标志位, 之后处理, 最后清除标志位

c
//声明为静态函数, 只能在本文件中使用, 其他文件可以有同名字的函数, 如果声明的是变量之后不可以使用extern在其他文件调用
static void EXTI_NVIC_Config(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}


void EXTI_Key_config(void)
{


	//开启时钟
	RCC_APB2PeriphClockCmd(KEY1_GPIO_CLOCK, ENABLE);
	//初始化引脚
	GPIO_InitTypeDef GPIO_InitStructure;
	//初始化NVIC
	EXTI_NVIC_Config();
	
	
	GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
		
	GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);
	//复用引脚,选择输入线
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
	//初始化引脚
	EXTI_InitTypeDef EXTI_InitStruct;
	EXTI_InitStruct.EXTI_Line = EXTI_Line0;	//设置为EXTI0
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;	//使能
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;	//设置为中断
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿触发
	EXTI_Init(&EXTI_InitStruct);
}

连接多个引脚相同的时候可以检测IDR寄存器