六、EXTI及中断
四、EXIT外部中断
中断系统
-
中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行
-
中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源
-
中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回
执行流程
中断向量表
-
STM32拥有68个可屏蔽中断通道,包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设
-
使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级
灰色部分是Cortex-M3内核里面的中断,非灰色部分是STM32外设的中断
NVIC
NVIC介绍
NVIC的名称叫做嵌套中断向量控制器
用来统一分配中断优先级和管理中断,一共可以分为16个优先级
属于内核外设
NVIC基本结构
NVIC优先级分组
- NVIC的中断优先级由优先级寄存器的4个bit位(0~15)决定,这4个bit位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级
- 抢占优先级高的可以打断其他正在进行的中断,也就是中断嵌套
- 响应优先级高的可以排在其他中断前面,但是不能打断其他中断程序
- 抢占优先级和响应优先级均相同的按中断号排队
分组方式 | 抢占优先级 | 响应优先级 |
---|---|---|
分组0 | 0个bit位,取值为0 | 4个bit位,取值为0~15 |
分组1 | 1个bit位,取值为0~1 | 3个bit位,取值为0~7 |
分组2 | 2个bit位,取值为0~3 | 2个bit位,取值为0~3 |
分组3 | 3个bit位,取值为0~7 | 1个bit位,取值为0~1 |
分组4 | 4个bit位,取值为0~15 | 0个bit位,取值为0 |
举例:比如选了分组1,那么抢占优先级有1个bit位,只能选01;而响应优先级有3个bit位,就可以选07。注意:值越小,优先级越高。
EXTI
EXTI简介
EXTI(Extern Interrupt)外部中断
EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
- 支持的触发方式:上升沿、下降沿、双边沿、软件触发
- 支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断(例如PA1和PB1不能同时触发,PA12和PC12不能同时触发)
- 通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒
- 触发响应方式:中断响应、事件响应(响应其他外设。例如触发ADC转换、DMA等)
EXTI基本流程
AFIO在中断中的作用
-
AFIO主要用于引脚复用功能的选择和重定义
-
在STM32中,AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择
这里要说的就是AFIO的中断引脚选择
他会根据我们的配置从相同的Pin中选一个出来输入EXTI中(从PA0~PG0中选一个引脚输入EXTI0)
EXTI框图
旋转编码器介绍
旋转编码器简介
光栅式:第一张图片
使用的硬件是对射红外传感器,对射红外传感器中间被遮挡时发出高电平,没有被遮挡时发出低电平。再通过有孔洞的光栅进行转动,时而被遮挡时而没有被遮挡,就会发出一串方波,我们就可以通过这串方波来判断旋转的速度
机械触点式:第二和第三张图片,第三张为拆解图
当旋转轴旋转时,下面带有金属触点的圆盘也会跟着转动,他能让两侧触点的通断产生一个90度的相位差,可以由此判断旋转方向
机械触点式电路
AFIO重映射引脚
/**
* @作用 更改指定引脚的映射。
* @参数 GPIO_Remap: 选择要重新映射的引脚。
* 该参数可以是以下值之一:
* @arg GPIO_Remap_SPI1 : SPI1 Alternate Function mapping
* @arg GPIO_Remap_I2C1 : I2C1 Alternate Function mapping
* @arg GPIO_Remap_USART1 : USART1 Alternate Function mapping
* @arg GPIO_Remap_USART2 : USART2 Alternate Function mapping
* @arg GPIO_PartialRemap_USART3 : USART3 Partial Alternate Function mapping
* @arg GPIO_FullRemap_USART3 : USART3 Full Alternate Function mapping
* @arg GPIO_PartialRemap_TIM1 : TIM1 Partial Alternate Function mapping
* @arg GPIO_FullRemap_TIM1 : TIM1 Full Alternate Function mapping
* @arg GPIO_PartialRemap1_TIM2 : TIM2 Partial1 Alternate Function mapping
* @arg GPIO_PartialRemap2_TIM2 : TIM2 Partial2 Alternate Function mapping
* @arg GPIO_FullRemap_TIM2 : TIM2 Full Alternate Function mapping
* @arg GPIO_PartialRemap_TIM3 : TIM3 Partial Alternate Function mapping
* @arg GPIO_FullRemap_TIM3 : TIM3 Full Alternate Function mapping
* @arg GPIO_Remap_TIM4 : TIM4 Alternate Function mapping
* @arg GPIO_Remap1_CAN1 : CAN1 Alternate Function mapping
* @arg GPIO_Remap2_CAN1 : CAN1 Alternate Function mapping
* @arg GPIO_Remap_PD01 : PD01 Alternate Function mapping
* @arg GPIO_Remap_TIM5CH4_LSI : LSI connected to TIM5 Channel4 input capture for calibration
* @arg GPIO_Remap_ADC1_ETRGINJ : ADC1 External Trigger Injected Conversion remapping
* @arg GPIO_Remap_ADC1_ETRGREG : ADC1 External Trigger Regular Conversion remapping
* @arg GPIO_Remap_ADC2_ETRGINJ : ADC2 External Trigger Injected Conversion remapping
* @arg GPIO_Remap_ADC2_ETRGREG : ADC2 External Trigger Regular Conversion remapping
* @arg GPIO_Remap_ETH : Ethernet remapping (only for Connectivity line devices)
* @arg GPIO_Remap_CAN2 : CAN2 remapping (only for Connectivity line devices)
* @arg GPIO_Remap_SWJ_NoJTRST : Full SWJ Enabled (JTAG-DP + SW-DP) but without JTRST
* @arg GPIO_Remap_SWJ_JTAGDisable : JTAG-DP Disabled and SW-DP Enabled
* @arg GPIO_Remap_SWJ_Disable : Full SWJ Disabled (JTAG-DP + SW-DP)
* @arg GPIO_Remap_SPI3 : SPI3/I2S3 Alternate Function mapping (only for Connectivity line devices)
* When the SPI3/I2S3 is remapped using this function, the SWJ is configured
* to Full SWJ Enabled (JTAG-DP + SW-DP) but without JTRST.
* @arg GPIO_Remap_TIM2ITR1_PTP_SOF : Ethernet PTP output or USB OTG SOF (Start of Frame) connected
* to TIM2 Internal Trigger 1 for calibration (only for Connectivity line devices)
* If the GPIO_Remap_TIM2ITR1_PTP_SOF is enabled the TIM2 ITR1 is connected to
* Ethernet PTP output. When Reset TIM2 ITR1 is connected to USB OTG SOF output.
* @arg GPIO_Remap_PTP_PPS : Ethernet MAC PPS_PTS output on PB05 (only for Connectivity line devices)
* @arg GPIO_Remap_TIM15 : TIM15 Alternate Function mapping (only for Value line devices)
* @arg GPIO_Remap_TIM16 : TIM16 Alternate Function mapping (only for Value line devices)
* @arg GPIO_Remap_TIM17 : TIM17 Alternate Function mapping (only for Value line devices)
* @arg GPIO_Remap_CEC : CEC Alternate Function mapping (only for Value line devices)
* @arg GPIO_Remap_TIM1_DMA : TIM1 DMA requests mapping (only for Value line devices)
* @arg GPIO_Remap_TIM9 : TIM9 Alternate Function mapping (only for XL-density devices)
* @arg GPIO_Remap_TIM10 : TIM10 Alternate Function mapping (only for XL-density devices)
* @arg GPIO_Remap_TIM11 : TIM11 Alternate Function mapping (only for XL-density devices)
* @arg GPIO_Remap_TIM13 : TIM13 Alternate Function mapping (only for High density Value line and XL-density devices)
* @arg GPIO_Remap_TIM14 : TIM14 Alternate Function mapping (only for High density Value line and XL-density devices)
* @arg GPIO_Remap_FSMC_NADV : FSMC_NADV Alternate Function mapping (only for High density Value line and XL-density devices)
* @arg GPIO_Remap_TIM67_DAC_DMA : TIM6/TIM7 and DAC DMA requests remapping (only for High density Value line devices)
* @arg GPIO_Remap_TIM12 : TIM12 Alternate Function mapping (only for High density Value line devices)
* @arg GPIO_Remap_MISC : Miscellaneous Remap (DMA2 Channel5 Position and DAC Trigger remapping,
* only for High density Value line devices)
* @参数 NewState: 端口引脚重新映射的状态。
* 取值为:ENABLE或DISABLE。
* @返回值 None
*/
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState)
EXTI相关函数
具体使用在库函数文件夹的:stm32f10x_exti.c/.h文件中
typedef struct
{
uint32_t EXTI_Line; /*!< 指定要启用或禁用的EXTI线。*/
EXTIMode_TypeDef EXTI_Mode; /*!< 指定EXTI线的模式。 */
EXTITrigger_TypeDef EXTI_Trigger; /*!< 指定触发EXTI的信号沿 */
FunctionalState EXTI_LineCmd; /*!< 指定所选EXTI线的状态。 */
}EXTI_InitTypeDef;
// EXTI_Line可选的值
EXTI_Linex:其中x可以为0~19
// EXTI_Mode可选的值
typedef enum
{
EXTI_Mode_Interrupt = 0x00, // 中断响应
EXTI_Mode_Event = 0x04 // 事件响应
}EXTIMode_TypeDef;
// EXTI_Trigger可选的值
typedef enum
{
EXTI_Trigger_Rising = 0x08, // 上升沿触发
EXTI_Trigger_Falling = 0x0C, // 下降沿触发
EXTI_Trigger_Rising_Falling = 0x10 // 上升、下降沿都可以触发
}EXTITrigger_TypeDef;
// EXTI_LineCmd可选的值
ENABLE // 使能
DISABLE // 失能
// 将EXTI外设寄存器初始化为其默认重置值。
void EXTI_DeInit(void);
// 根据指定初始化EXTI外设
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
// 用重置值填充每个EXTI_InitStruct成员。
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);
// 产生一个软件中断。
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);
// 检查是否设置了指定的EXTI行标志。主函数中检测指定的EXTI通道是否触发中断
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);
// 清除EXTI的行挂起标志。主函数中清除指定的EXTI通道的中断标志位
void EXTI_ClearFlag(uint32_t EXTI_Line);
// 检查指定的EXTI中断线路是否触发中断。中断函数中检测指定的EXTI通道是否触发中断
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
// 清除EXTI的行挂起位。中断函数中清除指定的EXTI通道的中断标志位
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
NVIC相关函数
具体使用在库函数文件夹的:misc.c/.h文件中
NVIC_PriorityGroupConfig填入typedef struct
{
uint8_t NVIC_IRQChannel; /*!< 指定要启用或禁用的IRQ通道 */
uint8_t NVIC_IRQChannelPreemptionPriority; /*!< 指定IRQ通道的抢占优先级*/
uint8_t NVIC_IRQChannelSubPriority; /*!< 指定IRQ通道的响应优先级*/
FunctionalState NVIC_IRQChannelCmd; /*!< 定义的IRQ通道将被启用或禁用。*/
} NVIC_InitTypeDef;
// NVIC_IRQChannel可选的值
/* 注意:这里只是STM32F10X_MD的中断通道,全部型号的在stm32f10x.h文件中 */
ADC1_2_IRQn
USB_HP_CAN1_TX_IRQn
USB_LP_CAN1_RX0_IRQn
CAN1_RX1_IRQn
CAN1_SCE_IRQn
EXTI9_5_IRQn
TIM1_BRK_IRQn
TIM1_UP_IRQn
TIM1_TRG_COM_IRQn
TIM1_CC_IRQn
TIM2_IRQn
TIM3_IRQn
TIM4_IRQn
I2C1_EV_IRQn
I2C1_ER_IRQn
I2C2_EV_IRQn
I2C2_ER_IRQn
SPI1_IRQn
SPI2_IRQn
USART1_IRQn
USART2_IRQn
USART3_IRQn
EXTI15_10_IRQn
RTCAlarm_IRQn
USBWakeUp_IRQn
// NVIC_IRQChannelPreemptionPriority可选的值
使用NVIC_PriorityGroupConfig函数进行优先级分组后确定
// NVIC_IRQChannelSubPriority可选的值
使用NVIC_PriorityGroupConfig函数进行优先级分组后确定
// NVIC_IRQChannelCmd可选的值
ENABLE // 使能
DISABLE // 失能
// 配置优先级分组:抢占优先级和响应优先级。
// 参数为 NVIC_PriortyGroupx:其中x为0~4,对应5个分组
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
// 根据NVIC_InitTypeDef结构体成员属性的参数初始化NVIC外设。
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
// 设置向量表位置和偏移量。
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);
// 选择系统进入低功耗模式的条件。
void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);
外设的GPIO配置
stm32f10x_md的中断函数
EXPORT WWDG_IRQHandler [WEAK]
EXPORT PVD_IRQHandler [WEAK]
EXPORT TAMPER_IRQHandler [WEAK]
EXPORT RTC_IRQHandler [WEAK]
EXPORT FLASH_IRQHandler [WEAK]
EXPORT RCC_IRQHandler [WEAK]
EXPORT EXTI0_IRQHandler [WEAK]
EXPORT EXTI1_IRQHandler [WEAK]
EXPORT EXTI2_IRQHandler [WEAK]
EXPORT EXTI3_IRQHandler [WEAK]
EXPORT EXTI4_IRQHandler [WEAK]
EXPORT DMA1_Channel1_IRQHandler [WEAK]
EXPORT DMA1_Channel2_IRQHandler [WEAK]
EXPORT DMA1_Channel3_IRQHandler [WEAK]
EXPORT DMA1_Channel4_IRQHandler [WEAK]
EXPORT DMA1_Channel5_IRQHandler [WEAK]
EXPORT DMA1_Channel6_IRQHandler [WEAK]
EXPORT DMA1_Channel7_IRQHandler [WEAK]
EXPORT ADC1_2_IRQHandler [WEAK]
EXPORT USB_HP_CAN1_TX_IRQHandler [WEAK]
EXPORT USB_LP_CAN1_RX0_IRQHandler [WEAK]
EXPORT CAN1_RX1_IRQHandler [WEAK]
EXPORT CAN1_SCE_IRQHandler [WEAK]
EXPORT EXTI9_5_IRQHandler [WEAK]
EXPORT TIM1_BRK_IRQHandler [WEAK]
EXPORT TIM1_UP_IRQHandler [WEAK]
EXPORT TIM1_TRG_COM_IRQHandler [WEAK]
EXPORT TIM1_CC_IRQHandler [WEAK]
EXPORT TIM2_IRQHandler [WEAK]
EXPORT TIM3_IRQHandler [WEAK]
EXPORT TIM4_IRQHandler [WEAK]
EXPORT I2C1_EV_IRQHandler [WEAK]
EXPORT I2C1_ER_IRQHandler [WEAK]
EXPORT I2C2_EV_IRQHandler [WEAK]
EXPORT I2C2_ER_IRQHandler [WEAK]
EXPORT SPI1_IRQHandler [WEAK]
EXPORT SPI2_IRQHandler [WEAK]
EXPORT USART1_IRQHandler [WEAK]
EXPORT USART2_IRQHandler [WEAK]
EXPORT USART3_IRQHandler [WEAK]
EXPORT EXTI15_10_IRQHandler [WEAK]
EXPORT RTCAlarm_IRQHandler [WEAK]
EXPORT USBWakeUp_IRQHandler [WEAK]
红外对射传感器计数案例
接线图
代码示例
#include "stm32f10x.h" // Device header
#include "OLED.h"
// 无符号16位整数,用来记录红外线被遮挡的次数
uint16_t count = 0;
int main()
{
// 开启GPIOB和AFIO的外设时钟。EXTI和NVIC的时钟不用管
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
// 定义GPIO_InitTypeDef类型的结构体
GPIO_InitTypeDef GPIO_InitStructer;
// 通过STM32F10xx参考手册可知,GPIO作为外部中断输入时,浮空、上拉、下拉输入都可以
GPIO_InitStructer.GPIO_Mode = GPIO_Mode_IPD; // 上拉输入
GPIO_InitStructer.GPIO_Pin = GPIO_Pin_14; // 选择引脚14
GPIO_InitStructer.GPIO_Speed = GPIO_Speed_50MHz; // 速度50MHz
GPIO_Init(GPIOB, &GPIO_InitStructer); // 根据结构体中的成员属性初始GPIOB
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14); // 将外部中断的14号线映射到GPIOB,即选择PB14为外部中断引脚
// 定义EXTI_InitTypeDef类型的结构体
EXTI_InitTypeDef EXTI_InitStructer;
EXTI_InitStructer.EXTI_Line = EXTI_Line14; // 要启用或禁用的EXTI通道,通道14
EXTI_InitStructer.EXTI_LineCmd = ENABLE; // 是否启用通道,启用
EXTI_InitStructer.EXTI_Mode = EXTI_Mode_Interrupt; // 中断响应还是事件响应,中断响应
EXTI_InitStructer.EXTI_Trigger = EXTI_Trigger_Falling; // 触发方式,上升沿、下降沿和双边沿,下降沿
EXTI_Init(&EXTI_InitStructer); // 根据结构体中的成员属性初始EXTI
// 设置NVIC优先级分组,分组2,抢占优先级2个bit位,响应优先级2个bit位
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// 定义NVIC_InitTypeDef类型的结构体
NVIC_InitTypeDef NVIC_InitStructer;
NVIC_InitStructer.NVIC_IRQChannel = EXTI15_10_IRQn; // 选择中断通道,外部中断15~10
NVIC_InitStructer.NVIC_IRQChannelCmd = ENABLE; // 是否开启通道,开启
NVIC_InitStructer.NVIC_IRQChannelPreemptionPriority = 1; // 设置抢占优先级,1
NVIC_InitStructer.NVIC_IRQChannelSubPriority = 1; // 设置响应优先级,1
NVIC_Init(&NVIC_InitStructer); // 根据结构体中的成员属性初始化NVIC
OLED_Init();
OLED_ShowString(1,1,"Count:");
while(1)
{
OLED_ShowNum(1, 7, count, 5);
}
}
// 外部中断15~10通道的中断函数
void EXTI15_10_IRQHandler(void)
{
// 判断是否是外部中断14号线触发的中断
if(EXTI_GetITStatus(EXTI_Line14) == SET)
{
count++;
// 清除外部中断14号线的中断标志位
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
选择编码器计数案例
接线图
示例代码
#include "stm32f10x.h" // Device header
#include "OLED.h"
int16_t num = 0;
int main()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitTypeDef GPIO_InitStructer;
GPIO_InitStructer.GPIO_Mode = GPIO_Mode_IPD;
GPIO_InitStructer.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructer.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructer);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);//将外部中断的0号线映射到GPIOB,即选择PB0为外部中断引脚
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);//将外部中断的1号线映射到GPIOB,即选择PB1为外部中断引脚
EXTI_InitTypeDef EXTI_InitStructer;
EXTI_InitStructer.EXTI_Line = EXTI_Line0 | EXTI_Line1;
EXTI_InitStructer.EXTI_LineCmd = ENABLE;
EXTI_InitStructer.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructer.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStructer);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructer;
NVIC_InitStructer.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructer.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructer.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructer.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructer);
NVIC_InitStructer.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStructer.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructer.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructer.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStructer);
OLED_Init();
OLED_ShowString(1,1,"Num:");
while(1)
{
OLED_ShowSignedNum(1,6,num,5);
}
}
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0) == SET)
{
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
num--;
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
void EXTI1_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line1) == SET)
{
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
{
num++;
}
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库