STM32学习路线开启篇:外部中断

目录

前言

一、中断系统

1.中断概念介绍

2.中断执行流程

3.STM32中断

4.NVIC的基本结构

5.NVIC优先级分组

6.EXTI外部中断

[6.1 介绍](#6.1 介绍)

[6.2 EXTI基本结构](#6.2 EXTI基本结构)

[6.3 AFIO复用IO口](#6.3 AFIO复用IO口)

[6.4 EXTI内部构图](#6.4 EXTI内部构图)

二、外部中断实验

1.旋转编码器计次实验

[1.1 介绍](#1.1 介绍)

[1.2 硬件电路](#1.2 硬件电路)

[1.3 实验代码](#1.3 实验代码)

2.对射式红外传感器计次实验


前言

关于外部中断EXTI外设的介绍与实验


提示:以下是本篇文章正文内容,下面案例可供参考

一、中断系统

1.中断概念介绍

  • 中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行
  • 中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源
  • 中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回

2.中断执行流程

3.STM32中断

F1系列最多:68个可屏蔽中断通道(中断源),包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设;

具体有几个中断源,每个型号不一样,需要查找数据手册
使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级(NVIC就是用于管理STM32的中断,以及中断优先级的)

简单来讲就是STM32中各个外设中配置好了哪些中断源,每个外设对应着几个中断函数,可以进行使用

4.NVIC的基本结构

NVIC是一个内核外设,CPU的小助手;STM32有很多中断源,这些中断源不会接在CPU上,而是让NVIC进行管理,一个外设可能占多个中断通道,再由NVIC进行优先级分配,输出一个中断优先级最高的给CUP

5.NVIC优先级分组

NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级
抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队

每一个中断都有16个优先级,对应四位二进制(0~15),值越小优先级越高;上图为分组情况,一般我们常使用分组2和分组4;分组2意思是高位2位设置抢占优先级,低2位设置响应优先级

当抢占优先级和响应优先级均相同的按中断号排队,上图中框起来的为中断号,值越小优先级越高

6.EXTI外部中断

6.1 介绍

  • EXTI(Extern Interrupt)外部中断
  • EXTI可以监测指定GPIO 口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
  • 支持的触发方式:上升沿/下降沿/双边沿/软件触发
  • 支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断
  • 通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒
  • 触发响应方式:中断响应/事件响应
    支持的触发方式:上升沿/下降沿/双边沿/软件触发;

上升沿:电平从低电平->高电平时触发中断

下降沿:电平从高电平->低电平时触发中断

双边沿:低电平->高电平与高电平->低电平均触发中断
软件触发:通过写一个程序代码实现中断
支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断;

例如PA0与PB0,只能选择一个设置中断,不能同时;只能PA0、PA1、PB2、...
通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒;

16个GOIO_Pin:对应0~15个中断通道口;也是EXTI外部中断的主要功能,进行引脚电平检测

PVD输出、RTC闹钟、USB唤醒、以太网唤醒:这几个通常是因为STM32处于低功耗模式下关机状态,需要唤醒时,会使用外部中断进行唤醒,了解即可
触发响应方式:中断响应/事件响应

中断响应:通过检测引脚电平变化,进行申请中断,给CPU处理

事件响应:当外部中断检测到引脚电平发生变化时,中断信号不会通向CPU,而是通向其它外设,用来触发其它外设的操作,属于外设之间的联动

6.2 EXTI基本结构

6.3 AFIO复用IO口

AFIO外设主要用于引脚复用功能的选择和重定义

作用:

1、复用引脚的重映射(图1)

2、中断引脚的选择(本节课)
图1

对应相同的Pin不能同时触发中断,如PA0与PB0,通过AFIO数据选择器的作用,只会选择一个Pin;对应EXTI有16个通道,那么也就对应着16个Pin入口

6.4 EXTI内部构图

二、外部中断实验

1.旋转编码器计次实验

1.1 介绍

什么样的设备需要用到外部中断,使用外部中断的好处?

对于STM32,想要获取的信号是外部驱动,很快的突发信号,比如旋转编码器的输出信号,它产生的信号是突发的,只能被动读取,如果不及时就会错过许多信号,所以要有脉冲过来,就立即进入中断函数处理

我们使用的是第二个

1.2 硬件电路

1.3 实验代码

Encoder.c

cpp 复制代码
#include "stm32f10x.h"                  // Device header

int16_t Encoder_Count;					//全局变量,用于计数旋转编码器的增量值

/**
  * 函    数:旋转编码器初始化
  * 参    数:无
  * 返 回 值:无
  */
void Encoder_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);		//开启AFIO的时钟,外部中断必须开启AFIO的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);						//将PB0和PB1引脚初始化为上拉输入
	
	/*AFIO选择中断引脚*/
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);//将外部中断的0号线映射到GPIOB,即选择PB0为外部中断引脚
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);//将外部中断的1号线映射到GPIOB,即选择PB1为外部中断引脚
	
	/*EXTI初始化*/
	EXTI_InitTypeDef EXTI_InitStructure;						//定义结构体变量
	EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;		//选择配置外部中断的0号线和1号线
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;					//指定外部中断线使能
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;			//指定外部中断线为中断模式
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;		//指定外部中断线为下降沿触发
	EXTI_Init(&EXTI_InitStructure);								//将结构体变量交给EXTI_Init,配置EXTI外设
	
	/*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
																//即抢占优先级范围:0~3,响应优先级范围:0~3
																//此分组配置在整个工程中仅需调用一次
																//若有多个中断,可以把此代码放在main函数内,while循环之前
																//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;			//选择配置NVIC的EXTI0线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设

	NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;			//选择配置NVIC的EXTI1线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;			//指定NVIC线路的响应优先级为2
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
}

/**
  * 函    数:旋转编码器获取增量值
  * 参    数:无
  * 返 回 值:自上此调用此函数后,旋转编码器的增量值
  */
int16_t Encoder_Get(void)
{
	/*使用Temp变量作为中继,目的是返回Encoder_Count后将其清零*/
	/*在这里,也可以直接返回Encoder_Count
	  但这样就不是获取增量值的操作方法了
	  也可以实现功能,只是思路不一样*/
	int16_t Temp;
	Temp = Encoder_Count;
	Encoder_Count = 0;
	return Temp;
}

/**
  * 函    数:EXTI0外部中断函数
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
  *           函数名为预留的指定名称,可以从启动文件复制
  *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
  */
void EXTI0_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line0) == SET)		//判断是否是外部中断0号线触发的中断
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
		{
			if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)		//PB0的下降沿触发中断,此时检测另一相PB1的电平,目的是判断旋转方向
			{
				Encoder_Count --;					//此方向定义为反转,计数变量自减
			}
		}
		EXTI_ClearITPendingBit(EXTI_Line0);			//清除外部中断0号线的中断标志位
													//中断标志位必须清除
													//否则中断将连续不断地触发,导致主程序卡死
	}
}

