STM32 外部中断EXTI

news2025/4/20 5:34:09

目录

外部中断基础知识

STM32外部中断框架

STM32外部中断机制框架

复用功能

重映射

中断嵌套控制器NVIC 

外部中断按键控制LED灯


外部中断基础知识

STM32外部中断框架

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

            

1.有20条外部中断线

2.有十六条是GPIO的中断线(0~15),同一个引脚号参与同一个line

3.第十九条line只在有ETH互联设备上才存在

4.GPIO的中断线要启用必须先要配置AFIO的寄存器EXTICRx  

STM32外部中断机制框架

1. 边沿检测:上升沿,下降沿或者双边沿(上下都中断)

输入线就是20条外部中断线,可能是GPIO0~15的十六根线,也有可能是其他的线,比如说GPIO进来之后产生了一个信号,进入边沿检测电路,我们通过这个要选择是上升沿还是下降沿(上升和下降是针对电平来说的,下降沿是从高电平到低电平,上升沿是从低电平到高电平),根据是上升沿发生中断还是下降沿发生中断,是配置我们中断发生的条件,

2. 软件配置中断或者事件寄存器

一般情况下是中断

3. 屏蔽中断寄存器或事件寄存器

还是以中断寄存器为主

4. 请求挂起寄存器

挂起就好比晾衣杆,把一个东西挂起来说明这个事件不工作了,比如我cpu正常运行我们的主程序,突然来挂起之后,就直接不工作了

5. 中断发送给NVIC中断控制器

NVIC就是个中断的控制器(当主程序运行时会出现很多钟中断的条件,可能是敲门了,可能是你爸打电话了,可能是你妈打电话给你了,假如说同一个时间节点三四个人打电话给你,你应该决定做哪些事情,NVIC就是帮你管理你先处理哪个事件,同时你有外部中断,串口中断,定时器中断,然后再恢复主程序)

6. 事件则产生一个脉冲响应  

复用功能

什么是复用功能?

处理器的引脚本身默认就是一个普通的GPIO,但是它还可以被复用成其他功能,我们称之为一个引脚的复用功能(GPIO如果是IO口作为输入线,第一要先配置GPIO,如果这个IO口当中我们外部中断的中断线引脚它也得配置复用功能,因为此时不在是一个普通的引脚,是我们外部中断外设的引脚)

                                       

 比如PA0可以当作WKUP引脚、串口引脚、AD引脚....,所以单片机里面每个引脚都有多个功能,

如果我们只是简单的拿来用作输入输出引脚,就正常配置就行,如果拿来作为特殊的功能用,就得配置复用功能

重映射

什么是重映射?

重映射属于复用功能的另外一个功能,可以把具有特殊功能的引脚,分配到其他引脚上去

如果某个功能被重映射了,那么这个功能将不再遵循其默认的默认分配。(相当说串口1是PA9和PA10,但如果把它配置成PB6和PB7原来的就作废了)

 PA7引脚能被当作定时器CH1N的功能,PB13也能当作定时器CH1N的引脚说明同样的外设功能可以用在多个引脚

串口1的发送,串口1的接收,正常情况下PA9是发送PA10是接收,但是当串口USART1_REMAP = 1 时 ,PB6也能用来当串口1的发送,PB7能用来当作串口1的接收 

中断嵌套控制器NVIC 

中断向量表

Cortex-M3内核支持256个中断,其中包含了16个内核中断(异常)和240个外部中断,并目具有256级的可编程中断设置。但是,STM32并没有使用CM3内核的全部东西,而是只用了它的一部分。STM32有84个中断,包括16个内核中断(异常)和68个可屏蔽中断,具有16级可编程的中断优先级。而STM32F103系列上面,16个内核中断(异常)不变,而可屏蔽中断只有60个(在107系列才有68个)**优先级号越小,优先级越高*。

中断优先级分组
这60个中断,怎么管理呢?这就涉及到STM32的中断分组。STM32可以将中断分成5个组,分别为组0-4;同时,对每个中断设置一个抢占优先级和响应优先级。分组配置是由SCB->AIRCR寄存器的bit10-8来定义的。SCB->AIRCR是在哪里的呢?由于这是CM3内核定义的具体的分配关系如下所示:

 Group0-4是中断优先级的五种模式(五位)

如果是Group0是四位可编程的,说明抢占优先级没有位,四位都给了响应优先级 

