3
\$\begingroup\$

I'm trying to get a rotary encoder working on my STM32.

  1. I have channel A & B being pulled up to 3V and debounced with 1uF capacitors.
  2. The board has channel A & B connected to PA11 & PA10 respectively and have configured hardware interrupts for both
  3. I've tried a number of different algorithms to decode the direction of rotation but no matter what I do I can't get consistent alternating interrupts (ie. ABABAB)
  4. I've tried triggering on both edges, just falling, just rising and no matter what I get the interrupts triggering seemingly randomly.

Is there something I'm doing wrong? Are the interrupts not enough to keep up with the speed of the encoder? Is the debouncing not enough (Looks okay on a scope but maybe the interrupts are more sensitive)? Is there a better way to do this in general? I've been stuck on this for a while so any help would be great. Let me know if more info is necessary.

*Code Edited to reflect changes (working algorithm)

INTERRUPT HANDLER

static int8_t states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
uint8_t RotaryCurrentState = 0x00;
uint8_t RotaryTransition = 0;
int8_t RotaryPosition = 0;
/*Rotary Encoder Interrupt Handler
Channel A (IRQ11) & B (IRQ10)
 CW --->
 A (IRQ11) ̄|___| ̄ ̄ ̄ ̄|___| ̄ ̄ ̄ ̄
 Interrupts ^ ^ ^ ^
 B (IRQ 10) ̄ ̄ ̄|___| ̄ ̄ ̄ ̄ ̄|___| ̄
 Interrupts ^ ^ ^ ^
 CCW <---
void EXTI4_15_IRQHandler(void)
{ 
 RotaryCurrentState = (Read_IO(ROTARY_A) << 1) | Read_IO(ROTARY_B);
 RotaryTransition = (RotaryTransition <<2 ) | RotaryCurrentState;
 RotaryPosition = RotaryPosition + states[RotaryTransition & 0x0F];
 EXTI_ClearITPendingBit(EXTI_Line10); //Clear Channel B
 EXTI_ClearITPendingBit(EXTI_Line11); //Clear Channel A
}

MAIN.C

//Initialize 
RotaryTransition = Read_IO(ROTARY_A) << 3 | Read_IO(ROTARY_B) << 2 | \
 Read_IO(ROTARY_A) << 1 | Read_IO(ROTARY_B);
while(1)
{
 //CW Transition
 if (RotaryPosition == 4)
 {
 STM_EVAL_LEDToggle(LED4);
 RotaryPosition = 0;
 }
 //CCW Transition
 else if (RotaryPosition == -4)
 {
 STM_EVAL_LEDToggle(LED3);
 RotaryPosition = 0;
 }
}
asked Feb 16, 2014 at 2:49
\$\endgroup\$
12
  • 2
    \$\begingroup\$ Encoders on the STM32F4 can be directly handled with timers. \$\endgroup\$ Commented Feb 16, 2014 at 4:09
  • \$\begingroup\$ Unfortunately I'm stuck using these IOs because of board design and IO limitations. I don't think they can do the decoding using the timer. \$\endgroup\$ Commented Feb 16, 2014 at 6:21
  • 2
    \$\begingroup\$ These encoders have two channels. The easiest way to use them is to use one as 'clock' and attach a single edge triggered interrupt to it, then the other channel acts as a 'direction' indicator. On each interrupt you inspect the value of the 'direction' channel and either add or subtract one step from the current value. \$\endgroup\$ Commented Feb 16, 2014 at 8:21
  • \$\begingroup\$ Important details: How exactly are the interrupt inputs being pulled high? Do the interrupt inputs have hysteresis? \$\endgroup\$ Commented Feb 16, 2014 at 13:17
  • \$\begingroup\$ Aren't you shifting way too much?? Shouldn't the 8 be a 2? \$\endgroup\$ Commented Feb 16, 2014 at 14:11

1 Answer 1

6
\$\begingroup\$

That's not a great algorithm in your handler. You should have ZERO ifs. No decisions.

Store your AB state, i.e., 00 or 01, then append your next state, i.e 0001 means AB went from 00 to 01,thus B changed from 0 to 1. Make this a +1. If starting from 00, and you change to 10, then call this a -1. Build a 16 element array of all possible transitions holding the number that needs to be added to your count if it occurs, noting that some are illegal and need to be handled.

0000 0 0 no transition
0001 1 +1
0010 2 -1
0011 3 0 Illegal, two transitions, and so on.

Index into this array on every transition, watching for illegal events and dealing with them as you see fit. Add the result to the count. Shift the new values to the old value spot in the index number, and repeat forever

In Pseudocode

 signed int8 add_subt[16] = { 0, 1 ,-1 , ....};
 unsigned int8 idx;
 signed int32 pos_count;
main() {
% initialize idx
idx = readA <<3 + readB<<2 + readA<<1 + readB;
while(1){}
}
interrupt_on_any_change(){
idx=idx<<2 & 0x0F + readA<<1 + readB;
pos_count=pos_count+add_subt[idx];
}

You could maintain err_idx to help you flag bad transitions

Doesn't that look a hair simpler??

answered Feb 16, 2014 at 14:04
\$\endgroup\$
12
  • \$\begingroup\$ These seems to make sense and I saw a similar example online while I was searching that implemented the array as a 4x6 state machine which I thought was an interesting approach. So basically I would trigger on both channels' rising & falling edge and then do I read the value of both lines on every interrupt? \$\endgroup\$ Commented Feb 16, 2014 at 15:35
  • \$\begingroup\$ Yup. trigger on every transition and read A and B into the bottom two bits of the index. \$\endgroup\$ Commented Feb 16, 2014 at 16:16
  • \$\begingroup\$ No need for a state machine, either. One array, by index, tells you if you need to increment or decrement your count. \$\endgroup\$ Commented Feb 16, 2014 at 16:18
  • 1
    \$\begingroup\$ Implemented and working great! Thanks for the suggestion. I've updated my code in the original post to the working version. \$\endgroup\$ Commented Feb 17, 2014 at 18:53
  • \$\begingroup\$ Good Job!! Originally, I thought you were trying to use this for a motor's quad encoder, in which case interrupts on the DIO's would probably be borderline, but then I actually linked through to the part you were working with!! I think you can actually do this a bit smaller by using two integers, one for increment and one for decrement, and bitmasking to the appropriate bit, something like count + incr&(1<<idx) - decr&(1<<idx) , but if you can afford the array, why not! \$\endgroup\$ Commented Feb 17, 2014 at 23:22

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.