28.EXTI外部中断原理与配置

EXTI外部中断原理与配置

参考资料
开发板配套资料
《STM32Fx开发指南-HAL库版本》-第x章 外部中断实验
STM32Fxxx官方资料:
《STM32Fxx中文参考手册》- 中断和事件

笔记基于正点原子官方视频
视频连接https://www.bilibili.com/video/BV1Wx411d7wT?p=71&spm_id_from=333.1007.top_right_bar_window_history.content.click
如有侵权,联系删除

一、IO口外部中断原理概述

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

28.EXTI外部中断原理与配置_第1张图片

2.IO口外部中断

① STM32的每个IO都可以作为外部中断输入。
每个外部中断线可以独立的配置触发方式(上升沿下降沿或者双边沿触发),触发/屏蔽,专用的状态位。
③ STM32供IO使用的中断线只有16个,但是STM32F系列的IO口多达上百个,STM32F103ZGT6(112个IO口),那么中断线怎么跟IO口对应呢?

3.GPIO和中断线映射关系

在位置:H:\2技术书籍\STM32F429开发指南中书《STM32F4xx中文参考手册》8.2.4小节及以后几小节有相关寄存器的详细介绍。
28.EXTI外部中断原理与配置_第2张图片
28.EXTI外部中断原理与配置_第3张图片
28.EXTI外部中断原理与配置_第4张图片

其中:
SYSCFG 外部中断配置寄存器 1 的低16位配置的是 EXTI0 至 EXTI3
SYSCFG 外部中断配置寄存器 2 的低16位配置的是 EXTI4 至 EXTI7
SYSCFG 外部中断配置寄存器 3 的低16位配置的是 EXTI8 至 EXTI11
SYSCFG 外部中断配置寄存器 4 的低16位配置的是 EXTI12 至 EXTI15

例如:
在 EXTI0 对应的四个位填入 0000,则配置的是 PA[0] 引脚
在 EXTI2 对应的四个位填入 0000,则配置的是 PA[2] 引脚
在 EXTI6 对应的四个位填入 0011,则配置的是 PD[6] 引脚
在 EXTI12 对应的四个位填入 0110,则配置的是 PG[12] 引脚
在 EXTI15 对应的四个位填入 0101,则配置的是 PF[15] 引脚

4.16个中断线就分配16个中断服务函数?
  • IO口外部中断在中断向量表中只分配了7个中断向量,也就是只能使用7个中断服务函数。
    28.EXTI外部中断原理与配置_第5张图片

  • 从表中可以看出,外部中断线5~9分配一个中断向量,共用一个服务函数外部中断线10~15分配一个中断向量,共用一个中断服务函数。

5.中断服务函数列表:
	EXTI0_IRQHandler 
	EXTI1_IRQHandler
	EXTI2_IRQHandler 
	EXTI3_IRQHandler 
	EXTI4_IRQHandler 
	EXTI9_5_IRQHandler 
	EXTI15_10_IRQHandler

二、IO口外部中断HAL库配置方法

1.外部中断操作使用到的函数分布文件
  • stm32fxxx_hal_gpio.c
    位置:工程模板 - HALLIB - stm32fxxx_hal_gpio.c
  • stm32fxxx_hal_gpio.h
    位置:工程模板 - HALLIB - stm32fxxx_hal_gpio.c - stm32fxxx_hal_gpio.h
2.外部中断配置
  • 外部中断的中断线映射配置触发方式都是在GPIO初始化函数中完成:
	GPIO_InitTypeDef GPIO_Initure;
	GPIO_Initure.Pin=GPIO_PIN_0;								 //PA0
	GPIO_Initure.Mode=GPIO_MODE_IT_RISING; 	//上升沿触发
	GPIO_Initure.Pull=GPIO_PULLDOWN;
	HAL_GPIO_Init(GPIOA,&GPIO_Initure);

