Search code examples
cembeddedexternalinterruptstm32

External Interrupt Setup on STM32L1 doesn't run ISR


for 4 days now, I am struggling to set up External interrupt on my STM32 and I have gone through tons of reading and other people's code to get it. But no luck. I have two buttons and when pressing either one of them I expect to light up an LED, this example is only to get it working, I wanted to have something functional before proceeding and building rest of the code. I am sorry if the code is a little messy, but I am working on neating my coding skills. I've gone through manuals and datasheets but nothing seems to help.

Here is my main.c

#include "stm32l1xx_hal.h"
#include "buttons.h"

static void MX_GPIO_Init(void);

bool RightButtonFlag = 0;
bool LeftButtonFlag = 0;


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

  while (1)
  {
      if(RightButtonFlag){
          Blue_ON();
      }
      if(LeftButtonFlag){
          Green_ON();
      }
  }

}
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();


  GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
{

Followed by part of stm32l1xx_it.c

#include "stm32l1xx_hal.h"
#include "stm32l1xx.h"
#include "stm32l1xx_it.h"
#include "buttons.h"

extern volatile uint8_t RightButtonFlag;
extern volatile uint8_t RightButtonFlag;

void EXTI15_10_IRQHandler(void)
{
    if(GPIOC->IDR & GPIO_IDR_IDR_10){
        RightButtonFlag = 1;
        EXTI->PR |= EXTI_PR_PR10;
    }

    if(GPIOC->IDR & GPIO_IDR_IDR_11){
            LeftButtonFlag = 1;
            EXTI->PR |= EXTI_PR_PR11;
    }
}

buttons.c

void controls_Interrupt_Init(void){

    RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; /* Enable System Configuration Register */


    SYSCFG->EXTICR[3] |= SYSCFG_EXTICR3_EXTI11_PC; /* Set up External Interrupt for Pin 11 Port C */
    SYSCFG->EXTICR[3] |= SYSCFG_EXTICR3_EXTI10_PC; /* Set up External Interrupt for Pin 10 Port C */

    EXTI->IMR |= EXTI_IMR_MR11;
    EXTI->IMR |= EXTI_IMR_MR10;

    EXTI->FTSR  |= EXTI_FTSR_TR11; /* Falling trigger Selection Reg. Trigger 11 */
    EXTI->FTSR  |= EXTI_FTSR_TR10; /* Falling trigger Selection Reg. Trigger 10 */

    NVIC_SetPriority(EXTI15_10_IRQn,1); /* Set Interrupt priority for pins 10-15 */
    NVIC_EnableIRQ(EXTI15_10_IRQn); /* Enable NVIC for Pins Between 10-15 */


}

and buttons.h

void controls_Interrupt_Init(void);

#define Blue_ON()           (HAL_GPIO_WritePin(BLUE_LED_PORT, BLUE_LED_PIN, 1))
#define Green_ON()          (HAL_GPIO_WritePin(GREEN_LED_PORT, GREEN_LED_PIN, 1))

I am fairly new to coding and with my poor experience I expect to have screwed up something very simple.


Solution

  • You have to declare the flags as volatile. That's a hint to the compiler that the variable can change any time, independent of the normal program flow.

    volatile bool RightButtonFlag = 0;
    volatile bool LeftButtonFlag = 0;
    

    When there is no volatile there, the compiler can assume that the flags never change in the main loop, and optimize it that way, that the variables will be loaded only once, before the loop.

    Declaring them volatile only in the scope of the interrupt handler does no good, because the optimization will still take place in main(), where the volatile declaration is not visible.

    It is good practice to move the declaration to a header file, and include that header everywhere the variable is referenced. Then the compiler can check that the types are indeed compatible.

    UPDATE

    The interrupt handler makes little sense. You set EXTI to detect a falling edge, then check if the input is high. Checking the GPIO data register is not reliable anyway, due to the bouncing effect of mechanical buttons.

    You should rather check EXTI->PR in the handler, and reset the pending bit with a simple assignment instead of |=, otherwise you could accidentally clear another pending bit too.

    void EXTI15_10_IRQHandler(void)
    {
        if(EXTI->PR & EXTI_PR_PR10){
            RightButtonFlag = 1;
            EXTI->PR = EXTI_PR_PR10;
        }
    
        if(EXTI->PR & EXTI_PR_11){
            LeftButtonFlag = 1;
            EXTI->PR = EXTI_PR_PR11;
        }
    }
    

    You can still check somewhere if GPIOC->IDR actually reflects the button state, to eliminate possible hardware problems.

    You can also try setting EXTI->SWIER from the debugger, or in the code, to simulate a button press.