一、什么是外部中断?
外部中断是指由微处理器或微控制器外部引脚(通常是GPIO引脚)上的外部事件触发的中断。这些外部事件可以是来自外部设备、传感器或其他外部信号源的触发。
二、外部中断详细内容
由AFIO时钟管理的寄存器有AFIO_EVCR(时间控制寄存器)、AFIO_MAPR(备用功能重映射和调试IO配置寄存器)、AFIO_EXTICRX(外部中断配置寄存器),所以在配置EXTI时需要先开启AFIO时钟。
1. EXTI总线分布
通过AFIO_EXTICRx配置GPIO线上的外部中断/事件,必须先使能AFIO时钟。
EXTI线0~EXIT线15总线控制GPIO口映射 。
● EXTI线16连接到PVD输出。
● EXTI线17连接到RTC闹钟事件。
● EXTI线18连接到USB唤醒事件。
● EXTI线19连接到以太网唤醒事件(只适用于互联型产品) 。
2. EXTI寄存器
(1)中断屏蔽寄存器(EXTI_IMR): 屏蔽(置0)或开放(置1)来自线x的中断请求。使能/失能中断。
(2) 挂起寄存器(EXTI_PR) : 没有发生(置0) /发生(置1)触发请求。
当在外部中断线上发生了选择的边沿事件,该位被置'1'。在该位中写入'1'可以清除它,也可以通过改变边沿检测的极性清除。
(3) 事件屏蔽寄存器(EXTI_EMR) : 屏蔽(置0)或开放(置1)来自线x的事件请求。使能/失能事件。
(4)上升沿触发选择寄存器(EXTI_RTSR) : 禁止(置0)或允许(置1)输入线x上的上升沿触发(中断和事件) 。
(5)下降沿触发选择寄存器(EXTI_FTSR) : 禁止(置0)或允许(置1)输入线x上的下降沿触发(中断和事件) 。
(6)软件中断事件寄存器(EXTI_SWIER) : 当该位为'0'时,写'1'将设置EXTI_PR中相应的挂起位。如果在EXTI_IMR和EXTI_EMR中允许产生该中断,则此时将产生一个中断。
注:通过清除EXTI_PR的对应位(写入'1'),可以清除该位为'0'。
3. EXTI框架图分析
(1) 如果采用外设(输入线)来产生中断,可以选择上升/下降/双边沿触发中断。此时软件中断事件寄存器必须写0。因为软件中断事件寄存器和边沿检测电路通过"或门"连接。如果软件中断寄存器置1,那么会直接产生中断信号,那么外设产生或不产生中断都没有作用。 通过在软件中对软件中断/事件寄存器写 1 ,也可以产生中断/事件请求。
(2) EXTI 可分为两大部分功能,一个是产生中断 ,另一个是产生事件 ,这两个功能从硬件上就有所不同。
●产生中断线路目的: 是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是软件级 的。
●产生事件线路目的: 就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输,属于硬件级 的。
●产生中断步骤: 根据输入线外设电路需要的边沿检测设置触发寄存器,并将 "软件中断事件寄存器" 置0(原因如上述),同时在 "中断屏蔽寄存器" 的相应位写 1 使能中断请求。当外部中断线上出现选定信号沿时,便会产生中断请求,"请求挂起寄存器" 的对应的挂起位会置1。在 "请求挂起寄存器" 的对应位写 1 ,将清除该中断请求。
●产生事件的步骤: 根据输入线外设电路需要的边沿检测设置触发寄存器,同时在 "事件屏蔽寄存器" 的相应位写 1 允许事件请求。当事件线上出现选定信号沿时,便会产生事件脉冲。
(3) "脉冲发生器" 去触发事件响应,它主要连到其它外设,中断信号来了就激活脉冲发生器,给它连接的其它外设发射一次脉冲信号。
(4) 两个"屏蔽寄存器"。它们都和我们的信号连到了一个 "与门" 上,这就意味着如果把这个屏蔽寄存器的某些位设为 0,来的中断信号就永远过不去这个与门,达到了一个"屏蔽"的效果。例如不希望某个中断通道起作用,那就把中断屏蔽寄存器的那一位设为 0,这里来的中断信号就永远到不了 NVIC;再例如不需要事件响应的功能,那么就把事件屏蔽器全设为 0,事件响应就无效了。
4. EXTI相关配置
(1)EXTI模式:
c
typedef enum
{
EXTI_Mode_Interrupt = 0x00, //产生中断
EXTI_Mode_Event = 0x04 //产生事件
}EXTIMode_TypeDef;
(2)EXTI触发类型:
c
typedef enum
{
EXTI_Trigger_Rising = 0x08, //上升沿
EXTI_Trigger_Falling = 0x0C, //下降沿
EXTI_Trigger_Rising_Falling = 0x10 //上升沿和下降沿都触发
}EXTITrigger_TypeDef;
5.EXTI示例:(以 PA10 为例)
(1)使能时钟(因为GPIO映射外部中断,所以需要开启AFIO时钟)
c
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // GPIOA 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); // AFIO 时钟
(2)配置GPIO引脚
c
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 以上拉输入模式为例
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
(3)配置GPIO口与外部中断线的映射关系。(PA10挂在EXTI10总线上)
c
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource10);//外部中断线10与PA10相映射。
(4)配置EXTI
c
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line10; // 选择中断线
EXTI_InitStructure.EXTI_LineCmd = ENABLE; // 使能中断线
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; // 以上升沿触发为例
EXTI_Init(&EXTI_InitStructure);
(5)配置中断优先组
c
NVIC_PriorityGroupConfig(NVIC_Priority_Group_2);
(6)配置NVIC
c
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; // PA10 选择EXTI15_10
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级,数值范围要注意符合前面分组的规范
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 响应优先级,数值范围要注意符合前面分组的规范
NVIC_Init(&NVIC_InitStructure);
(7)编写中断服务函数
c
void EXTI15_10_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line10) == SET) { // 判断 PX10 对应的挂起寄存器是否置位,如果置位说明产生了中断
EXTI_ClearITPendingBit(EXTI_Line10); // 清除中断标志位
// 处理你的中断 ...
}
}
为什么要在中断服务函数中清除中断标志位?
答:在处理外部中断信号时,中断服务程序应该在最后的清除操作中将中断标志位重新设置为"0",以保证系统正常运行。如果中断标志位没有被清除,那么即使该中断信号已经被处理完毕,处理器仍然会认为该中断信号还没有被处理,从而一直处于中断状态。
6.附录
(1)软件触发中断的库函数.
c
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);