EXTI外部中断以及示例

中断系统

中断:在主程序运行过程中,出现了特定的中断触发条件(中断源。比如对于外部中断来说,可以是引脚发生了电平跳变;对于定时器来说,可以是定时的时间到了;对于串口通信来说,可以是接收到了数据),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行

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

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

中断执行流程

中断程序的执行流程如下,当它执行到某个地方时,外设的中断条件满足了,那这时,无论主程序是在干什么事情(比如OLED显示程序才执行一半,Delay函数还在等待等)中断来了,主程序都得立即暂停,程序由硬件电路自动跳转到中断程序中,当中断程序执行完之后,程序再返回被暂停的地方继续运行(这个暂停的地方,叫做断点)。为了程序能在中断返回后继续原来的工作,在中断执行前,会对程序的现场进行保护,中断执行后,会再返回现场,这样保证主程序被中断了,回来之后也能继续执行。

中断嵌套的执行流程如下。当一个中断正在执行时,又有新的优先级更高的中断来,那个旧中断会被打断,执行新的中断,新的中断结束,再继续执行原来的中断,原来的中断结束,再继续主程序,这就是中断嵌套的执行流程。

c语言中,中断的执行流程如下。上面是主函数,while(1)死循环里就是主程序,正常情况下,程序就是在主程序中不断循环执行,当中断条件满足时,主程序就会暂停,然后自动跳转到中断程序里运行,中断程序执行完之后,再返回主程序执行。一般中断程序都是在一个子函数里,这个函数不需要我们调用,当中断来临时,由硬件自动调用这个函数,这就是在c语言中,中断的执行流程。

STM32中断

68个可屏蔽中断通道,包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设。

使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级。

下图灰色的是内核中断,我们一般不用,了解即可。

如下图所示就是stm32外设的中断了,外设电路检测到有什么异常或事件,需要提示一下CPU的时候,它就可以申请中断,让程序调到对应的中断函数里运行一次,用来处理这个异常或事件。图中最右边是中断的地址,因为程序中的中断函数,它的地址是由编译器来分配的,是不固定的,但是我们的中断跳转,由于硬件的限制,只能跳到固定的地址执行程序,所以为了硬件能够跳转到一个不固定的中断函数里,这里就需要在内存中定义一个地址的列表,这个列表的地址是固定的,中断发生后,就跳到这个固定位置,然后在这个固定位置,由编译器,再加上一个跳转到中断函数的代码,这样中断跳转就可以跳转到任意位置了,这个中断地址的列表,就叫中断向量表,相当于中断跳转的一个跳板,不过我们用c编程,是不需要管这个中断向量表的,因为编译器都帮我们做好了。

NVIC基本结构

NVIC(嵌套中断向量控制器),在stm32中,它是用来统一分配中断优先级和管理中断的,NVIC是一个内核外设,是CPU的小助手(如果把中断全接到cpu上,会很麻烦,毕竟CPU主要是用来运算的),NVIC有很多输入口,下图中线上划了个斜杠上面写了n(这个意思是:一个外设可能会同时占用多个中断通道,所以这里有n条线),然后NVIC只有一个输出口,NVIC根据每个中断的优先级分配中断的先后顺序,之后通过右边这一输出口就告诉CPU该处理哪个中断,对于中断先后顺序分配的任务,CPU不需要知道。

NVIC优先级分组

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

抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队

EXTI简介

EXTI(Extern Interrupt)外部中断

EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序

支持的触发方式:上升沿/下降沿/双边沿/软件触发

支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断

通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒

触发响应方式:中断响应/事件响应

EXTI基本结构

