【STM32标准库】【基础知识】外部中断


文章基于适用于STM32F4系列,作者使用STM32F401CCU6开发板。
本文章基于此系列和开发板展开讨论。

外部中断

什么是外部中断

广义上是指外设所产生的中断。
一般在实践中习惯将外部引脚的电平变化所引发的中断叫外部中断。
在标准库中将GPIO所产生的中断,PVD(电压检测),RTC(实时时钟),USB等等,合在一切叫做外部中断。

注意:STM32的所有GPIO均可作为外部中断使用,这和51单片机不一样
外部中断只有24个,0-15是GPIO的外部中断,剩下的中断号的对应关系请看标准库的定义*
本文是关于GPIO所产生的外部中断的。

外部中断的触发

GPIO的外部中断是由于电平变化所引起的,为了避免误触和重复中断,STM32使用边沿触发模式来触发中断。

上升沿触发

当GPIO的电平由低电平到高电平时会触发一次中断
就像这样
在这里插入图片描述

下降沿触发

当GPIO的电平由高电平到低电平时会触发一次中断
就像这样
在这里插入图片描述

上升下降沿触发

当GPIO的电平由低电平到高电平或者从高到低时会触发一次中断
就像这样
在这里插入图片描述

外部中断初始化

初始化思路

  1. 初始化GPIO为输入模式(上下拉和浮空都可,不能模拟输入)
  2. 打开时钟
  3. 将GPIO和中断连接起来
  4. 配置外部中断初始化结构体
  5. 用函数初始化外部中断
  6. 配置NVIC
  7. 编写中断服务函数
1.初始化GPIO

详情见之前关于GPIO的文章
GPIO
这里只放个例子

GPIO_InitTypeDef GPIO_Initstruct;						//声明GPIO初始化结构体
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);	//打开GPIOB的时钟
GPIO_Initstruct.GPIO_Mode=GPIO_Mode_IN;					//设置为输入模式
GPIO_Initstruct.GPIO_OType=GPIO_OType_OD;				//设置为开漏模式
GPIO_Initstruct.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1;			//引脚选择0和1
GPIO_Initstruct.GPIO_PuPd=GPIO_PuPd_UP;					//上拉模式
GPIO_Initstruct.GPIO_Speed=GPIO_High_Speed;				//高速模式
GPIO_Init(GPIOB,&GPIO_Initstruct);						//初始化GPIO
2. 打开时钟

这个不用解释了吧,只需要知道外部中断的控制器的时钟挂载在APB2时钟组下
因此使用这个函数配置时钟

void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)

这个外设的打开时钟命令基本上是固定的,也就是这句

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
3.GPIO和外部中断的连接

上文提到过,STM32的GPIO都可以当作外部中断使用,但GPIO外部中断只有16个
因此我们需要一个对应关系,让GPIO和外部中断对应起来。
STM32的GPIO端口号对应着外部中断号
例如:所有GPIO组的0管脚对应外部中断0,所有GPIO组的1管脚对应外部中断1
因此不能同时使用2个组的同一管脚号来触发中断。
例如:能用A组的0引脚触发外部中断0,B组的1引脚触发外部中断1.
但不可以使用A组的0引脚和B组的0引脚同时触发外部中断0

我们使用这个函数来配置连接关系

void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex)

第一个参数是有关GPIO的信息
取值可以是

EXTI_PortSourceGPIOA 到 EXTI_PortSourceGPIOK

第二个参数是选择哪个外部中断的
取值可以是

EXTI_PinSource0 到 EXTI_PinSource15

例子

SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB,EXTI_PinSource1);

第一句是指将GPIOA组的引脚0设置为外部中断0
第二句是指将GPIOB组的引脚1设置为外部中断1

4.外部中断初始化结构体

结构体的定义是这个

typedef struct
{
  uint32_t EXTI_Line;  							//选择需要配置的中断
  EXTIMode_TypeDef EXTI_Mode; 					//选择模式
  EXTITrigger_TypeDef EXTI_Trigger;				//选择触发模式
  FunctionalState EXTI_LineCmd; 				//打开或者关闭
}EXTI_InitTypeDef;

EXTI_Line
是需要配置的中断

可以使用或命令( | )同时初始化多个值
取值可以是

EXTI_Line0 到 EXTI_Line23
其中EXTI_Line0 到 EXTI_Line15是关于GPIO的外部中断

EXTI_Mode
是选择模式

取值可以是

EXTI_Mode_Interrupt				//中断模式
EXTI_Mode_Event					//事件模式

中断模式就是我们在这里配置需要用的
事件模式是不产生中断的,可以用这个模式产生一个信号来驱动某些外设,以此来做到不占用CPU的算力,之后使用时会提到
另外中断本身也是事件的一种,其实就是将某些需要CPU响应的事件叫做中断,并进入某个函数。


EXTI_Trigger
是选择中断触发方式的

取值可以是

EXTI_Trigger_Rising					//上升沿触发
EXTI_Trigger_Falling				//下降沿触发
EXTI_Trigger_Rising_Falling			//上升沿和下降沿触发

具体上升沿和下降沿是什么请看上文的解释。


EXTI_LineCmd
使能开关

取值可以是

DISABLE				//关闭(失能)
ENABLE				//打开(使能)
5.初始化外部中断

就是使用这个函数进行初始化

void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)

需要传入的是配置外部中断的结构体的地址
请使用取址符(&)
例子

EXTI_Init(&EXTI_Initstruct);
6.配置NVIC

这部分在之前的文章中介绍过了
中断和NVIC
关于NVIC配置结构体的外部中断中断名称(NVIC_IRQChannel)
可以取值为