如果是Group1是四位可编程的,说明抢占优先级一位,三位都给了响应优先级 

一般情况下看中断类型选择合适的分组

             

 

 CM3中定义了8个Bit(若处理器使用高 3 位(bit7~bit5),则实际可配置的优先级等级为2^3=8级(0~7),数值越小优先级越高(如 0 为最高,7 为最低)。若使用全部 8 位,则可配置2^8=256级优先级。,完全够用)用于设置中断源的优先级,而STM32只选用其中的4个Bit(但是又把这4位分为抢占优先级和响应优先级,抢占优先级和响应优先级就好比父优先级和子优先级,如果两个中断发生的情况下先比较抢占优先级谁数值更低谁就优先,如果抢占优先级一样那比较响应优先级,响应优先级谁更小谁先运行,那如果还是一样就打开stm32f10x.h表再进行比较)抢占优先级的级别高于响,应优先级,而数值越小所代表的的优先级越高,介绍一下抢占优先级、响应优先级的区别:

高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的;

(你正在打游戏突然你妈喊你回家吃饭)
抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断;
抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行;
如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行:

除此之外有两点需要注意:
打断的情况只会与抢占优先级有关,和响应优先级无关!(中断嵌套:单片机正在执行工作,突然中断来了,然后有来了个高优先级中断能够打断低优先级的中断)
一般情况下,系统代码执行过程中,只设置一次中断优先级分组,比如分组2(Group2),设置好分组之后一般不会再改变分组。随意改变分组会导致中断管理混乱,程序出现意想不到的执行结果

中断优先级控制函数结构体
中断优先级控制函数(在misc.h中的找到NVIC_InitTypeDef结构体)
        NVlc setPriorityGrouping().
中断优先级控制结构体参数
        NVIC IRQChannel(通道):定义初始化的是哪一个中断,这个可以在stm32f10x.h文件中查到每个中断对应的名字,如USART1_IRQn(串口1);

在mian.c中的misc.h,如果是EXTI0表示A0 B0 C0,我们是什么中断源就配置什么中断就行,只有0 1 2 3 4有单独的

 如果用的是5 6 7 8 9的IO口的引脚统一用EXTI9_5_IRQN,如果IO口用的是11 12 13 14 15的引脚统一用EXTI9_10_IRQN


 因为每一个外设你要中断,你都要统一配置中断的优先级

        NVICIRQChannelPreemptionPriority:定义此中断的抢占优先级别;
        NVIC IRQChannelsubPriority:定义此中断的响应优先级别;
        NVIC IRQChannelcmd:该中断是否使能。
NVIC Init()函数初始化NVIC寄存器 

中断优先级设置步骤

1、系统运行后先设置中断优先级分组,调用函数(0-5,五选一)

2、针对每个中断,设置对应的抢占优先级和响应优先级

3、如果需要挂起/解挂,查看中断当前激活状态,分别调用相关函数即可

外部中断按键控制LED灯

按键控制LED的开发流程:

(按键和单片机连接PC13或者PA0,但是现在如果把PC13或者PA0配置成外部中断,会发现不在是一个普通的引脚了,要配置复用功能,所以我们要用到EXTI和复用AFIO)

                     

第一步:使能功能复用时钟

第二步:配置复用寄存器

第三步:配置中断屏蔽寄存器

 软件流程设计

初始化系统

        初始化GPIO、EXTI外设时钟

        初始化按键和LED引脚

        初始化EXTI外部中断条件

        初始化NVIC嵌套中断控制器

编写外部中断函数

        判断中断发生控制LED灯 

初始化GPIO、EXTI外设时钟和初始化按键和LED引脚

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
	//同时初始化GPIOA和AFIO(包括了复位和中断时钟)
	GPIO_InitTypeDef GPIOInitstruct;
	
	GPIOInitstruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_0;
	GPIOInitstruct.GPIO_Mode = GPIO_Mode_IPD;

	GPIO_Init(GPIOA, &GPIOInitstruct);

初始化EXTI外部中断条件 

在stm32f10x_exti.c(外部中断库)中的stm32f10x_exti.h找到EXTI_InitTypeDef结构体

