外部中断EXTI

1. 外部中断基础知识

1.1 STM32外部中断框架

中断的概念:在主程序运行过程中,出现了特定的中断触发条件,使得CPU暂停当前正在运行的程序,转而去处理中断程序处理完后又返回原来被暂停的位置继续运行
在这里插入图片描述

  • 20条外部中断线
  • 十六条是GPIO的中断线(0~15),同一个引脚号参与同一个line
  • GPIO的中断线要启用必须先要配置AFIO的寄存器EXTICRx
    在这里插入图片描述

1.2 STM32外部中断机制框架

在这里插入图片描述

  1. 边沿检测(中断发生条件)上升沿 (低电平往高电平的变化),下降沿 或者 双边沿 (只要电平发生变化就触发)
  2. 软件配置中断或者事件寄存器
  3. 屏蔽中断寄存器事件寄存器
  4. 请求挂起寄存器 (不工作)
  5. 中断发送给NVIC中断控制器 (管理先做哪一个中断)
  6. 事件则产生一个脉冲响应

2. 复用功能

处理器的引脚本身默认就是一个普通的GPIO,但是它还可以被复用成其他功能,我们称之为一个引脚的复用功能
在这里插入图片描述
在这里插入图片描述

3. 重映射

重映射属于复用功能的另外一个功能,可以把具有特殊功能的引脚分配到其他引脚上去
如果某个功能被重映射了,那么这个功能将不再遵循其默认的默认分配
在这里插入图片描述

比如STM32F1xx手册中第120页中:
串口1 发送和接收默认引脚 PA9PA10,当 USART1_REMAP = 1 时,发送和接收引脚为 PB6PB7
(比如 PA9 和 PA10 已经当做普通引脚使用了,这个时候需要USART1的功能,可以配置PB6和PB7成为USART1 的功能来使用)
在这里插入图片描述

4. 嵌套向量中断控制器NVIC

4.1 中断向量表

Cortex-M3内核支持256个中断,其中包含了16个内核中断(异常)240个外部中断,并且具有256级的可编程中断设置。但是,STM32并没有使用CM3内核的全部东西,而是只用了它的一部分STM32有84个中断,包括16个内核中断(异常)68个可屏蔽中断,具有16级可编程的中断优先级。而STM32F103系列上面,16个内核中断(异常)不变,而可屏蔽中断只有60个(在107系列才有68个)。

优先级号越小,优先级越高。(优先级负数为内核中断)
STM32F1xx手册中第130页中:
在这里插入图片描述

4.2 中断优先级分组

这60个中断,怎么管理呢?这就涉及到STM32的中断分组。STM32可以将中断分成5个组分别称为组0~4,同时,对每个中断设置一个抢占优先级响应优先级分组设置是由SCB->AIRCR寄存器的bit10~8来定义的。SCB->AIRCR是在哪里的呢?由于这是CM3内核定义的。
在这里插入图片描述
在这里插入图片描述

CM3中定义了8个Bit用于设置中断源的优先级,而STM32只选用其中的4个Bit 抢占优先级的级别高于响应优先级,而数值越小所代表的的优先级越高,介绍一下抢占优先级、响应优先级的区别:

  • 高优先级的抢占优先级可以打断正在进行的低抢占优先级中断的;
  • 抢占优先级相同的中断高响应优先级不可以打断低响应优先级的中断
  • 抢占优先级相同的中断当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行
  • 如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行

除此之外还有两点需要注意:

  • 打断的情况只会与抢占优先级有关和响应优先级无关!(中断嵌套)
  • 一般情况下,系统代码执行过程中,只设置一次中断优先级分组,比如分组2设置好分组之后一般不会再改变分组。随意改变分组会导致中断优先级配置混乱影响系统稳定性

4.3 中断优先级控制函数结构体

  • 中断优先级控制函数
    • NVIC_SetPriorityGrouping()
  • 中断优先级控制结构体参数
    • NVIC_IRQChannel定义初始化的是哪一个中断,这个可以在stm32f10x.h文件中查到每个中断对应的名字,例如UART1_IRQn
    • NVIC_IRQChannelPreemptionPriority:定义此中断的 抢占优先级级别。
    • NVIC_IRQChannelSubPriority:定义此中断的 响应优先级级别。
    • NVIC_IRQChannelCmd:该中断是否使能。
  • NVIN_Init()函数初始化NVIC寄存器

4.4 中断优先级设置步骤

1、系统运行后先设置中断优先级分组,调用函数。
2、针对每个中断,设置对应的抢占优先级响应优先级
3、如果需要挂起或恢复某个中断,应首先查询该中断当前的激活状态,然后根据状态分别调用对应的挂起(suspend)恢复(resume)函数

5. 外部中断按键控制LED灯

