STM32中断—EXTI外部中断

5. 中断

本文来自于《STM32——江科大》的笔记整理。

中断系统

中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行

中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源

中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回

中断执行流程

在这里插入图片描述

STM32中断

  • 68个可屏蔽中断通道,包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设
  • 使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级

本节学习

  • EXRI1——EXRI2——EXRI3——EXRI4——EXRI9_5——EXRI15_10
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

后面的地址是干什么的?

这是因为中断函数,它的地址是由编译器来分配,是不固定的,但是我们的中断跳转,由于硬件的限制,只能跳到固定的地址执行程序,所以为了能让硬件跳转到一个不固定的中断函数里

NVIC基本结构

在这里插入图片描述

NVIC优先级分组

  • NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级
  • 抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队
分组方式抢占优先级响应优先级
分组00位,取值为04位,取值为0~15
分组11位,取值为0~13位,取值为0~7
分组22位,取值为0~32位,取值为0~3
分组33位,取值为0~71位,取值为0~1
分组44位,取值为0~150位,取值为0

5.1 EXTI外部中断

5.1.1 EXTI简介

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

5.1.2 EXTI基本结构

在这里插入图片描述

5.1.2 AFIO复用IO口

  • AFIO主要用于引脚复用功能的选择和重定义
  • 在STM32中,AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择

在这里插入图片描述
在这里插入图片描述

5.1.3 EXTI框图

在这里插入图片描述

总结:

  • 对应想要获取的信号是外部驱动的很快的突发信号,对应这种情况,可以考虑使用STM32的外部中断

5.1.4 旋转编码器介绍

  • 旋转编码器:用来测量位置、速度或旋转方向的装置,当其旋转轴旋转时,其输出端可以输出与旋转速度和方向对应的方波信号,读取方波信号的频率和相位信息即可得知旋转轴的速度和方向
  • 类型:机械触点式/霍尔传感器式/光栅式

机械触点一般不用于电机测速

在这里插入图片描述

硬件电路

在这里插入图片描述

我们只需要把这个外部中断的路线打通即可

在这里插入图片描述

5.1.5 对射式红外线传感器计次数

在这里插入图片描述

AFIO的库函数和GPIO在一个文件里

void GPIO_AFIODeInit(void);

void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_EventOutputCmd(FunctionalState NewState);
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);

CountSensor.c

在这里插入图片描述

PB14号引脚的电平信号,就顺利通过了AFIO,进入到后级的EXTI电路了

#include "stm32f10x.h"                  // Device header

//定义一个全局变量计数
//默认0
uint16_t CountSensor_Count;

void CountSensor_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//开启时钟
	//EXTI和NVIC两个外设的时钟一直都是打开的
	//NVIC是内核的外设,是不需要开启时钟的
  GPIO_InitTypeDef GPIO_InitStructure;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
  GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	//PB14号引脚的电平信号,就顺利通过了AFIO,进入到后级的EXTI电路了
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);
	
	//初始化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);
	
	//配置NOIC
	//分组方式整个芯片就只能用一组
	//可以放到主函数的最开始
	//放到模块里需要保证每个模块一致
	//取值范围抢占优先级(0-3),响应优先级(0-3)
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStruct;
	//需要在当前工程搜索IRQn_Type
	//选择对应的中断通道
	NVIC_InitStruct.NVIC_IRQChannel=EXTI15_10_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	//设置抢占优先级(0-3)
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
	//设置响应优先级(0-3)
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVIC_InitStruct);
	
}

uint16_t CountSensor_Get(void)
{
	return CountSensor_Count;
}


//参考启动文件文件中的中断向量表
//中断函数的名字都是固定的(无参,无返回值)
void EXTI15_10_IRQHandler(void)
{
	//先进行,中断标准位的判断(确保是我们想要的中断源触发的这个函数)
	if(EXTI_GetITStatus(EXTI_Line14)==SET)
	{
		CountSensor_Count++;
		
		//注意需要清除中断标志位,否则程序会不断的响应中断
		//程序就卡死在了中断函数
		EXTI_ClearITPendingBit(EXTI_Line14);
	}

}

CountSensor.h

#ifndef __COUNTSENOR_H
#define __COUNTSENOR_H
#include "stm32f10x.h"                  // Device header

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



#endif

main.c
OLED代码 -->见STM32——OLED显示屏

