【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相低电平时,才判断为反转

代码如下:

相关推荐
梭七y3 分钟前
记录学习《手动学习深度学习》这本书的笔记(十)
笔记·深度学习·学习
GodKK老神灭23 分钟前
ESP32蓝牙开发笔记(十四)
笔记
硅谷秋水29 分钟前
ROBOVERSE:面向可扩展和可泛化机器人学习的统一平台、数据集和基准
人工智能·深度学习·学习·机器学习·机器人
敢敢のwings31 分钟前
论文速读:《CoM:从多模态人类视频中学习机器人操作,助力视觉语言模型推理与执行》
学习·机器人·音视频
前端小崔2 小时前
从零开始学习three.js(14):一文详解three.js中的粒子系统Points
开发语言·前端·javascript·学习·3d·webgl·数据可视化
阿辉___2 小时前
AI应用开发实战分享
java·学习·aigc
GodKK老神灭2 小时前
ESP32蓝牙开发笔记(十五)
笔记
Chef_Chen2 小时前
从0开始学习大模型--Day03--Agent规划与记忆
学习
moxiaoran57533 小时前
Kubernetes(k8s)学习笔记(八)--KubeSphere定制化安装
笔记·学习·kubernetes
百分百题库APP3 小时前
中级注册安全工程师的《安全生产专业实务》科目如何选择专业?
学习·考试·题库·考证