STM32第十节(中级篇):EXTI(第二节)——GPIO外部中断代码讲解

前言

        好久不见,从今天开始我们接着学习有关EXTI中断的内容。本节课我们通过分析GPIO外部中断代码来讲解我们新的知识点。


STM32第十节(中级篇):EXTI(第二节)——GPIO外部中断代码讲解

        首先,我们拷贝一份上节课的文件,然后进行我们本节课的学习。我们先新建一个文件夹到USER里面,然后写一个.c.h文件,分别为bsp_exti.c以及bsp_exti.h文件。然后导入到keil5中。

EXTI函数库代码讲解

初始化GPIO

        我们打开bsp_exti.c文件,然后创建一个函数void EXIT_Key_Config(void),使得按键初始化,即连接到我们的GPIO端口。首先初始化GPIO,GPIO_InitTypeDef  GPIO_InitStruct; 。在这里,我们之前编写的led函数中就写过关于GPIO的宏定义。并且在bsp_exti.h文件中写入。以下代码就是我们bsp_exti.h文件中所需要的全部代码:

#ifndef  __BSP_EXTI_H_
#define  __BSP_EXTI_H_

#include "stm32f10x.h"

#define KEY1_INT_GPIO_PIN           GPIO_Pin_0
#define KEY1_INT_GPIO_PORT          GPIOA
#define KEY1_INT_GPIO_CLK           RCC_APB2Periph_GPIOA

void EXIT_Key_Config(void);

#endif /* __BSP_EXTI_H_ */

        我们可以看到,我们初始化GPIO的时候,还是老三样:初始化PIN,PORT以及打开为RCC_APB2Periph_GPIOAde的时钟,并将其宏定义为#define KEY1_INT_GPIO_CLK。

        然后我们打开bsp_exti.c文件,然后编写我们的函数。

        我们首先要打开APB2线路上的时钟,使用RCC_APB2PeriphClockCmd(KEY1_INT_GPIO_CLK, ENABLE);函数。他有两个参量,一个是打开RCC_APB2Periph_GPIOA端口,即KEY1_INT_GPIO_CLK。第二个参量我们设置为ENABLE 使得使能打开

        然后我们配置GPIO结构体内容,包括PIN要设置为KEY1_INT_GPIO_PIN,即Pin0。然后就是MODE的配置,配置为浮空输入模式,就是使用外部决定电压。我们默认有一个电阻拉低了电平。

        最后就是设置GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStruct);    函数,我们在这里就不阐述过多,直接写入成员变量KEY1_INT_GPIO_PORT, &GPIO_InitStruct,表示控制的是GPIOA端口以及取GPIO_InitStruct的地址。代码如下所示:

	GPIO_InitTypeDef  GPIO_InitStruct;

    // 初始化GPIO
	RCC_APB2PeriphClockCmd(KEY1_INT_GPIO_CLK, ENABLE);	
	GPIO_InitStruct.GPIO_Pin = KEY1_INT_GPIO_PIN;  
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;  //浮空输入模式		
	GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStruct);	

初始化EXTI用于产生中断/事件

        从这个框图中我们可以知道要先选择输入线,那么我们在选择输入线的时候,自然是要配置寄存器,你们我们配置的是那个寄存器呢?显而易见我们要配置的是AFIO_EXTICR1寄存器。 

         首先,我们选择GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);函数来进行初始化EXTI。里面有两个形参,分别是GPIO_PortSourceGPIOA, 以及GPIO_PinSource0。我们刚刚操作的是AFIO外设,我们知道,输入线默认的时钟是关闭的,那么我们就要配置时钟,即    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);函数,打开RCC_APB2Periph_AFIO端口上的时钟。

        然后就是配置GPIO_InitStruct初始化结构体成员:

	EXTI_InitTypeDef  EXTI_InitStruct;

	// 初始化EXTI
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
	
	EXTI_InitStruct.EXTI_Line = EXTI_Line0;
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;  //触发选择(上升沿)
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStruct);	//写入寄存器中(指针)

初始化NIVC处理中断

        我们知道,在刚刚我们已经配置好了选择线,那么现在就要打开NVIC,涉及到中断。那么我们就要配置中断优先级:那么我们就要编写EXTI_NVIC_Config()函数。我们要找到关于NVIC结构体,编写结构体成员变量,我们先定义一个变量,叫NVIC嵌套向量中断寄存器:NVIC_InitTypeDef  NVIC_InitStruct;定义结构体变量。接着我们就要进行中断分组:使用NVIC_PriorityGroupConfig(unit32_t NVIC_PriorityGroup)函数,配置中断分组。

    NVIC_InitTypeDef NVIC_InitStruct;
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	

        在配置完中断分组之后,我们就要初始化每个结构体成员,接着我们设置一个static关键字,使得该函数只能在该文件下使用,其他的文件函数是调用不了的:

static void EXTI_NVIC_Config(void)
{
	NVIC_InitTypeDef NVIC_InitStruct;
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	
	NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStruct);
}

        在我们配置中断优先级的时候,我们如果选择组0,那么我们就有0个位来表示(0)我们的主优先级,剩下四个位(16)来表示子优先级,对于剩下的中断优先级分组也是如此:

编写中断服务函数

        编译之后无错误之后,我们的中断服务函数要写在stm32f10x.it.c文件中。我们找到void SysTick_Handler(void)函数,在其下方编写新的函数void EXTI0_IRQHandler(void):