最左边是GPIO口的外设,每个GPIO外设有16个引脚,所以进来16根线;如果每个引脚占用一个通道,那EXTI的16个通道是不够用的,所以在这里会有一个AFIO中断引脚选择的电路模块,这个AFIO就是一个数据选择器(可以将图中前面的3个GPIO外设的16个引脚中的其中一个连接到后面的EXTI通道(16个GPIO通道),所以对于PA0\PB0\PC0这些,通过AFIO选择之后只有其中一个能接到EXTI的通道0上),然后通过AFIO选择后的16个通道,就能接到了EXTI边沿检测及控制电路上,同时下面这4个蹭网的外设(PVD\PTC\USB\ETH)也是并列接进来的,这些加起来就组成了EXTI的20个输入信号,然后经过EXTI电路之后,分为了两种输出,也就是中断响应和事件响应,上面接到了NVIC用来触发中断,下面有20条输出线路到了其它外设,也就是事件响应。

AFIO复用IO口

内部电路就是一系列的数据选择器,如上图的最上面输入是PA0\PB0\PC0等尾号都是0,然后通过数据选择器最终选择一个,连接到EXTI0上,上面写的文字是说配置这个寄存器的哪一个位就可以决定选择哪一个输入,图中后面部分内容都雷同。

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

在STM32中,AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择。

EXTI框图

或门(无直边)。它可以有多个输入,但只能有一个输出。执行的是或的逻辑,在输入端(曲边),只要有一个高电平1,输出的高电平就为1;只有全部输入低电平0,输出才为0。(尖头为输出)。(或1为1,全0则0)

与门(直边)。它可以有多个输入,但只能有一个输出。执行的是与的逻辑,在输入端(直边),只要有一个是低电平0,输出就是0;只有全部输入1,输出才为1。(与0为0,全1则1)

非门(三角号加个圈)。它只有一个输入,一个输出;输入1就输出0,输入0就输出1,执行的是非得逻辑(圈为输出,取反)

数据选择器(一个梯形)。有多个输入,一个输出,在侧面有选择控制端,根据控制端的数据,从输入选择一个接到输出。

表示20根线,代表20个通道

EXTI外部中断的特性和使用场景

旋转编码器的输出信号,可能很久都不会拧它,这时不需要stm32做任何事情,但是我一拧它,就会有很多脉冲波形需要stm32接收,这个信号是突发的,stm32不知道什么时候会来,同时它是外部驱动的,stm32只能被动读取,最后这个信号非常快,stm32稍微晚一点来读取就会错过很多波形,所以对于这种情况来说,就可以考虑使用stm32的外部中断,有脉冲过来,stm32立即进入中断函数处理,没有脉冲的时候stm32就专心做其他事情;另外还有比如,红外遥控接收头的输出,接收到要遥控数据之后,它会输出一端波形,这个波形转瞬即逝,并且不会等你,所以就需要使用外部中断来读取;最后还有按键,虽然它的动作是外部驱动的突发事件但我并不推荐使用外部中断来读取按键,因为外部中断不好处理按键抖动和松手检测,对于按键来说,它的输出波形也不是转瞬即逝的,所以要求不高的话可以在主程序中循环读取,也可以考虑一下定时器中断读取的方式(这样即可以后台读取按键值、不会阻塞主程序也可以很好的处理按键抖动和松手检测的问题)

旋转编码器简介

旋转编码器:用来测量位置、速度或旋转方向的装置,当其旋转轴旋转时,其输出端可以输出与旋转速度和方向对应的方波信号,读取方波信号的频率和相位信息即可得知旋转轴的速度和方向

类型:机械触点式/霍尔传感器式/光栅式

硬件电路

对射式红外传感器接线图

程序-对射式红外传感器(+代码注释)

当我们的挡光片或者编码盘在这个对射式红外传感器中间经过时,这个DO就会输出电平跳变的信号,然后这个电平跳变的信号触发STM32 PB14号口的中断,我们在中断函数里,执行变量++的程序,然后主循环里用OLED显示这个变量,这样第一个程序就完成了。

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.H"
#include "countsensor.h"

int main(void)
{
	OLED_Init();
	CountSensor_Init();
	
	OLED_ShowString(1,1,"Count:");   //字符串需要用双引号括起来
	
	while(1)
	{
		OLED_ShowNum(1,7,CountSensor_Get(),5);
	}
}

countsensor.c

#include "stm32f10x.h"                  // Device header

uint16_t CountSensor_Count;