#include "stm32f10x.h"                  // Device header
#include "Delay.h"   
#include "OLED.h"
#include "CountSensor.h"


int main()
{
    OLED_Init();
	CountSensor_Init();
	
	OLED_ShowString(1,1,"Count:");//显示字符串

	while(1)
	{
		OLED_ShowNum(1,7,CountSensor_Get(),5);
	}
}

5.1.6 旋转编码器计次

连线图

在这里插入图片描述

中断代码思路

Encoder.c

#include "stm32f10x.h"                  // Device header

//定义一个全局变量计数
//默认0
int16_t Encoder_Count;

void Encoder_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//开启时钟
	//EXTI和NVIC两个外设的时钟一直都是打开的
	//NVIC是内核的外设,是不需要开启时钟的
  GPIO_InitTypeDef GPIO_InitStructure;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 ;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
  GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	//PB14号引脚的电平信号,就顺利通过了AFIO,进入到后级的EXTI电路了
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);
	
	//初始化EXTI
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line=EXTI_Line0 | EXTI_Line1;//0行
	EXTI_InitStructure.EXTI_LineCmd=ENABLE;//开启
	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;//中断模式
	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;//下降沿触发
	
	EXTI_Init(&EXTI_InitStructure);
	

	
	//配置NOIC
	//分组方式整个芯片就只能用一组
	//可以放到主函数的最开始
	//放到模块里需要保证每个模块一致
	//取值范围抢占优先级(0-3),响应优先级(0-3)
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStruct;
	//需要在当前工程搜索IRQn_Type
	//选择对应的中断通道(0)
	NVIC_InitStruct.NVIC_IRQChannel=EXTI0_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	//设置抢占优先级(0-3)
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
	//设置响应优先级(0-3)
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVIC_InitStruct);
	
	//需要在当前工程搜索IRQn_Type
	//选择对应的中断通道(1)
	NVIC_InitStruct.NVIC_IRQChannel=EXTI1_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	//设置抢占优先级(0-3)
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
	//设置响应优先级(0-3)
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=2;
	NVIC_Init(&NVIC_InitStruct);
	
	
}
//返回变化值
uint16_t Encoder_Get(void)
{
	int16_t Temp;
	Temp=Encoder_Count;
	return Temp;
}


//参考启动文件文件中的中断向量表
//中断函数的名字都是固定的(无参,无返回值)
void EXTI0_IRQHandler(void)
{
	//先进行,中断标准位的判断(确保是我们想要的中断源触发的这个函数)
	if(EXTI_GetITStatus(EXTI_Line0)==SET)
	{
		//读取PB1的电平,是不是低电平
		if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0)
		{
			Encoder_Count--;
		}
		
		//注意需要清除中断标志位,否则程序会不断的响应中断
		//程序就卡死在了中断函数
		EXTI_ClearITPendingBit(EXTI_Line0);
	}

}

void EXTI1_IRQHandler(void)
{
	//先进行,中断标准位的判断(确保是我们想要的中断源触发的这个函数)
	if(EXTI_GetITStatus(EXTI_Line1)==SET)
	{
		//读取PB1的电平,是不是低电平
		if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==0)
		{
			Encoder_Count++;
		}
		
		//注意需要清除中断标志位,否则程序会不断的响应中断
		//程序就卡死在了中断函数
		EXTI_ClearITPendingBit(EXTI_Line1);
	}

}

Encoder.h

#ifndef __ENCODER_H
#define __ENCODER_H
#include "stm32f10x.h"                  // Device header

void Encoder_Init(void);
uint16_t Encoder_Get(void);


#endif

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"   
#include "OLED.h"
#include "Encoder.h"

int16_t Num;

int main()
{
  OLED_Init();
	Encoder_Init();
	
	OLED_ShowString(1,1,"Num:");//显示字符串

	while(1)
	{
		Num+=Encoder_Get();
		OLED_ShowSignedNum(1,5,Num,5);
	}
}


建议:在中断函数里不要执行耗时过长的代码

在这里插入图片描述

既然都看到最后了,可否给个呢,在这里插入图片描述关注收藏不迷路


给那些看完的朋友,奖励一个 赤赤博客-后端+前端,觉得不错的话可以推荐给身边的朋友哟!
在这里插入图片描述


  • 5
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安赫'

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值