STM32的中断概念-从EXTI到NVIC由外到内一文搞清中断概念-EXTI的初始化和NVIC的初始化-按键中断实验

1.概念

为什么引入中断?

中断是为使单片机具有对外部或内部随机发生的事件实时处理而设置的,中断功能的存在,很大程度上提高了单片机处理外部或内部事件的能力。

中断系统特点

①分时操作。CPU 可以分时为多个 I/O 设备服务,提高了计算机的利用率;
②实时响应。CPU 能够及时处理应用系统的随机事件,系统的实时性大大增强;
③可靠性高。CPU 具有处理设备故障及掉电等突发性事件能力,从而使系统可靠性提高

中断过程

对于单片机来讲,中断是指CPU在处理某一时间A时,发生了另一事件B请求CPU立刻去处理(中断发生或中断请求);CPU暂时停止当前的工作(中断响应),转而去处理事件B(中断服务),待CPU处理事件B完成后,再回到原来事件A被中断的地方继续处理事件A(中断返回)。这一过程称为中断,注意是整个过程,而不是单一的停止一件事的意思。# 中断和异常!!!
STM32F103 在内核水平上搭载了一个异常响应系统, 支持为数众多的系统异常和外部中断。
其中系统异常有 8 个(如果把 Reset 和 HardFault 也算上的话就是 10 个),外部中断有 60个。除了个别异常的优先级被定死外,其它异常的优先级都是可编程的。有关具体的系统异常和外部中断可在标准库文件 stm32f10x.h 这个头文件查询到,在 IRQn_Type 这个结构体里面包含了 F103 系列全部的异常声明。
我们以STM32为例来讲解中断,提到中断就不得不提到EXTI和NVIC了,前者是单片机的一个外设,而NVIC一般是在内核中。

2.NVIC—嵌套向量中断控制器

NVIC 中文名字嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对 Cortex-M3 内核里面的 NVIC 进行裁剪,把不需要的部分去掉,所以说 STM32 的 NVIC 是 Cortex-M3 内核的 NVIC 的一个子集。
一般在芯片手册中会给出芯片的中断向量表。
以下是关于NVIC的几个概念。

1.中断优先级

NVIC允许为不同的中断设置优先级。优先级决定了中断的相对顺序,当多个中断同时发生时,具有较高优先级的中断将先被处理。NVIC通常支持多级优先级,可以进一步细化中断的优先级设置。
在 NVIC 有一个专门的寄存器:中断优先级寄存器 NVIC_IPRx,用来配置外部中断的优先级,IPR 宽度为 8bit,原则上每个外部中断可配置的优先级为 0~255,数值越小,优先级越高。但是绝大多数 CM3 芯片都会精简设计,以致实际上支持的优先级数减少,在F103 中,只使用了高 4bit,如下所示
在这里插入图片描述
用于表达优先级的这 4bit,又被分组成抢占优先级和子优先级。如果有多个中断同时响应,抢占优先级高的就会 抢占 抢占优先级低的优先得到执行,如果抢占优先级相同,就比较子优先级。如果抢占优先级和子优先级都相同的话,就比较他们的硬件中断编号,编号越小,优先级越高。
优先级分组
优先级分组真值表:
在这里插入图片描述

2.中断向量表

中断向量表是一个存储中断处理程序地址的表格。NVIC负责管理中断向量表,并在中断发生时根据中断号找到相应的中断处理程序地址。每个中断在中断向量表中占据一个位置,该位置存储了相应中断处理程序的地址。
下图是STM32F103产品的中断向量表
在这里插入图片描述在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述

3.中断嵌套

NVIC支持中断嵌套功能,即当一个中断正在处理时,如果更高优先级的中断请求发生,处理器可以中断当前中断的处理,转而处理更高优先级的中断。这种中断嵌套机制允许处理多个并发的中断事件,并确保较高优先级的中断得到及时处理。

4.中断处理程序

中断处理程序是与特定中断相关联的代码块。当中断请求被响应时,NVIC会跳转到相应中断处理程序的地址,执行其中断处理代码。中断处理程序负责处理中断事件,并在处理完毕后返回到原来的执行点。

3.EXTI—外部中断/事件控制器

