上节主要讲述了STM32 的中断基础知识以及一般配置过程。外部中断与其他模块中断相对要复杂一点。外部中断一般都是由GPIO触发,是在整个系统中需要经常用到。
介绍
STM32F4 有多达23个中断,每个中断都由相应的单独寄存器进行配置,也可以单独进行屏蔽相关不影响。每个GPIO都可以设置成中断输入线,占用Ext0 至 Exit1 15, 示意图如下:
STM32 有多少达140个GPIO,被分别按照A~I分成16组, 由上图可知 GPIO按照组被分到EXTI0~Exit15 16个中断,每个中断内的GPIO所有编号在组内都是一样,只是分布在不同的组内,除了上述16个中断,还有其他7个外部中断(如下图所示),共有23个中断。
中断/事件线 | 输入中断源 |
EXIT0 | PA0,PB0,PC0,PD0,PE0,PF0,PG0,PH0,PI0 |
EXIT1 | PA1,PB1,PC1,PD1,PE1,PF1,PG1,PH1,PI1 |
EXIT2 | PA2,PB2,PC2,PD2,PE2,PF2,PG2,PH2,PI2 |
EXIT3 | PA3,PB3,PC3,PD3,PE3,PF3,PG3,PH3,PI3 |
EXIT4 | PA4,PB4,PC4,PD4,PE4,PF4,PG4,PH4,PI4 |
EXIT5 | PA5,PB5,PC5,PD5,PE5,PF5,PG5,PH5,PI5 |
EXIT6 | PA6,PB6,PC6,PD6,PE6,PF6,PG6,PH6,PI6 |
EXIT7 | PA7,PB7,PC7,PD7,PE7,PF7,PG7,PH7,PI7 |
EXIT8 | PA8,PB8,PC8,PD8,PE8,PF8,PG8,PH8,PI8 |
EXIT9 | PA9,PB0,PC9,PD9,PE9,PF9,PG9,PH9,PI9 |
EXIT10 | PA10,PB10,PC10,PD10,PE10,PF10,PG10,PH10,PI10 |
EXIT11 | PA11,PB11,PC11,PD11,PE11,PF11,PG11,PH11,PI1 |
EXIT12 | PA12,PB12,PC12,PD12,PE12,PF12,PG12,PH12,PI2 |
EXIT13 | PA13,PB13,PC13,PD13,PE13,PF13,PG13,PH13,PI13 |
EXIT14 | PA14,PB14,PC14,PD14,PE14,PF14,PG14,PH14,PI14 |
EXIT15 | PA15,PB15,PC15,PD15,PE15,PF15,PG15,PH15,PI15 |
EXIT16 | PVD输出 |
EXIT17 | RTS闹钟事件 |
EXIT18 | USB OTG FS唤醒司机 |
EXIT19 | 以太网唤醒事件 |
EXIT20 | USB OTG HS唤醒事件 |
EXIT21 | RTC入侵和事件搓戳事件 |
EXIT22 | RTC唤醒事件 |
EXTI 挂载总线
EXTI在STM32 总线位置如下图所示:
EXTI中断挂载在APB2总线上,故要使用EXIT中断就要先使能APB2 时钟。
EXTI框架
EXTI框架(如下图所示),图中的23数字为23根中断,每根一个单独单独寄存器相互不影响。
由中断框架图可以看出,中断模块有两个输出:
1:至NVIC中断控制器,通知内核产生一个中断。
2:通过脉冲发生器产生一个脉冲(事件)
上述两种功能互斥不可同时使用,故EXIT称为EXIT中断/事件模块,即可以产生一个中断,也可以当作发生一个事件。整个EXTI框架中分成两个流程:中断和事件。下面将按照这两个流程发生过程分布做介绍:
EXTI中断
EXTI中断流程是开发过程中经常使用到的,主要分成6步。
上图是整个中断的产生流程:
1:为中断输入,EXIT控制器可以通过配置 SYSCFG_EXTICRX(1~4)寄存器确定相应GPIO对应相应中断,确定中断源是哪个GPIO:
由上述寄存器可知,每个EXTI中断配置占4bit,最多配置16个GPIO配置,实际STM32F只有9组,每个寄存器可以配置4个EXTI,共有16EXTI中断,故SYSCFG_EXTICR系列寄存器有4个。配置给寄存器,STM32提供给官方API,其内部实现如下:
/**
* @brief Selects the GPIO pin used as EXTI Line.
* @param EXTI_PortSourceGPIOx : selects the GPIO port to be used as source for
* EXTI lines where x can be (A..K) for STM32F42xxx/43xxx devices, (A..I)
* for STM32F405xx/407xx and STM32F415xx/417xx devices or (A, B, C, D and H)
* for STM32401xx devices.
*
* @param EXTI_PinSourcex: specifies the EXTI line to be configured.
* This parameter can be EXTI_PinSourcex where x can be (0..15, except
* for EXTI_PortSourceGPIOI x can be (0..11) for STM32F405xx/407xx
* and STM32F405xx/407xx devices and for EXTI_PortSourceGPIOK x can
* be (0..7) for STM32F42xxx/43xxx devices.
*
* @retval None
*/
void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex)
{
uint32_t tmp = 0x00;
/* Check the parameters */
assert_param(IS_EXTI_PORT_SOURCE(EXTI_PortSourceGPIOx));
assert_param(IS_EXTI_PIN_SOURCE(EXTI_PinSourcex));
tmp = ((uint32_t)0x0F) << (0x04 * (EXTI_PinSourcex & (uint8_t)0x03));
SYSCFG->EXTICR[EXTI_PinSourcex >> 0x02] &= ~tmp;
SYSCFG->EXTICR[EXTI_PinSourcex >> 0x02] |= (((uint32_t)EXTI_PortSourceGPIOx) << (0x04 * (EXTI_PinSourcex & (uint8_t)0x03)));
}
2:配置中断边沿检测电路,配置中断是上升沿触发(EXTI_RTSR)还是下降沿触发(EXTI_FTSR), 如果中断触发之后信中断信号将传递到3.
由表可知 EXTI_RTSR和EXTI_FTSR寄存器每个EXTI占1bit, 禁止,1允许,两个寄存器不能同时设置1.
3:中断发生后,传递给3.3部分实际上是一个或门电路,与软件中断寄存器求与操作。可以通过配置软件中断事件寄存器(EXTI_SWIER),来控制是否启动中断。
也可以通过手动配置寄存,来触发一个软件中断,其官方API:
/**
* @brief Generates a Software interrupt on selected EXTI line.
* @param EXTI_Line: specifies the EXTI line on which the software interrupt
* will be generated.
* This parameter can be any combination of EXTI_Linex where x can be (0..22)
* @retval None
*/
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line)
{
/* Check the parameters */
assert_param(IS_EXTI_LINE(EXTI_Line));
EXTI->SWIER |= EXTI_Line;
}
4:当来自 3的中断到达4后,将会与中断屏蔽寄存器(EXTI_IMR)求与,用来确定该中断是否屏蔽,如果该中断被屏蔽将不会进入到下一步
5:当中断进入到4后没有被屏蔽,中断将进入到挂起请求寄存器():
当中断发生后,中断将会挂起。在中断函数处理完之后,每次都要将其清除为0,才能够使下次中断到来,代表“这次中断处理完成,可以来下次中断”,主要是用来防止 “处理该中断过程中,中断处理函数还没有处理完这次中断,又重新触发新的中断”。 官网固件API为
void EXTI_ClearITPendingBit(uint32_t EXTI_Line)
{
/* Check the parameters */
assert_param(IS_EXTI_LINE(EXTI_Line));
EXTI->PR = EXTI_Line;
}
6: 上次中断被清理后,这次中断可以发生,中断将被送到NVIC
EXTI事件
EXTI事件主要流程如下,其主要过程和EXTI中断类似,限于篇幅不在详述
EXTI配置用例
以官方提供的固件用例讲解配置过程,配置PA0为外部中断,上升沿触发,代码如下:
static void EXTILine0_Config(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable GPIOA clock */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
/* Enable SYSCFG clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
/* Configure PA0 pin as input floating */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Connect EXTI Line0 to PA0 pin */
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);
/* Configure EXTI Line0 */
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
/* Enable and set EXTI Line0 Interrupt to the lowest priority */
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
1:使能 PA0和 EXIT挂载总线时钟,分别为AHB1和APB 总线
2:配置GPIO PA0端口为输入模式
3:配EXTI Line0的中断为PA0
4: 配置EXTI0中断触发模式,并使能
5:通过NVIC使能EXTI0_IRQn,并配置优先级
中断处理函数如下:
/**
* @brief This function handles External line 0 interrupt request.
* @param None
* @retval None
*/
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0) != RESET)
{
/**EXIT0 process***/
/* Clear the EXTI line 0 pending bit */
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
参考文档
《 零死角玩转STM32》
《STM32F4XX中文参考手册》
《arm体系结构与编程》