Skip to main content
Visitor II
September 19, 2024
Solved

Using an encoder: Jitter Incorrectly Trigger Interrupts

  • September 19, 2024
  • 4 replies
  • 1808 views

Hello,

I‘m creating a counter that increments/decrements according to cw/ccw direction of an encoder. 
An interrupt is triggered by the rising and falling edge on each channel. 
Due to mechanical jitter, the interrupts are triggered (sometimes) simultaneously and will increment by values greater than 1. I was thinking of using a delay but how can I make my interrupt handlers more robust to prevent this occurring?

 

 

int main(){

 FLASH_Init();

 RCC_Init();

 GPIO_Init();

 USART2_Init();

 EXTI0_Init();

 EXTI1_Init();

 NVIC_Init(); 

 while(1){

 

 }

}

 

void EXTI0_IRQHandler(void){

 if (EXTI -> PR & (0x01U)){

 if (((GPIOA -> IDR & (0x1U)) && (GPIOA -> IDR & (0x2U))) || ((!(GPIOA -> IDR & (0x1U))) && (!(GPIOA -> IDR & (0x2U))))){

 STATE++;

 printf("Encoder counts = %d \n\r",STATE);

 //GPIOA -> ODR ^= (0x01U << 5);

 }

 if (((GPIOA -> IDR & (0x1U)) && (!(GPIOA -> IDR & (0x2U)))) || ((!(GPIOA -> IDR & (0x1U))) && (GPIOA -> IDR & (0x2U)))) {

 STATE --;

 printf("Encoder counts = %d \n\r",STATE);

 }

 }

 EXTI -> PR = (0xFFFFFU);

// NVIC -> ICER[0] |= (0x1U << 6);

// NVIC -> ISER[0] |= (0x1U << 7);

}

 

void EXTI1_IRQHandler(void){

 if (EXTI -> PR & (0x01U<<1)){

 if (((!(GPIOA -> IDR & (0x2U))) && (GPIOA -> IDR & (0x1U))) || ((GPIOA -> IDR & (0x2U)) && (!(GPIOA -> IDR & (0x1U))))) {

 STATE ++;

 printf("Encoder counts = %d \n\r",STATE);

 //GPIOA -> ODR ^= (0x01U << 5);

 }

 if (((GPIOA -> IDR & (0x2U)) && (GPIOA -> IDR & (0x1U))) || ((!(GPIOA -> IDR & (0x2U))) && (!(GPIOA -> IDR & (0x1U))))){

 STATE --;

 printf("Encoder counts = %d \n\r",STATE);

 }

 

 }

 EXTI -> PR = (0xFFFFFU);

// NVIC -> ICER[0] |= (0x1U << 7);

// NVIC -> ISER[0] |= (0x1U << 6);

}

 

    This topic has been closed for replies.
    Best answer by XPChi

    Thanks for the advice. It took a while, but I have 4 encoders running on my PCB now with no issues at all. Here are a few things I've done (which I understand may not be the best approach but have solved the issue):

    • A debounce period that is flagged by the EXTI handler and cleared by a timer after a required period has elapsed
    • The EXTI handler does as little logic processing as possible - it simply sets/clears flags

    • Another timer is used as a 'heartbeat' signal and circular buffer

    The printf statement was removed after all testing/development was finalised. This was a good (but troublesome) learning experience

    4 replies

    Technical Moderator
    September 19, 2024

    Hello @XPChi and welcome to the community,

    First of all, remove all these printfs from the interrupt handlers. This blocks the execution for a considerable time.

    See this link: https://developer.arm.com/documentation/ka002394/latest/

    Second, I'm wondering why you are clearing all the EXTI flags for a specific EXTI interrupt. Also you need to clear the interrupt as soon as you get into the interrupt handler.

    See for example how The EXTI interrupt handler is implemented in HAL:

    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);
     }
    }

     

    XPChiAuthor
    Visitor II
    September 19, 2024

    Hi @mƎALLEm , thanks for the welcome. I'm avoiding using HAL as I would rather have full control of all the registers that I'm using and also for debugging purposes.
    I'm aware of the interrupt clear, this due to the fact that only EXTI0 and EXTI1 are being used. I clear them after an interrupt has been triggered to prevent a jitter enabling an interrupt incorrectly.

     

    I've cleared  the interrupt in the beginning as you suggested, however, this doesn't seem to ignore any mechanical noise.

    Thanks 
    XPChi

    Technical Moderator
    September 19, 2024

    @XPChi wrote:

    I'm aware of the interrupt clear, this due to the fact that only EXTI0 and EXTI1 are being used. 


    Better to clear them separately.

     


    @XPChi wrote:

    I'm avoiding using HAL as I would rather have full control of all the registers that I'm using and also for debugging purposes.


    I'm not suggesting you to use HAL but inspiring from it.

    Did you also remove all the printfs from the IRQ handlers?

    Super User
    September 19, 2024

    > EXTI -> PR = (0xFFFFFU);

    Don't blindly clear all the flags. You may be clearing ones which haven't been processed yet. Only clear the flag you have handled.

    Graduate II
    September 19, 2024

    Why are you using GPIO interrupts to read the encoder? You're like recreating the wheel.

    Instead, use a Timer in encoder mode which does all the hard work for you. 

    Just create a new project in STM32CubeIDE in Timer Encoder mode and extract the parts you need from the HAL driver.

    Technical Moderator
    September 20, 2024

    @Karl Yamashita wrote:

    Why are you using GPIO interrupts to read the encoder? You're like recreating the wheel.

     

     Indeed!

    XPChiAuthorAnswer
    Visitor II
    February 5, 2025

    Thanks for the advice. It took a while, but I have 4 encoders running on my PCB now with no issues at all. Here are a few things I've done (which I understand may not be the best approach but have solved the issue):

    • A debounce period that is flagged by the EXTI handler and cleared by a timer after a required period has elapsed
    • The EXTI handler does as little logic processing as possible - it simply sets/clears flags

    • Another timer is used as a 'heartbeat' signal and circular buffer

    The printf statement was removed after all testing/development was finalised. This was a good (but troublesome) learning experience