EXTI(External interrupt/event controller)—外部中断/事件控制器,管理了控制器的 20个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性
中断控制器和外设的配合:STM32微控制器的外设通常具有自己的中断控制器和中断请求。外设通过中断请求线将外设通过中断请求线将中断信号发送给中断控制器。中断控制器负责接收和处理外设的中断请求,并将其映射到相应的中断向量表中的中断号。处理器通过NVIC与中断控制器协作,根据中断号找到对应的中断处理程序,并执行相应的操作。

1.EXTI的功能框图

在这里插入图片描述

2.外部中断线选择

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

中断线的选择如图:
在这里插入图片描述
我们使用EXTI时要用到GPIO的复用功能,开启时钟时不要忘了

3.EXTI的寄存器

中断屏蔽寄存器(EXTI_IMR)

在这里插入图片描述

事件屏蔽寄存器(EXTI_EMR)

在这里插入图片描述

上升沿触发选择寄存器(EXTI_RTSR)

在这里插入图片描述

下降沿触发选择寄存器(EXTI_FTSR)

在这里插入图片描述

软件中断事件寄存器(EXTI_SWIER)

在这里插入图片描述

挂起寄存器(EXTI_PR)

在这里插入图片描述

4. 中断回调函数实现

在启动文件 startup_stm32f10x_hd.s 中我们预先为每个中断都写了一个中断服务函数,只是这些中断函数都是为空,为的只是初始化中断向量表。实际的中断服务函数都需要我们重新编写,为了方便管理我们把中断服务函数统一写在 stm32f10x_it.c 这个库文件中。

当发生中断时,系统自动执行中断处理函数。

void KEY1_IRQHandler(void)
{
//判断是否发生中断
	if(EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET) 
	{
//发生中断,进行操作
		LED1_TOGGLE;
//清除中断标志位
		EXTI_ClearITPendingBit(KEY1_INT_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);   //清除中断标志位

#NVIC 中断配置固件库函数

void NVIC_EnableIRQ(IRQn_Type IRQn) 使能中断
void NVIC_DisableIRQ(IRQn_Type IRQn) 失能中断
void NVIC_SetPendingIRQ(IRQn_Type IRQn) 设置中断悬起位
void NVIC_ClearPendingIRQ(IRQn_Type IRQn) 清除中断悬起位
uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) 获取悬起中断编号
void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) 设置中断优先级  用到!
uint32_t NVIC_GetPriority(IRQn_Type IRQn) 获取中断优先级
void NVIC_SystemReset(void) 系统复位




void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
参数:EXTI_InitStruct
typedef struct
{
  uint32_t EXTI_Line;    // 外部中断线  
  EXTIMode_TypeDef EXTI_Mode;    //外部中断模式                                       
  EXTITrigger_TypeDef EXTI_Trigger;  //触发方式               
  FunctionalState EXTI_LineCmd;      //使能中断
}EXTI_InitTypeDef;

中断回调函数

中断回调函数命名在中断向量表钟在启动文件内
在这里插入图片描述

中断处理函数的名字叫法: 中断处理函数名字是有要求的去启动代码中找

中断处理函数写在stm32f10x_it.c文件中

WWDG_IRQHandler
PVD_IRQHandler
TAMPER_IRQHandler
RTC_IRQHandler
FLASH_IRQHandler
RCC_IRQHandler
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
DMA1_Channel1_IRQHandler
DMA1_Channel2_IRQHandler
DMA1_Channel3_IRQHandler
DMA1_Channel4_IRQHandler
DMA1_Channel5_IRQHandler
DMA1_Channel6_IRQHandler
DMA1_Channel7_IRQHandler
ADC1_2_IRQHandler
USB_HP_CAN1_TX_IRQHandler
USB_LP_CAN1_RX0_IRQHandler
CAN1_RX1_IRQHandler
CAN1_SCE_IRQHandler
EXTI9_5_IRQHandler
TIM1_BRK_IRQHandler
TIM1_UP_IRQHandler
TIM1_TRG_COM_IRQHandler
TIM1_CC_IRQHandler
TIM2_IRQHandler
TIM3_IRQHandler
TIM4_IRQHandler
I2C1_EV_IRQHandler
I2C1_ER_IRQHandler
I2C2_EV_IRQHandler
I2C2_ER_IRQHandler
SPI1_IRQHandler
SPI2_IRQHandler
USART1_IRQHandler
USART2_IRQHandler
USART3_IRQHandler
EXTI15_10_IRQHandler
RTCAlarm_IRQHandler
USBWakeUp_IRQHandler
TIM8_BRK_IRQHandler
TIM8_UP_IRQHandler
TIM8_TRG_COM_IRQHandler
TIM8_CC_IRQHandler
ADC3_IRQHandler
FSMC_IRQHandler
SDIO_IRQHandler
TIM5_IRQHandler
SPI3_IRQHandler
UART4_IRQHandler
UART5_IRQHandler
TIM6_IRQHandler
TIM7_IRQHandler
DMA2_Channel1_IRQHandler
DMA2_Channel2_IRQHandler
DMA2_Channel3_IRQHandler
DMA2_Channel4_5_IRQHandler

