STM32F0 External Interrupts - Tutorial 3

Last post we have figured out how to make the LED blink and learn some GPIO functions including: write, toggle and read. In this STM32F0 tutorial, we will learn how to configure, use GPIOs as external interrupt signal to trigger an LED without depending on main loop routine with CubeMX. We will:

  1. Understand some basic knowledge about interrupts of STM32F0
  2. Configure the STM32CubeMX project and synchronize files with Source Insight
  3. 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 delay in your main routine, but you also need your program to act quickly when there are input from user or other devices such as input button or sensors that requires periodically readout. So, 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:

  1. The code inside the interrupt handler must be as brief as possible.
  2. Never use delay or wait or any kind of interval inside the interrupt handler.
  3. Their shared variables must be carefully managed.

Nested vectored interrupt controller (NVIC)

This is what you may not hear about when working with 8bit microcontroller 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).

vector table vector table1

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 it is. The priority can be changed by user from the program. There are many interrupts source: EXTI line, Timer, ADC,… and we will focus first on the external interrupt lines (red dot in the table).

STM32F0 support 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 resume when the high priority handler completed.

figure_2_nested_interrupt Source: http://community.arm.com/docs/DOC-2607

External Interrupt

Turn 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. First part of the video states the problem then following with 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);
  }

Edited 2016:

More proper way, in case you use more than one EXTI pins:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)  
{
    if (GPIO_Pin==GPIO_Pin_x) //x:0-15
    {
        //do do something here
    }

    if (GPIO_Pin==GPIO_Pin_y) //y:0-15
    {
        //do do something else here
    }
}

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:

external interrupt So it means that you cannot use PA0 and PB0 as EXTI lines at the same time but PA0 and PB1 is ok. The User button on the STM32F0 kit was connected to PA0 as follow:

button_sch 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.

irq_sign

Since we want to fire the interrupt when the button is both pressed and released, we need to configure it to detect both edge in the CubeMX as in the video. You can change to detect only one edge as well depend on your input signal.

It’s your turn !

Now, 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 you result in the comment 😀

Next tutorial will be about Timer and Counter 🙂