/**
  * 函    数:EXTI1外部中断函数
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
  *           函数名为预留的指定名称,可以从启动文件复制
  *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
  */
void EXTI1_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line1) == SET)		//判断是否是外部中断1号线触发的中断
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
		{
			if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)		//PB1的下降沿触发中断,此时检测另一相PB0的电平,目的是判断旋转方向
			{
				Encoder_Count ++;					//此方向定义为正转,计数变量自增
			}
		}
		EXTI_ClearITPendingBit(EXTI_Line1);			//清除外部中断1号线的中断标志位
													//中断标志位必须清除
													//否则中断将连续不断地触发,导致主程序卡死
	}
}

Encoder.h

cpp 复制代码
#ifndef __ENCODER_H
#define __ENCODER_H

void Encoder_Init(void);
int16_t Encoder_Get(void);

#endif

main.c

cpp 复制代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Encoder.h"

int16_t Num;			//定义待被旋转编码器调节的变量

int main(void)
{
	/*模块初始化*/
	OLED_Init();		//OLED初始化
	Encoder_Init();		//旋转编码器初始化
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "Num:");			//1行1列显示字符串Num:
	
	while (1)
	{
		Num += Encoder_Get();				//获取自上此调用此函数后,旋转编码器的增量值,并将增量值加到Num上
		OLED_ShowSignedNum(1, 5, Num, 5);	//显示Num
	}
}

2.对射式红外传感器计次实验

在Hardware内新建工程模块

进行外部中断,我们只需按照上图的顺序,依次操作GPIO->AFIO->EXTI->NVIC

CountSensor.c

cpp 复制代码
#include "stm32f10x.h"                  // Device header

uint16_t CountSensor_Count;				//全局变量,用于计数

/**
  * 函    数:计数传感器初始化
  * 参    数:无
  * 返 回 值:无
  */
void CountSensor_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);		//开启AFIO的时钟,外部中断必须开启AFIO的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);						//将PB14引脚初始化为上拉输入
	
	/*AFIO选择中断引脚*/
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);//将外部中断的14号线映射到GPIOB,即选择PB14为外部中断引脚
	
	/*EXTI初始化*/
	EXTI_InitTypeDef EXTI_InitStructure;						//定义结构体变量
	EXTI_InitStructure.EXTI_Line = EXTI_Line14;					//选择配置外部中断的14号线
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;					//指定外部中断线使能
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;			//指定外部中断线为中断模式
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;		//指定外部中断线为下降沿触发
	EXTI_Init(&EXTI_InitStructure);								//将结构体变量交给EXTI_Init,配置EXTI外设
	
	/*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
																//即抢占优先级范围:0~3,响应优先级范围:0~3
																//此分组配置在整个工程中仅需调用一次
																//若有多个中断,可以把此代码放在main函数内,while循环之前
																//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;		//选择配置NVIC的EXTI15_10线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
}

/**
  * 函    数:获取计数传感器的计数值
  * 参    数:无
  * 返 回 值:计数值,范围:0~65535
  */
uint16_t CountSensor_Get(void)
{
	return CountSensor_Count;
}

/**
  * 函    数:EXTI15_10外部中断函数
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
  *           函数名为预留的指定名称,可以从启动文件复制
  *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
  */
void EXTI15_10_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line14) == SET)		//判断是否是外部中断14号线触发的中断
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)
		{
			CountSensor_Count ++;					//计数值自增一次
		}
		EXTI_ClearITPendingBit(EXTI_Line14);		//清除外部中断14号线的中断标志位
													//中断标志位必须清除
													//否则中断将连续不断地触发,导致主程序卡死
	}
}

GPIO输入配置:GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;

注意点:

1、STM32中系统默认开启了EXTI外设的时钟;

2、NVIC为内核外设,不需要开启时钟;

3、AFIO相关库函数存储在GPIO中;

CountSensor.h

cpp 复制代码
#ifndef __COUNT_SENSOR_H
#define __COUNT_SENSOR_H

void CountSensor_Init(void);
uint16_t CountSensor_Get(void);

#endif

main.c

cpp 复制代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "CountSensor.h"

int main(void)
{
	/*模块初始化*/
	OLED_Init();			//OLED初始化
	CountSensor_Init();		//计数传感器初始化
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "Count:");	//1行1列显示字符串Count:
	
	while (1)
	{
		OLED_ShowNum(1, 7, CountSensor_Get(), 5);		//OLED不断刷新显示CountSensor_Get的返回值
	}
}

以下为课堂笔记,记录了一些函数的作用


相关推荐
btzhy4 小时前
STM32单片机:基本定时器应用:PWM 生成(STM32L4xx)
stm32·单片机·嵌入式硬件·基本定时器应用:pwm生成
guangshui5164 小时前
18006.STM32通过SPI读取LAN9253数据
stm32·单片机·嵌入式硬件
hazy1k6 小时前
51单片机基础-静态数码管显示
stm32·单片机·嵌入式硬件·51单片机
@小张要努力8 小时前
STM32学习记录-0.1 STM32外设
stm32·嵌入式硬件·学习
qq_3840490310 小时前
stm32第二天之基本定时器
stm32·单片机·嵌入式硬件
一支闲人11 小时前
带你了解STM32:SPI通信(软件部分)
stm32·单片机·嵌入式硬件·基础知识·适用于新手小白
单片机系统设计14 小时前
基于STM32的智能物联网加湿器/智能家居
stm32·单片机·物联网·毕业设计·智能家居
一支闲人15 小时前
带你了解STM32:I2C通信(第二部分)
stm32·单片机·嵌入式硬件·基础知识·适用于新手小白
充哥单片机设计15 小时前
【STM32项目开源】基于STM32的自适应车流交通信号灯
stm32·单片机·嵌入式硬件