EXTI0_IRQn				//外部中断0
EXTI1_IRQn				//外部中断1
EXTI2_IRQn				//外部中断2
EXTI3_IRQn				//外部中断3
EXTI4_IRQn				//外部中断4
EXTI9_5_IRQn			//外部中断5-9
EXTI15_10_IRQn			//外部中断10-15

注意外部中断0 到 外部中断4有独立的中断优先级和中断服务函数,而外部中断5-9共用一个中断优先级和中断服务函数,外部中断10-15同理,
可以在外部中断5-9中使用检测中断标志位的方式来区分是哪个中断触发

7.中断服务函数

因为所以的中断服务函数都在启动的汇编文件中定义过了,因此名字不能错误
注意:区分大小写
中断服务函数的定义时应该这样写

void EXTI0_IRQHandler(void){/*这里写函数内容*/}
void EXTI1_IRQHandler(void){/*这里写函数内容*/}
void EXTI2_IRQHandler(void){/*这里写函数内容*/}
void EXTI3_IRQHandler(void){/*这里写函数内容*/}
void EXTI4_IRQHandler(void){/*这里写函数内容*/}
void EXTI9_5_IRQHandler(void){/*这里写函数内容*/}
void EXTI15_10_IRQHandler(void){/*这里写函数内容*/}

外部中断的使用

当外部中断触发时,就会在一个叫做外部中断挂起寄存器的特点位置写入数据
也就是一个标志位,可以使用标准库函数读取和更改标准位
每当这个标志位置为设置状态时()就会自动跳转到对应的中断服务函数

为了避免出现异常调用,一般在中断服务函数里做一个验证,读取这个标志位是否为置位状态
而且每当中断服务函数结束并且需要下次中断执行中断服务函数,
则需要在函数的最后清除中断标志位

用这个函数读取中断标志位

ITStatus EXTI_GetITStatus(uint32_t EXTI_Line)

返回值是

RESET			//没触发中断
SET				//触发了中断

这是清除外部中断的标志位的函数

void EXTI_ClearITPendingBit(uint32_t EXTI_Line)

这两个函数的输入值可以是

EXTI_Line0 到 EXTI_Line23
其中EXTI_Line0 到 EXTI_Line15是关于GPIO的外部中断

这是一个使用外部中断的模板

//GPIO初始化
void GPIO(void)
{
	GPIO_InitTypeDef GPIO_Initstruct;						//声明GPIO初始化结构体
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);	//打开GPIO时钟
	GPIO_Initstruct.GPIO_Mode=GPIO_Mode_IN;					//输入模式
	GPIO_Initstruct.GPIO_OType=GPIO_OType_OD;				//开漏输入模式
	GPIO_Initstruct.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1;			//引脚0,1
	GPIO_Initstruct.GPIO_PuPd=GPIO_PuPd_UP;					//上拉模式
	GPIO_Initstruct.GPIO_Speed=GPIO_High_Speed;				//高速模式
	GPIO_Init(GPIOB,&GPIO_Initstruct);						//初始化GPIO
}
//外部中断初始化
void EXTI_init(void)
{
	EXTI_InitTypeDef EXTI_Initstruct;								//创建外部中断初始化结构体
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);			//打开时钟
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB,EXTI_PinSource0);	//将GPIO与外部中断连接
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB,EXTI_PinSource1);	//同上
	EXTI_Initstruct.EXTI_Line=EXTI_Line0|EXTI_Line1;							//配置的是外部中断0,1
	EXTI_Initstruct.EXTI_LineCmd=ENABLE;							//使能
	EXTI_Initstruct.EXTI_Mode=EXTI_Mode_Interrupt;					//选择中断模式
	EXTI_Initstruct.EXTI_Trigger=EXTI_Trigger_Falling;				//下降沿模式
	EXTI_Init(&EXTI_Initstruct);									//初始化外部中断0,1
}
//配置NVIC
void EXTI_NVIC(void)
{
	NVIC_InitTypeDef NVIC_Initstruct;						//声明NVIC初始化结构体
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);			//选定NVIC分组
	NVIC_Initstruct.NVIC_IRQChannel=EXTI0_IRQn;				//配置的外部中断0
	NVIC_Initstruct.NVIC_IRQChannelCmd=ENABLE;				//使能
	NVIC_Initstruct.NVIC_IRQChannelPreemptionPriority=1;	//主优先级
	NVIC_Initstruct.NVIC_IRQChannelSubPriority=1;			//副优先级
	NVIC_Init(&NVIC_Initstruct);							//初始化外部中断0的NVIC
	NVIC_Initstruct.NVIC_IRQChannel=EXTI1_IRQn;				//配置的外部中断1
	NVIC_Initstruct.NVIC_IRQChannelCmd=ENABLE;				//同上
	NVIC_Initstruct.NVIC_IRQChannelPreemptionPriority=1;	//同上
	NVIC_Initstruct.NVIC_IRQChannelSubPriority=2;			//同上
	NVIC_Init(&NVIC_Initstruct);							//初始化外部中断1的NVIC
}
//初始化函数
void Init(void)
{
	GPIO();
	EXTI_init();
	EXTI_NVIC();
}
//外部中断0的中断服务函数
void EXTI0_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line0)!=RESET)			//标志位被值位(产生中断)
  	{
  		/*需要操作的内容*/
  		EXTI_ClearITPendingBit(EXTI_Line0);			//清除中断标志位
  	}
}
//外部中断1的中断服务函数
void EXTI1_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line1)!=RESET)			//标志位被值位(产生中断)
  	{
  		/*需要操作的内容*/
  		EXTI_ClearITPendingBit(EXTI_Line1);			//清除中断标志位
  	}
}
  • 3
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值