stm32外部中断详解

本文详细介绍了STM32的外部中断(EXTI)工作原理、配置过程,包括外部中断线、中断向量表、中断优先级设置,以及如何使用库函数配置GPIO和NVIC。通过实例演示了如何配置多个中断源并处理中断服务。
摘要由CSDN通过智能技术生成

目录

前言

一、外部中断是什么

二、外部中断概述

1.外部中断线

2.框图分析

3.中断向量表

4.中断优先级配置

三、库函数使用

1.配置初始化

2.中断服务函数

3.主函数

4.有一个问题,既然Px0全部连接到一条线上,那么能不能有假设PA0和PB0,PC0都用中断呢

总结

前言

相比较于51单片机来说,stm32拥有大量的外部中断资源,利用好外部中断能够带来很多便利

一、外部中断是什么

EXTI(External interupt/event Controller外部中断/事件控制器)控制,在 APB2 总线上,F1系列有20个EXTI线。好比于你在烧一壶水,你并不知道什么时候烧开,这时你需要不断的去查看是否烧开了,这种在单片机中就是查询,若是你使用了定时烧水,那么定时时间到了会提醒你,就不用你一次一次的去查看了,这种就是单片机中使用了外部中断,即在外部事件发生时,产生一个中断,提醒CPU去处理这个中断

二、外部中断概述

1.外部中断线

STM32供IO使用的中断线只有16个,但是STM32F10x系列的IO口多达上百个,STM32F103ZET6(112),STM32F103RCT6(51),下图会给出答案

112通用I/O端口以下图的方式连接到16个外部中断/事件线上

 EXTI线16连接到PVD输出

 EXTI线17连接到RTC闹钟事件

EXTI线18连接到USB唤醒事件 

EXTI线19连接到以太网唤醒事件(只适用于互联型产品)

我们可以给每一条中断线配置边沿触发方式

2.框图分析

红线是中断--属于软件级的

  1. 是GPIO的任意一个引脚,一般是存在电平变化的信号,使用AFIO的寄存器进行配置
  2. 是一个边沿检测电路,通过上升/下降沿触发选择器寄存器,对应位给1开启边沿信号检测,检测到了就在3或门处给个1,而两个寄存器可以配置上升沿/下降沿,或者上升沿和下降沿都触发
  3. 是一个或门电路,软件中断事件寄存器由软件置位 1,这一操作会把请求挂起寄存器的相应位置1
  4. 是一个与门电路,中断屏蔽寄存器设置为1表示开放中断,编号 4 电路输出的信号会被保存到挂起寄存器(EXTI_PR)内,如果确定编号 4 电路输出为 1 就会把 EXTI_PR(挂起寄存器) 对应位置 1,在该位中写入’1’可以清除它,也可以通过改变边沿检测的极性清除。
  5. 发送到内核NVIC中请求产生中断,并需求中断服务函数

绿线是事件-最终输出一个脉冲信号--属于硬件级的

6.是一个与门电路,那么可以简单的控制 EXTI_EMR 来实现是否产生事件的目的,EXTI_EMR事件屏蔽寄存器控制, 写1使能, 来到脉冲发生器

7.脉冲发生器,输入1就 产生脉冲

8.脉冲信号,就是产生事件的线路最终的产物,这个脉冲信号可以给其他外设电路使用,比如定时器 TIM、模拟数字转换器 ADC 等等,这样的脉冲信号一般用来触发 TIM 或者 ADC 开始转换

3.中断向量表

在中断向量表里给我们列出了各种外部中断

下图只是部分,具体请看参考手册

 

 黑色部分是内核水平的中断,白色部分是外部中断

4.中断优先级配置

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

外部中断线10~15分配一个中断向量,共用一个中断服务函数。

 现在NVIC中配置优先级分组,然后在利用库函数配置每个的优先级

如下,我配置的是两个按键的中断,优先级分组为0,那根据上图可知主优先级没有,只有子优先级

我又给两个中断配置了一样的优先级,这叫软件优先级,当软件优先级相同时,就比较硬件优先级(中断向量表里面的排序就是硬件优先级,越小,优先级越高)

