【细说STM32】【九】外部中断 您所在的位置:网站首页 小型贵宾犬 【细说STM32】【九】外部中断

【细说STM32】【九】外部中断

2023-08-10 15:29| 来源: 网络整理| 查看: 265

二、外部中断概述

STM32中,每一个GPIO都可以触发一个外部中断,但是,GPIO的中断是以组为一个单位的,同组间的外部中断同一时间只能使用一个。比如说,PA0,PB0,PC0,PD0,PE0,PF0,PG0这些为1组,如果我们使用PA0作为外部中断源,那么其他的就不能再使用了。在此情况下,我们只能使用类似于PB1,PC2这种末端序号不同的外部中断源。

STM32的中断控制器支持19个外部中断/事件请求:

线0~15:对应外部IO口的输入中断。

线16:连接到PVD输出。

线17:连接到RTC闹钟事件。

线18:连接到USB唤醒事件。

每个外部中断线可以独立的配置触发方式(上升沿,下降沿或者双边沿触发),触发/屏蔽,专用的状态位。

每一组使用一个中断标志EXTIx。EXTI0-EXTI4这5个外部中断有着自己单独的中断响应函数,EXIT5-EXIT9共用一个中断响应函数,EXIT10-EXIT15共用一个中断响应函数。

三、常用库函数

①voidGPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);

//设置IO口与中断线的映射关系

exp: GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);

②voidEXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);

//初始化中断线:触发方式等

③ITStatusEXTI_GetITStatus(uint32_t EXTI_Line);

//判断中断线中断状态,是否发生

④voidEXTI_ClearITPendingBit(uint32_t EXTI_Line);

//清除中断线上的中断标志位

EXTI_Init()函数

EXTI_Init()函数的定义是:voidEXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);

下面我们用一个使用范例来说明这个函数的使用:

EXTI_InitTypeDef EXTI_InitStructure;

EXTI_InitStructure.EXTI_Line=EXTI_Line15;

EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;

EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;

EXTI_InitStructure.EXTI_LineCmd = ENABLE;

EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct 中指定的参数初始化外设EXTI 寄存器

例子设置中断线15 上的中断为下降沿触发。STM32 的外设的初始化都是通过结构体来设置初始值的,结构体EXTI_InitTypeDef的成员变量:

typedef struct

{

uint32_t EXTI_Line;

EXTIMode_TypeDef EXTI_Mode;

EXTITrigger_TypeDef EXTI_Trigger;

FunctionalState EXTI_LineCmd;

}EXTI_InitTypeDef;

从定义可以看出,有4 个参数需要设置。第一个参数是中断线的标号,取值范围为EXTI_Line0~EXTI_Line15。这个函数配置的是某个中断线上的中断参数。第二个参数是中断模式,可选值为中断EXTI_Mode_Interrupt 和事件EXTI_Mode_Event。第三个参数是触发方式,可以是下降沿触发EXTI_Trigger_Falling,上升沿触发EXTI_Trigger_Rising,或者任意电平(上升沿和下降沿)触发EXTI_Trigger_Rising_Falling,最后一个参数就是使能中断线了。

NVIC_Init()函数

我们设置好中断线和GPIO 映射关系,然后又设置好了中断的触发模式等初始化参数。既然是外部中断,涉及到中断我们当然还要设置NVIC 中断优先级。这里我们就接着上面的范例,设置中断线2 的中断优先级。

NVIC_InitTypeDefNVIC_InitStructure;

NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn; //使能按键外部中断通道

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2,

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子优先级2

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道

NVIC_Init(&NVIC_InitStructure); //中断优先级分组初始化

其结构体NVIC_InitTypeDef的成员变量:

typedefstruct

{

uint8_t NVIC_IRQChannel; //设置中断通道

uint8_t NVIC_IRQChannelPreemptionPriority;//设置响应优先级

uint8_t NVIC_IRQChannelSubPriority; //设置抢占优先级

FunctionalState NVIC_IRQChannelCmd; //使能

}NVIC_InitTypeDef;

从定义可以看出,有4 个参数需要设置。第一个参数是设置中断通道。第二个参数是设置响应优先级。第三个参数是设置抢占优先级,最后一个参数就是使能中断通道。

NVIC_PriorityGroupConfig()函数

系统运行后先设置中断优先级分组。调用函数:voidNVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);整个系统执行过程中,只设置一次中断分组。针对每个中断,设置对应的抢占优先级和响应优先级:voidNVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);

中断管理方法:对STM32中断进行分组,组0~4。同时,对每个中断设置一个抢占优先级和一个响应优先级值。该分组的设置是由SCB->AIRCR 寄存器的bit10~8 来定义的。

EXTIx_IRQHandler函数

我们配置完中断优先级之后,接着我们要做的就是编写中断服务函数。中断服务函数的名字是在 MDK 中事先有定义的。 这里需要说明一下, STM32 的 IO 口外部中断函数只有 6 个,分别为:

EXPORT EXTI0_IRQHandler

EXPORT EXTI1_IRQHandler

EXPORT EXTI2_IRQHandler

EXPORT EXTI3_IRQHandler

EXPORT EXTI4_IRQHandler

EXPORT EXTI9_5_IRQHandler

EXPORT EXTI15_10_IRQHandler

中断线0-4 每个中断线对应一个中断函数,中断线5-9 共用中断函数EXTI9_5_IRQHandler,中断线10-15 共用中断函数EXTI15_10_IRQHandler。

在编写中断服务函数的时候会经常使用到两个函数,第一个函数是判断某个中断线上的中断是否发生(标志位是否置位):

