STM32F4 External interrupts
Each STM32F4 device has 23 external interrupt or event sources.
They are split into 2 sections.
First interrupt section is for external pins (P0 to P15) on each port,
and other section is for other events, like RTC interrupt, Ethernet interrupt, USB interrupt and so on.
GPIO as Interrupt
Interrupt lines
I will show now how to configure GPIO pin to be an interrupt and how to handle it in your code with CMSIS function.
In section one (GPIOs) we have 16 interrupt lines.
They are line0 to line15 and they also represent pin number.
This means, PA0 is connected to Line0and PA13 is connected to Line13.
You have to know that PB0 is also connected to Line0 and PC0 also and so on.
This is for all pins on board, All Px0 (where x is GPIO name) pins are connected to Line0
and let’s say all Px3 are connected to Line3 on the Interrupt channel.
All pins with same number are connected to line with same number.
They are multiplexed to one line.
IMPORTANT: You can not use two pins on one line simultaneously:
- PA0 and PB0 and PC0 and so on, are connected to Line0,
so you can use only one pin at one time to handle interrupt from there. - PA0 and PA5 are connected to different lines, they can be used at the same time.
Each line can trigger an interrupt on rising, falling or rising_falling enge on signal.
Interrupt handlers
OK, now you have selected your pin you want to use.
But you have to handle interrupt somehow.
This process is described below.
STM32F4 has 7 interrupt handlers for GPIO pins.
They are in table below:
Irq | Handler | Description |
---|---|---|
EXTI0_IRQn | EXTI0_IRQHandler | Handler for pins connected to line 0 |
EXTI1_IRQn | EXTI1_IRQHandler | Handler for pins connected to line 1 |
EXTI2_IRQn | EXTI2_IRQHandler | Handler for pins connected to line 2 |
EXTI3_IRQn | EXTI3_IRQHandler | Handler for pins connected to line 3 |
EXTI4_IRQn | EXTI4_IRQHandler | Handler for pins connected to line 4 |
EXTI9_5_IRQn | EXTI9_5_IRQHandler | Handler for pins connected to line 5 to 9 |
EXTI15_10_IRQn | EXTI15_10_IRQHandler | Handler for pins connected to line 10 to 15 |
This table show you which IRQ you have to set for NVIC (first column)
and function names to handle your interrupts (second column).
You have probably also figured, that only lines 0 to 4 have own IRQ handler.
Yes, lines 5-9 have the same interrupt handler and this is also for lines 10 to 15.
After you set settings for EXTI, you have to add them into NVIC.
Example
In this example, we will set pin PD0 and PB12 to be a GPIO interrupts.
Code below should be well documented to understand how it works.
/** * External interrupts example * * @author Tilen Majerle * @email tilen@majerle.eu * @website http://stm32f4-discovery.com * @ide Keil uVision 5 */ #include "stm32f4xx.h" #include "stm32f4xx_exti.h" #include "stm32f4xx_syscfg.h" #include "misc.h" /* Configure pins to be interrupts */ void Configure_PD0(void) { /* Set variables used */ GPIO_InitTypeDef GPIO_InitStruct; EXTI_InitTypeDef EXTI_InitStruct; NVIC_InitTypeDef NVIC_InitStruct; /* Enable clock for GPIOD */ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); /* Enable clock for SYSCFG */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); /* Set pin as input */ GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz; GPIO_Init(GPIOD, &GPIO_InitStruct); /* Tell system that you will use PD0 for EXTI_Line0 */ SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOD, EXTI_PinSource0); /* PD0 is connected to EXTI_Line0 */ EXTI_InitStruct.EXTI_Line = EXTI_Line0; /* Enable interrupt */ EXTI_InitStruct.EXTI_LineCmd = ENABLE; /* Interrupt mode */ EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; /* Triggers on rising and falling edge */ EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising_Falling; /* Add to EXTI */ EXTI_Init(&EXTI_InitStruct); /* Add IRQ vector to NVIC */ /* PD0 is connected to EXTI_Line0, which has EXTI0_IRQn vector */ NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn; /* Set priority */ NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x00; /* Set sub priority */ NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x00; /* Enable interrupt */ NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; /* Add to NVIC */ NVIC_Init(&NVIC_InitStruct); } void Configure_PB12(void) { /* Set variables used */ GPIO_InitTypeDef GPIO_InitStruct; EXTI_InitTypeDef EXTI_InitStruct; NVIC_InitTypeDef NVIC_InitStruct; /* Enable clock for GPIOB */ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); /* Enable clock for SYSCFG */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); /* Set pin as input */ GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz; GPIO_Init(GPIOB, &GPIO_InitStruct); /* Tell system that you will use PB12 for EXTI_Line12 */ SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource12); /* PB12 is connected to EXTI_Line12 */ EXTI_InitStruct.EXTI_Line = EXTI_Line12; /* Enable interrupt */ EXTI_InitStruct.EXTI_LineCmd = ENABLE; /* Interrupt mode */ EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; /* Triggers on rising and falling edge */ EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising_Falling; /* Add to EXTI */ EXTI_Init(&EXTI_InitStruct); /* Add IRQ vector to NVIC */ /* PB12 is connected to EXTI_Line12, which has EXTI15_10_IRQn vector */ NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn; /* Set priority */ NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x00; /* Set sub priority */ NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x01; /* Enable interrupt */ NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; /* Add to NVIC */ NVIC_Init(&NVIC_InitStruct); } /* Set interrupt handlers */ /* Handle PD0 interrupt */ void EXTI0_IRQHandler(void) { /* Make sure that interrupt flag is set */ if (EXTI_GetITStatus(EXTI_Line0) != RESET) { /* Do your stuff when PD0 is changed */ /* Clear interrupt flag */ EXTI_ClearITPendingBit(EXTI_Line0); } } /* Handle PB12 interrupt */ void EXTI15_10_IRQHandler(void) { /* Make sure that interrupt flag is set */ if (EXTI_GetITStatus(EXTI_Line12) != RESET) { /* Do your stuff when PB12 is changed */ /* Clear interrupt flag */ EXTI_ClearITPendingBit(EXTI_Line12); } } int main(void) { /* System init */ SystemInit(); /* Configure PD0 as interrupt */ Configure_PD0(); /* Configure PB12 as interrupt */ Configure_PB12(); while (1) { } }
軟件環境:MDK470a
硬件環境:STM32F4-Discovery。按鍵B1連接在F4芯片的PA0引腳。當按鍵按下時,引腳電平被拉底。
功能描述:按壓按鍵B1時,觸發外部中斷。進入中斷處理函數后,通過串口發送消息。
實現步驟:
1、打開PA時鍾,設置PA0引腳為輸入。
void EXTI_GPIO_Congig(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Pin = GPIO_PinSource0;
GPIO_InitStructure.GPIO_Speed =GPIO_Speed_100MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
}
2、打開系統配置控制器(System configuration controller)時鍾。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
關於系統控制寄存器(SYSCFG)的功能,是F4系列新增的。功能如下:
The system configuration controller is mainly used to remap the memory accessible in the code area,
select the Ethernet PHY interface and manage the external interrupt line connection to the GPIOs.
SYSCFG主要用於映射訪問CODE區域的內存、選擇以太網的什么接口,管理外部中斷線到GPIO的連接。
設置外部中斷,還要設置SYSCFG的外部中斷配置寄存器。
3、配置外部中斷EXTI的工作方式.
映射到PA0,即線0,使用中斷模式下降沿觸發。
設置EXTI寄存器的工作方式交給了庫函數。
void EXTI_Config(void)
{
EXTI_GPIO_Congig();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStructure);
}
4、編寫中斷處理函數,實現向串口打印信息。
固定的函數名:void EXTI0_IRQHandler(void)。
進入中斷處理函數后,首先檢查是否為線0的中斷。如果是,則清除這個中斷標志。之后就可以發送消息了。
消息發送完成之后,清除在處理外部中斷期間到來的外部中斷。使用EXTI_ClearITPendingBit()完成
void EXTI0_IRQHandler(void)
{
if(SET == EXTI_GetITStatus(EXTI_Line0))
{
EXTI_ClearFlag(EXTI_Line0);
printf("i am in exti irqhandler\r\n");
printf("and the extiflag is cleared\r\n");
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
Example code: configure PB1 as interrupt pin
#include <stdio.h> #include <stm32f4xx.h> uint32_t i=0; void EXTI1_IRQHandler(void) { if(EXTI->PR & (1<<1)) { i++; //increase value if interrupt happen } EXTI->PR=(1<<1); //clear interrupt flag for EXTI1 } void exticonf(void) { //Config PB1 as interrupt pin NVIC_EnableIRQ(EXTI1_IRQn); // Enable IRQ for ext. signals, line EXTI1_IRQn //NVIC_EnableIRQ(EXTI9_5_IRQn); //External Line[9:5] Interrupts //NVIC_EnableIRQ(EXTI15_10_IRQn); //External Line[15:10] Interrupts
NVIC_SetPriority(EXTI1_IRQn, 15); SYSCFG->EXTICR[0] = SYSCFG_EXTICR1_EXTI1_PB; // select PB to make IRQ EXTI1 EXTI->RTSR = 0x00000002; // allow positive edge interrupt for EXTI1 EXTI->IMR = 0x00000002; // enable interrupt on EXTI1 } int main (void) { RCC->APB2ENR |= 0x00004000; // Clock SYSCFG - system configuration controller, necessary for interrupt exticonf(); while(1) { }; }
EXTI Interrupts
/** * @brief configures specified GPIO pin as output. * @param GPIOx: where x can be (A..I) to select the GPIO peripheral. * @param GPIO_Pin: specifies the port bit to be configured in output mode. * This parameter can be any combination of GPIO_Pin_x where x can be (0..15). * @param GPIO_Mode: Specify GPIO Configuration i.e. input/output/ADC/AF * This parameter can be a value of @ref GPIOMode_TypeDef * @retval None */ void InitGPIO(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIOMode_TypeDef GPIO_Mode) { GPIOPuPd_TypeDef PuPdMode = 0; GPIO_InitTypeDef GPIO_InitStructure; switch(GPIO_Mode) { case GPIO_Mode_OUT: PuPdMode = GPIO_PuPd_NOPULL; //digital output. Not using open drain mode as I do not know how that operates break; case GPIO_Mode_IN: PuPdMode = GPIO_PuPd_NOPULL; //digital read have Pin as input floating break; case GPIO_Mode_AN: PuPdMode = GPIO_PuPd_NOPULL; //for analogue read have Pin as input floating break; case GPIO_Mode_AF: //need to do a remapping if using alternate functions PuPdMode = GPIO_PuPd_UP; //for PWM have not looked at accounting for the various other alternate functions break; } GPIO_InitStructure.GPIO_Pin = GPIO_Pin; GPIO_InitStructure.GPIO_Mode = GPIO_Mode; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //used for digital output and PWM output //this setting does not matter for ADC and digital read GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz; GPIO_InitStructure.GPIO_PuPd = PuPdMode; GPIO_Init(GPIOx, &GPIO_InitStructure); }
The following function can be used to accomplish steps 2 to 5
/** * @brief attach an external interrupt source to a GPIO pin. * @param EXTI_PortSourceGPIOx : selects the GPIO port to be used as source for * EXTI lines where x can be (A..I). * @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). * @param EXTI_Line: Specifies the EXTI Line to be configured. * This parameter can be EXTI_LINEx where x can be (0..15) * @param EXTI_Trigger: Specify whether Interrupt is generated on the rising, falling or rising and falling edges * @param Priority: Priority of the Interrupt (lower the number the higher the priority) * @retval None * @NOTE: Note that there are 22 EXTI interrupt sources. This function can only be used to configure upto * 15 of those interrupts sources */ void Attach_GPIO_Interrupt(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex, uint32_t EXTI_Line, EXTITrigger_TypeDef EXTI_Trigger, uint8_t Priority) { uint8_t EXTI_IRQn = 0; switch (EXTI_Line) { case 0x1: EXTI_IRQn = EXTI0_IRQn; break; case 0x2: EXTI_IRQn = EXTI1_IRQn; break; case 0x4: EXTI_IRQn = EXTI2_IRQn; break; case 0x8: EXTI_IRQn = EXTI3_IRQn; break; case 0x10: EXTI_IRQn = EXTI4_IRQn; break; case 0x20: case 0x40: case 0x80: case 0x100: case 0x200: EXTI_IRQn = EXTI9_5_IRQn; break; case 0x400: case 0x800: case 0x1000: case 0x2000: case 0x4000: case 0x8000: EXTI_IRQn = EXTI15_10_IRQn; break; } /* Connect EXTI Line to appropriate GPIO Pin */ SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOx, EXTI_PinSourcex); NVIC_InitTypeDef NVIC_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; /* Configure EXTI Line */ EXTI_InitStructure.EXTI_Line = EXTI_Line; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); /* Enable and set EXTI Line Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = EXTI_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = Priority; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); }
void Peripheral_Init() { //enable clock for GPIOA RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); InitGPIO(GPIOA, GPIO_Pin_0, GPIO_Mode_IN); //initialise PA0 is input //attach interrupt to GPIO Attach_GPIO_Interrupt(EXTI_PortSourceGPIOA, EXTI_PinSource0, EXTI_Line0 ,EXTI_Trigger_Rising,1 ); }
the Interrupt Service Routine (the function that executes should an EXTI event occur) is
void EXTI0_IRQHandler(void) //EXTI0 ISR { if(EXTI_GetITStatus(EXTI_Line0) != RESET) //check if EXTI line is asserted { EXTI_ClearFlag(EXTI_Line0); //clear interrupt //Enter your code here } }
#include "stm32f4xx.h" #include "stm32f4xx_syscfg.h" #include "stm32f4xx_rcc.h" #include "stm32f4xx_gpio.h" #include "stm32f4xx_exti.h" #include "misc.h" EXTI_InitTypeDef EXTI_InitStructure; void EXTILine0_Config( void ); void LEDInit( void ); void ExtInt( void ) { LEDInit( ); /* Configure EXTI Line0 (connected to PA0 pin) in interrupt mode */ EXTILine0_Config( ); /* Generate software interrupt: simulate a rising edge applied on EXTI0 line */ EXTI_GenerateSWInterrupt( EXTI_Line0 ); while ( 1 ) { } } /** * @brief Configures LED GPIO. * @param None * @retval None */ void LEDInit( ) { GPIO_InitTypeDef GPIO_InitStructure; /* Enable the GPIO_LED Clock */ RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOD, ENABLE ); /* Configure the GPIO_LED pin */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init( GPIOD, &GPIO_InitStructure ); } /** * @brief Configures EXTI Line0 (connected to PA0 pin) in interrupt mode * @param None * @retval None */ void EXTILine0_Config( void ) { 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 = 0x01; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init( &NVIC_InitStructure ); } /** * @brief This function handles External line 0 interrupt request. * @param None * @retval None */ void EXTI0_IRQHandler( void ) { if ( EXTI_GetITStatus( EXTI_Line0 ) != RESET ) { /* Toggle LED1 */ GPIO_ToggleBits( GPIOD, GPIO_Pin_12 ); /* Clear the EXTI line 0 pending bit */ EXTI_ClearITPendingBit( EXTI_Line0 ); } } int main( void ) { ExtInt( ); while ( 1 ) { } }