在上面代码配置GPIO_Initure.Mode时,里面模式的子代码里就有关于触发方式的代码。
在位置:工程模板 - HARDWARE - exit.c 文件中有关于触发方式的代码,如下图:
28.EXTI外部中断原理与配置_第6张图片

  • 和串口中断一样,HAL库同样提供了外部中断通用处理函数HAL_GPIO_EXTI_IRQHandler,我们在外部中断服务函数中会调用该函数处理中断。
	//中断服务函数
	void EXTI0_IRQHandler(void)
	{
		HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);		//调用中断处理公用函数
	}
	void EXTI2_IRQHandler(void)
	{
		HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);		//调用中断处理公用函数
	}
  • HAL_GPIO_EXTI_IRQHandler函数内部通过判断中断来源引脚,最终调用外部中断回调函数HAL_GPIO_EXTI_Callback来处理中断。
    28.EXTI外部中断原理与配置_第7张图片
	void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
	{
		if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
		{ 
			__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
			HAL_GPIO_EXTI_Callback(GPIO_Pin);
		}
	}
  • 用户最终编写中断处理回调函数来编写中断处理逻辑。
	void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
	{
		switch(GPIO_Pin)
		{
			case GPIO_PIN_0:
			//控制逻辑
			break;
			case GPIO_PIN_2:
			//控制逻辑
			break;
		}
	}
3.外部中断的一般配置步骤

① 使能IO口时钟。
② 初始化IO口,设置触发方式:HAL_GPIO_Init();
③ 设置中断优先级,并使能中断通道。
④ 编写中断服务函数:函数中调用外部中断通用处理函数HAL_GPIO_EXTI_IRQHandler。
⑥ 编写外部中断回调函数:HAL_GPIO_EXTI_Callback;

4.外部中断实验硬件连接

28.EXTI外部中断原理与配置_第8张图片
KEY0->PH3 上拉输入,下降沿触发
KEY1->PH2 上拉输入,下降沿触发
KEY2->PC13 上拉输入,下降沿触发
WK_UP->PA0 下拉输入,上升沿触发

三、IO口外部中断实验

实现的功能:
按键KEY0按下: 同时控制LED0和LED1翻转。
按键KEY1按下: LED1状态翻转。
按键KEY2按下: LED0翻转。
按键WK_UP按下:控制LED0和LED1互斥点亮。

这里使用之前的按键输入实验作为模板进行外部中断实验

1.在HARDWARE文件夹下面创建exti.c和exti.h

在HARDWARE文件夹下面创建exti.c和exti.h,并添加到工程中
28.EXTI外部中断原理与配置_第9张图片
28.EXTI外部中断原理与配置_第10张图片

2.搭建exti.h头文件
1)搭建exti.h框架

28.EXTI外部中断原理与配置_第11张图片

3.搭建exti.c头文件
1)搭建exti.c文件框架

28.EXTI外部中断原理与配置_第12张图片

2)编写中断服务函数框架

因为使用到了PH3, PH2 , PC13,PA0四个引脚,所以要分别对这四个引脚编写中断服务函数

中断服务列表

	EXTI0_IRQHandler 
	EXTI1_IRQHandler
	EXTI2_IRQHandler 
	EXTI3_IRQHandler 
	EXTI4_IRQHandler 
	EXTI9_5_IRQHandler 
	EXTI15_10_IRQHandler

可见用到了EXTI0_IRQHandler、EXTI2_IRQHandler、EXTI3_IRQHandler、EXTI15_10_IRQHandler 四个句柄函数,
同样在位置:工程文件 - CORE - starup_stm32f429xx.s中也可以看到配置好的句柄函数
28.EXTI外部中断原理与配置_第13张图片

所以在exti.c文件中搭建中断服务函数框架
28.EXTI外部中断原理与配置_第14张图片

3)编写外部中断回调函数

在位置:工程文件 - HALLIB - stm32f4xx_hal.c - stm32f4xx_hal_gpio.h 文件中
28.EXTI外部中断原理与配置_第15张图片

至此exti.c文件框架搭建完毕
#include “exti.h”

void EXTI_Init(void)
{

}

void EXTI0_IRQHandler(void)
{

}

void EXTI2_IRQHandler(void)
{

}

void EXTI3_IRQHandler(void)
{

}

void EXTI15_10_IRQHandler(void)
{

}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{

}

4.使能IO口时钟、设置触发方式

按键模式配置
KEY0->PH3 上拉输入,下降沿触发
KEY1->PH2 上拉输入,下降沿触发
KEY2->PC13 上拉输入,下降沿触发
WK_UP->PA0 下拉输入,上升沿触发

