STM32 Programming. Part 15: External EXTI Interrupts
Posted: 17 Oct 2023, 06:48
EXTI external event/interruption controller capabilities
- Sets the edge of the pulse at the EXTI channel input at which the interrupt will be generated. You can set a rising edge trigger, a falling edge trigger, or both triggers at once.
Each channel has its own interrupt request wait bit, which must be cleared in the corresponding interrupt handler
The EXTI controller is guaranteed to detect pulses whose duration is longer than the APB2 bus clock period. I.e. if the APB2 bus frequency is equal to 72 MHz, EXTI will correctly detect edges of signals with frequencies lower than 72 MHz.
EXTI channels
EXTI channels have the following names: EXTI0, EXTI1, EXTI2 . EXTI19. In total, we have 20 channels at our disposal. And EXTI0 - EXTI15 can be connected to one of the GPIO ports. EXTI16 is connected inside the MCU to the output of the programmable voltage detector PVD, EXTI17 to the RTC Alarm event, EXTI18 to the USB of the microcontroller, and EXTI19 to the Ethernet controller, if there is one.
At the moment we are interested in those EXTI channels that can be connected to GPIO ports. And there is one nuance here. At the input of each EXTI channel there is a multiplexer, which allows selecting the GPIO pin as follows:
I.e. EXTI0 can be connected to one of the port 0 pins, EXTI1 to one of the port 1 pins, and so on. For each line the multiplexer value can be selected independently, i.e. EXTI0 can be connected to PA0, EXTI1 to PB1, and so on. However, this organization of connections has some limitation that must be taken into account: we cannot simultaneously register events from, for example, lines PA0 and PB0, as they are connected to the same multiplexer.
Internal Device
The block diagram of the EXTI controller is shown below:
The input signal to the EXIT channel is supplied from the Input Line. It then goes to the rising and falling edge detectors. Next, the signal from the detectors goes to the OR logic element, which will generate an interrupt request signal either when the edge detectors are triggered or when the Software interrupt event bit is set. Next, the interrupt request signal goes to 2 logic AND elements that will pass this signal to event generation (lower element) and interrupt generation (upper element) if the appropriate bits are set. Next, the interrupt generation signal sets a bit in the Pending request register which results in a request to the NVIC interrupt controller. Once the EXTI registers have been described, this block diagram will become clearer.
GPIO configuration
According to the Reference manual, in order for a port pin to work in conjunction with EXTI, the pin must be set to input. No additional GPIO settings are required.
Configuration registers
There are registers located in the address space of the EXTI controller for configuring interrupts, rising edges, and so on. However, the settings of the multiplexer select GPIO pin that is connected to the corresponding EXTI channel are done in the AFIO (Alternate function I/O) registers. We will look at all EXTI registers and AFIO registers that relate to EXTI multiplexer settings.
Interrupt mask register (EXTI_IMR) - Interrupt mask register
MRx: Channel interrupt permissions x
- 0 - interrupt disabled
- 1 - interrupt enabled
We haven't dealt with events yet, this register can be simply ignored
Rising trigger selection register (EXTI_RTSR) - Rising edge detector enable register
TRx: Enable the rising edge trigger of channel x
- 0 - trigger disabled
- 1 - trigger enabled
TRx: Enable the falling edge trigger of channel x
- 0 - trigger disabled
- 1 - trigger enabled
SWIERx: Software interrupt line x
If the interrupt of a given EXTI channel is enabled in the EXTI_IMR register, writing a '1' to the corresponding bit, if it was previously '0', sets the corresponding bit in the EXTI_PR register and generates an interrupt request for that EXTI channel.
This bit is cleared when the corresponding bit in the EXTI_PR register is cleared.
Pending register (EXTI_PR) - Pending register
PRx: Waiting bit for channel x interrupt processing
- 0 - no interrupt request trigger events occurred
- 1 - events of interrupt request triggers have occurred
A bit in the EXTI_PR register is reset by writing a '1' value to the corresponding bit. Writing a '0' has no effect.
AFIO registers
Now let's look at a few registers in AFIO that are also involved in setting up the EXTI external interrupt controller.
External interrupt configuration register 1 (AFIO_EXTICR1) - External interrupt configuration register 1
EXTIx[3:0]: Configuration of EXTI channel x multiplexer
- 0000: PA[x] pin selection
- 0001: Pin selection PB[x]
- 0010: Pin selection PC[x]
- 0011: Pin selection PD[x]
- 0100: Pin selection PE[x]
- 0101: Pin selection PF[x]
- 0110: Pin selection PG[x]
Here everything is the same as for AFIO_EXTICR1, only for EXTI4..7 channels.
External interrupt configuration register 3 (AFIO_EXTICR3) - External interrupt configuration register 3
Configuration of EXTI8..11 channels
External interrupt configuration register 4 (AFIO_EXTICR4) - External interrupt configuration register 4
Configuration of EXTI12..15 channels
Interrupt handlers
An interrupt can be enabled or disabled for each EXTI channel independently of the other channels. However, interrupt requests from multiple EXTI channels can be tied to the same handler. Here is a list of all EXTI related handlers:
- EXTI0_IRQHandler
- EXTI1_IRQHandler
- EXTI2_IRQHandler
- EXTI3_IRQHandler
- EXTI4_IRQHandler
- EXTI9_5_IRQHandler
- EXTI15_10_IRQHandler
- PVD_IRQHandler
- RTC_Alarm_IRQHandler
- USBWakeUp_IRQHandler
The last 3 handlers belong to EXTI16, EXTI17, EXTI18 lines and we are not interested in them now.
Example program
As a demonstration, let's set up an external interrupt on the rising and falling edge of the pulse on the PA0 pin of the microcontroller. Create the void EXTI_Init(void) function, in which we will make all the necessary settings:
Code: Select all
void EXTI_Init(void)
{
}
Code: Select all
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; //Tacting GPIOA
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; //Tacting AFIO
Code: Select all
/*
GPIO setting
Pin: PA0
Mode: Input Pull Up
*/
GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0);
GPIOA->CRL |= (0x02 << GPIO_CRL_CNF0_Pos); //Pull Up/Pull Down Input
GPIOA->ODR |= (1 << 0); //Pull Up.
Now we are going to configure EXTI. Since we have chosen pin PA0, the interrupt will hang on the EXTI0 channel. We need to select PA0 as a signal source in the AFIO_EXTICR1 register using the EXTI0[3:0] bit group. This is done like this:
Code: Select all
AFIO->EXTICR[0] &= ~(AFIO_EXTICR1_EXTI0); //The EXTI channel zero is connected to the PA0 port
Code: Select all
EXTI->RTSR |= EXTI_RTSR_TR0; //Interrupt on pulse rise
EXTI->FTSR |= EXTI_FTSR_TR0; //Break on the pulse fall.
Code: Select all
EXTI->PR = EXTI_PR_PR0; //Reset the interrupt flag
//before enabling the interrupt itself
EXTI->IMR |= EXTI_IMR_MR0; //Enable the interrupt of EXTI channel 0
NVIC_EnableIRQ(EXTI0_IRQn); // Enable interrupt in the interrupt controller.
Here is the full code for the function:
Code: Select all
void EXTI_Init(void)
{
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; //Tacting GPIOA
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; //Acting AFIO
/*
GPIO setting
Pin: PA0
Mode: Input Pull Up
*/
GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0);
GPIOA->CRL |= (0x02 << GPIO_CRL_CNF0_Pos); //Pull Up/Pull Down Input
GPIOA->ODR |= (1 << 0); //Pull Up/Pull Down
/*
EXTI setting
*/
AFIO->EXTICR[0] &= ~(AFIO_EXTICR1_EXTI0); //Zero EXTI channel is connected to PA0 port
EXTI->RTSR |= EXTI_RTSR_TR0; //Break on pulse rise
EXTI->FTSR |= EXTI_FTSR_TR0; //Break on pulse fall
EXTI->PR = EXTI_PR_PR0; //Reset the interrupt flag
//before enabling the interrupt itself
EXTI->IMR |= EXTI_IMR_MR0; //Enable interrupt of EXTI channel 0
NVIC_EnableIRQ(EXTI0_IRQn); // Enable interrupt in interrupt controller
}
Code: Select all
void EXTI0_IRQHandler(void)
{
EXTI->PR = EXTI_PR_PR0; //Reset interrupt flag
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
}
Code: Select all
void main(void)
{
EXTI_Init();
for(;;)
{
}
}
Here you should pay attention to a bunch of asm("nop")-s in the interrupt handler. The point is that right after calling EXTI->PR = EXTI_PR_PR0; at least 2 processor cycles are needed before exiting the interrupt handling function, so that the interrupt flag has time to reset. Otherwise, the interrupt handler will be called again. Therefore, it is advisable to reset the interrupt flag in the EXTI_PR register at the beginning of the handler.
And lastly, a little experiment. The Reference manual says that when a microcontroller pin works as a signal source for EXTI, it should be configured as an input. What will happen if this pin is configured as an output? How will the system behave?
Code: Select all
void EXTI_Init(void)
{
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; //Tactivating GPIOA
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; //Acting AFIO
/*
GPIO setting
Pin: PA0
Mode: Output
*/
GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0);
GPIOA->CRL |= (0x00 << GPIO_CRL_CNF0_Pos) | (0x01 << GPIO_CRL_MODE0_Pos);
/*
EXTI setting
*/
AFIO->EXTICR[0] &= ~(AFIO_EXTICR1_EXTI0); //Zero EXTI channel is connected to PA0 port
EXTI->RTSR |= EXTI_RTSR_TR0; //Break on pulse rise
EXTI->FTSR |= EXTI_FTSR_TR0; //Break on pulse fall
EXTI->PR = EXTI_PR_PR0; //Reset the interrupt flag
//before enabling the interrupt itself
EXTI->IMR |= EXTI_IMR_MR0; //Enable interrupt of EXTI channel 0
NVIC_EnableIRQ(EXTI0_IRQn); // Enable interrupt in interrupt controller
}