欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【STM32】外部中断

程序员文章站 2024-02-25 10:58:16
...

00. 目录

01. 外部中断简介

外部中断的代码主要分布在固件库的 stm32f4xx_exti.h 和 stm32f4xx_exti.c 文件中。

STM32F4 的每个 IO 都可以作为外部中断的中断输入口,这点也是 STM32F4 的强大之处。STM32F407 的中断控制器支持 22
个外部中断/事件请求。每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置。

STM32F407 的 22 个外部中断为:

  • EXTI 线 0~15:对应外部 IO 口的输入中断。
  • EXTI 线 16:连接到 PVD 输出。
  • EXTI 线 17:连接到 RTC 闹钟事件。
  • EXTI 线 18:连接到 USB OTG FS 唤醒事件。
  • EXTI 线 19:连接到以太网唤醒事件。
  • EXTI 线 20:连接到 USB OTG HS(在 FS 中配置)唤醒事件。
  • EXTI 线 21:连接到 RTC 入侵和时间戳事件。
  • EXTI 线 22:连接到 RTC 唤醒事件。

从上面可以看出,STM32F4 供 IO 口使用的中断线只有 16 个,但是 STM32F4 的 IO 口却远远不止 16 个,那么 STM32F4 是怎么把 16 个中断线和 IO 口一一对应起来的呢?于是 STM32就这样设计,GPIO 的管教 GPIOx.0~GPIOx.15(x=A,B,C,D,E,F,G,H,I)分别对应中断线 0~15。这样每个中断线对应了最多 9 个 IO 口,以线 0 为例:它对应了 GPIOA.0、GPIOB.0、GPIOC.0、GPIOD.0、GPIOE.0、GPIOF.0、GPIOG.0,GPIOH.0,GPIOI.0。而中断线每次只能连接到 1 个 IO口上,这样就需要通过配置来决定对应的中断线配置到哪个 GPIO 上了。下面我们看看 GPIO跟中断线的映射关系图:

【STM32】外部中断

02. 外部中断配置

2.1 使能 IO口时钟,初始化 IO

我们要使用 IO 口作为中断输入,所以我们要使能相应的 IO 口时钟,以及初始化相应的 IO 口为输入模式

2.2 开启 SYSCFG 时钟,设置 IO 口与中断线的映射关系

只要我们使用到外部中断,就必须打开 SYSCFG 时钟。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能 SYSCFG 时钟

配置 GPIO 与中断线的映射关系。在库函数中,配置 GPIO 与中断线的映射关系的函数 SYSCFG_EXTILineConfig ()来实现的:

void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex);

该函数将 GPIO 端口与中断线映射起来,程序示例:

SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);

2.3 初始化线上中断,设置触发条件

中断线上中断的初始化是通过函数 EXTI_Init()实现的。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 寄存器

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

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

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

2.5 编写中断服务函数

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

DCD     EXTI0_IRQHandler                  ; EXTI Line0                                             
DCD     EXTI1_IRQHandler                  ; EXTI Line1                                             
DCD     EXTI2_IRQHandler                  ; EXTI Line2                                             
DCD     EXTI3_IRQHandler                  ; EXTI Line3                                             
DCD     EXTI4_IRQHandler                  ; EXTI Line4   
DCD     EXTI9_5_IRQHandler                ; External Line[9:5]s   
DCD     EXTI15_10_IRQHandler              ; External Line[15:10]s  

中断线 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)

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

03. 外部中断步骤

使用 IO 口外部中断的一般步骤:
1)使能 IO 口时钟,初始化 IO 口为输入。
2)使能 SYSCFG 时钟,设置 IO 口与中断线的映射关系。
3)初始化线上中断,设置触发条件等。
4)配置中断分组(NVIC),并使能中断。
5)编写中断服务函数。

04. 硬件设计

探索者 STM32F4 开发板上载有的 4 个按钮(KEY_UP、KEY0、KEY1 和 KEY2),来控制板上的 2 个 LED(DS0 和 DS1)和蜂鸣器,其中 KEY_UP 控制蜂鸣器,按一次叫,再按一次停;KEY0 控制 DS0,按一次亮,再按一次灭;KEY1 控制 DS1,
效果同 KEY2;KEY2 则同时控制 DS0 和 DS1,按一次,它们的状态就翻转一次。
【STM32】外部中断

05. 程序示例一

KEY0控制LED1的翻转

exti.h文件

#ifndef __EXTI_H__
#define __EXTI_H__

#include "sys.h"

//中断初始化函数
 void EXTIX_Init(void);

#endif /*__EXTI_H__*/

exti.c文件

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

//中断初始化函数
 void EXTIX_Init(void)
 {
	 EXTI_InitTypeDef EXTI_InitStruct;
	 
	 NVIC_InitTypeDef NVIC_InitStruct;
	 
	 //按键初始化
	 KEY_Init();
	 
	 //初始化SYSCFG时钟
	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
	 
	 //KEY0 --> PE4
	 SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource4);
	 
	 //配置中断
	 EXTI_InitStruct.EXTI_Line = EXTI_Line4;
	 EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
	 EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
	 EXTI_InitStruct.EXTI_LineCmd = ENABLE;
	 EXTI_Init(&EXTI_InitStruct);
	 
	 //配置中断分组
	 NVIC_InitStruct.NVIC_IRQChannel = EXTI4_IRQn;
	 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
	 NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
	 NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;

	 NVIC_Init(&NVIC_InitStruct);
 }