typedef struct
{
  uint32_t EXTI_Line;      //外部中断线                                                                                                                                                   
   
  EXTIMode_TypeDef EXTI_Mode; //外部中断模式

  EXTITrigger_TypeDef EXTI_Trigger; //外部中断是上升沿还是下降沿还是双边沿
                                         
  FunctionalState EXTI_LineCmd;   //使能

}EXTI_InitTypeDef; 

EXTI_InitTypeDef EXTIInitstruct;

    EXTIInitstruct.EXTI_Line = EXTI_Line0;
//因为是按键引脚是PA0所以配置中断线是EXTI_Line0
	EXTIInitstruct.EXTI_Mode = EXTI_Mode_Interrupt;
//选择模式是中断
	EXTIInitstruct.EXTI_Trigger = EXTI_Trigger_Falling;
//选择的是下降沿
	EXTIInitstruct.EXTI_LineCmd = ENABLE;
	
	EXTI_Init(&EXTIInitstruct);

 初始化NVIC嵌套中断控制器在mian.c中的misc.h找到NVIC_InitTypeDef结构体和优先级分组函数

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//配置优先级前先分组
NVIC_InitTypeDef NVICInitstruct;
	
	NVICInitstruct.NVIC_IRQChannel = EXTI0_IRQn;

	NVICInitstruct.NVIC_IRQChannelPreemptionPriority = 0;

	NVICInitstruct.NVIC_IRQChannelSubPriority = 0;
//因为只有一个中断配置最高优先级
	NVICInitstruct.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&NVICInitstruct);

 在stm32f10x_dam.c中的stm32f10x_gpio.h外部中端线GPIO配置函数

GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);

 编写外部中断函数

中断函数配置好之后如果中断条件发生会先去中断向量表里去先找到EXTI0_IRQHandler这个函数用户有没有定义,如果有定义就执行定义的,如果没定义也跑但内容是空的

函数名在启动文件中,因为我们单片机上电先要执行前面所有内容

weak:弱定义;如果用户没有自己定义的中断函数,则系统默认执行系统自定义的中断函数,如果用户有定义中断函数则优先执行用户的中断函数

所有复制EXTI0_IRQHandler,复制到main函数以外,因为我们的中断函数不需要调用,因为只要我们配置了就会自动跳转过来,如何判断:stm32f10x_exti.c(外部中断库)中的stm32f10x_exti.h找到外部中断获取中断标志位函数EXTI_GetITStatus(uint32_t EXTI_Line)和外部中断清除中断标志位EXTI_ClearITPendingBit(uint32_t EXTI_Line);两个函数配套使用

typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus;

int  main()
{
	
	Shake_Init();
	Led_Init();
	
	while(1)
	{

	}


}
void EXTI0_IRQHandler()
{
//如果外部中断获取中断标志位说明发生中断(就是按键按下的时候)
    if(EXTI_GetITStatus(EXTI_Line0) != RESET )
    {
        GPIO_ResetBits(GPIOA,GPIO_Pin_0);
		
		GPIO_SetBits(GPIOA,GPIO_Pin_0);
    //因为我们的中断可能会发生很多次,所有每进行一次中断需要清除一次,方便下一次进入
    //如果没有调用清除中断函数只能进行一次,因为它已经置位了,清除之后它又能进入ëEXTI0_IRQHandler函数中
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
    
}

 总体代码:

#include "stm32f10x.h"
#include "shake.h"

void Shake_Init()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	GPIO_InitTypeDef GPIOInitstruct;
	EXTI_InitTypeDef EXTIInitstruct;
	NVIC_InitTypeDef NVICInitstruct;
	
	
	GPIOInitstruct.GPIO_Pin = GPIO_Pin_0;
	GPIOInitstruct.GPIO_Mode = GPIO_Mode_IPU;

	GPIO_Init(GPIOA, &GPIOInitstruct);
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
	
	EXTIInitstruct.EXTI_Line = EXTI_Line0;
	EXTIInitstruct.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTIInitstruct.EXTI_Trigger = EXTI_Trigger_Falling;
	EXTIInitstruct.EXTI_LineCmd = ENABLE;
	
	EXTI_Init(&EXTIInitstruct);
	
	NVICInitstruct.NVIC_IRQChannel = EXTI0_IRQn;
	NVICInitstruct.NVIC_IRQChannelPreemptionPriority = 0;
	NVICInitstruct.NVIC_IRQChannelSubPriority = 0;
	NVICInitstruct.NVIC_IRQChannelCmd = ENABLE;
	
	NVIC_Init(&NVICInitstruct);
	
	
}