static void EXTI_NVIC_Config(void)
{
	NVIC_InitTypeDef NVICA_InitStructure,NVICC_InitStructure;
	
	//配置优先级分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
	//配置优先级
	//配置按键KA0
	NVICA_InitStructure.NVIC_IRQChannel                   = KEYA_INT_EXTI_IRQChanned;
	NVICA_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVICA_InitStructure.NVIC_IRQChannelSubPriority        = 1; 
	NVICA_InitStructure.NVIC_IRQChannelCmd 				 = ENABLE;
	NVIC_Init(&NVICA_InitStructure);
	    
	
	//配置按键C13
	NVICC_InitStructure.NVIC_IRQChannel                   = KEYC_INT_EXTI_IRQChanned;
	NVICC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVICC_InitStructure.NVIC_IRQChannelSubPriority        = 1; 
	NVICC_InitStructure.NVIC_IRQChannelCmd 				  = ENABLE;
	NVIC_Init(&NVICC_InitStructure);
}

下面是中断服务函数 ,名字一定要正确!

EXTI0_IRQHandler          

EXTI1_IRQHandler

EXTI2_IRQHandler          

EXTI3_IRQHandler          

EXTI4_IRQHandler          

EXTI9_5_IRQHandler        

EXTI15_10_IRQHandler     

三、库函数使用

步骤

1.开启时钟-->AFIO,GPIO

2.配置GPIO结构体,EXTI结构体

3.选择中断源 --void GPIO_EXTILineConfig(参数);

4.初始化NVIC--优先级分组,配置优先级(初始化结构体)

5.中断服务函数,记得清除标志位

1.配置初始化

由于选择中断线需要用到AFIO的寄存器,所以这里要开启AFIO的时钟

#ifndef __BSP_KEY_H
#define __BSP_KEY_H

#define KEYA_INT_GPIO_PIN      	    GPIO_Pin_0
#define KEYA_INT_GPIO_PORT			GPIOA
#define KEYA_INT_GPIO_CLK			RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO
#define KEYA_INT_EXTI_Mode          EXTI_Mode_Interrupt
#define	KEYA_INT_EXTI_Line          EXTI_Line0
#define KEYA_INT_EXTI_TRIGGER       EXTI_Trigger_Rising
#define KEYA_INT_EXTI_IRQChanned    EXTI0_IRQn
#define KEYA_INT_EXTI_PinSource     GPIO_PinSource0    

#define KEYC_INT_GPIO_PIN      	    GPIO_Pin_13
#define KEYC_INT_GPIO_PORT			GPIOC
#define KEYC_INT_GPIO_CLK			RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO
#define KEYC_INT_EXTI_Mode          EXTI_Mode_Interrupt
#define	KEYC_INT_EXTI_Line          EXTI_Line13
#define KEYC_INT_EXTI_TRIGGER       EXTI_Trigger_Falling
#define KEYC_INT_EXTI_IRQChanned    EXTI15_10_IRQn
#define KEYC_INT_EXTI_PinSource     GPIO_PinSource13   

