基于STM32F103ZET6库函数外部中断实验

基于STM32F103ZET6库函数外部中断实验

外部中断简介

通过板载的 3 个按键,控制板载的两个 LED 的亮灭以及蜂鸣器的发声。
代码主要分布在固件库的 stm32f10x_exti.h 和 stm32f10x_exti.c 文件中。

STM32 的每个 IO 都可以作为外部中断 的中断输入口,这点也是 STM32 的强大之处。STM32F103 的中断控制器支持 19 个外部中断/ 事件请求。每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置。STM32F103 的 19 个外部中断为:
线 0~15:对应外部 IO 口的输入中断。
线 16:连接到 PVD 输出。
线 17:连接到 RTC 闹钟事件。
线 18:连接到 USB 唤醒事件。

GPIO 跟中断线的映射关系图:
在这里插入图片描述
在库函数中,配置 GPIO 与中断线的映射关系的函数 GPIO_EXTILineConfig()来实现的:

void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource) ;

该函数将 GPIO 端口与中断线映射起来,使用范例是:

GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2); 

将中断线 2 与 GPIOE 映射起来,那么很显然是 GPIOE.2 与 EXTI2 中断线连接了。

中断线上中断的初始化是通过函数 EXTI_Init()实现的。

void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct); 
EXTI_InitTypeDef  EXTI_InitStructure;    
EXTI_InitStructure.EXTI_Line=EXTI_Line4;    
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 寄存器 

看看结构体 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。
最后一个参数就是使能中断线 了。

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

NVIC_InitTypeDef NVIC_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);                          								//中断优先级分组初始化 

配置完中断优先级之后,接着我们要做的就是编写中断服务函数。中断服务函数的名 字是在 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_t EXTI_Line)

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

void EXTI_ClearITPendingBit(uint32_t EXTI_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 口外部中断的一般步骤

1)初始化 IO 口为输入。
2)开启 AFIO 时钟
3)设置 IO 口与中断线的映射关系。
4)初始化线上中断,设置触发条件等。
5)配置中断分组(NVIC),并使能中断。
6)编写中断服务函数。

软件设计

exti.c

#include "exti.h"
#include "led.h"
#include "key.h"
#include "delay.h"
#include "usart.h"
#include "beep.h"

//外部中断0服务程序
void EXTIX_Init(void)
{
 
	EXTI_InitTypeDef EXTI_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	KEY_Init();	 														//	按键端口初始化

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

	//GPIOE.3	  中断线以及中断初始化配置 下降沿触发 
	//KEY1
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3);
	EXTI_InitStructure.EXTI_Line=EXTI_Line3;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;	
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
	EXTI_Init(&EXTI_InitStructure);	  									//根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器

	//GPIOE.4	  中断线以及中断初始化配置 下降沿触发	
	//KEY0
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource4);
	EXTI_InitStructure.EXTI_Line=EXTI_Line4;
	EXTI_Init(&EXTI_InitStructure);	  									//根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器


	//GPIOA.0	  中断线以及中断初始化配置 上升沿触发   
	//WK_UP
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0); 
	EXTI_InitStructure.EXTI_Line=EXTI_Line0;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
	EXTI_Init(&EXTI_InitStructure);										//根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
	
	
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;					//使能按键WK_UP所在的外部中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;		//抢占优先级2, 
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;				//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;						//使能外部中断通道
	NVIC_Init(&NVIC_InitStructure); 									//根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
	
	NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;					//使能按键KEY1所在的外部中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;		//抢占优先级2 
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;				//子优先级1 
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;						//使能外部中断通道
	NVIC_Init(&NVIC_InitStructure);  	  								//根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
	
	NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;					//使能按键KEY0所在的外部中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;		//抢占优先级2 
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;				//子优先级0 
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;						//使能外部中断通道
	NVIC_Init(&NVIC_InitStructure);  	  								//根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
 
}

//外部中断0服务程序 
void EXTI0_IRQHandler(void)
{
	delay_ms(10);														//消抖
	if(WK_UP==1)	 	 												//WK_UP按键
	{				 
		BEEP=!BEEP;	
	}
	EXTI_ClearITPendingBit(EXTI_Line0); 								//清除LINE0上的中断标志位  
}
 

//外部中断3服务程序
void EXTI3_IRQHandler(void)
{
	delay_ms(10);														//消抖
	if(KEY1==0)	 														//按键KEY1
	{				 
		LED1=!LED1;
	}		 
	EXTI_ClearITPendingBit(EXTI_Line3);  								//清除LINE3上的中断标志位  
}

void EXTI4_IRQHandler(void)
{
	delay_ms(10);														//消抖
	if(KEY0==0)	 														//按键KEY0
	{
		LED0=!LED0;
		LED1=!LED1; 
	}		 
	EXTI_ClearITPendingBit(EXTI_Line4);  								//清除LINE4上的中断标志位  
}

1.因为我们的 WK_UP 按键是高电平有效的,而 KEY0 和 KEY1 是低电平有效的,所以我们设置 WK_UP 为上升沿触发中断,而 KEY0 和 KEY1 则设置为下降沿触发。
2.这里我们把所有中断都分配到第二组,把按键的抢占优先级设置成一样, 而子优先级不同,这三个按键,KEY0 的优先级最高。
3.在最 后有一句 EXTI_ClearITPendingBit(EXTI_Line3);通过该句清除已经发生的中断请求。

exti.h

#ifndef __EXTI_H
#define __EXIT_H	 
#include "sys.h"

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

#endif

main.c

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "exti.h"
#include "beep.h"

int main(void)
 {		
 
	delay_init();	    	 						//延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 							//串口初始化为115200
	LED_Init();		  								//初始化与LED连接的硬件接口 
	BEEP_Init();		 							//初始化蜂鸣器IO
	EXTIX_Init();         							//初始化外部中断输入 
	LED0=0;											//先点亮红灯
	while(1)
	{	    
		printf("OK\r\n");	
		delay_ms(1000);	  
	}	 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

是北豼不太皮吖

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

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

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

打赏作者

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

抵扣说明:

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

余额充值