I bought the STM32F411 core, and now I'm trying to understand the various pieces of HAL. Starting with external interrupts, it seemed like a good idea, because there is a button on the board that is connected to PC13. So I set up a simple switch mode. 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 press the button, an interrupt is generated, and the blinking frequency changes from 1 to 2 Hz (or vice versa). It works as intended, but why? I forgot to clear the pending interrupt flag, so the ISR should be called again and again. The technical description clearly states that
When the selected edge appears on the external interrupt line, an interrupt request is generated. A pending bit corresponding to the interrupt line is also set. This request is reset by writing '1 to the pending register.
Reading a little further shows that this is slightly different for events:
When the selected edge appears in the event line, an event pulse is generated. The deferred bit corresponding to the event line is not set.
However, I do not set the button output mode to any of the GPIO_MODE_EVT_... modes, so I do not use the event mechanism (to be honest, I still do not know what it is - I just think that I am not using it. Any hints are welcome).
So, somewhere I would have to call void HAL_NVIC_ClearPendingIRQ (IRQn_Type IRQn) , right? It seems that software does not need to clear the flag, since the ISR is not called more than once per falling edge. I added a breakpoint in HAL_GPIO_EXTI_Callback to test this.
Edit
As indicated in the comments, the flag clear code is in the ST implementation of the GPIO interrupt handler:
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin) { if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET) { __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin); HAL_GPIO_EXTI_Callback(GPIO_Pin); } }
This handler must be called by the actual ISR (which runs in my code), and it clears the pending flag corresponding to the GPIO_Pin argument. So I have to write an ISR that sorts 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 using pin as an argument. For each external interrupt, the number of contacts will be checked approximately ~ 3 times (in the ISR, in the handler, and in the callback)!
If this is the solution, I want to return my problem.