#endif
void EXTI_Key_Config(void)
{
	GPIO_InitTypeDef KEYA_InitStruct,KEYC_InitStruct;
	EXTI_InitTypeDef EXTI0_InitStruct,EXTI13_InitStruct;
	//初始化NVIC
	EXTI_NVIC_Config();
	//初始化KA0
	//GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;速度不用配置
	RCC_APB2PeriphClockCmd(KEYA_INT_GPIO_CLK,ENABLE);//开启Key_A0相关的外设时钟和AFIO时钟
	KEYA_InitStruct.GPIO_Mode  = GPIO_Mode_IN_FLOATING;//配置成浮空输入
	KEYA_InitStruct.GPIO_Pin   = KEYA_INT_GPIO_PIN;
	GPIO_Init(KEYA_INT_GPIO_PORT,&KEYA_InitStruct);

	//初始化KC13
	//GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;速度不用配置
	RCC_APB2PeriphClockCmd(KEYC_INT_GPIO_CLK,ENABLE);//开启Key_C13相关的外设时钟和AFIO时钟
	KEYC_InitStruct.GPIO_Mode  = GPIO_Mode_IN_FLOATING;//配置成浮空输入
	KEYA_InitStruct.GPIO_Pin   = KEYC_INT_GPIO_PIN;
	GPIO_Init(KEYC_INT_GPIO_PORT,&KEYC_InitStruct);
	
	
	//初始化EXTI0
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,KEYA_INT_EXTI_PinSource);//配置EXTI信号源
	EXTI0_InitStruct.EXTI_Line = KEYA_INT_EXTI_Line;//EXTI0
	EXTI0_InitStruct.EXTI_Mode = KEYA_INT_EXTI_Mode;//中断
	EXTI0_InitStruct.EXTI_Trigger = KEYA_INT_EXTI_TRIGGER; //上升沿
	EXTI0_InitStruct.EXTI_LineCmd = ENABLE;//使能中断
	EXTI_Init(&EXTI0_InitStruct);
	
	//初始化EXTI13
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,KEYC_INT_EXTI_PinSource);//配置EXTI信号源
	EXTI13_InitStruct.EXTI_Line = KEYC_INT_EXTI_Line;//EXTI13
	EXTI13_InitStruct.EXTI_Mode = KEYC_INT_EXTI_Mode;//中断
	EXTI13_InitStruct.EXTI_Trigger = KEYC_INT_EXTI_TRIGGER; //下降沿
	EXTI13_InitStruct.EXTI_LineCmd = ENABLE;//使能中断
	EXTI_Init(&EXTI13_InitStruct);
}

2.中断服务函数

注意:中断服务函数一定要正确,在起始文件中寻找

void EXTI0_IRQHandler()
{
	if(EXTI_GetFlagStatus(EXTI_Line0) != RESET) //判断标志位
	{
		LED_G_TOGGLE;//翻转LED
		EXTI_ClearFlag(EXTI_Line0);//清除标志位
	}
}

void EXTI15_10_IRQHandler()
{
	if(EXTI_GetFlagStatus(EXTI_Line13) != RESET)
	{
		LED_R_TOGGLE;
		EXTI_ClearFlag(EXTI_Line13);
	}
}

3.主函数

void main()
{
	LED_GPIO_Config();
	EXTI_Key_Config();
	while(1);
}

4.有一个问题,既然Px0全部连接到一条线上,那么能不能有假设PA0和PB0,PC0都用中断呢

我认为是可以的,虽然理论上不能同时使用

思路是这样的:在中断服务函数中进行IO电平的查询,即对GPIO的IDR寄存器进行读取

我这里是对给PA0和PB0,PC13都配置中断,来检测PA0和PB0能不能通过查询IDR的方式来一起使用,然后我在中断服务函数中进行GPIO->IDR的读取操作,判断是哪个IO口请求了中断,分别进行中断的处理

本人试了一下,并没有实现,有可能是翻转LED的IO口来进入中断不好用,也可能是就不能这样子进行配置,目前不知道具体原因,希望可以和大家多多交流,找出答案

//中断配置NVIC

static void NVIC_Config()
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStruct);
	
	NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;	
	NVIC_Init(&NVIC_InitStruct);
}


//初始化代码
	EXTI_InitTypeDef EXTI_InitStruct;
	
	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);
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,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);


	GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource13);
	EXTI_InitStruct.EXTI_Line = EXTI_Line13;
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStruct);
	
//中断服务函数

void EXTI0_IRQHandler(void)
{
	if(EXTI_GetFlagStatus(EXTI_Line0) == SET)
	{
			if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) == SET)//读取按键1
		{
			LED_R_TOGGLE;//翻转红灯
			EXTI_ClearFlag(EXTI_Line0);
		}
		if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0) == RESET)//判断PB0是否请求中断
		{
			LED_B_TOGGLE;//翻转蓝灯
			EXTI_ClearFlag(EXTI_Line0);
		}
	}
}
void EXTI15_10_IRQHandler(void)
{
	if(EXTI_GetFlagStatus(EXTI_Line13) == SET)//读取按键2
	{
		LED_G_TOGGLE;//翻转PB0
		EXTI_ClearFlag(EXTI_Line13);
	}
}

总结

本章讲解的是STM32的外部中断,通过外部中断的原理、STM32外部中断概述以及外部中断库函数配置流程来详细讲述了STM32的外部中断这一外设,希望读者能够认真学习。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值