cancel
Showing results for 
Search instead for 
Did you mean: 

Using an encoder: Jitter Incorrectly Trigger Interrupts

XPChi
Associate

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

}

 

6 REPLIES 6
SofLit
ST Employee

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

 

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.
TDK
Guru

> 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.

If you feel a post has answered your question, please click "Accept as Solution".

Hi @SofLit , 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


@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?

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.
Karl Yamashita
Lead III

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.

Tips and Tricks with TimerCallback https://www.youtube.com/@eebykarl
If you find my solution useful, please click the Accept as Solution so others see the solution.

@Karl Yamashita wrote:

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

 

 Indeed!

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.