cancel
Showing results for 
Search instead for 
Did you mean: 

Rotary encoder turn detection

PTiha
Senior

Hello!

I want to use a simple rotary encoder with STM32F429.

I programmed the TIM4 timer into 'ENCODERMODE_X4' mode.

Is there a way for me to detect the rotation of the rotary knob by interrupt?

I don't want to do periodically TIMx->CNT sampling if there is more elegant method.

Thanks!

4 REPLIES 4

We use a external line interrupt configuration(on our STM32F301) on the input pins attached to the encoders pulse outputs.

Here is the setup for one of the two encoders pulse outputs(our encoder input is coming in GPIO_PC14):

   SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource14);

   // Configure EXTI line

   EXTI_InitStructure.EXTI_Line   = EXTI_Line14;

   EXTI_InitStructure.EXTI_Mode   = EXTI_Mode_Interrupt;

   EXTI_InitStructure.EXTI_Trigger   = EXTI_Trigger_Falling;

   EXTI_InitStructure.EXTI_LineCmd   = ENABLE;

   EXTI_Init(&EXTI_InitStructure);

Once set up and the interrupts are enabled, you will get an EXTI15_10_IRQHandler interrupt on every falling edge of the encoder input as the user turns the encoder.

PTiha
Senior

Thanks for the help!

In the meantime, I tried the following:

I enabled CC interrupt on TIM4 Channel1. When an interrupt occurs due to a rising edge on TI1, I read the TIM4-> CNT register in the interrupt handler.

It is working, however I have two concerns with this workaround:

  • The interrupt is occurs not due to a change in CNT but to a change in one of the encoder phases
  • Because of the former, the interrupt occurs just at the beginning or middle of a counting phase. This also depends on the direction of rotation.

I remember we played around a bit before we decided on this solution. Our interrupt is triggered off encoder A and when we get the interrupt we sample the encoder B level. If the encoder B level is high during encoder A interrupt, the encoder is turning one way. If encoder B is low, it is turning the other.

Here is a sloppy example:

void EXTI15_10_IRQHandler(void)

{

volatile uint32_t encoderB;

   //encoderA = GPIO_ReadInputDataBit(GPIOC, ENCODER_A);

   encoderB = GPIO_ReadInputDataBit(GPIOC, ENCODER_B);

   if((EXTI->IMR & EXTI_IMR_MR14) && (EXTI->PR & EXTI_PR_PR14))

   {

      if (encoderB)

      {

         tempSetPoint++;

         if(tempSetPoint > flashData.setHigh)

            tempSetPoint = flashData.setHigh;

      }

      else

      {

         tempSetPoint--;

         if(tempSetPoint < flashData.setLow)

            tempSetPoint = flashData.setLow;

      }

      encHead++;

      encHead   &= (NUM_ENCODER_SAMPLES-1);

      // Clear the interrupt flag

      EXTI->PR = EXTI_PR_PR14;

   }

   else if(EXTI->PR & EXTI_PR_PR14)

   {

      // Clear the interrupt flag

      EXTI->PR = EXTI_PR_PR14;

   }

}

Hope this helps.

PTiha
Senior

Thanks a lot!

I was a little busy, but now I managed to deal with the topic a bit.