【STM32 学习笔记】EXTI外部中断

EXTI外部中断


表的详细内容在STM32F10xxx参考手册132页有,

然后右边这里还有个中断的地址,这个地址是干什么的呢?这个是因为我们程序中的中断函数,它的地址是由编译器来分配的,是不固定的。但是我们的中断跳转由于硬件的限制,只能跳到固定的地址执行程序,所以为了能让硬件跳转到一个不固定的中断函数里 ,这里就需要在内存中定义一个地址的列表。这个列表地址是固定的,中断发生后,就跳到这个固定位置,然后在这个固定位置由编译器,再加上一条跳转到中断函数的代码,这样中断跳转就可以跳转到任意位置。这个中断地址的列表,就叫中断向量表

NVIC基本结构

这个NVIC的名字叫做嵌套中断向量控制器,在STM32中,它是用来统一分配中断优先级和管理中断的。

NVIC是一个内核外设 ,是CPU的小助手。STM32的中断非常多,如果把这些中断全都接到CPU上,那CPU还得引出很多线进行适配,设计上就很麻烦,并且如果很多中断同时申请,或者中断很多产生了拥堵,CPU也会很难处理,毕竟CPU主要是用来运算的,中断分配的任务就放到别的地方吧,所以NVIC就出现了。

NVIC有很多输入口,你有多少个中断线路,都可以接过来,比如这里可以接到EXTI、TIM、ADC、USART等等,这里线上画了个斜杠,上面写个n,这个意思是一个外设可能会同时占用多个中断通道,所以这里有n条线。然后NVIC只有一个输出口,NVIC根据每个中断的优先级分配中断的先后顺序,之后,通过右边这一个输出口就告诉CPU,你该处理哪个中断。对于中断先后顺序分配的任务,CPU不需要知道。
13:18~14:00举了例子 && 14:00讲了下面的NVIC中断分组

EXTI结构


但相同的Pin不能同时触发中断:这个意思就是,比如PA0和PB0不能同时用,或者,PA1、PB1、PC1这样的,端口GPIO_Pin一样的。

然后再看一下外部中断占用的通道,其中有16个GPIO_Pin,这就对应GPIO_Pin_0到GPIO_Pin_15,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒,这些加起来总共有20个中断线路。这里的16个GPIO_Pin是外部中断的主要功能,后面跟着的这四个东西其实是来"蹭网"的。因为这个外部中断有个功能,就是从低功耗模式的停止模式下唤醒STM32,那对于PVD电源电压监测,当从电源从电压过低恢复时,就需要PVD借助一下外部中断退出停止模式;对于RTC闹钟来说,有时候为了省电,RTC定一个闹钟之后,STM32会进入停止模式,等到闹钟响的时候再唤醒,这也需要借助外部中断;还有USB唤醒、以太网唤醒,也都是类似的作用。
中断响应 ,就是申请中断,让CPU执行中断函数;事件响应 是STM32对外部中断增加的一种额外的功能。当外部中断检测到引脚电平变化时,正常的流程是选择触发中断,但是在STM32中,也可以选择触发一个事件,如果选择触发事件,那外部中断的信号就不会通向CPU了,而是通向其它外设,用来触发其它外设的操作,比如触发ADC转换、触发DMA等。所以总结一下:中断响应 是正常的流程,引脚电平变化触发中断;事件响应不会触发中断,而是触发别的外设操作,属于外设之间的联合工作。

这里注意一下,本来20路输入,应该有20路中断的输出,但是可能ST公司觉得这20个输出太多了,比较占用NVIC的通道资源,所以就把其中外部中断的 9~5 和15 ~ 10给分到一个通道里。也就是说,外部中断的9~5会触发同一个中断函数,15~10也会触发同一个中断函数,在编程的时候,我们在这两个中断函数里,需要再根据标志位来区分到底是哪个中断进来的。

外部中断的使用场景:

就是对于STM32来说,想要获取的信号是外部驱动的很快的突发信号。比如旋转编码器的输出信号,你可能很久都不会拧它,这时不需要STM32做任何事,但是我一拧它,就会有很多脉冲波形需要STM32接收。这个信号是突发的,STM32不知道什么时候会来,同时它是外部驱动的,STM32只能被动读取,最后这个信号非常快,STM32稍微晚一点来读取,就会错过很多波形。那对于这种情况来说,就可以考虑使用STM32的外部中断了。有脉冲过来,STM32立即进入中断函数处理,没有脉冲的时候,STM32就专心做其它事情。

另外还有,比如红外遥控接收头的输出,接收到遥控数据之后,它会输出一段波形,这个波形转瞬即逝,并且不会等你,所以就需要我们用外部中断来读取。

最后还有按键,虽然它的动作也是外部驱动的突发事件,但我并不推荐用外部中断来读取按键。因为用外部中断不好处理按键抖动和松手检测的问题,对于按键来说,它的输出波形也不是转瞬即逝的。所以要求不高的话可以在主程序中循环读取,如果不想用主循环读取的话,可以考虑一下定时器中断读取的方式。这样既可以做到后台读取按键值、不阻塞主程序,也可以很好地处理按键抖动和松手检测的问题。
NVIC以及中断、事件手册讲解视频

代码实战2:如何使用中断和对射式红外传感器&旋转编码器

注意:我们这里是用到了PB14来做外部中断的

5-1 对射式红外传感器计次接线图

当挡光片在对射式红外传感器中间经过时,DO输出电平跳变信号,触发PB14号口的中断,在中断断数执行Num++

  • 第一步,配置RCC,将程序涉及外设的时钟都打开

    提示:有GPIOB和AFIO

  • 第二步,配置GPIO,选择端口为输入模式

  • 第三步,配置AFIO,选择硬件所用用的那一路GPIO,连接到后面的EXTI

    涉及函数如下:
    void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)

    作用:选择用作EXTI线的GPIO引脚。

    参数说明:

参数 说明
GPIO_PortSource 选择要用作EXTI线路源的GPIO端口。取值为GPIO_PortSourceGPIOx,其中x为(A...G)。
GPIO_PinSource GPIO_PinSource:要配置的EXTI线路。该参数可以为GPIO_PinSourcex,其中x可以为(0...15)。
  • 第四步,配置EXTI,选择边沿触发方式,比如上升沿、下降沿或者双边沿,还有选择触发响应方式,可以选择中断响应和事件响应
    涉及函数如下:
    void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)
    作用:根据EXTI InitStruct中的指定参数初始化EXTI外设。
    参数说明:
参数 说明
EXTI_InitStruct 指向EXTI InitTypeDef结构体的指针包含ExTI外设的配置信息。

EXTI InitTypeDef结构体说明:

c 复制代码
typedef struct
{
  uint32_t EXTI_Line;          
  EXTIMode_TypeDef EXTI_Mode;     
  EXTITrigger_TypeDef EXTI_Trigger; 
  FunctionalState EXTI_LineCmd;                        
}EXTI_InitTypeDef;

参数说明以及举例

举例:

c 复制代码
/* Enables external lines 12 and 14 interrupt generation on falling 
edge */ 
EXTI_InitTypeDef EXTI_InitStructure; 
EXTI_InitStructure.EXTI_Line = EXTI_Line12 | EXTI_Line14; 
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; 
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; 
EXTI_InitStructure.EXTI_LineCmd = ENABLE; 
EXTI_Init(&EXTI_InitStructure); 
  • 第五步,配置NVIC,给我们这个中断选择一个合适的优先级
    涉及函数如下:
    void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
    作用:配置优先级分组:抢占优先级和子优先级。
    参数说明:
参数 说明
NVIC_PriorityGroup 指定优先级分组位长度。

取值范围:

例如:

c 复制代码
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  • 最后,通过NVIC,外部中断信号就能进入CPU了,这样CPU才能收到中断信号,才能跳转到中断函数里执行中断程序
    涉及函数如下:
    void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
    作用:根据NVIC InitStruct中指定的参数初始化NVIC外设。
    参数说明:
参数 说明
NVIC_InitStruct 指向NVIC InitTypeDef结构体的指针指定NVic外设的配置信息。

举例:

c 复制代码
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);

中断函数的格式:

根据中断向量表,找到所需中断函数,这里面以IRQHandler结尾的字符串就是中断函数的名字,再根据名字写中断函数。

例如:void EXTI15_10_IRQHandler(void){ }

这就是中断函数的格式,中断函数都是无参无返回值的,中断函数的名字不要写错了,写错了就进不了中断了,最好是直接从启动文件复制过来,这样就不会有问题了。

注:启动文件为

然后在中断函数里,一般都是先进行一个中断标志位的判断 ,确保是我们想要的中断源触发的这个函数,因为这个函数EXTI10到EXTI15都能进来,所以要先判断一下是不是我们想要的EXTI14进来的。所用函数:EXTI_GetITStatus(uint32_t EXTI_Line)

最后,中断程序结束后,一定要再调用一下清除中断标志位的函数 ,因为只有中断标志位置1了,程序就会跳转到中断函数。如果你不清除中断标志位,那它就会一直申请中断,这样程序就会不断响应中断,执行中断函数,那程序就卡死在中断函数里了。所用函数:EXTI_ClearITPendingBit(uint32_t EXTI_Line)

中断函数就不用声明了,因为中断函数不需要调用,它是自动执行的。

其它涉及函数:
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line)

作用:检查指定的 EXTI 线路触发请求发生与否(是不是我们想要的中断触发源)

返回值:(SET或RESET)

参数说明:

参数 说明
EXTI_Line EXTI_Line:要检查的EXTI行。EXTI_Linex:外部中断线x,其中x(0...19)

void GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)

作用:读取指定端口管脚的输入

参数说明:

参数 说明
GPIOx GPIOx:其中x可以为(A...G)选择GPIO外设。
GPIO_Pin 指定要读取的端口位。该参数是GPIO_Pin_x,其中x可以是(0...15)。

void EXTI_ClearITPendingBit(uint32_t EXTI_Line)

作用:清除EXTI线路挂起位

参数说明:

参数 说明
EXTI_Line 指定要清除的EXTI行。该参数可以是ExTI Linex的任意组合,其中x可以是(0...19)

EXTI和NVIC两个外设,这两个外设的时钟是一直都打开着的,不需要我们再开启时钟了。EXIT模块是由NVIC模块直接控制的,并不需要单独的外设时钟。NVIC也不需要开启时钟,是因为NVIC是内核的外设,内核的外设都是不需要开启时钟的。

代码如下:

蓝线部分是我自己需要注意的地方

5-2 旋转编码器计次37:30

在写中断函数的核心思想:

只有在B相下降沿和A相低电平时,才判断为正转

在A相下降沿和B相低电平时,才判断为反转

代码如下:

相关推荐
MarkHD4 小时前
智能体在车联网中的应用:第51天 模仿学习与离线强化学习:破解数据效率与安全困局的双刃剑
学习·安全
Drawing stars7 小时前
JAVA后端 前端 大模型应用 学习路线
java·前端·学习
崇山峻岭之间7 小时前
Matlab学习记录33
开发语言·学习·matlab
玄〤8 小时前
黑马点评中 VoucherOrderServiceImpl 实现类中的一人一单实现解析(单机部署)
java·数据库·redis·笔记·后端·mybatis·springboot
科技林总8 小时前
【系统分析师】3.5 多处理机系统
学习
ytttr8739 小时前
基于STM32和W5500芯片的Modbus TCP协议栈实现
stm32·嵌入式硬件
芯思路9 小时前
STM32开发学习笔记之三【按键】
笔记·stm32·学习
Lips6119 小时前
2026.1.11力扣刷题笔记
笔记·算法·leetcode
charlie11451419110 小时前
从 0 开始的机器学习——NumPy 线性代数部分
开发语言·人工智能·学习·线性代数·算法·机器学习·numpy
咚咚王者10 小时前
人工智能之核心基础 机器学习 第十二章 半监督学习
人工智能·学习·机器学习