目录
【EXTI_GetFlagStatus和EXTI_GetITStatus的区别】
【1】外部中断
外部中断就是芯片识别外部的信号,进行判断,如果满足自己设置的中断条件就会自动跳转到中断函数内部执行函数里面的内容。因为平时我们的程序都放在while下的,执行主要内容,但有时我们有一些紧急的情况需要处理,这时候中断就会打断主程序先把紧急的事情干完了,再干自己的正常工作。
一.GPIO外设的初始化
初始化还是跟上节的初始化流程是一样的,如果忘记了可以看一下上一节,注意中断是检测的外部信号脉冲,所以记得把这里的GPIO_Mode改成输入模式,输入模式有三种,忘记了的同学可以看一下上一节,代码举例如下:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
二.AFIO中断引脚的设置
当然AFIO作为挂载在APB2上的外设第一步就是打开时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
这里简单介绍一下AFIO,它用于管理 GPIO 引脚的复用功能和重映射。AFIO 允许将某些引脚的默认功能切换为其他功能,比如说,外部中断线映射,也就是接下来,我们选择的中断线。还比如说,通过AFIO我们可以复用引脚功能,例如,可以将 UART 的 TX 和 RX 引脚从默认的引脚映射到其他 GPIO 引脚上。接着说我们现在要进行的中断配置,STM32的外部中断的第二步就是中断线的设置,AFIO给出了16个通道给中断,而且就是对应Pin0~15的端口;那么只给出了这么多的通道,而且有GPIOA或GPIOB或GPIOX等这么多外设容易冲突,那么就有个规则:比如,当GPIOA_Pin_1即GPIOA已经占了通道1(通道几对应它的端口几),通道1就已经用完了,那么这些GPIOB或GPIOC啥的GPIOX它们的Pin_1即它们的端口1就不能在选择这个通道作为中断,只能用除了1之外的通道;也可以这么理解,相当于给了16条通道线,每条通道线,对应连接端口0~15即Pin_X(0~15),但是每条线只能选择一个GPIO类型的外设。


这个函数就是用来配置这个AFIO的中断线的,虽然它的名字里面没有AFIO,但其实函数内部是操作AFIO的,第一个参数就是GPIO_PortSourceGPIOx(x:A~G)---用来选择用哪一个GPIO外设第二个参数就是GPIO_PinSourcex(x:0~15)---用来选择哪一个中断线。
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource1);
三.EXTI的配置
首先这里先介绍一下有关EXTI(外部中断事件控制器)的库函数:
1.EXTI_DeInit();

这个函数说将EXTI外设的配置重新配置为缺省值,那么缺省值是什么呢,缺省值其实就是官方人员配置的默认值这个默认值呢,就是刚开始上电时的值,那么这个函数其实就是起到了一个清除配置的作用;
2.EXTI_Init()

这个函数就是对EXTI外设的初始化,跟GPIO_Init有异曲同工之妙。其实,看到这里会发现,STM32的外设的函数都有个惯用套路:
【外设的初始化和设置】
1.打开时钟XXX_Cmd
2.声明一个结构体变量:XXX_InitTypeDef 变量;
3.为结构体变量的成员变量填充值;
4.调用数 XXX_Init()来初始化外设
注:EXTI和NVIC的时钟是一直开启的,就不需要手动开启了
3.EXTI_GenerateSWInterrupt()

这个函数是用来程序触发中断的特殊类型中断的函数,比如你写了一些代码,在这个地方需要它进入中断,就用这个函数直接生成软件中断。
4.EXTI_GetFlagStatus()

当外部中断线已经达成了触发条件,那么这个EXTI的线路标志位就会被置1,那么这个函数就可以直接获取这个线路标志位的状态。
5.EXTI_ClearFlag()

外部中断的标志位需要手动清0,那么该函数就是用来清除外部中断触发中断条件标志位;
6.EXTI_GetITStatus()

