STM32F0 Tutorial 3: External Interrupts
In the last post, we figured out how to make the LED blink and learn some GPIO functions including writing, toggling, and reading. In this STM32F0 tutorial, we will learn how to configure and use GPIOs as an external interrupt signal to trigger an LED without depending on the main loop routine with CubeMX. We will:
- Understand some basic knowledge about interrupts of STM32F0
- Configure the STM32CubeMX project and synchronize files with Source Insight
- Test some code and see what we can do next with this Interrupt controller.
Interrupts are some kinds of emergent signals that may pop up to the CPU and request it to pause the main program to execute some critical code at some other places, which is called Interrupt handler or Interrupt service routine (ISR), and jump back, resume the main program from where it is paused. Therefore, it can be used to handle some events at unexpected moment that does not require much execution time. For example, you have a very long code and many long delays in your main routine, but you also need your program to act quickly when there is input from the user or other devices such as input buttons or sensors that require periodic readout. So, the interrupt handler will help you to do that instead of putting a lot of wasted condition checking syntax inside the main program to catch the input events.
Some properties to achieve reliable interrupt handlers that we need to remember:
- The code inside the interrupt handler must be as brief as possible.
- Never use delay or wait or any kind of interval inside the interrupt handler.
- Their shared variables must be carefully managed.
Nested vectored interrupt controller (NVIC)
This is what you may not hear about when working with 8bit microcontrollers such as AVR or PIC. The NVIC and the processor core interface are closely coupled, which enables low latency interrupt processing and efficient processing of late arriving interrupts. All interrupts including the core exceptions are managed by the NVIC. The STM32F0 has more than 30 interrupts which depend on the STM32F0 device line. Each interrupt corresponds with a physical address (interrupt vector) which is simply used to indicate the position of the interrupt handler so that it can jump correctly. The vector table can be found in the STM32F0 reference manual (p. 218).
The NVIC has the ability to handle interrupts with different priorities as seen from the table above, the smaller number the priority column is, the higher priority is. The priority can be changed by the user. There are many interrupt sources: EXTI line, Timer, ADC,… and we will focus first on the external interrupt lines (red dot in the table).
STM32F0 supports the nesting of interrupts, which means during the execution of a low priority interrupt handler, a high priority service can pre-empt and the low priority handler is suspended, and resumed when the high priority handler is completed.
External Interrupt
Turning back to the main part of this tutorial, I will show how to use the external interrupt of the STM32F0. The proposed problem is that you have a very long delay in your main loop that you can not catch the input button correctly. In this case, please watch and follow the video below to get it run initially before we dig deeper. The first part of the video states the problem and then follows with an interrupt solution.
The code in the video:
//paste the below code into file Stm32f0xx_it.c
/* USER CODE BEGIN EXTI0_1_IRQn 0 */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0));
/* USER CODE END EXTI0_1_IRQn 0 */
//insert this code to main.c
/* Infinite loop */
while (1)
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_9);
HAL_Delay(3000);
}
So why do we choose EXTI0_1?
What makes me really interested in STM32 is that all of the IO pins can be used as the external interrupt line (EXTI) while AVR only has 2 fixed pins for that. However, it does not mean that you can use all 55 pins of the STM32F051 as EXTI lines at the same time. In fact, we can use up to 16 EXTI lines simultaneously due to the following external interrupt GPIO mapping:
So it means that you cannot use PA0 and PB0 as EXTI lines at the same time but PA0 and PB1 are ok. The User button on the STM32F0 kit was connected to PA0 as follows:
Obviously, EXTI0 is the one we need. Besides, EXTI0 and EXTI1 share the same interrupt handler (vector EXTI0_1 in the vector table) so just need to make sure that you check which one is triggered inside the handler (not mentioned in the video). Moreover, EXTI2 and EXTI3 share the EXTI2_3 handler and EXTI4 to EXTI15 will share the EXTI4_15 handler.
Next, what about the signal edge (falling, rising)?
The falling and rising edge of the signal is simply the transition from high to low and vice versa.
Since we want to fire the interrupt when the button is both pressed and released, we need to configure it to detect both edges in the CubeMX as in the video. You can change to detect only one edge as well depending on your input signal.
It’s your turn!
Now, a simple exercise for you. You can try using the button and EXTI to control 2 LEDs on the kit. When you press the button, the green LED will blink slowly, let’s say 2 seconds delay and the blue LED blinks faster at 0.2s. When you release the button, reverse the LEDs: green blinks fast, blue blinks slowly. It would be nice if you can share your result in the comment 😀
The next tutorial will be about Timer and Counter 🙂