因为用到了PA、PC、PH三个IO口,所以要对这三个口进行初始化,然后对IO口模式进行配置,具体使能方式前文按键配置有详细说明。
代码如下

	    GPIO_InitTypeDef GPIO_Initure;
	    
	    __HAL_RCC_GPIOA_CLK_ENABLE();               //开启GPIOA时钟
	    __HAL_RCC_GPIOC_CLK_ENABLE();               //开启GPIOC时钟
	    __HAL_RCC_GPIOH_CLK_ENABLE();               //开启GPIOH时钟
	        
	    GPIO_Initure.Pin=GPIO_PIN_0;                //PA0
	    GPIO_Initure.Mode=GPIO_MODE_IT_RISING;      //上升沿触发
	    GPIO_Initure.Pull=GPIO_PULLDOWN;			//下拉
		GPIO_Initure.Speed=GPIO_SPEED_HIGH;			//高速
	    HAL_GPIO_Init(GPIOA,&GPIO_Initure);			
	    
	    GPIO_Initure.Pin=GPIO_PIN_13;               //PC13
	    GPIO_Initure.Mode=GPIO_MODE_IT_FALLING;     //下降沿触发
	    GPIO_Initure.Pull=GPIO_PULLUP;				//上拉
		GPIO_Initure.Speed=GPIO_SPEED_HIGH;			//高速
	    HAL_GPIO_Init(GPIOC,&GPIO_Initure);			
	    
	    GPIO_Initure.Pin=GPIO_PIN_2|GPIO_PIN_3;     //PH2,3
	    GPIO_Initure.Mode=GPIO_MODE_IT_FALLING;     //下降沿触发
	    GPIO_Initure.Pull=GPIO_PULLUP;				//上拉
		GPIO_Initure.Speed=GPIO_SPEED_HIGH;			//高速
	    HAL_GPIO_Init(GPIOH,&GPIO_Initure);
5.设置中断优先级、使能中断通道

其中共调用了4个中断,所以要配置4条中断线
在位置:工程文件 - HALLIB - stm32f4xx_hal.c - stm32f429xx.h 文件中有中断线函数的头文件名
28.EXTI外部中断原理与配置_第16张图片

配置在 HAL_NVIC_SetPriority(EXTI0_IRQn,2,0); 和 HAL_NVIC_EnableIRQ(EXTI0_IRQn); 中

    //中断线0-PA0
    HAL_NVIC_SetPriority(EXTI0_IRQn,2,0);       //抢占优先级为2,子优先级为0
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);             //使能中断线0
    
    //中断线2-PH2
    HAL_NVIC_SetPriority(EXTI2_IRQn,2,1);       //抢占优先级为2,子优先级为1
    HAL_NVIC_EnableIRQ(EXTI2_IRQn);             //使能中断线2
    
    //中断线3-PH3
    HAL_NVIC_SetPriority(EXTI3_IRQn,2,2);       //抢占优先级为2,子优先级为2
    HAL_NVIC_EnableIRQ(EXTI3_IRQn);             //使能中断线2
    
    //中断线13-PC13
    HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,3);   //抢占优先级为2,子优先级为3
    HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);         //使能中断线13 

至此,外部中断初始化函数编写完毕,代码如下