void CountSensor_Init(void)
{
	//开启RCC时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //开启GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);  //开启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); //初始化GPIOB外设
	
	//配置AFIO
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
	
	//配置EFTI
	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);
	
	//配置NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	
}
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 __COUNTSENSOR_H
#define __COUNTSENSOR_H

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


#endif

旋转编码器接线图

程序-旋转编码器接线图+代码注释)

main.c

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

int16_t num;

int main(void)
{
	OLED_Init();
	Encoder_Init();

	OLED_ShowString(1,3,"I LOVE YOU");   //字符串需要用双引号括起来

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

Encoder.c

#include "stm32f10x.h"                  // Device header

int16_t Encoder_Count;

void Encoder_Init(void)
{
	//开启RCC时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //开启GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);  //开启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); //初始化GPIOB外设
	
	//配置AFIO
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);	
	//配置EFTI
	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);
	
	//配置NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	
	NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_Init(&NVIC_InitStructure);
}

int16_t Encoder_Get(void)
{
	int16_t temp;
	temp = Encoder_Count;
	Encoder_Count = 0;
	return temp;
}

void EXTI0_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line0) == SET)
	{
		if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0)
		{
			Encoder_Count--;
		}
		EXTI_ClearITPendingBit(EXTI_Line0);
	}
}

void EXTI1_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line1) == SET)
	{
		if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0) == 0)
		{
			Encoder_Count++;
		}
		EXTI_ClearITPendingBit(EXTI_Line1);
	}
}

Encoder.h 

#ifndef _ENCODER_H_
#define _ENCODER_H_


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


#endif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/438125.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第一套试卷大题

1.邻接矩阵和邻接表的写法: **介绍:**该图是一个无向图,所以邻接矩阵一定是对称的,而邻接表某节点的边数为无向图某节点的连接数 无向图的邻接矩阵: 无向图的邻接表(不唯一): 根据无…

细说券商VIP快速交易通道与交易所报单一文看懂

最近的行情渐入佳境,很多朋友都喜欢做短线或者打板交易。可以往往发现打板进去的要么就是量能不足没有封住的,或者说是炸板的,想要交易一字板又打板不进去,这种就很让人苦恼。今天我们就来解答下这种到底该怎么处理?首…

springboot233大学生就业需求分析系统

大学生就业需求分析系统设计与实现 摘 要 信息数据从传统到当代,是一直在变革当中,突如其来的互联网让传统的信息管理看到了革命性的曙光,因为传统信息管理从时效性,还是安全性,还是可操作性等各个方面来讲&#xff…

自动化测试之web自动化(Selenium)

🔥 交流讨论:欢迎加入我们一起学习! 🔥 资源分享:耗时200小时精选的「软件测试」资料包 🔥 教程推荐:火遍全网的《软件测试》教程 📢欢迎点赞 👍 收藏 ⭐留言 &#x1…

水牛社:专为创业者和网赚小白精心打造的助手

近几年来,经济发展快是快,物价也在蹭蹭往上涨,但工资却不见明显提升,随着生活成本的增加,单单靠工资,已经很难维持生活开支,加之疫情的影响,很多行业发展不景气,一些岗位…

js【详解】自动类型转换

运算符 Symbol 数字 会报错 Cannot convert a Symbol value to a number Symbol 字符串 会报错 Cannot convert a Symbol value to a string 存在对象,数组,函数时 对象,数组,函数会先执行其 toString() 方法,…

[数据结构]OJ用队列实现栈

225. 用队列实现栈 - 力扣(LeetCode) 官方题解:https://leetcode.cn/problems/implement-stack-using-queues/solutions/432204/yong-dui-lie-shi-xian-zhan-by-leetcode-solution/ 首先我们要知道 栈是一种后进先出的数据结构&#xff0c…

SpringMVC 中的常用注解和用法

⭐ 作者:小胡_不糊涂 🌱 作者主页:小胡_不糊涂的个人主页 📀 收录专栏:JavaEE 💖 持续更文,关注博主少走弯路,谢谢大家支持 💖 注解 1. MVC定义2. 注解2.1 RequestMappin…

