Ganesh Dagadi
Ganesh Dagadi

Reputation: 76

Unable to Toggle LED using interrupt on STM32

I am trying to toggle an LED using STM32C0x Reference Manual. I have connected the LED to port A pin 0 and button to port A pin 1. The circuit is correct as the LED toggles successfully when I dont use interrupts but use a while loop with if statement. However while using interrupts, the pressing the button does not do anything.

void enablePinGPIOPortA(unsigned short pin , short isInput);
void enableInterrupt(void);
int main(void){
    enablePinGPIOPortA(0 , 0);
    enablePinGPIOPortA(1 , 1);
    enableInterrupt();
    while(1);
}

void enablePinGPIOPortA(unsigned short pin , short isInput){
    RCC->IOPENR |= RCC_IOPENR_GPIOAEN;
    //reset the pin
    GPIOA->MODER &= ~(0b11 << 2*pin);
    if(!isInput){
        GPIOA->MODER |= 0b01 << 2*pin;
    }else{
        //select pull down for input
        GPIOA->PUPDR |= (0b10 << (2*pin));
    }
}

void enableInterrupt(){
    RCC->APBENR2 |= 1;
    EXTI->EXTICR[1] |= 0b00;
    EXTI->RTSR1 |= 0x2;
    EXTI->IMR1 |= (1 << 1);
    NVIC_EnableIRQ(EXTI0_1_IRQn);
    RCC->APBENR2 &= ~(1);
}

void EXTI0_1_IRQHandler(void){
    GPIOA->ODR ^= 1;
    EXTI->RPR1 &= ~(1 << 1);
}

I assume the enableGPIOA function is correct as it worked without interrupts. However I'm not sure about the enableInterrupt function. I'm using a simulator https://wokwi.com/projects/new/st-nucleo-c031c6

Upvotes: 0

Views: 161

Answers (1)

Clifford
Clifford

Reputation: 93556

EXTI_RPR1 bits are cleared by writing 1. So:

EXTI->RPR1 &= ~(1 << 1);

should be

EXTI->RPR1 |= (1 << 1) ;

EXTI->EXTICR[1] |= 0b00; is incorrect (althugh probably not causing your problem in this case). OR'ing with zero has no effect. Whatever is in that register will remain in that register. Its reset value is zero, so in the absence of any other write elsewhere, that is what it will contain. However it is an attempt to set the wrong bits in the wrong register. It is a write to EXTI_EXTCR1 which is the control register for EXTI4 to EXIT7 while your input is on PORTA.1 so you need EXTI1. To enable the EXTI1 for PORTA.1:

EXTI->EXTICR[0] &= ~0xFFFF00FF ;

Still though since the reset state is zero in any case, this will only have any effect if it were previously written with some other value elsewhere. To be fair the reference manual in an attempt to be concise, has made the EXTICRx register description rather confusing. In brief:

  • EXTI->EXTICR[0] - CR for EXTI0 - EXTI3
  • EXTI->EXTICR[1] - CR for EXTI4 - EXTI7
  • EXTI->EXTICR[2] - CR for EXTI8 - EXTI11
  • EXTI->EXTICR[3] - CR for EXTI12- EXTI15

Then in each of EXTI->EXTICR[n] the source identifier is an 8 bit value with the lower EXTI of the four in the least significant byte.

Finally, connecting a mechanical switch to an interrupt line directly with no hardware debounce circuit is unlikely to work reliably, and detecting a button through interrupts like in this way is not particularly practical. Ways to debounce a switch interrupt include:

  • disable the interrupt in the interrupt handler, and start a timer, then re-enable the interrupt in the timer expiry interrupt.
  • timestamp the interrupt and on subsequent interrupts, if the insufficient time as passed since the timestamp, ignore the interrupt (i.e. do not toggle the LED and do not reset the timestamp).

The second is simpler, and can be implemented with the Cortex-M SYSCLK rather then using a hardware timer. In either case it is only worth doing in cases where instantaneous response is required - limit switches on a machine or collision detection on a robot perhaps. Otherwise you could simply poll the input periodically (on a timer interrupt if necessary).

Upvotes: 1

Related Questions