#include "stm32f10x.h"
#include "main.h"
#include "led.h"
#include "bear.h"
#include "key.h"
#include "relay.h"
#include "shake.h"


 void delay(uint16_t time)
	{
    uint16_t i = 0;
    while(time --)
    {
        i = 12000;
        while(i--);
    }

	}



int  main()
{
	
	Shake_Init();
	Led_Init();
	
	GPIO_ResetBits(GPIOA,GPIO_Pin_1);
	
	while(1)
	{

	}


}

void EXTI0_IRQHandler()
{
	
	if(EXTI_GetITStatus(EXTI_Line0) != RESET )
	{
	
		
			GPIO_ResetBits(GPIOA,GPIO_Pin_1);
		  delay(1000);
		
			GPIO_SetBits(GPIOA,GPIO_Pin_1);
			delay(1000);

		
			EXTI_ClearITPendingBit(EXTI_Line0);
		
			
		
		
	}
	
	
}


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux:基础IO---动静态库

文章目录 1. 动静态库前置知识1.1 动静态库知识回顾1.2 什么是动静态库 2. 动静态库2.1 站在库的制作者的角度2.2 站在库的使用者的角度2.3 动态库是怎么被加载的(原理) 序:上一篇文章我们从认识到理解,从理解到实现场景&#xff…

深度学习-torch,全连接神经网路

