# STM32 Rotary Encoder with Hardware Interrupts

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

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

• Encoders on the STM32F4 can be directly handled with timers. – Scott Seidman Feb 16 '14 at 4:09
• 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. – spizzak Feb 16 '14 at 6:21
• 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. – jippie Feb 16 '14 at 8:21
• Important details: How exactly are the interrupt inputs being pulled high? Do the interrupt inputs have hysteresis? – Joe Hass Feb 16 '14 at 13:17
• Aren't you shifting way too much?? Shouldn't the 8 be a 2? – Scott Seidman Feb 16 '14 at 14:11

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
while(1){}
}

interrupt_on_any_change(){
idx=idx<<2 & 0x0F + readA<<1 + readB;