0

one of EXTI doesn't work. In fact, I have two EXTI interruptions from PB3 and PB4. whenever I push the button from PB3, the LED power_ON, and whenever I push the button from PB4, the LED power_OFF. The issue that I have, is EXTI3 work and EXTI4 doesn't.

First I activate AFIO clock

RCC->APB2ENR |= (1 << 0); //activate AFIO

second I enable RCC for GPIOB :

    RCC->APB2ENR &=~(1 << 3)); // clear RCC for GPIOB 
    RCC->APB2ENR |= (1 << 3)); // Enable RCC for GPIOB
    GPIOB->CRL &= ~((1 << 10) | (1 << 9) | (1 << 11) | (1 << 8)); // Clear GPIO PB2 MODE  LED
    GPIOB->CRL &= ~((1 << 12) | (1 << 13) | (1 << 14) | (1 << 15)); // Clear GPIO PB3 MODE  Button1
    GPIOB->CRL &= ~((1 << 16) | (1 << 17) | (1 << 18) | (1 << 19)); // Clear GPIO PB4 MODE  Button2

    GPIOB->CRL |= 0x00000200; // Set GPIO PB2 (01 : output)
    GPIOC->CRL |= ((1 << 15) | (1<<19)); // Set GPIO PB3 and PB4 MODE (10 : input)

After that, I configure both EXTI3 and EXTI4 :

    AFIO->EXTICR[0] &= ~AFIO_EXTICR2_EXTI3;
    AFIO->EXTICR[1] &= ~AFIO_EXTICR2_EXTI4;
    AFIO->EXTICR[0] |= AFIO_EXTICR2_EXTI3_PB;
    AFIO->EXTICR[1] |= AFIO_EXTICR2_EXTI4_PB;
    EXTI->RTSR |= EXTI_RTSR_TR3;
    EXTI->RTSR |= EXTI_RTSR_TR4;
    EXTI->FTSR &=~(EXTI_FTSR_FT3);
    EXTI->FTSR &=~(EXTI_FTSR_FT4);
    EXTI->IMR |= EXTI_IMR_MR3;
    EXTI->IMR |= EXTI_IMR_MR4;
    NVIC_SetPriority(EXTI3_IRQn, 0); 
    NVIC_SetPriority(EXTI4_IRQn, 0); 
    NVIC_EnableIRQ(EXTI3_IRQn);
    NVIC_EnableIRQ(EXTI4_IRQn);
    GPIOB->ODR &= ~(1<<2);  //Initialize LED with power_OFF state

and Finally I call the function headers for both EXTIs:

    void EXTI3_IRQHandler(void)
    {
     if (EXTI->PR & EXTI_PR_PR3)
       {
         GPIOB->ODR |= 1<<2;
         for(volatile int i=0; i<20000000; ++i){
            __asm("nop");
           }

        EXTI->PR |= EXTI_PR_PR3; 
      }
     }

    void EXTI4_IRQHandler(void)
     {
      if (EXTI->PR & EXTI_PR_PR4)
        {
         GPIOB->ODR &= ~(1<<2);
         for(volatile int i=0; i<20000000; ++i){
            __asm("nop");
            }

        EXTI->PR |= EXTI_PR_PR4; 
      }
     } 