//中断线4处理函数
void EXTI4_IRQHandler(void)
{
	printf("KEY0按下啦....\r\n");
	//消抖
	delay_ms(10);
	
	if (0 == KEY0)
	{
		LED1 = !LED1;
	}

	//清中断
	EXTI_ClearITPendingBit(EXTI_Line4);
}

main.c文件

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

int main(void)
{ 
	
	BEEP = 0;
	LED1 = 0;
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	BEEP_Init();
	LED_Init();
	
	delay_init(168);
	uart_init(115200);


	//外部中断初始化
	EXTIX_Init();
	
	while(1)
	{
		printf("程序正在运行.....\r\n");
		
		delay_ms(1000);
	}	
}

06. 程序示例二

exti.h文件

#ifndef __EXTI_H__
#define __EXTI_H__

#include "sys.h"

//中断初始化函数
 void EXTIX_Init(void);

#endif /*__EXTI_H__*/

exti.c文件

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

//中断初始化函数
 void EXTIX_Init(void)
 {
	 EXTI_InitTypeDef EXTI_InitStruct;
	 
	 NVIC_InitTypeDef NVIC_InitStruct;
	 
	 //按键初始化
	 KEY_Init();
	 
	 //初始化SYSCFG时钟
	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
	 
	 //KEY0 --> PE4
	 SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource4);
	 
	 //KEY1 --> PE3
	 SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource3);
	 
	 //KEY1 --> PE2
	 SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource2);
	 
	 //KEY_UP --> PA0
	 SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);
	 
	 //配置中断 KEY0 KEY1 KEY2
	 EXTI_InitStruct.EXTI_Line = EXTI_Line4 | EXTI_Line3 | EXTI_Line2;
	 EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
	 EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
	 EXTI_InitStruct.EXTI_LineCmd = ENABLE;
	 EXTI_Init(&EXTI_InitStruct);
	 
 
	 //配置中断 KEY_UP
	 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);
	 
	 
	 //配置中断分组 KEY0
	 NVIC_InitStruct.NVIC_IRQChannel = EXTI4_IRQn;
	 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
	 NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
	 NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;

	 NVIC_Init(&NVIC_InitStruct);
	 
	 //配置中断分组 KEY1
	 NVIC_InitStruct.NVIC_IRQChannel = EXTI3_IRQn;
	 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
	 NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
	 NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;

	 NVIC_Init(&NVIC_InitStruct);
	 
	 //配置中断分组 KEY2
	 NVIC_InitStruct.NVIC_IRQChannel = EXTI2_IRQn;
	 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
	 NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
	 NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;

	 NVIC_Init(&NVIC_InitStruct);
	 
	 //配置中断分组 WK_UP
	 NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
	 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
	 NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
	 NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;

	 NVIC_Init(&NVIC_InitStruct);
 }

//中断线4处理函数
void EXTI4_IRQHandler(void)
{
	printf("KEY0按下啦....\r\n");
	//消抖
	delay_ms(10);
	
	if (0 == KEY0)
	{
		LED1 = !LED1;
	}

	//清中断
	EXTI_ClearITPendingBit(EXTI_Line4);
}


//中断线3处理函数
void EXTI3_IRQHandler(void)
{
	printf("KEY1按下啦....\r\n");
	//消抖
	delay_ms(10);
	
	if (0 == KEY1)
	{
		LED2 = !LED2;
	}

	//清中断
	EXTI_ClearITPendingBit(EXTI_Line3);
}


//中断线2处理函数
void EXTI2_IRQHandler(void)
{
	printf("KEY2按下啦....\r\n");
	//消抖
	delay_ms(10);
	
	if (0 == KEY2)
	{
		LED1 = !LED1;
		LED2 = !LED2;
	}

	//清中断
	EXTI_ClearITPendingBit(EXTI_Line2);
}


//中断线0处理函数
void EXTI0_IRQHandler(void)
{
	printf("KEY_UP按下啦....\r\n");
	//消抖
	delay_ms(10);
	
	if (1 == WK_UP)
	{
		BEEP = !BEEP;
	}

	//清中断
	EXTI_ClearITPendingBit(EXTI_Line0);
}

main.c文件

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

int main(void)
{ 
	
	BEEP = 0;
	LED1 = 0;
	LED2 = 0;
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	BEEP_Init();
	LED_Init();
	
	delay_init(168);
	uart_init(115200);


	//外部中断初始化
	EXTIX_Init();
	
	while(1)
	{
		printf("程序正在运行.....\r\n");
		
		delay_ms(1000);
	}	
}

07. 附录

7.1 【STM32】STM32系列教程汇总

网址:【STM32】STM32系列教程汇总

08. 声明

该教程参考了正点原子的《STM32 F4 开发指南》