目录
一、概述
关于EXTI外部中断,首先,简单介绍一下要用到的外设AFIO、EXTI、NVIC等等;然后,介绍触发外部中断到CPU响应中断函数的过程;最后,就是外部中断的简单应用,包含两个实验:①对射式红外传感器计数;②旋转编码器计次。
二、主要外设介绍
2.1 AFIO复用IO口
- 在这里AFIO主要功能就是中断引脚选择。AFIO由很多的数据选择器组成,可完成数据选择,这样就可以减少IO口。例如:STM32F103C8T6有两组GPIO,GPIOA和GPIOB,共有32个引脚,都可以触发中断,但是相同Pin脚不能同时触发中断,假如一个IO口一个中断通道,就要32个通道,太占IO资源。如图所示,从PA0、PB0中选择一位进入EXTI0通道;PA1、PB1选择一位进入EXTI1通道...所以相同Pin脚不能同时触发中断。
2.2 EXTI简介
EXTI框图:
- EXTI(Extern Interrupt)外部中断
- EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
- 支持的触发方式:上升沿/下降沿/双边沿/软件触发
- 支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断
- 通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒
- 触发响应方式:中断响应/事件响应
2.3 NVIC中断优先级分组
- NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级
- 抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队
分组方式 |
抢占优先级 |
响应优先级 |
分组0 |
0位,取值为0 |
4位,取值为0~15 |
分组1 |
1位,取值为0~1 |
3位,取值为0~7 |
分组2 |
2位,取值为0~3 |
2位,取值为0~3 |
分组3 |
3位,取值为0~7 |
1位,取值为0~1 |
分组4 |
4位,取值为0~15 |
0位,取值为0 |
三、EXTI外部中断整体结构
- AFIO中断引脚选择。选择合适的中断通道,PA0、PB0就是EXTI0通道,PA1、PB1就是EXTI1通道...,为节省通道资源,EXTI5-EXTI9就合成一个通道EXTI9_5,EXTI10-EXTI15就合成一个通道EXTI15_10。
- EXTI边沿检测及控制。设置触发的方式:上升沿/下降沿/双边沿/软件触发;设置中断响应还是事件响应等等。
- NVIC优先级分组。分为高n位的抢占优先级和低4-n位的响应优先级。
四、实验程序
4.1 对射式红外传感器模块
- 无遮挡时,D0输出低电平,有遮挡时输出高电平。
模块程序如下:
#include "stm32f10x.h" // Device header
uint16_t count;
/*
*函数名 :CountSensor_Init(void)
*函数功能 :红外传感器触发外部中断初始化
*输入 :无
*输出 :无
*/
void CountSensor_Init(void)
{
//GPIO初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能GPIOB时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_14;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);//GPIOB初始化
//AFIO初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//使能AFIO时钟
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);//选择EXTI14线
//EXIT初始化
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Line=EXTI_Line14;
EXTI_InitStruct.EXTI_LineCmd=ENABLE;
EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStruct);//EXIT初始化
//NVIC初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//选择NVIC2组
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel=EXTI15_10_IRQn;//EXTI14线属于EXTI15_10通道
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStruct); //NVIC初始化
}
/*
*函数名 :Getcount(void)
*函数功能 :返回计数数据
*输入 :无
*输出 :count:计数数据
*/
uint16_t Getcount(void)
{
return count;
}
void EXTI15_10_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line14)==SET)//判断是否EXTI14线申请的中断
{
EXTI_ClearITPendingBit(EXTI_Line14);//软件清零中断标志位
count++;
}
}
4.2 旋转编码器模块
- 由上图可知,不旋转时,A,B初始电平都为高电平。
- 旋转时输出信号都为方波信号。
- 顺时针旋转时,A相超前B相90°。
- 逆时针旋转时,B相超前A相90°。
模块程序如下:
- 和上面差不多,多了一个GPIO口引脚触发中断
#include "stm32f10x.h"
int16_t count;
/*
*函数名 :Encode_Init(void)
*函数功能 :旋转编码器外部中断初始化
*输入 :无
*输出 :无
*/
void Encode_Init(void)
{
//GPIO初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
//AFIO初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);
//EXIT初始化
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Line=EXTI_Line0|EXTI_Line1;
EXTI_InitStruct.EXTI_LineCmd=ENABLE;
EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStruct);
//NVIC初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel=EXTI0_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel=EXTI1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority=2;
NVIC_Init(&NVIC_InitStruct);
}
int16_t Getencode(void)
{
int16_t temp=0;
temp=count;
count=0;
return temp;
}
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0)==SET)
{
EXTI_ClearITPendingBit(EXTI_Line0);
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==1)
{
count--;
}
}
}
void EXTI1_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line1)==SET)
{
EXTI_ClearITPendingBit(EXTI_Line1);
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==1)
{
count++;
}
}
}
五、实验现象
- 用遮挡板遮挡红外传感器的凹槽,当拿开遮挡板时,计数加一。
- 顺时针旋转旋转编码器时,计数加一;逆时针旋转时,计数减一。