按键控制LED的开发流程:
第一步:使能功能复用时钟
在这里插入图片描述
第二步,配置复用寄存器
第三步,配置中断屏蔽寄存器

固件库按键控制LED灯

外部中断EXTI结构体:
typedef struct
{
  uint32_t EXTI_Line;	// 外部中断线(比如 GPIOA 0~15)
   
  EXTIMode_TypeDef EXTI_Mode;	// 外部中断模式(中断, 事件)

  EXTITrigger_TypeDef EXTI_Trigger;	// 边沿检测(触发条件:上升沿,下降沿,双边沿)

  FunctionalState EXTI_LineCmd;	// 表示外部中断线的使能或禁用状态
}EXTI_InitTypeDef;


外部中断EXTI相关库函数:
void EXTI_DeInit(void);
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);
void EXTI_ClearFlag(uint32_t EXTI_Line);
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);

软件流程设计

  • 初始化系统
    • 初始化GPIO、EXTI外设时钟
    • 初始化按键和LED引脚
    • 初始化EXTI外部中断条件
    • 初始化NVIC嵌套中断控制器
  • 编写外部中断函数
    • 判断中断发生控制LED灯

WEAK: 弱定义。
如果用户没有自己定义的中断函数,则系统默认执行系统自定义的中断函数
如果用户定义了中断函数,则优先执行用户的中断函数

// exti.h 头文件
void Exti_Init(void);
// exti.h 源文件
#include "stm32f10x.h"
#include "exti.h"

// 初始化GPIO按键 EXTI外部中断 NVIC中断控制器 函数
void Exti_Init(void)
{
	GPIO_InitTypeDef GPIO_initstruct;	// 定义GPIO结构体变量
	EXTI_InitTypeDef EXTI_initstruct;	// 定义外部中断EXTI结构体变量
	NVIC_InitTypeDef NVIC_initstruct; // 定义NVIC嵌套中断控制器结构体变量
	
	// 初始化时钟, GPIOA 和 AFIO外部中断时钟使能
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
	
	// 优先级分组配置 2位配置抢占优先级 2位配置响应优先级
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	GPIO_initstruct.GPIO_Pin = GPIO_Pin_0;		// 初始化A0引脚
	GPIO_initstruct.GPIO_Mode = GPIO_Mode_IPU; // 初始化A0为上拉输入模式
	GPIO_Init(GPIOA, &GPIO_initstruct); 	  // 初始化A0结构体
	// GPIOA0变成外部中断功能
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
	
	EXTI_initstruct.EXTI_Line = EXTI_Line0;	// 初始化外部中断线0
	EXTI_initstruct.EXTI_Mode = EXTI_Mode_Interrupt; // 初始化外部中断模式
	EXTI_initstruct.EXTI_Trigger = EXTI_Trigger_Falling; // 初始化下降沿触发条件
	EXTI_initstruct.EXTI_LineCmd = ENABLE;	// 外部中断线使能
	EXTI_Init(&EXTI_initstruct);	// 初始化EXTI结构体
	
	NVIC_initstruct.NVIC_IRQChannel = EXTI0_IRQn;	// 初始化中断线0
	NVIC_initstruct.NVIC_IRQChannelPreemptionPriority = 0; // 初始化抢占优先级
	NVIC_initstruct.NVIC_IRQChannelSubPriority = 0;	// 初始化响应优先级
	NVIC_initstruct.NVIC_IRQChannelCmd = ENABLE;	// 该中断使能
	NVIC_Init(&NVIC_initstruct);	// 初始化 NVIC寄存器
}
// main.c
#include "stm32f10x.h"
#include "main.h"
#include "LED.h"
#include "exti.h"

void delay(uint16_t time)
{
	uint16_t i=0;
	while(time--)
	{
		i = 12000;
		while(i--);
	}
}

int  main()
{
	LED_Init();
	Exti_Init();
	GPIO_SetBits(GPIOA, GPIO_Pin_1);
	
	while(1)
		{
			
		}
}

// 外部中断函数, 主要满足完整的中断触发条件, 函数就会由硬件自动执行
// "中断配置条件" 必须同时包含 EXTI配置 + NVIC使能
void EXTI0_IRQHandler()
{
	// 如果检查到外部中断线(EXTI0)发生了中断请求
	if(EXTI_GetITStatus(EXTI_Line0) != RESET)
	{
		GPIO_ResetBits(GPIOA, GPIO_Pin_1);
		delay(1000);
		GPIO_SetBits(GPIOA, GPIO_Pin_1);
		delay(1000);
		
		// 清除外部中断线(EXTI0)的中断挂起标志位, 方便下次还能运行函数
		EXTI_ClearITPendingBit(EXTI_Line0);
	}
}
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