Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

STM32F411: is clearing an external interrupt flag really necessary?

I've bought an STM32F411 nucleo board and now I'm trying to understand various bits and pieces of the HAL. Starting with external interrupts seemed to be a good idea, because the board has a push button which is connected to PC13. So I've set up a simple toggle-the-frequency blinky. The code below is a bit simplified:

#define LED_PIN GPIO_PIN_5
#define BTN_PIN GPIO_PIN_13

static uint32_t blink_period = 250;

int main(void)
{
  HAL_Init();
  SystemClock_Config();

  __GPIOA_CLK_ENABLE();
  GPIO_InitTypeDef pinConfig;
  pinConfig.Pin = (LED_PIN);
  pinConfig.Pull = GPIO_NOPULL;
  pinConfig.Mode = GPIO_MODE_OUTPUT_PP;
  pinConfig.Speed = GPIO_SPEED_FAST;
  HAL_GPIO_Init(GPIOA, &pinConfig);

  __GPIOC_CLK_ENABLE();
  pinConfig.Pin = (BTN_PIN);
  pinConfig.Pull = GPIO_NOPULL;
  pinConfig.Mode = GPIO_MODE_IT_FALLING;
  pinConfig.Speed = GPIO_SPEED_LOW;
  HAL_GPIO_Init(GPIOC, &pinConfig);
  HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0x0F, 0x00);
  HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);

  while (1)
  {
    HAL_GPIO_TogglePin(GPIOA, LED_PIN);
    HAL_Delay(blink_period);
  }
}

void EXTI15_10_IRQHandler(void)
{
  HAL_GPIO_EXTI_IRQHandler(BTN_PIN);
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if(GPIO_Pin == BTN_PIN)
  {
    if (blink_period == 500)
    {
      blink_period = 250;
    }
    else
    {
      blink_period = 500;
    }
  }
}

When I push the button, an interrupt is generated and the blinky frequency changes from 1 to 2 Hz (or vice-versa). This works as intended, but why? I forgot to clear the pending interrupt flag, so the ISR should be called over and over. The datasheet clearly states that

When the selected edge occurs on the external interrupt line, an interrupt request is generated. The pending bit corresponding to the interrupt line is also set. This request is reset by writing a ‘1’ in the pending register.

Reading a bit further reveals that this is a bit different for events:

When the selected edge occurs on the event line, an event pulse is generated. The pending bit corresponding to the event line is not set.

However, I'm not setting the button pin mode to any of the GPIO_MODE_EVT_... modes so I'm not using the event mechanism (to be honest I don't yet know what that even is - I just think that I'm not using it. Any hints are welcome).

So somewhere I should have to call void HAL_NVIC_ClearPendingIRQ (IRQn_Type IRQn), shouldn't I? It seems that clearing the flag by software is not necessary, because the ISR is not called more than once per falling edge. I've added a breakpoint in HAL_GPIO_EXTI_Callback to verify this.

Edit

As mentioned in the comments, the flag clearing code is in ST's implementation of the GPIO interrupt handler:

void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
  /* EXTI line interrupt detected */
  if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
  {
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
    HAL_GPIO_EXTI_Callback(GPIO_Pin);
  }
}

This handler needs to be called by the actual ISR (which is done in my code) and it clears the pending flag corresponding to the GPIO_Pin argument. So I have to write an ISR which sorts out which flags are set, and call HAL_GPIO_EXTI_IRQHandler for each, which in turn calls my HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin), again with the pin as an argument. For each external interrupt, the pin number would get checked some ~3 times (in the ISR, in the handler and in the callback)!

If that is the solution, I want my problem back.

like image 952
Christoph Avatar asked Nov 23 '14 11:11

Christoph


People also ask

What happens if interrupt flag is not cleared?

If you don't clear the flag, and the hardware doesn't clear the flag (this occurs for some flags but not all) then as soon as the interrupt ends the interrupt controller will see the interrupt flag and fire the ISR again.

Why do we need to clear interrupt flag?

Generally, you clear it when you enter the routine, indicating that you are going to handle whatever is causing the interrupt from that moment in time forward. If another interrupt event occurs during your servicing of the initial interrupt you don't want to lose that interrupt notification.

Why do you need to clear the interrupt after it has been set?

The main reason for clearing the interrupt condition before servicing the interrupt is to not lose a interrupt.

What does it mean to clear an interrupt?

Clearing an interrupt means to tell the system that you've handled the reason for the interrupt and are ready for the system to return to normal operation (like interrupting you the next time around).


1 Answers

You don't have to call HAL_NVIC_ClearPendingIRQ (IRQn_Type IRQn) because the pending bit in the NVIC will be cleared automatically upon entering HAL_GPIO_EXTI_IRQHandler.

The HAL_GPIO_EXTI_IRQHandler() implementation clears the pending bit in the peripheral, not in the NVIC. If it didn't clear the pending bit by calling __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin), then the handler would be called again and again. The point is that you must distinguish between the interrupt pending bit in the peripheral and the pending bit in the NVIC.

like image 162
LennyB Avatar answered Oct 21 '22 21:10

LennyB