【STM32】中断系统 —— 外部中断

使用的单片机机型为STM32F103C8T6

文章目录

中断

中断是为使CPU具有对外界紧急事件的实时处理能力而设置的

当CPU正在处理某件事时,外界发生了紧急事件请求,要求CPU暂停当前的工作,转而去处理这个紧急事件,处理完后,再回到原来被中断的地方,继续原来的工作,这个过程被称为中断


中断程序流程

左侧图:在主程序执行期间,发现有中断请求到来,会先打个断点,然后进行中断响应,执行中断处理程序,完成后返回断点,继续执行主程序

右侧图:以程序地址空间的形式展示处理中断的过程,发生中断时,会跳转到中断处理程序的地址空间,执行完再跳转回主程序的地址空间

STM32有68个可屏蔽中断通道,包含 EXTI、SPI、I2C、RTC等多个外设


NVIC 嵌套中断控制

中断优先级

当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源。通过不同设置不同中断优先级,就可以实现中断嵌套

中断嵌套

当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回

NVIC(嵌套中断控制系统)

STM32 使用 NVIC 统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级

NVIC 的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高 n 位的抢占优先级和低 4 - n 位的响应优先级

抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,两个优先级均相同的按中断号排队
优先级值越低,优先级越高,如 1 比 2 优先级高

EXTI 外部中断

EXTI (EXtern Interrupt) 外部中断

EXTI 可以监测指定 GPIO口的电平信号,当其指定的 GPIO 口产生电平变化时,EXTI 将立即向 NVIC 发出中断申请,经过

NVIC 裁决后即可中断 CPU 主程序,使 CPU 执行 EXTI 对应的中断处理函数

因为监测GPIO口,所以可以设置不同的触发方式

支持的触发方式:上升沿、下降沿、双边沿、软件触发

  • 支持的 GPIO口:所有GPIO口,但相同的Pin不能同时触发中断,即GPIOA_Pin_9 和 GPIOB_Pin_9 不能同时触发
  • 通道数:16个GPIO_Pin,外加PVD输出,RTC闹钟,USB唤醒,以太网唤醒
  • 触发响应方式:中断响应、事件响应

EXTI基本结构

AFIO 主要用于复用功能引脚重映射,中断引脚选择

从图中看出,AFIO 左侧接各个GPIO,每个 GPIO 的某一引脚,对应右侧通向 EXTI 的引脚。比如GPIOA_Pin_1 和 GPIOB_Pin_1 都对应 1号引脚。

所以相同的 GPIO_Pin不能同时触发中断

EXTI框图

外设接口为 AFIO 选择后的引脚,通过配置中断触发方式,将外设接口的中断放入配置的寄存器

再通过边沿检测电路,可以选择向上到请求挂起寄存器,此路为中断响应,下路为事件响应

综上,外部中断流程如下:

首先,GPIO 引脚变化,通过 AFIO 引脚选择通向 EXTI 边沿检测,再通过中断通道至 NVIC 中断控制器发出中断

编程示例

对射红外传感器单中断

对射红外传感器

DO为数据输出,当中间被遮挡时,DO输出低电平,反之输出高电平

AO为模拟输入,用于DAC

  • 程序目标:实现对射红外传感器计次,每遮挡一次,计数加一

  • 程序原理:将传感器的DO接在某个GPIO,并配置外部中断,当该引脚为低电平时,产生中断,在中断处理函数中计数加一

  • 接线图如下:OLED用于调试

初始化中断

对于外部中断,初始化参看下图

需要配置的部分包括:GPIO、AFIO、EXTI、NVIC。其中GPIO 和 AFIO 为外设,还需要开启时钟

配置GPIO

将 DO 接在 B14引脚,所以需要配置 GPIOB。因为对射红外传感器默认输出高电平,所以GPIO引脚配置为上拉输入,也默认为高电平

c 复制代码
//初始化GPIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;				//上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;					//B14
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	
GPIO_Init(GPIOB, &GPIO_InitStructure);

配置AFIO

进行引脚选择,将 GPIOB 的 B14 映射到14号引脚

c 复制代码
//初始化AFIO	函数定义在gpio.h
//将指定GPIO的指定引脚映射为中断引脚
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);		//开启时钟,外部中断必须开启AFIO时钟
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);

配置EXTI

因为遮挡时输出低电平,电平由高转为低,我们可以采用下降沿触发

c 复制代码
//初始化EXTI	函数定义在exti.h
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line14;				//中断引脚	
EXTI_InitStructure.EXTI_LineCmd = ENABLE;				//使能开关	
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;		//中断触发
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;	//下降沿触发
EXTI_Init(&EXTI_InitStructure);

配置NVIC

NVIC 为配置中断优先级

c 复制代码
//中断优先级分组	函数定义在misc.h
//抢占分组2,响应分组2
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

//初始化NVIC	函数定义在misc.h
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;		//IRQ通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//使能开关
//因为中断优先级分组为2,即各自2位,表示范围0~3
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//抢占优先级1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//响应优先级1
NVIC_Init(&NVIC_InitStructure);

至此,外部中断初始化完成,完整代码如下:

c 复制代码
/**
  * @brief		初始化对射红外计数传感器,注意GPIO和AFIO的时钟开启
  * @parm		无
  * @retval		无
  */
void CounterSonsor_Init(void)
{
	//初始化GPIO
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启时钟
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;				//上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;					//B14
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	//初始化AFIO	函数定义在gpio.h
	//将指定GPIO的指定引脚映射为中断引脚
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);		//开启时钟,外部中断必须开启AFIO时钟
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
	
	//初始化EXTI	函数定义在exti.h
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line = EXTI_Line14;				//中断引脚	
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;				//使能开关	
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;		//中断触发
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;	//下降沿触发
	EXTI_Init(&EXTI_InitStructure);
	
	//中断优先级分组	函数定义在misc.h
	//抢占分组2,响应分组2
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	//初始化NVIC	函数定义在misc.h
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;		//IRQ通道
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//使能开关
	//因为中断优先级分组为2,即各自2位,表示范围0~3
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//抢占优先级1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//响应优先级1
	NVIC_Init(&NVIC_InitStructure);
}

中断处理函数的声明在 startup_stm32f10x_md.s

STM32 将 EXTI10 ~ 15 的中断处理都集中在该处理函数中

所以在函数中还需要具体判断是否是目标中断到来

代码如下:

c 复制代码
uint16_t Count;

//中断处理函数		定义在startup启动文件
void EXTI15_10_IRQHandler(void)
{
	//10~15中断都触发该函数,需要判断一下
	if(EXTI_GetITStatus(EXTI_Line14) == SET)//SET == 1
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)
			Count++;
		//还需要清除中断标志位
		EXTI_ClearFlag(EXTI_Line14);
	}
}

完整项目链接:【STM32】对射式红外传感器中断

多中断旋转编码器调数

旋转编码器

用于测量位置、速度或旋转方向的装置,当其旋转轴旋转时,其输出端可以输出与旋转速度和方向对应的方波信号,读取方波信号的频率和相位信息即可得知旋转轴的速度和方向


硬件电路

A 和 B 端分别用于发出方波信号,可通过方波信号辨别正转和反转。此处的正转为顺时针转动

通过在一侧上下沿时,检测另一侧的电平来判断正反转

  • 程序目标:实现旋转编码器调数,顺时针旋转加数,逆时针旋转减数
  • 程序原理:将旋转编码器的 A 和 B 引脚接在 GPIO,通过外部中断检测上下沿,在中断处理函数操作计数
  • 接线图如下:

配置中断

c 复制代码
void Encoder_Init(void)
{
	//初始化GPIO
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启时钟
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;				//上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;		//B0 & B1
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	//初始化AFIO	函数定义在gpio.h
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);		//开启时钟,外部中断必须开启AFIO时钟
	//将指定GPIO的指定引脚映射为中断引脚
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);
	
	//初始化EXTI	函数定义在exti.h
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;				//中断引脚	
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;				//使能开关	
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;		//中断触发
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;	//下降沿触发
	EXTI_Init(&EXTI_InitStructure);
	
	//中断优先级分组	函数定义在misc.h
	//抢占分组2,响应分组2
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	//初始化NVIC	函数定义在misc.h
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;		//IRQ通道
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//使能开关
	//因为中断优先级分组为2,即各自2位,表示范围0~3
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//抢占优先级1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//响应优先级1
	NVIC_Init(&NVIC_InitStructure);
	
	NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;		//IRQ通道
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//使能开关
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//抢占优先级1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;			//响应优先级2
	NVIC_Init(&NVIC_InitStructure);
}

配置了 0 和 1 中断通道。

判断逻辑:A下降沿时,检测B高电平为正转;B下降沿时,检测A高电平为反转

相应中断处理函数如下:

c 复制代码
//中断处理函数		定义在startup...h启动文件
void EXTI0_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line0) == SET)
	{
		//正转
		if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 1)
			Increment++;
		EXTI_ClearFlag(EXTI_Line0);
	}
}

//中断处理函数		定义在startup...h启动文件
void EXTI1_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line1) == SET)
	{
		//正转
		if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 1)
			Increment--;
		EXTI_ClearFlag(EXTI_Line1);
	}
}

完整程序链接:【STM32】旋转编码器调数


以上就是本篇博客的所有内容,感谢你的阅读

如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。

相关推荐
Lbs_gemini060339 分钟前
C++研发笔记14——C语言程序设计初阶学习笔记12
c语言·开发语言·c++·笔记·学习
lucy153027510795 小时前
【青牛科技】BISS0001高性能的传感信号处理集成电路芯片,广泛用于安防、自控等领域能
科技·单片机·智能家居·信号处理·安防·工控主板
白天看海5 小时前
40 基于单片机的温湿度检测判断系统
单片机·嵌入式硬件
电子设计师5 小时前
45 基于单片机的信号选择与温度变化
单片机·嵌入式硬件
zcb8496443715 小时前
27 基于51单片机的方向盘模拟系统
嵌入式硬件·51单片机·proteus·方向盘
test猿6 小时前
电与计算机的关系
单片机·嵌入式硬件·物联网
没有名字的鬼6 小时前
C_字符串的一些函数
c语言·开发语言·算法
Heris997 小时前
零基础快速掌握——c语言基础【二维数组】
c语言·开发语言·数据结构·算法
tjsoft7 小时前
delphi 12 idhttpsever(S)+idhttp(C) 实现简单的JSON API服务
c语言·开发语言·json
编程圈子7 小时前
STM32 HAL库开发学习3.STM32启动浅析
stm32·嵌入式硬件·学习