目录
EXTI外部中断
STM32外部中断介绍
外部中断线与112个IO口的对应关系
112个IO口每个都可以与各自所属的外部中断线相连接,由于只有16个外部中断线,因此每7个IO口使用一个外部中断线,例如:PA4,PB4,PC4……PG4共有EXTI4中断控制总线。由于是共有,因此在任何时候只有PA4,PB4,PC4……PG4其中一个IO口占用外部中断线。
中断服务函数简介
外部中断与中断事件请求的原理
中断执行的流程
中断执行的步骤 |
详细操作 |
1 |
输入线电平的变化 |
2 |
边沿检测电路(有三种检测模式:上升沿,下降沿和上升沿&下降沿) |
3 |
②与门电路(此时,若软件中断请求寄存器置1,或门就会不依赖于边沿检测电路的返回值一直返回1) |
4 |
信号传递进“请求挂起寄存器” |
5 |
信号通过⑥与门电路(中断屏蔽寄存器如果返回0,无论请求挂起寄存器返回什么值都会使得NVIC中断控制寄存器接收低电平信号) |
6 |
NVIC控制器会直接访问中断向量地址,去执行对应的中断服务函数 |
我们一般说“只有中断被挂起的时候,中断标志位才会触发中断进而执行中断服务函数”,这句话的意思是“当请求挂起寄存器使能后,一旦触发中断的条件满足也就是边沿检测电路返回有效电平,中断标志位才会被置1(中断标志位在NVIC控制器中)。满足“中断标志位置1&中断请求被挂起”这两个条件后,立刻执行中断服务函数”。
这里要特别分清楚两个库函数:
USART_ClearFlag |
清除完成标志位 |
USART_ClearITPendingBit |
清除中断标志位 |
中断事件执行的流程
中断执行的步骤 |
详细操作 |
1 |
输入线电平的变化 |
2 |
边沿检测电路(有三种检测模式:上升沿,下降沿和上升沿&下降沿) |
3 |
②与门电路(此时,若软件中断请求寄存器置1,或门就会不依赖于边沿检测电路的返回值一直返回1) |
4 |
或门返回的信号与事件屏蔽寄存器一起进入⑤与门电路中,如果事件屏蔽寄存器返回0那么与门电路不依赖于或门返回的信号直接返回0 |
5 |
如果与门返回1,那么就会启动脉冲发生器,进而直接启动其他功能,例如:外部中断触发可直接触发ADC |
6 |
用脉冲触发器去联动相应的外设 |
屏蔽寄存器的作用
我们可以从上面的流程可以得知:
中断屏蔽寄存器 |
屏蔽寄存器返回0会使得与门直接返回0,即无效电平。此时,无论是否有中断请求都不会触发NVIC中断 |
事件屏蔽寄存器 |
屏蔽寄存器返回0会使得与门直接返回0,即无效电平。此时,无论或门返回的是否是有效电平都已不重要了,都不会使能脉冲触发器,更不会引起事件中断 |
中断与事件的区别
中断&事件
中断 |
需要CPU参与的,需要软件的中断服务函数才能完成中断后产生的结果 |
事件 |
靠脉冲发生器产生一个脉冲,进而由硬件自动完成这个事件产生的结果,当然相应的联动部件需要先设置好,比如引起DMA操作,AD转换……等 |
简单举例
外部I/O触发AD转换,来测量外部物品的重量;如果使用传统的中断通道,需要I/O触发产生外部中断,外部中断服务程序启动AD转换,AD转换完成中断服务程序提交最后结果;要是使用事件通道,I/O触发产生事件,然后联动触发AD转换,AD转换完成中断服务程序提交最后结果;相比之下,后者不要软件参与AD触发,并且响应速度也更块;要是使用事件触发DMA操作,就完全不用软件参与就可以完成某些联动任务了。
为什么要在中断服务函数中清除中断标志位?
如果进入中断不清除标志位,那么这一中断服务程序结束后由于标志位还是置位的并且中断是允许的,那么还会再次进入该中断,就会发生一直在执行中断程序的情况。
为什么配置中断功能时要在APB2中使能AFIO相关功能?
与AFIO 相关的寄存器
事件控制寄存器(AFIO_EVCR) |
复用重映射和调试I/O 配置寄存器(AFIO_MAPR) |
外部中断配置寄存器1(AFIO_EXTICR1) |
外部中断配置寄存器2(AFIO_EXTICR2) |
外部中断配置寄存器3(AFIO_EXTICR3) |
外部中断配置寄存器4(AFIO_EXTICR4) |
注意:AFIO本意是“Alternate Function IO(多功能IO口)”,不仅仅针对于端口重映射。只要是对对寄存器 AFIO_EVCR、AFIO_MAPR 和 AFIO_EXTICRX 进行读写操作前,就要打开 AFIO 的时钟,而不仅仅是重映射的时候才打开!!!
重映射与端口复用的区别
端口复用 |
只要使用stm32就要用到外设,外设是与GPIO引脚共用(复用)的,即某些引脚既可以单纯作为输入输出,又可以作为具有一定功能的外设(如ADC,串口等等)的引脚端口 |
端口重映射 |
复用功能(AFIO)从不同的GPIO管脚引出,stm32引入外设引脚重映射的概念,内置外设端口一般具有默认引脚,还可以通过设置重映射寄存器的方法,把这个外设的端口映射到其他的引脚 |
代码示例
Main.c
#include "led.h"
#include "exti.h"
#include "key.h"
#include "stm32f10x.h"
int main()
{
LED_InitConfig();
KEY_InitConfig();
EXTI_InitConfig();
while(1);
}
void EXTI4_IRQHandler()
{
if(EXTI_GetITStatus(EXTI_Line4) == SET) // 如果中断标志位为1,说明中断触发
{
LED0 = !LED0;
}
EXTI_ClearITPendingBit(EXTI_Line4); // 清除中断标志位
}
LED.c
#include "led.h"
#include "stm32f10x.h"
void LED_InitConfig()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 使能GPIOB的外部时钟
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 配置LED0的属性
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB, GPIO_Pin_5); // 初始化LED0的电平
}
LED.h
#ifndef _LED_H
#define _LED_H
#include "sys.h"
void LED_InitConfig();
#define LED0 PBout(5)
#endif
KEY.c
#include "key.h"
#include "stm32f10x.h"
void KEY_InitConfig()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); // 使能GPIOE的时钟
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 配置KEY0的属性
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure);
}
KEY.h
#ifndef _KEY_H
#define _KEY_H
void KEY_InitConfig();
#endif
EXTI.c
#include "exti.h"
#include "key.h"
#include "stm32f10x.h"
void EXTI_InitConfig()
{
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
KEY_InitConfig(); // 初始化KEY0
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); // 使能AFIO时钟
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 配置中断分组
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn; // 配置中断优先级,使能中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4); // 配置GPIO端口引脚与中断线的关系
EXTI_InitStructure.EXTI_Line = EXTI_Line4; // EXTI外部中断初始化配置
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStructure);
}
EXTI.h
#ifndef _EXTI_H
#define _EXTI_H
void EXTI_InitConfig();
#endif