#按键中断实验
*** 中断都是由内核的NVIC处理的 配置外部处理器时不要忘了先配置NVIC***
下面用一个按键中断演示一下:

static void EXTI_NVIC_Config(void)
{
	NVIC_InitTypeDef NVIC_InitStruct;     //NVIC结构体定义
	//nvic优先级组1
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);   //配置优先级组
	NVIC_InitStruct.NVIC_IRQChannel=EXTI0_IRQn ;      //我用的是stm32VET6 按键一是用的GPIOA 0号引脚  所以选择EXTI0_IRQ外部中断线
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;  //配置抢占优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;   //配置子优先级
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;      //使能NVIC通道中的线
	NVIC_Init(&NVIC_InitStruct);       EXTI0_IRQn 
	
	NVIC_InitStruct.NVIC_IRQChannel=EXTI13_IRQn ;//   按键二   使用的是GPIOC  13号线
	NVIC_Init(&NVIC_InitStruct);     //初始化函数
}

void EXTI_Config(void) //可以在一个函数体内先配置NVIC然后配置EXTI ,搞清楚EXTI和NVIC的关系很关键
{
	GPIO_InitTypeDef GPIO_InitStruct;    //外部中断 也要用GPIO和内核通信
	EXTI_InitTypeDef EXTI_InitStruct;     //定义外部中断处理器结构体

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);// 打开按键一的时钟   挂载在APB2总线的
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO,ENABLE);  //开时钟
	//配置中断优先级
	EXTI_NVIC_Config();    //调用NVIC配置   内核中配置好处理优先级
	//配置按键1
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;  
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	//初始化exti    按键一  GPIOA0  --EXTI0
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);    //打开复用功能
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);    //配置GPIOA0为EXTI信号源
	EXTI_InitStruct.EXTI_Line=EXTI_Line0;	     //----EXTI0    --PA0  PB0  PC0   PD0  PE0
	EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;
	EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Rising;
	EXTI_InitStruct.EXTI_LineCmd=ENABLE;
	EXTI_Init(&EXTI_InitStruct);
	//按键二 
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_13;  
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	GPIO_Init(GPIOC,&GPIO_InitStruct);
	//初始化exti
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource13);
	EXTI_InitStruct.EXTI_Line=EXTI_Line13;	          //EXTI13    ___PB13
	EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;
	EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Rising;
	EXTI_InitStruct.EXTI_LineCmd=ENABLE;
	EXTI_Init(&EXTI_InitStruct);
}

中断回调函数一般放在stm32f10x_it.c里,其实哪都行。
这些终端回调函数一般都是设置好的,存放在启动文件里,需要用的时候去找。

void EXTI0_IRQHandler(void)
{
	if(EXTI_GetFlagStatus(EXTI_Line0)!=RESET)
	{
		LED_G_TOGGLE;
		EXTI_ClearITPendingBit(EXTI_Line0);
	}
}
void EXTI15_10_IRQHandler(void)
{
	if(EXTI_GetFlagStatus(KEY2_INT_EXTI_LINE)!=RESET)
	{
		BEEP_TOGGLE;
		EXTI_ClearITPendingBit(KEY2_INT_EXTI_LINE);
	}
}
  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值