easy rotary encoder implementation + good easy to implement code? - Page 1
Forum section: electronics / projects

author: SArepairman
author_rank: Frequent Contributor
timestamp: August 29, 2014, 02:38:24 am
content: I am looking to use a rotary encoder (one with a push button) to be a user interface for something I am building.I did the coding to interface one in a lab class before, but it was in assembly, it was frustrating and it did not work that great, if you turned it too quick everything went to hell. I also remember the teacher saying that he spent quite some time looking for a decent rotary encoder for us to use, as there are alot of crap picks out there.Does anyone have a recommendation for a good rotary encoder that has a usable push button on it. I am scared that it will be frustrating to use (I.E. it will rotate upon being pushed), and I am not sure how to find a quality product.Also, can anyone recommend a good library that interfaces with one? I am looking to use the encoder to control a PIC mcu, I'm guessing that they are popular enough for good code to be out there, I would rather not reinvent the wheel.

author: David_AVD
author_rank: Super Contributor
timestamp: August 29, 2014, 02:52:09 am
content: For the actual encoder control, I've used the Bournes PEC11R-4020K-S0024 that can be got from Digikey, Mouser, etc.

author: Mr.B
author_rank: Unknown
timestamp: August 29, 2014, 02:59:39 am
content: From a code perspective;Use some hardware debouncing and then google "c++ rotary encoder library"... There are miles of resources out there.Most common and most reliable use interrupts for both EncA and EncB to ensure that you don't miss a pulse.

author: jmole
author_rank: Regular Contributor
timestamp: August 29, 2014, 03:11:12 am
content: Made a tutorial on one using a PSoC and Arduino here:

author: mikerj
author_rank: Super Contributor
timestamp: August 29, 2014, 07:42:37 am
content: By far the best way to handler encoders is to use a simple state machine to track the transitions and check they are 'legal'. This builds in immunity from contact bounce. The state machine can be processed in an interrupt driven by the encoder lines, or it can be polled.ThisArduino codeshows how to do it, the important bit is the state machine in the read_encoder() function. This simply builds up a 4 bit value, where the upper two bits are the old encoder pin values and the lower two are the current encoder pin values. This 4 bit value is then used as an index to a table that tells you whether the encoder has turned clockwise, anti-clockwise or stayed the same (i.e. no change or an illegal change).

author: firewalker
author_rank: Super Contributor
timestamp: August 29, 2014, 07:57:22 am
content: I prefer the method in the following link. I just works flawlessly. I think it is one of the most elegant way to interface quadrature encoders.http://www.buxtronix.net/2011/10/rotary-encoders-done-properly.htmlThe code above adopted for plain C.https://mega.co.nz/#!m5hAkKLT!XqMBtjBwskuaeylIIU9tWytww2IzlVnNlR9bnQN3LaI

author: salbayeng
author_rank: Frequent Contributor
timestamp: August 29, 2014, 08:10:37 am
content: The firmware is fairly trivial.This is what I do on an Atmega(a) the encoder has two wires A and B connect to two pins, e.g. enc_ra and enc_rb(b) setup the pin change interrupt on enc_ra , point the vector at Right_pulse_isr(c) declare a variable right_encoder as a signed integer (or a signed long)(d) use this code as the Interrupt service routine (This is Bascom , but you get the idea):Right_pulse_isr:If Enc_ra = Enc_rb Then Decr right_encoder Else Incr right_encoderreturn(e) Warning: There is an upper speed limit imposed by ISR latency, so be careful with other ISR's running (e.g. buffered serial), but typically with a 16MHz CPU, pulse rates up to 20kHz are possible(f) If you get erratic counts due to EMI etc, then a RC network (eg 1k / 1nf) will help

author: mikerj
author_rank: Super Contributor
timestamp: August 29, 2014, 08:20:54 am
content: Quote from: firewalker on August 29, 2014, 07:57:22 amThat's exactly the same state machine method as the Arduino link, it's the only truly reliable method for reading encoders.Quote from: salbayeng on August 29, 2014, 08:10:37 amThat's the way NOT to do it. This method leaves you wide open to false counts from e.g. contact bounce on mechanical encoders.

author: salbayeng
author_rank: Frequent Contributor
timestamp: August 29, 2014, 08:56:37 am
content: Oh , the only drawback with my simple approach is that it counts every second quad count , so, a 100 pulse per rev encoder , that might have been able to count 400 quad counts, will only count 200 double counts, this is not a problem with manual input encoders as they have 4 quadcounts per detent, so my code will generate 2,4,6,8 as you turn the knob while a "real" quad count algorithm will count 4,8,16,20 , obviously both result in the same functionality.

author: salbayeng
author_rank: Frequent Contributor
timestamp: August 29, 2014, 11:57:51 am
content: @mikerj.All interrupt driven encoder routines have blind spots, my simple approach may miscount for noise events shorter than the interrupt latency, the state table approach will miscount on noise then usually fix itself on the next pulse.My simple approach works flawlessly with hall effects and a manual CNC input encoder wheel, except in extreme EMI conditions.And adding an RC filter followed by a schmidt trigger (such that the time constant is longer than the interrupt latency) will result in 100% accuracy.An added bonus of the simple approach is it easily "calibrated" , i.e. so it will count 0.995 counts or 1.024 pulses per count (or convert from mm to inches, whatever)I use the LS3677 encoder interfaces on my big machine controllers, these are almost perfect, but a little analog tweaking minimises the rare occurance of "metastability bias with simultaneous EMI on both channels".I did develop a state machine encoder interface back in 2008 (prior to using the LS3677), but I think these have been around for ages. Mine was a bit like the Arduino approach.http://www.circuitsathome.com/mcu/reading-rotary-encoder-on-arduinoI left_shift the Achannel into ABindex, then left_shift the Bchannel in. Then mask off top nibble, the lower four bits are now oldA,oldB,newA,newB , this is a pointer into a lookup tabel that has the three possible results of 16 state transitions, +1,-1, or 0,... so the relevant line of coding is....Count = count + LUT(ABindex)So that's 4 lines of code all up, and given that I could use a state type encoder, I prefer to use the simpler approach, and adding an analog RC filter is no additional burden to the dozens of ferrite beads/TVS's/PTC's that already populate the PCBs.