void EXTI_Init(void)				//外部中断初始化
{
    GPIO_InitTypeDef GPIO_Initure;
    
    __HAL_RCC_GPIOA_CLK_ENABLE();               //开启GPIOA时钟
    __HAL_RCC_GPIOC_CLK_ENABLE();               //开启GPIOC时钟
    __HAL_RCC_GPIOH_CLK_ENABLE();               //开启GPIOH时钟
    
    GPIO_Initure.Pin=GPIO_PIN_0;                //PA0
    GPIO_Initure.Mode=GPIO_MODE_IT_RISING;      //上升沿触发
    GPIO_Initure.Pull=GPIO_PULLDOWN;			//下拉
	GPIO_Initure.Speed=GPIO_SPEED_HIGH;			//高速
    HAL_GPIO_Init(GPIOA,&GPIO_Initure);			
    
    GPIO_Initure.Pin=GPIO_PIN_13;               //PC13
    GPIO_Initure.Mode=GPIO_MODE_IT_FALLING;     //下降沿触发
    GPIO_Initure.Pull=GPIO_PULLUP;				//上拉
	GPIO_Initure.Speed=GPIO_SPEED_HIGH;			//高速
    HAL_GPIO_Init(GPIOC,&GPIO_Initure);			
    
    GPIO_Initure.Pin=GPIO_PIN_2|GPIO_PIN_3;     //PH2,3
    GPIO_Initure.Mode=GPIO_MODE_IT_FALLING;     //下降沿触发
    GPIO_Initure.Pull=GPIO_PULLUP;				//上拉
	GPIO_Initure.Speed=GPIO_SPEED_HIGH;			//高速
    HAL_GPIO_Init(GPIOH,&GPIO_Initure);
    
    //中断线0-PA0
    HAL_NVIC_SetPriority(EXTI0_IRQn,2,0);       //抢占优先级为2,子优先级为0
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);             //使能中断线0
    
    //中断线2-PH2
    HAL_NVIC_SetPriority(EXTI2_IRQn,2,1);       //抢占优先级为2,子优先级为1
    HAL_NVIC_EnableIRQ(EXTI2_IRQn);             //使能中断线2
    
    //中断线3-PH3
    HAL_NVIC_SetPriority(EXTI3_IRQn,2,2);       //抢占优先级为2,子优先级为2
    HAL_NVIC_EnableIRQ(EXTI3_IRQn);             //使能中断线2
    
    //中断线13-PC13
    HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,3);   //抢占优先级为2,子优先级为3
    HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);         //使能中断线13  	
}
6.编写中断服务器函数

上文说到,中断服务函数格式如下:
28.EXTI外部中断原理与配置_第17张图片

在位置:工程文件 - HALLIB - stm32f4xx_hal.c - stm32f4xx_hal_gpio.h 文件中也有其定义句柄函数名
28.EXTI外部中断原理与配置_第18张图片

把该句柄函数放在中断服务器函数中,代码如下

void EXTI0_IRQHandler(void)
{
	HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);		//调用中断处理公用函数
}

void EXTI2_IRQHandler(void)
{
	HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);		//调用中断处理公用函数
}

void EXTI3_IRQHandler(void)
{
	HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3);		//调用中断处理公用函数
}

void EXTI15_10_IRQHandler(void)
{
	HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);		//调用中断处理公用函数
}
7.编写外部中断回调函数

因为在HAL库中,有关于位带操作的定义,所以可以直接使用LED0、LED1、KEY0、KEY1、KEY2、WK_UP变量名来编写函数
28.EXTI外部中断原理与配置_第19张图片
28.EXTI外部中断原理与配置_第20张图片

编写好按键控制逻辑,代码如下

#include "delay.h"
#include "led.h"
#include "key.h"

//中断服务程序中需要做的事情
//在HAL库中所有的外部中断服务函数都会调用此函数
//GPIO_Pin:中断引脚号
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    delay_ms(100);      //消抖
    switch(GPIO_Pin)
    {
        case GPIO_PIN_0:
            if(WK_UP==1) 
            {
				LED1=!LED1;//控制LED0,LED1互斥点亮
				LED0=!LED1;
            }
            break;
        case GPIO_PIN_2:
            if(KEY1==0)  //LED1翻转
            {
                LED1=!LED1;    
            }
            break;
        case GPIO_PIN_3:
            if(KEY0==0)  //同时控制LED0,LED1翻转 
            {
                LED0=!LED0;
				LED1=!LED1;
            }
            break;

        case GPIO_PIN_13:
            if(KEY2==0)  
            {
				LED0=!LED0;//控制LED0翻转
            }
            break;
    }
}

至此,exti.c文件编写完毕,代码如下:

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