【EXTI_GetFlagStatus和EXTI_GetITStatus的区别】
这个函数也是用来读取中断标志位的,区别于EXTI_GetFlagStatus(),它多了一个判断是否被特定的寄存器进行了中断屏蔽,也就是说,当触发了中断条件时,这时单纯是满足条件而将标志位置1,如果被特定寄存器进行中断屏蔽了,就是单纯的置位而不响应中断,那么EXTI就是单纯读取标志位,而EXTI_GetITStatus还会判断是否被中断屏蔽,也就是这个函数进行读取中断是否响应时的标志位,也就是读取中断状态。
7.EXTI_ClearITPendingBit()

这个函数就是对应EXTI_GetI TStatus(),状态标志位的清除了
8.软件代码配置
如同上面介绍的外设的基本套路的配置这里也是这样做的
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line=EXTI_Line1;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;
EXTI_Init(&EXTI_InitStructure);
这里解释一下上面的代码含义
1. EXTI_InitTypeDef EXTI_InitStructure; 这个很熟悉了,就是结构体变量的定义
2.第一个成员变量:
EXTI_Line这个是指的是你选择的哪一条中断线。参数:EXTI_LineX(X:0~19),这个中断线就是我们上面AFIO选择的那一条中断线,这里同样也可以进行或运算,选中多条中断线;当然这里总共二十条中断线,有四条特殊的中断线。

3.第二个成员变量:
EXTI_LineCmd这个指的是否开启你选中的中断的线
参数:ENABLE或DISABLE
4.第三个成员变量:
EXTI_Mode指的是你要选择的中断方式,这里分别是中断方式和事件方式;中断方式就是当检测到配置的中断线,触发事件(如上升沿、下降沿或双边沿)时,此时会产生一个中断请求,微控制器会进入中断服务程序来处理这个事件;事件方式指的是用于生成软件可检测的事件,而不是生成硬件中断,也就是它不是通过产生中断请求来打断程序,而是你可以通过程序查询它的标志位来知道它现在的中断线已经触发你配置的事件了。
参数:EXTI_Mode_Interrupt(中断)或EXTI_Mode_Event(事件)
5.第四个成员变量:
EXTI_Trigger指的是触发方式,这里有三种触发方式分别是上升沿或下降沿或上升沿和下降沿触发上升沿和下降沿触发自然是上升沿触发了,下降沿也可以触发了。
参数:EXTI_Trigger_Rising(上升)
EXTI_Trigger_Falling(下降)
EXTI_Trigger_Rising_Falling(上升沿和下降沿)
6.结构变量初始化EXTI_Init();
四、NVIC的配置
NVIC:嵌套中断向量列表控制器;
【NVIC的分组】
NVIC是用来设置中断优先级的,毕竟有这么多中断线输入进来,不可能同时发生,这时就要用中断优先级给它们排个队,那么这里的优先级又分为抢占优先级和响应优先级,首先这里就给出了几种排队分组的方式,你要先选择一种排队分组的方式,才能进行设置优先级去排列的先后顺序,记住如果你设置了多个中断线,但是只能分一次组,也就是这个分组的代码只能出现一次,不能分多个组。

【抢占优先级和响应优先级的介绍】
数值越小的优先级越高,这里从三种情况来解释抢占优先级(先占优先级)和响应优先级(从优先级);比如,甲程序的抢占优先级和乙程序的抢占优先级相同时,当甲程序比乙程序的响应优先级高,那么当甲和乙的中断程序同时到来时,系统会先响应甲的中断;当甲程序的响应优先级和乙程序的响应优先级相同时或不同时,甲程序的抢占优先级比乙的抢占优先级高,那么这时候如果乙进入了中断程序就会被甲的程序给打断,执行完甲的再回头来执行乙的;假如抢占优先级和响应优先级都相同就按照系统排列的中断号进行排队,数字越小,中断优先级越高。

简单对上面这个总结一下,就是先判断抢占优先级,它是级别最大的,再判断响应优先级,然后填的数字越小,级别越大。
这里先了解一些有关NVIC的有关函数:
1.NVIC_PrioritygRoupConfig()

这个函数就是首先给优先级进行分组的,上面介绍了有5组的形式可以选,值得注意的是你写程序的时候,写的这些中断数字都是根据这个组来写优先级,要在这个组的范围内,可以通过查找这个函数定义来看一下组的范围。
2.NVIC_Init()