3. 数据集加载案例 通过一些数据集的加载案例,真正了解数据类及数据加载器。 3.1 加载csv数据集 代码参考如下 import torch from torch.utils.data import Dataset, DataLoader import pandas as pd ​ ​ class MyCsvDataset(Dataset):def __init__(self, fil…

Codex CLI - 自然语言命令行界面

本文翻译整理自:https://github.com/microsoft/Codex-CLI 文章目录 一、关于 Codex CLI相关链接资源 二、安装系统要求安装步骤 三、基本使用1、基础操作2、多轮模式 四、命令参考五、提示工程与上下文文件自定义上下文 六、故障排查七、FAQ如何查询可用OpenAI引擎&…

实现窗口函数

java 实现窗口函数 public class SlidingWin {public static void main(String[] args) {SlidingWin slidingWin new SlidingWin();double v slidingWin.SlidWin(2);System.out.println(v);}public double SlidWin(int k){int [] array new int[]{2,4,5,6,9,10,12,23,1,3,8…

清华《数据挖掘算法与应用》K-means聚类算法

使用k均值聚类算法对表4.1中的数据进行聚类。代码参考P281。 创建一个名为 testSet.txt 的文本文件,将以下内容复制粘贴进去保存即可: 0 0 1 2 3 1 8 8 9 10 10 7 表4.1 # -*- coding: utf-8 -*- """ Created on Thu Apr 17 16:59:58 …

MATLAB - 小车倒立摆的非线性模型预测控制(NMPC)

系列文章目录 目录 系列文章目录 前言 一、摆锤/小车组件 二、系统方程 三、控制目标 四、控制结构 五、创建非线性 MPC 控制器 六、指定非线性设备模型 七、定义成本和约束 八、验证非线性 MPC 控制器 九、状态估计 十、MATLAB 中的闭环仿真 十一、使用 MATLAB 中…

HAL库配置RS485+DMA+空闲中断收发数据

前言: (1)DMA是单片机集成在芯片内部的一个数据搬运工,它可以代替单片机对数据进行传输、存储,节约CPU资源。一般应用场景,ADC多通道采集,串口收发(频繁进入接收中断)&a…

【java实现+4种变体完整例子】排序算法中【计数排序】的详细解析,包含基础实现、常见变体的完整代码示例,以及各变体的对比表格

以下是计数排序的详细解析,包含基础实现、常见变体的完整代码示例,以及各变体的对比表格: 一、计数排序基础实现 原理 通过统计每个元素的出现次数,按顺序累加得到每个元素的最终位置,并填充到结果数组中。 代码示…

嵌入式单片机开发 - Keil MDK 编译与烧录程序

Keil MDK 编译程序 1、Keil MDK 编译按钮 Build 按钮:重新编译整个工程的所有源文件,无论它们是否被修改过 Rebuild 按钮:仅编译修改过的文件及其依赖项,未修改的文件直接使用之前的编译结果 2、Keil MDK 编译结果 linking... …

裂项法、分式分解法——复杂分式的拆解

目录 一、裂项法 1. 核心思想 2. 适用场景 3. 步骤 4. 例题 二、分式分解 1. 核心思想 2. 适用场景 3. 步骤 4.例题 一、裂项法 1. 核心思想 将一项拆解为多项之差,使得在求和时中间项相互抵消,最终仅剩首尾少数项。 2. 适用场景 级数求和…

黑马点评秒杀优化

异步优化秒杀业务 回顾之前的内容黑马点评 秒杀优惠券集群下一人一单超卖问题-CSDN博客,为了处理并发情况下的线程安全和数据一致性的问题,我们已经完成了查询优惠券信息、判断秒杀是否开始和结束、检查库存、用户ID加锁、创建订单和扣减库存。 尽管之前…

JavaScript 的演变:2023-2025 年的新特性解析

随着Web技术的飞速发展,ECMAScript(简称ES)作为JavaScript的语言标准,也在不断进化。 本文将带你学习 ECMAScript 2023-2025 的新特性。 一、ECMAScript 2023 新特性 1.1 数组的扩展 Array.prototype.findLast()/Array.protot…

[Java · 初窥门径] Java 注释符

🌟 想系统化学习 Java 编程?看看这个:[编程基础] Java 学习手册 0x01:Java 注释符简介 在编写程序时,为了使代码易于理解,通常会为代码加一些注释。Java 注释就是用通俗易懂的语言对代码进行描述或解释&a…

Docker环境下SpringBoot程序内存溢出(OOM)问题深度解析与实战调优

文章目录 一、问题背景与现象还原**1. 业务背景****2. 故障特征****3. 核心痛点****4. 解决目标** 二、核心矛盾点分析**1. JVM 与容器内存协同失效****2. 非堆内存泄漏****3. 容器内存分配策略缺陷** 三、系统性解决方案**1. Docker 容器配置**2. JVM参数优化(容器…

【计算机网络】网络基础(协议,网络传输流程、Mac/IP地址 、端口号)

目录 1.协议简述2.网络分层结构2.1 软件分层2.2 网络分层为什么? 是什么?OSI七层模型TCP/IP五层(或四层)结构 3. 网络与操作系统之间的关系4.从语言角度理解协议5.网络如何传输局域网通信(同一网段) 不同网…

pgsql中使用jsonb的mybatis-plus和jps的配置

在pgsql中使用jsonb类型的数据时,实体对象要对其进行一些相关的配置,而mybatis和jpa中使用各不相同。 在项目中经常会结合 MyBatis-Plus 和 JPA 进行开发,MyBatis_plus对于操作数据更灵活,jpa可以自动建表,两者各取其…

使用MetaGPT 创建智能体(2)多智能体

先给上个文章使用MetaGPT 创建智能体(1)入门打个补丁: 补丁1: MeteGTP中Role和Action的关联和区别?这是这两天再使用MetaGPT时候心中的疑问,这里做个记录 Role(角色)和 Action&…

C# 使用.NET内置的 IObservable<T> 和 IObserver<T>-观察者模式

核心概念 IObservable<T> 表示 可观察的数据源&#xff08;如事件流、实时数据&#xff09;。 关键方法&#xff1a;Subscribe(IObserver<T> observer)&#xff0c;用于注册观察者。 IObserver<T> 表示 数据的接收者&#xff0c;响应数据变化。 三个核心…

Redis——网络模型之IO讲解

目录 前言 1.用户空间和内核空间 1.2用户空间和内核空间的切换 1.3切换过程 2.阻塞IO 3.非阻塞IO 4.IO多路复用 4.1.IO多路复用过程 4.2.IO多路复用监听方式 4.3.IO多路复用-select 4.4.IO多路复用-poll 4.5.IO多路复用-epoll 4.6.select poll epoll总结 4.7.IO多…

vue3 传参 传入变量名

背景&#xff1a; 需求是&#xff1a;在vue框架中&#xff0c;接口传参我们需要穿“变量名”&#xff0c;而不是字符串 通俗点说法是&#xff1a;在网络接口请求的时候&#xff0c;要传属性名 效果展示&#xff1a; vue2核心代码&#xff1a; this[_keyParam] vue3核心代码&…