STM32之外部中断 EXTI
STM32中断控制线支持19个外部中断/事件请求,每个中断都有对应状态位和独立的触发与屏蔽设置。STM32F103的19个外部中断为:
线0-15:对应GPIO口的输入中断。
线16:连接到PVD输出(掉电检测,掉电时可立即保存重要数据作用)。
线17:连接RTC闹钟事件。 线18:连接到USB唤醒事件
STM32的每个IO口都可以作为外部中断源的输入端,而IO口使用的中断线只有16根,且引脚GPIOx.0~GPIOx.15(x=A~G)分别对应中断线0-15.这样每个中断线对应了7个IO口。如线0对应引脚GPIOA.0/GPIOB.0/GPIOC.0/GPIOD.0/GPIOE.0/GPIOF.0/GPIOG.0,但是每个中断线每次只能连接到1个IO口上。即同一时刻EXTIx只能响应一个端口的事件触发,不能同时响应所有IO端口的事件,但可以分时复用。
*1、GPIO与中断映射配置通过函数来实现
void GPIO_EXTILineConfig ( uint8_t GPIO_PortSource,uint8_t GPIO_PinSource );
例如
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE , GPIO_PinSource2);
即外部中断线2就和GPIOE映射起来了,显然是GPIOE.2连接EXTI2中断线。
设置中断触发方式:
void EXTI_Init( EXTI_InitTypeDef * EXTI_InitStruct )
例如设置中断线line4的中断为下降沿触发
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line=EXTI_Line4;//选择中断线4,即EXTI4
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;//中断使能
EXTI_Init(&EXTI_InitStructure);
//根据EXTI_InitStruct结构体中指定的参数初始化外设 EXTI 寄存器
设置好了中断线和GPIO的映射关系,然后又设置好了中断的触发模式等参数,涉及到中断,当然不能忘了设置中断控制体NVIC的中断优先级。
2、设置NVIC的中断优先级
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn; //使能外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
//抢占优先级 2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子优先级 2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure); //中断优先级分组初始化
3、 配置完中断优先级后,需要编写中断服务程序
stm32f10x_it.c文件是专门用来存放中断服务函数的。文件中默认只有几个关于系统异常的中断服务函数,而且都是空函数,在需要的时候自行编写。相信部分像我一样的初学者会有疑问,中断服务函数的名称可以自己定义吗?不行。中断服务程序的名字必须要与启动文件startup_stm32f10x_hd.s中的中断向量表定义的一致。
需要注意的一点,IO口16个外部中断只有7个函数名:
EXTI0_IRQHandler ;EXTI Line0
EXTI1_IRQHandler ;EXTI Line1
EXTI2_IRQHandler ;EXTI Line2
EXTI3_IRQHandler ;EXTI Line3
EXTI4_IRQHandler ;EXTI Line4
EXTI9_5_IRQHandler ;EXTI Line9..5
EXTI15_10_IRQHandler;EXTI Line15..10
中断线在5后就不能单独命名,如果写成EXTI5_IRQHandler…编译器是不会报错的,不过中断服务程序不能正常工作。
编写中断服务函数经常需要使用两个函数:
ITStatus EXTI_GetITStatus(uint32_t EXTI_line);
//放在中断服务程序开头,检测中断标志位
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
//放在函数的结尾
第一个是判断某个中断线上的中断是否发生(即标志位是否置位)
第二个是清除中断线上的标志位(即清除标志位)
常用的外部中断服务程序格式
格式一:
void EXTI4_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line4) != RESET)//判断是否置位
Dosomething();//执行中断事件
EXTI_ClearITpendingBit(EXTI_Line4);//清除Line4标志位
}
格式二:
void EXTI_IRQHandler(void)
{
if(EXTI_GetFlagStatus(EXTI_Line4) != RESET)
Dosomething();
EXTI_ClearFlag(EXTI_Line4);
}
EXTI_GetITStatus() 会先判断中断该中断是否使能,使能了的话在判断该标志位是否置位;而EXTI_GetFlagStatus()会直接判断中断标志位是否置位(所以显得马虎一点)。一般情况下首选EXTI_GetITStatus().
关于外部中断EXTI大概就这些了,其他的还要靠自己去动手操作才能掌握更多。
-- From #IRONMAN#
-- 4/24/2017