调用Mybatis plus中的saveBatch方法报找不到表的问题

1.问题现象 在用Mybatis plus开发的项目中,用自带的API批量保存的方法saveBatch操作时,发现报没有找到表的错误。 错误日志截图如下: 表实际是存在的,且发现其他的方法都没有问题,包括save、update等单个的方法&…

Docker网络+原理+link+自定义网络

目录 一、理解Docker网络 1.1 运行tomcat容器 1.2 查看容器内部网络地址 1.3 测试连通性 二、原理 2.1 查看网卡信息 2.2 再启动一个容器测试网卡 2.3 测试tomcat01 和tomcat02是否可以ping通 2.4 只要删除容器,对应网桥一对就没了 2.5 结论 三、--link 3.…

C++基础3:C++的数组和函数

此专栏为移动机器人知识体系下的编程语言中的 C {\rm C} C从入门到深入的专栏,参考书籍:《深入浅出 C {\rm C} C》(马晓锐)和《从 C {\rm C} C到 C {\rm C} C精通面向对象编程》(曾凡锋等)。 3.C的数组和函数 3.1 一维数组概述 一维数组定义和初始化。 …

基于iOS真机的Appium自动化测试

必要条件 XCode > 6.0, 7.1.1(注意Appium并不一定支持最新版本的Xcode)Mac OS X 10.10 or 更高, 建议使用10.11.1 Xcode 安装 APP Store安装 注意事项: Xcode 安装包很大(5G左右),Xcode移动到应用程序…

2024年腾讯云学生服务器优惠活动「云+校园」政策解读

2024年腾讯云学生服务器优惠活动「云校园」,学生服务器优惠价格:轻量应用服务器2核2G学生价30元3个月、58元6个月、112元一年,轻量应用服务器4核8G配置191.1元3个月、352.8元6个月、646.8元一年,CVM云服务器2核4G配置842.4元一年&…

linux命令行或桌面 显卡压力测试

windows下的压力测试非常简单,有很多图形化的测试工具 在github上找到一个项目:github链接 1.下载工具 cd /usr/localgit clone https://github.com/wilicc/gpu-burn如果没有安装git,则先安装 apt-get install git2.安装 cd /usr/local/…

Linux/Validation

Enumeration nmap 第一次扫描发现系统对外开放了22,80,4566和8080端口,端口详细信息如下 系统对外开放了4个端口,从nmap的结果来看,8080无法访问,手动尝试后4566也无法访问,只能从80端口开始 …

晶圆上特性表征

测试仪器: 半导体器件表征系统(DC&CV):Keysight B1500A 半导体器件分析仪(B1500A)测量能力: 1.IV、CV、脉冲/动态IV范围为0.1 fA-1 A/0.5 uV-200 V 2.器件、材料、半导体、有源/无源元件的…

华为数通方向HCIP-DataCom H12-821题库(多选题:41-60)

第41题 BGP OPEN消息中携带如下哪些信息? A、路由属性 B、BGP Router ID C、Hold time D、本地自治系统(AS)号 【参考答案】BCD 【答案解析】 B. BGP Router ID:OPEN消息中包含发送方BGP路由器的Router ID,用于唯一标识BGP路由器。C.Hold time:OPEN消息中包含发送方BGP路由…

Java多线程——如何保证原子性

目录 引出原子性保障原子性CAS 创建线程有几种方式?方式1:继承Thread创建线程方式2:通过Runnable方式3:通过Callable创建线程方式4:通过线程池概述ThreadPoolExecutor API代码实现源码分析工作原理:线程池的…

牛客每日一题之 前缀和

目录 题目介绍: 算法原理: 前缀和: 代码实现: 题目介绍: 题目链接:【模板】前缀和_牛客题霸_牛客网 算法原理: 先讲讲暴力解法每次求出数组下标r之前元素的和,再减去数组下标l-…

pycuda安装失败问题

pycuda安装失败问题 遇到一个pycuda安装失败的问题, 这里有一个合理的解释可以去尝试一下,看起来很有道理: