05 EXTI外部中断

前言

又鸽了几天的文章,最近在做一个手表项目,这个项目用到了很多知识,特别是中断的知识特别的多,所以这一篇文章来讲讲外部中断,等下一章说一下内部中断。

一、什么是中断

例如你现在在搞一个项目,然后突然看到你自己写的便条,上面写着今天该写文章了,然后你就会停下手中的项目,转去写文章,当文章写完后又继续的做项目,这个过程就是一个中断。

也就是说中断就是一个打断当前执行的任务,转去执行另外一个任务,当这个任务执行完成后就会返回执行被打断的任务。

img

上面就是中断执行的一个逻辑。

二、如何使用中断

上面我们简单的了解了一下中断,大概了解了中断是需要外部信号的,但是光有外部信号进来也不行,我们还需要配置内部,就比如上面举的例子,你看到了便条,但是你忘记这件事了,是不是就会忽略掉那个便条,stm32也是一样,你设置了外部的信号,但是你没设置stm32内部,让它知道这个信号是中断,这样是没用的,所以我们要对stm32进行配置。

在配置之前首先要了解stm32处理中断的硬件环境,这样我们才知道如何配置。

1.stm32中断结构

img

因为我们这里讲的是外部中断,在stm32中控制外部中断的是EXTI控制器,所以这里有GPIO口,GPIO口经过AFIO引脚选择器后选择出引脚转到EXTI检测器后会到NVIC中进行排队触发,如果前面有正在执行的中断,NVIC会根据这个中断的抢占优先级来进行强占,如果没有正常排队;如果同时有两个中断同时触发,会根据它们之间的响应优先级来进行排队响应。

1.1 AFIO中断引脚选择

这个连接这GPIO口的所有引脚,进入了这个器件后,这个器件会将同一个号码的引脚分为一个引脚,比如说PA1、PB1、PC1…分为一个引脚,其他的引脚也是这样的分类,我们配置引脚为中断接受引脚后,AFIO会将这个引脚设置为对应的名字,然后将这个引脚发送给EXTI进行检测和控制,这里可以看到上图有一个16,代表的是只能接受16个引脚。

1.2 EXTI边缘检测

当配置好后,就可以将配置的引脚设置进EXTI中进行配置模式,配置好后进入NVIC进行配置抢占优先级和响应优先级。

1.3 NVIC优先级配置

到NVIC中就开始配置优先级,当优先级配置好后,NVIC就会让配置好的这个中断进行执行。

这样就是一个中断的执行过程,下面就是介绍一下配置了。

2.配置stm32的中断

了解了上面的那些元器件,接下来就是代码的配置实现了。

1.打开时钟

我们在做32的时候要,无论是做什么都需要打开对应的时钟,打开后才能正常的使用。

在上面的硬件介绍中我们了解到,需要打开GPIO的时钟、AFIO的时钟。

为什么不打开EXTI和NVIC的时钟呢?因为EXTI是和GPIO在一起的,当打开了GPIO的时钟,EXTI的时钟就打开了,而NVIC是在内核中的,这个时钟是在单片机启动有时钟时就可以直接使用,所以不用再麻烦的打开时钟进行操作。

AFIO的时钟是属于APB2中的,所以打开时钟的代码也很简单:

RCC_APB2PriphClockCmd(RCC_APB2Periph_GPIOx, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

其中RCC_APB2Periph_GPIOx是你要开启的GPIO口的时钟,这里就不具体举例子了。

2.配置GPIO口

打开时钟后就可以配置GPIO口了,一般我们使用GPIO口来接受中断信号,所以需要配置GPIO口,配置的方法之前在将IO口的操作时已经讲了,主要是GPIO口该是什么模式来进行接受呢?

其实和输入一样,如果这个按钮是接地,那这个就设置为上拉输入,如果是接电源,那就下拉输入:

GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;   // 这里默认为接地
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_x;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOx, &GPIO_InitStruct);

3.配置AFIO控制

配置完成后我们接下来AFIO控制,了解过AFIO的都知道,AFIO是GPIO重映像的一个部件,在32中有一些引脚有一个重映像功能,如果我们需要使用到这个重映像的话就需要使用AFIO进行重映像配置。

在这也是,其实你可以理解为将接受中断信号的引脚重映像为中断引脚,配置的方法很简单,只需要使用下面的一个语句就可以了:

GPIO_EXTILineConfig(GPIO_PortSourceGPIOx, GPIO_PinSourcex);

其中GPIO_PortSourceGPIOx是GPIO组,如果要设置为GPIOB的,那这里就写GPIO_PortSourceGPIOB

GPIO_PinSourcex是GPIO口中需要配置的引脚,比如说是12引脚,那这里就填写GPIO_PinSource12

4.配置EXTI功能

配置完成AFIO后现在就要来配置EXTI中断控制器了,配置方法其实和GPIO口的配置一样,首先创建一个结构体,然后配置结构体中的内容,然后交给初始化函数就可以了,配置代码如下:

EXTI_InitTypeDef EXTI_InitStruct = {0};
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;   // 设置中断模式
EXTI_InitStruct.EXTI_LineCmd = ENABLE;    // 使能中断通道
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;     // 触发模式
EXTI_InitStruct.EXTI_Line = EXTI_Linex;   // 设置通道
EXTI_Init(&EXTI_InitStruct);

其中EXTI_Linex是设置通道,比如上面设置12引脚为中断模式,那这里就填写EXTI_Line12即可。

5.配置NVIC

现在需要配置个NVIC就可以将stm32的中断配置完成了,配置NVIC的步骤也是和配置GPIO一样,但是在设置之前需要给NVIC一个分组,这个分组的作用是设置抢占优先级和响应优先级的范围,每个组的抢占和响应可选的值不一样,下面是分组对应的优先级的范围:

组名抢占优先级响应优先级
NVIC_PriorityGroup_00~40
NVIC_PriorityGroup_10~30~1
NVIC_PriorityGroup_20~20~2
NVIC_PriorityGroup_30~10~3
NVIC_PriorityGroup_400~4

配置的代码如下:

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

这里设置是组二,这个设置NVIC的组只有设置的第一次有用,如果你在其他再次调用了这个函数,组配置的不一样那也不会进行更改,相当于只要执行了一次这个函数就再也修改不了了,设置完成后就可以开始配置NVIC了:

NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn;    // 选择启用的IRQ通道
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;   // 设置抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;          // 设置响应优先级
NVIC_Init(&NVIC_InitStruct);

这里需要注意一下,这里的抢占和响应的优先级如果超过了配置的组中的优先级,那会出问题,不要超过即可。

这里EXTI15_10_IRQn是设置启用的通道,前面配置的通道需要在这变为IRQ通道,这里可以在源码中进行查看对应的通道,比如说我配置的是2引脚,那么这里写EXTI2_IRQn

这里就写完配置中断的代码了,全部的代码如下:

6.配置完整代码

// 开启时钟
RCC_APB2PriphClockCmd(RCC_APB2Periph_GPIOx, ENABLE);    // 这里需要对应的GPIO口
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

// 配置GPIO口
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;   // 这里默认为接地
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_x;    // 这里需要写响应的引脚
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOx, &GPIO_InitStruct);

// 配置AFIO
GPIO_EXTILineConfig(GPIO_PortSourceGPIOx, GPIO_PinSourcex);    // 这里需要写具体的GPIO口和引脚

// 配置EXTI
EXTI_InitTypeDef EXTI_InitStruct = {0};
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;   // 设置中断模式
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStruct.EXTI_Line = EXTI_Linex;   // 这里需要写具体的通道
EXTI_Init(&EXTI_InitStruct);

// 配置NVIC组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_x);  // 这里要写具体的组

// 配置NVIC
NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn;    // 选择启用的IRQ通道
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;   // 设置抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;          // 设置响应优先级
NVIC_Init(&NVIC_InitStruct);

配置完成中断后,stm32知道要执行中断了,但是中断的内容却没有,就比如你现在看到字条了,你知道要写文章了,已经停止做项目的操作了,但是你不知道该写什么文章,所以我们现在要做的就是告诉32,你触发了这个中断后要进行什么操作。

这一步就叫做写中断服务函数,让它接收到这个中断后能转去执行对应的函数和内容。

3.书写中断服务函数

这里中断服务函数的函数名不是想叫什么就可以叫什么的,而是在启动文件中已经标注好了的,我们只需要给它拿下来就可以了

img

这里可以看到很多,我们找在NVIC配置好的那个IRQ通道的名称的就可以了,比如是EXTI2_IRQn,那这里我们要的是EXTI2_IRQHandler,一样的道理,这里就以这个EXTI2_IRQHandler举例子,那我们开始写函数:

void EXTI2_IRQHandler(void)
{

}

这样中断函数就写好了,现在就是需要在里面填写内容了,内容一般是自己来规定的,但是有一些内容需要写好,首先是判断中断标志位,要判断一下是不是这个引脚触发的,函数如下:

EXTI_GetITStatus(EXTI_Linex);

EXTI_Linex是设置的通道,然后这个函数的返回值是SETRESET,当触发时返回SET,没触发时返回RESET

然后就是清除中断标志位,要手动进行一次复位,使得下一次能够正常的进行触发,函数如下:

EXTI_ClearITPendingBit(EXTI_Linex);

我们在中断服务函数中就先写好这样的代码:

void EXTI2_IRQHandler(void)
{
    if (EXTI_GetITStatus(EXTI_Linex) == SET)
    {
        // 你自己的代码

        // ===========
        EXTI_ClearITPendingBit(EXTI_Linex);
    }
}

中断服务函数可以写在任意的位置,不需要在main函数中进行调用,这个中断函数的调用又系统来进行。

总结

在中断函数中尽量不要执行一些耗费大量时间的内容,我一般是使用变量来进行控制,控制变量然后在main函数中判断这个变量的值,然后才执行相应的操作,一般我用中断来进行按键的控制,因为这个效率比较高,但是抖动问题还是比较严重的,可以用做其他的地方。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

恰柠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值