void EXTI0_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line0) != RESET)
	{
		LED_G_TOGGLE;
	}
	EXTI_ClearITPendingBit(EXTI_Line0);
}

        我们还需要在stm32f10x.it.c文件中添加bsp_led,h文件,这样我们的中断函数就写完啦。接下来是代码展示(包括KEY2的搭建):

代码展示

bsp_exti.c文件

static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 配置NVIC为优先级组1 */
	/* 提示 NVIC_PriorityGroupConfig() 在整个工程只需要调用一次来配置优先级分组*/
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  
  /* 配置中断源:按键1 */
  NVIC_InitStructure.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ;
  /* 配置抢占优先级 */
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 配置子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中断通道 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  
  /* 配置中断源:按键2,其他使用上面相关配置 */  
  NVIC_InitStructure.NVIC_IRQChannel = KEY2_INT_EXTI_IRQ;
  NVIC_Init(&NVIC_InitStructure);
}

 /**
  * @brief  配置 IO为EXTI中断口,并设置中断优先级
  * @param  无
  * @retval 无
  */
void EXTI_Key_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure; 
	EXTI_InitTypeDef EXTI_InitStructure;

	/*开启按键GPIO口的时钟*/
	RCC_APB2PeriphClockCmd(KEY1_INT_GPIO_CLK,ENABLE);
  RCC_APB2PeriphClockCmd(KEY2_INT_GPIO_CLK,ENABLE);
												
	/* 配置 NVIC 中断*/
	NVIC_Configuration();
	
/*--------------------------KEY1配置-----------------------------*/
	/* 选择按键用到的GPIO */	
  GPIO_InitStructure.GPIO_Pin = KEY1_INT_GPIO_PIN;
  /* 配置为浮空输入 */	
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure);

	/* 选择EXTI的信号源 */
  GPIO_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE, KEY1_INT_EXTI_PINSOURCE); 
  EXTI_InitStructure.EXTI_Line = KEY1_INT_EXTI_LINE;
	
	/* EXTI为中断模式 */
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	/* 上升沿中断 */
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
  /* 使能中断 */	
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);
	
  /*--------------------------KEY2配置-----------------------------*/
	/* 选择按键用到的GPIO */	
  GPIO_InitStructure.GPIO_Pin = KEY2_INT_GPIO_PIN;
  /* 配置为浮空输入 */	
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(KEY2_INT_GPIO_PORT, &GPIO_InitStructure);

	/* 选择EXTI的信号源 */
  GPIO_EXTILineConfig(KEY2_INT_EXTI_PORTSOURCE, KEY2_INT_EXTI_PINSOURCE); 
  EXTI_InitStructure.EXTI_Line = KEY2_INT_EXTI_LINE;
	
	/* EXTI为中断模式 */
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	/* 下降沿中断 */
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
  /* 使能中断 */	
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);
}

bsp_exti.h文件

#ifndef __EXTI_H
#define	__EXTI_H


#include "stm32f10x.h"


//引脚定义
#define KEY1_INT_GPIO_PORT         GPIOA
#define KEY1_INT_GPIO_CLK          (RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO)
#define KEY1_INT_GPIO_PIN          GPIO_Pin_0
#define KEY1_INT_EXTI_PORTSOURCE   GPIO_PortSourceGPIOA
#define KEY1_INT_EXTI_PINSOURCE    GPIO_PinSource0
#define KEY1_INT_EXTI_LINE         EXTI_Line0
#define KEY1_INT_EXTI_IRQ          EXTI0_IRQn

#define KEY1_IRQHandler            EXTI0_IRQHandler


#define KEY2_INT_GPIO_PORT         GPIOC
#define KEY2_INT_GPIO_CLK          (RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO)
#define KEY2_INT_GPIO_PIN          GPIO_Pin_13
#define KEY2_INT_EXTI_PORTSOURCE   GPIO_PortSourceGPIOC
#define KEY2_INT_EXTI_PINSOURCE    GPIO_PinSource13
#define KEY2_INT_EXTI_LINE         EXTI_Line13
#define KEY2_INT_EXTI_IRQ          EXTI15_10_IRQn

#define KEY2_IRQHandler            EXTI15_10_IRQHandler

void EXTI_Key_Config(void);


#endif /* __EXTI_H */

stm32f10_it.c文件

void KEY1_IRQHandler(void)
{
  //确保是否产生了EXTI Line中断
	if(EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET) 
	{
		// LED1 取反		
		LED1_TOGGLE;
    //清除中断标志位
		EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);     
	}  
}

void KEY2_IRQHandler(void)
{
  //确保是否产生了EXTI Line中断
	if(EXTI_GetITStatus(KEY2_INT_EXTI_LINE) != RESET) 
	{
		// LED2 取反		
		LED2_TOGGLE;
    //清除中断标志位
		EXTI_ClearITPendingBit(KEY2_INT_EXTI_LINE);     
	}  
}

小结

        我们本节的内容到这里就都结束啦,下节课开始我们就开始学习有关SysTick(滴答计时器)相关的内容。

作者:Zhang-jk

物联沃分享整理
物联沃-IOTWORD物联网 » STM32第十节(中级篇):EXTI(第二节)——GPIO外部中断代码讲解

发表回复