author: dannyf
author_rank: Super Contributor
timestamp: August 29, 2014, 12:46:17 pm
content: QuoteHere is a simpler approach that is incredibly bounce-resistant:1) shift enc_stat left by 2;2) read encoder's A pin into enc_stat.1, and B pin into enc_stat.0;3) look up a table (of -1, 0, 1, a total of 16 values) based on enc_stat's last four bits.Done.ps: it is a simplified state machine.

author: Artraze
author_rank: Contributor
timestamp: August 29, 2014, 01:34:27 pm
content: If you are flexible about what microcontroller to use, I know that the STM32 series supports encoder counting via their timers and will operate up to ~10MHz on the inputs without missing pulses. Additionally, if you use one of the 4 channel timers, you can connect the button to a capture input and have it both capture the exact position and fire an interrupt where the button is pressed.I imagine that other micros have such functionality so it might be worth looking around. On the other hand, while hardware counting (and capture) is somewhat vital for motor applications, for a manual input encoder it's more just a convenience.

author: nessatse
author_rank: Regular Contributor
timestamp: August 29, 2014, 02:07:03 pm
content: I posted some code for a state based, velocity sensitive implementation a while ago here:https://www.eevblog.com/forum/microcontrollers/any-good-examples-on-isr-based-rotary-encoder-library-'with'-velocity/msg291163/#msg291163

author: dannyf
author_rank: Super Contributor
timestamp: August 29, 2014, 03:01:53 pm
content: One way to achieve speed sensitive reading is to use a "pulse counter" to indicate how many consecutive movements the encoder has generated:Code:if (encoder reading is non-zero) //encoder rotation detectedencoder_flag = (encoder_flag << 1) | 0x01; //add 1 bitelse encoder_flag = 0; //encoder flag resetif (encoder_flag >= ENC_FAST) encoder reading *= 10; //increment at 10x of the normal speedThis allows the encoder to detect fast rotations without requiring any additional hardware.

author: salbayeng
author_rank: Frequent Contributor
timestamp: August 30, 2014, 04:16:13 am
content: @ DannyFThere is an extra 4th step in the state based approach you describe(2.9)mask off all but 4 lower bits of enc_stat(it only compiles to one instruction: AND Register Immediate b00001111 , so maybe hair splitting)One could skip this step by1) shift enc_stat RIGHT by 2;2) read encoder's A pin into enc_stat.3,3) and B pin into enc_stat.2;4) look up a table (of -1, 0, 1, a total of 16 values) based on enc_statThere is a subtle bias in the state based approach (actually in all of the code based aproaches) , more obvious in the way I have written it above, specifically the A pin is read first , then maybe a uS later the B pin is read. It would be better to copy the port, then use encoder_A_copy, encoder_B_copy to avoid skew.This will generate miscounts during fairly rare EMI double pulse events, where both A and B change state simultaneously, and the two pulse are seperated by the approximate response time of the ISR.

author: SArepairman
author_rank: Frequent Contributor
timestamp: August 30, 2014, 06:49:02 am
content: alright adc and ten turn pot glued to a button it will be I feel like I'm i just released a barrel of snakes.john kerry is needed in this thread

author: rs20
author_rank: Super Contributor
timestamp: August 30, 2014, 07:37:06 am
content: Quote from: salbayeng on August 30, 2014, 04:16:13 amBy EMI, do you mean electromagnetic interference? In that case, what on earth are you on about? Switch bounce is a real thing that almost always happens with mechanical switches; whereas a switch signal giving false readings due to EMI is a horrible fail in hardware design. In any case, an EMI pulse causing a 00 to be read as a 01 or 10 is no big deal because the next reading will be 00; which is classified as no net rotation, unless you have a really terrible decoding algorithm.

author: TerminalJack505
author_rank: Super Contributor
timestamp: August 30, 2014, 04:11:37 pm
content: Quote from: SArepairman on August 30, 2014, 06:49:02 amYes, this seems to be one of those topics that everyone has strong opinions of how to implement.My preference is to use a finite state machine. The nice thing about an FSM is that you can encode most of it into a flash-based look-up table.I've attached a few FSM diagrams that I've drawn-up previously to give you some ideas.

author: FrankBuss
author_rank: Unknown
timestamp: August 30, 2014, 04:32:40 pm
content: I used the same idea with the state machine, implemented with a lookup table. Works very reliable with an ALPS encoder EC12E2420404 with 24 impulses per revolution and a 1 kHz interrupt:http://www.frank-buss.de/attiny/index.htmlI guess there are ALPS encoders with a button, too, and they are inexpensive. But it is always difficult to press the button and not rotate it at the same time, especially if the encoder has a high resolution.