void EXTI_Init(void)				//外部中断初始化
{
    GPIO_InitTypeDef GPIO_Initure;
    
    __HAL_RCC_GPIOA_CLK_ENABLE();               //开启GPIOA时钟
    __HAL_RCC_GPIOC_CLK_ENABLE();               //开启GPIOC时钟
    __HAL_RCC_GPIOH_CLK_ENABLE();               //开启GPIOH时钟
    
    GPIO_Initure.Pin=GPIO_PIN_0;                //PA0
    GPIO_Initure.Mode=GPIO_MODE_IT_RISING;      //上升沿触发
    GPIO_Initure.Pull=GPIO_PULLDOWN;			//下拉
	GPIO_Initure.Speed=GPIO_SPEED_HIGH;			//高速
    HAL_GPIO_Init(GPIOA,&GPIO_Initure);			
    
    GPIO_Initure.Pin=GPIO_PIN_13;               //PC13
    GPIO_Initure.Mode=GPIO_MODE_IT_FALLING;     //下降沿触发
    GPIO_Initure.Pull=GPIO_PULLUP;				//上拉
	GPIO_Initure.Speed=GPIO_SPEED_HIGH;			//高速
    HAL_GPIO_Init(GPIOC,&GPIO_Initure);			
    
    GPIO_Initure.Pin=GPIO_PIN_2|GPIO_PIN_3;     //PH2,3
    GPIO_Initure.Mode=GPIO_MODE_IT_FALLING;     //下降沿触发
    GPIO_Initure.Pull=GPIO_PULLUP;				//上拉
	GPIO_Initure.Speed=GPIO_SPEED_HIGH;			//高速
    HAL_GPIO_Init(GPIOH,&GPIO_Initure);
    
    //中断线0-PA0
    HAL_NVIC_SetPriority(EXTI0_IRQn,2,0);       //抢占优先级为2,子优先级为0
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);             //使能中断线0
    
    //中断线2-PH2
    HAL_NVIC_SetPriority(EXTI2_IRQn,2,1);       //抢占优先级为2,子优先级为1
    HAL_NVIC_EnableIRQ(EXTI2_IRQn);             //使能中断线2
    
    //中断线3-PH3
    HAL_NVIC_SetPriority(EXTI3_IRQn,2,2);       //抢占优先级为2,子优先级为2
    HAL_NVIC_EnableIRQ(EXTI3_IRQn);             //使能中断线2
    
    //中断线13-PC13
    HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,3);   //抢占优先级为2,子优先级为3
    HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);         //使能中断线13  	
}

void EXTI0_IRQHandler(void)
{
	HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);		//调用中断处理公用函数
}

void EXTI2_IRQHandler(void)
{
	HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);		//调用中断处理公用函数
}

void EXTI3_IRQHandler(void)
{
	HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3);		//调用中断处理公用函数
}

void EXTI15_10_IRQHandler(void)
{
	HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);		//调用中断处理公用函数
}


//中断服务程序中需要做的事情
//在HAL库中所有的外部中断服务函数都会调用此函数
//GPIO_Pin:中断引脚号
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    delay_ms(100);      //消抖
    switch(GPIO_Pin)
    {
        case GPIO_PIN_0:
            if(WK_UP==1) 
            {
				LED1=!LED1;//控制LED0,LED1互斥点亮
				LED0=!LED1;
            }
            break;
        case GPIO_PIN_2:
            if(KEY1==0)  //LED1翻转
            {
                LED1=!LED1;    
            }
            break;
        case GPIO_PIN_3:
            if(KEY0==0)  //同时控制LED0,LED1翻转 
            {
                LED0=!LED0;
				LED1=!LED1;
            }
            break;

        case GPIO_PIN_13:
            if(KEY2==0)  
            {
				LED0=!LED0;//控制LED0翻转
            }
            break;
    }
}
8.编写main.c函数
  • 引入#include “exti.h” 头文件
  • 把初始化按键函数 KEY_Init(); 更改为EXTI_Init();

至此,main.c函数编写完毕,代码如下

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

int main(void)
{
    HAL_Init();                     //初始化HAL库   
    Stm32_Clock_Init(360,25,2,8);   //设置时钟,180Mhz
    delay_init(180);                //初始化延时函数
    uart_init(115200);              //初始化USART
    LED_Init();                     //初始化LED 
    EXTI_Init();                    //外部中断初始化
    while(1)
    {
        printf("OK\r\n");           //打印OK提示程序运行
        delay_ms(1000);             //每隔1s打印一次 
    }
}
9.下载到开发板

现象这里不再赘述

至此,IO口外部中断实验结束

你可能感兴趣的:(单片机,stm32,嵌入式硬件)