I changed the pins but I got the same issue, where do you think the issue came from ?

  • It may not be related to your issue, but the NOP busy-wait loop in an IRS is ill-advised. If you want to debounce the switches, disable the interrupt then switch it back on after a period outside of the interrupt context, or (simpler) timestamp the first interrupt from SYSCLK and ignore subsequent interrupts if it is not more than _n_ milliseconds after the first. – Clifford May 17 '23 at 09:01
  • Also not related to your problem but MODE for example is a 4 bit field. Surely `GPIOB->CRL &= ~((0xf << 8)` is a more intuitive expression than `GPIOB->CRL &= ~((1 << 10) | (1 << 9) | (1 << 11) | (1 << 8))` for example. i.e. a four-bit field with LSB at bit 8. – Clifford May 17 '23 at 09:08
  • "nop"s are a bad idea, because MCU can choose to internally entirely ignore this instruction and go straight to the next one (it's explicitly stated in ARM docs not to use "nop" to do nothing). Do you enter both interrupt handlers? Do you exit both interrupt handlers? I don't see you clear any interrupt flags, is that ok for EXTI? – Ilya May 17 '23 at 10:01
  • yes I know that 'nop' is to be avoided, it's just my way of debugging. for the ISR functions, I only enter in EXTI4, and not in EXTI3. I clear the interrupt flags with EXTI->PR |= EXTI_PR_PR4 – lucas mure May 17 '23 at 10:10
  • `EXTI->PR |= EXTI_PR_PR4` is incorrect. It will clear _all_ pending EXTI interrupts, not just EXTI4. – Clifford May 17 '23 at 12:07

1 Answers1

0

The EXTI Pending Register EXTI_PR has "rc_w1" semantics:

From RM0008 §2.2:

read/clear (rc_w1)

Software can read as well as clear this bit by writing 1. Writing ‘0’ has no effect on the bit value.

So say for example EXTI3 and EXTI4 interrupts were pending simultaneously, EXTI_PR would contain 0x00000018. So if say EXTI3_IRQHandler runs first, the line:

EXTI->PR |= EXTI_PR_PR3;

Being read-modify-write will read 0x18, uselessly bit-OR in 0x08, and write 0x18, clearing both EXIT3 and EXTI4 so that EXTI4_IRQHandler will not be invoked.

To clear the specific EXTI line, you simply write the one bit:

EXTI->PR = EXTI_PR_PR3 ;

because writing a zero has no effect.

Your method of debouncing is ill-advised, and will delay other lower or equal priority interrupts and the main context processing. Further, testing the PR bit in an EXTI handler that is associated with a single bit is unnecessary - the ISR is running, so the PR must be set.

Suggest:

void EXTI3_IRQHandler(void)
{
    static uint32_t timestamp = systick() ;

    uint32_t now = systick() ;
    if( now - timestamp > DEBOUNCE_TIME )
    {
        timestamp = now ;
        GPIOB->ODR |= 1<<2;
    }

    EXTI->PR EXTI_PR_PR3; 
}

void EXTI4_IRQHandler(void)
{
    static uint32_t timestamp = systick() ;

    uint32_t now = systick() ;
    if( now - timestamp > DEBOUNCE_TIME )
    {
        timestamp = now ;
        GPIOB->ODR &= ~(1<<2) ;
    }

    EXTI->PR EXTI_PR_PR4; 
}

Where systick() is some hypothetical timing function which may be implemented like:

volatile uint32_t tick = 0 ;
  
void SysTick_Handler(void)  
{
    tick++ ;
}

uint32_t systick()
{
    return tick ; 
}

Where for a systick in milliseconds, you would initialise and start SYSTICK with the CMSIS SysTick_Config function thus:

SysTick_Config(SystemCoreClock / 1000) ;

You might then set DEBOUNCE_TIME to 20 (milliseconds), no delays, no blocking or busy-waits in interrupts and the rest of your system will have far more deterministic timing characteristics.

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • Thank you for the correction. I just added what you have mentioned. But still the handler function EXTI4 was not working. So replace EXTI4 by EXTI15_10 specially for PC13 and it works. I don't know the reason but it works. – lucas mure May 17 '23 at 13:47
  • @lucasmure I note that PC13 is connected to the on-board USER push-button - are you using that or connecting a separate button? The on-board button has an _external_ pull-up; perhaps it works because it has a pull-up and you have not enabled the internal pull-up, so for PB4 it would not work? PB3 is connected to the on-board ST-Link SWO, which probably makes it a bad choice, but may be effectively pulling it up explaining why EXTI3 is "working" - until you try to debug! – Clifford May 17 '23 at 16:44
  • @lucasmure : https://www.st.com/resource/en/schematic_pack/mb1136-default-c05_schematic.pdf – Clifford May 17 '23 at 16:55