EXTI外部中断
1、中断系统
- 中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行
- 中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源
- 中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回。
2、中断执行流程
3、STM32中断
- 68个可屏蔽中断通道,包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设
- 使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级
中断地址的作用:
\qquad 在程序中的中断函数,地址由编译器进行分配,不固定,但中断跳转由于硬件的限制,只能跳转到固定的地址执行程序 ,为了能让硬件跳转到一个不固定的中断函数里,就需要在内存中定义一个地址的列表,列表地址是固定的,中断发生后,就跳到这个固定位置,在这个固定位置,由编译器,再加上一条跳转到中断函数的代码,这样中断跳转就可以跳转到任意位置了,这个中断地址的列表,就叫中断问量表
。
4、NVIC基本结构
NVIC:嵌套中断向量控制器
在STM32中,NVIC用来统一分配中断优先级和管理中断,属于内核外设
5、NVIC优先级分组
- NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级
- 抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队
中断优先级:值越小,优先级越高
6、EXTI简介(引脚电平变化,申请中断)
- EXTI(Extern Interrupt)外部中断
- EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
- 支持的触发方式:上升沿/下降沿/双边沿/软件触发
- 支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断
- 通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒
- 触发响应方式:中断响应/事件响应
中断响应是正常的流程,引脚电平变化触发中断
事件响应不会触发中断,而是触发别的外设(ADC,DMA等)操作,属于外设之间的联合工作
7、EXTI基本结构
EXTI9_5和EXTI15_10:通过标志位区分哪个中断触发的
其它外设:用来触发其它外设操作的,也就是事件响应
8、AFIO复用IO口
- AFIO主要用于引脚复用功能的选择和重定义
- 在STM32中,AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择
9、EXTI框图
10、旋转编码器简介
- 旋转编码器:用来测量位置、速度或旋转方向的装置,当其旋转轴旋转时,其输出端可以输出与旋转速度和方向对应的方波信号,读取方波信号的频率和相位信息即可得知旋转轴的速度和方向
- 类型:机械触点式/霍尔传感器式/光栅式
电路描述:
\qquad 左右两边电路分别接了上拉电阻R1,R2,在默认状态时,A,B输出为高电平,看哪边电路导通,此时与GND相连,则输出口为低电平
11、程序设计:
1.使用对射式红外传感器触发外部中断
\qquad
当挡光片在这个对射式红外传感器中间经过时,DO输出电平变化的信号,电平跳变的信号触发STM32 PB14号口的中断,在中断函数中,执行变量自加,主循环调用OLED显示该变量。
\qquad
接上模块后,使用挡光片在传感器中间槽上来回进出,进入时开关指示灯灭,出来后开关指示灯亮,说明高低电平输出没问题
步骤:
1.配置RCC,打开时钟
2.配置GPIO,选择端口为输入模式
3.配置AFIO,选择所使用的这一路GPIO,连接到EXTI
4.配置EXTI,选择边沿触发方式(上升,下降,双边),选择触发响应方式(中断响应,事件响应)
6.配置NVIC,给中断选择合适优先级
c文件:
#include "stm32f10x.h" // Device header
static uint32_t countSensor = 0;
static uint8_t flag_count = 0;
void countSensor_init(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable GPIOB clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
/* Enable AFIO clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
/* Configure PB14 pin as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Connect EXTI14 Line to PB14 pin */
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
/* Configure EXTI14 line */
EXTI_InitStructure.EXTI_Line = 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_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* Enable and set EXTI14 Interrupt to the lowest priority */
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
uint32_t getCountValue(void)
{
if(flag_count==1){
if(countSensor<10){
countSensor++;
}else{
countSensor=0;
}
flag_count = 0;
}
return countSensor;
}
void EXTI15_10_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line14) != RESET)
{
flag_count = 1;
/* Clear the EXTI line 14 pending bit */
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
h文件:
#ifndef __COUNTSENSOR_H
#define __COUNTSENSOR_H
extern void countSensor_init(void);
extern uint32_t getCountValue(void);
#endif
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,getCountValue(),2);
}
}
2.旋转编码器触发外部中断
c文件:
#include "stm32f10x.h" // Device header
int32_t Encoder_Count;
void encoder_init(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable GPIOB clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
/* Enable AFIO clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
/* Configure PB0/PB1 pin as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Connect EXTI0/1 Line to PB1/0 pin */
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);
/* Configure EXTI1/0 line */
EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* Enable and set EXTI0 Interrupt to the lowest priority */
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Enable and set EXTI1 Interrupt to the lowest priority */
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
int32_t Encoder_Get(void)
{
int32_t Temp;
Temp = Encoder_Count;
Encoder_Count = 0;
return Temp;
}
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0) != RESET)
{
/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
Encoder_Count --;
}
}
/* Clear the EXTI line 0 pending bit */
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
void EXTI1_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line1) != RESET)
{
/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
{
Encoder_Count ++;
}
}
/* Clear the EXTI line 1 pending bit */
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
h文件
#ifndef __ENCODER_H
#define __ENCODER_H
extern void encoder_init(void);
extern int32_t Encoder_Get(void);
#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "encoder.h"
int32_t num ;
int main(void)
{
OLED_Init();
encoder_init();
OLED_ShowString(1, 1, "count:");
while (1)
{
num += Encoder_Get();
OLED_ShowSignedNum(1,7,num,5);
}
}
注意:在配置外设GPIO时,如果不确定该配置为什么模式,可参阅参考手册外设的GPIO配置
,里面有每个外设的各个引脚所配置的模式 ,如下
使用外部中断模块的特性:
\qquad 对于stm32,想要获取的信号是外部驱动的很快的突发信号,如旋转编码器,红外遥控接收头,按键等。