ITStatus EXTI_GetITStatus(uint32_tEXTI_Line);

这个函数一般使用在中断服务函数的开头判断中断是否发生。另一个函数是清除某个中断线上的中断标志位:

void EXTI_ClearITPendingBit(uint32_tEXTI_Line);

这个函数一般应用在中断服务函数结束之前, 清除中断标志位。

常用的中断服务函数格式为:

void EXTI3_IRQHandler(void)

{

if(EXTI_GetITStatus(EXTI_Line3)!=RESET)//判断某个线上的中断是否发生

{

中断逻辑…

EXTI_ClearITPendingBit(EXTI_Line3); //清除 LINE 上的中断标志位

}

}

在这里需要说明一下,固件库还提供了两个函数用来判断外部中断状态以及清除外部状态标志位的函数 EXTI_GetFlagStatus 和 EXTI_ClearFlag,他们的作用和前面两个函数的作用类似。只是在 EXTI_GetITStatus 函数中会先判断这种中断是否使能,使能了才去判断中断标志位,而EXTI_GetFlagStatus 直接用来判断状态标志位。

四、程序

使用IO口外部中断的一般步骤:

①初始化IO口为输入。

GPIO_Init();

②开启IO口复用时钟。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

③设置IO口与中断线的映射关系。

void GPIO_EXTILineConfig();

④初始化线上中断,设置触发条件等。

EXTI_Init();

⑤配置中断分组(NVIC),并使能中断。

NVIC_Init();

⑥编写中断服务函数。

EXTIx_IRQHandler();

⑦清除中断标志位

EXTI_ClearITPendingBit();

本例是外部按键中断,外部中断配置主要在EXTIX_Init()函数和EXTI15_10_IRQHandler()函数

1)中断初始化配置

voidEXTIX_Init(void)

{

EXTI_InitTypeDef EXTI_InitStructure;

NVIC_InitTypeDef NVIC_InitStructure;

GPIO_InitTypeDefGPIO_InitStructure;

// 按键端口初始化

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);//使能PORTE时钟

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;//KEY1

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//设置成上拉输入

GPIO_Init(GPIOE,&GPIO_InitStructure);//初始化GPIOE

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能复用功能时钟

//GPIOE.15中断线以及中断初始化配置 下降沿触发

GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource15);

EXTI_InitStructure.EXTI_Line= EXTI_Line15; //KEY1

EXTI_InitStructure.EXTI_Mode= EXTI_Mode_Interrupt;

EXTI_InitStructure.EXTI_Trigger= EXTI_Trigger_Falling;

EXTI_InitStructure.EXTI_LineCmd= ENABLE;

EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器

//配置中断分组并使能中断

NVIC_InitStructure.NVIC_IRQChannel= EXTI15_10_IRQn; //使能按键KEY1所在的外部中断通道

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 0x02;//抢占优先级2,

NVIC_InitStructure.NVIC_IRQChannelSubPriority= 0x02; //子优先级2

NVIC_InitStructure.NVIC_IRQChannelCmd= ENABLE; //使能外部中断通道

NVIC_Init(&NVIC_InitStructure);

}

2)中断服务函数需使用中断线所对应的中断服务函数,本例按键是PE15,则所对应的中断服务函数为EXTI15_10_IRQHandler();

void EXTI15_10_IRQHandler(void)

{

delay_ms(10);//消抖

if(EXTI_GetITStatus(EXTI_Line15) != RESET)//确保是否产生了EXTI Line中断

{

GPIOE->ODR^= GPIO_Pin_9; //LED1状态翻转

GPIOE->ODR^= GPIO_Pin_10; //LED2状态翻转

GPIOE->ODR^= GPIO_Pin_12; //LED3状态翻转

printf("key isactivern");

}

EXTI_ClearITPendingBit(EXTI_Line15); //清除LINE15上的中断标志位

}

KEY1在driver_exti.h头文件中定义了

#define KEY1GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_15)//读取按键1

delay_ms();函数封装成driver_delay.c和driver_delay.h,调用driver_delay.h,就可以直接使用。

LED状态翻转此处是直接对寄存器操作,亦可使用

GPIO_WriteBit(GPIOE,GPIO_Pin_9,(BitAction)((1-(GPIO_ReadOutputDataBit(GPIOE,GPIO_Pin_9))));

GPIO_WriteBit(GPIOE,GPIO_Pin_12,(BitAction)((1-(GPIO_ReadOutputDataBit(GPIOE,GPIO_Pin_10))));

GPIO_WriteBit(GPIOE,GPIO_Pin_12,(BitAction)((1-(GPIO_ReadOutputDataBit(GPIOE,GPIO_Pin_12)))));

在主函数中需要先设置中断优先级分组。调用函数:void NVIC_PriorityGroupConfig(uint32_tNVIC_PriorityGroup);整个系统执行过程中,只设置一次中断分组。延用前面所编写的串口初始化和LED初始化,达到按键触发后LED显示效果与串口打印

int main(void)

{

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置NVIC中断分组2:2位抢占优先级,2位响应优先级

uart_init(115200); //串口初始化为115200

LED_Init(); //初始化与LED连接的硬件接口

EXTIX_Init(); //外部中断初始化

while(1)

{

printf("OKrn");

delay_ms(200);

}

}

效果显示:当有按键中断产生,串口打印出key is actve,并且三个LED亮灭转换

LED点亮:

LED熄灭:

作者:冯美文

点击原文链接,可以下载代码。返回搜狐,查看更多



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有