这个函数就是经典的配置外设初始化的函数了,这里NVIC的初始化就用这个来配置
3.软件编写
1.先进行优先级分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); //这里选择分组方式0
2.定义结构体变量
NVIC_InitTypeDef NVIC_InitStructure;
3.引出成员变量
第一个成员变量:
NVIC_InitStructure.NVIC_IRQChannel=EXTI9_5_IRQn;
这个指的是中断通道,这个该怎么选呢,根据上一节的双击右键跳转定义再全局搜索

根据自己芯片的型号,在启动文件这里,就是名字写着setup那个文件里,可以找到EXTI的通道有两个外部中断,EXTI9_5_IRQn和EXTI15_10_IRQn,随便选择一个。
第二个成员变量:
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
这个指的是要不要开启这个中断通道,参数就是ENABLE或者DISABLE
第三个成员变量:
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
这个就是抢占优先级,根据那个分组里的范围自行选择,注意分完组后,抢占优先级和响应优先级就有取值范围了。
第四个成员变量:
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
这个就是响应优先级
4.NVIC初始化
NVIC_Init(&NVIC_InitStructure);
这个就是将NVIC按照自己的配置初始化了
5.中断函数
当上面都配置好了,接下来就等外部信号触发后,执行中断服务函数里的内容了,那么这里中断函数号也是有特定的名称的,你的特定通道是有对应的中断函数名的,可以在启动文件(启动文件在上一节有介绍)里面,当然这个也有固定格式,如果你只用一次中断函数,那么可以不这样写,如果要多次中断,必须在中断服务函数里面,写如果检测到中断标志位,手动清除中断标志位,代码如下。

void EXTI9_5_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line6)==SET) //当检测到中断标志位置位了,也就是进入中断了
{
EXTI_ClearITPendingBit(EXTI_Line6); //这里必须要手动将标志位清0
}
}
【2】实战练习
按一下按键来改变led的亮暗状态,我这里分模块写,就不都放在main函数这一页了
init.c:
#include "stm32f10x.h" // Device header
uint16_t count;
void init_intial(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef InitStructure;
InitStructure.GPIO_Mode=GPIO_Mode_IPU; //这里采用上拉输入
InitStructure.GPIO_Pin=GPIO_Pin_6;
InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&InitStructure);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource6); //选择中断通道6
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line=EXTI_Line6;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //这里只有一个中断函数可以虽然设置优先级
NVIC_InitTypeDef NVIC_initStruct;
NVIC_initStruct.NVIC_IRQChannel=EXTI9_5_IRQn;
NVIC_initStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_initStruct.NVIC_IRQChannelPreemptionPriority=1;
NVIC_initStruct.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_initStruct);
}
uint16_t return_num(void)
{
return count; //将计数标志位返回
}
void EXTI9_5_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line6)==SET)
{
count++; //设置一个计数标志位,给led做判断使用
EXTI_ClearITPendingBit(EXTI_Line6);
}
}
init.h
#ifndef __INIT_H
#define __INIT_H
void init_intial(void);
uint16_t return_num(void);
#endif
led.c
#include "stm32f10x.h" // Device header
void led_init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef InitStucture;
InitStucture.GPIO_Mode=GPIO_Mode_Out_PP;
InitStucture.GPIO_Pin=GPIO_Pin_0;
InitStucture.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&InitStucture);
GPIO_SetBits(GPIOA,GPIO_Pin_0);
}
led.h
#ifndef __LED_H
#define __LED_H
void led_init(void);
#endif
main.c
#include "stm32f10x.h" // Device header
#include "init.h"
#include "led.h"
uint16_t num;
int main()
{
init_intial(); //中断函数的初始化
led_init(); //led的初始化
while(1)
{
num=return_num(); //读取进入中断的计数标志位
if(num%2==1)
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
else
GPIO_SetBits(GPIOA,GPIO_Pin_0);
}
}
proteus仿真:
STM32中断仿真测试
本文详细介绍了STM32中GPIO外设初始化、AFIO中断引脚设置、EXTI的配置,包括EXTI_GetFlagStatus和EXTI_GetITStatus的区别,以及NVIC的抢占优先级和响应优先级设置。通过实战练习,演示了如何使用中断实现按键控制LED亮暗变化。
1203

被折叠的 条评论
为什么被折叠?



