Author Topic: easy rotary encoder implementation + good easy to implement code?  (Read 15002 times)

0 Members and 1 Guest are viewing this topic.

Offline SArepairmanTopic starter

  • Frequent Contributor
  • **
  • Posts: 885
  • Country: 00
  • wannabee bit hunter
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.
 

Offline David_AVD

  • Super Contributor
  • ***
  • Posts: 2806
  • Country: au
Re: easy rotary encoder implementation + good easy to implement code?
« Reply #1 on: August 29, 2014, 02:52:09 am »
For the actual encoder control, I've used the Bournes PEC11R-4020K-S0024 that can be got from Digikey, Mouser, etc.
 

Offline Mr.B

  • Supporter
  • ****
  • Posts: 1237
  • Country: nz
Re: easy rotary encoder implementation + good easy to implement code?
« Reply #2 on: August 29, 2014, 02:59:39 am »
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.
« Last Edit: August 29, 2014, 03:22:49 am by Mr.B »
I approach the thinking of all of my posts using AI in the first instance. (Awkward Irregularity)
 
The following users thanked this post: electronx

Offline jmole

  • Regular Contributor
  • *
  • Posts: 211
  • Country: us
    • My Portfolio
Re: easy rotary encoder implementation + good easy to implement code?
« Reply #3 on: August 29, 2014, 03:11:12 am »
Made a tutorial on one using a PSoC and Arduino here:
 

Offline mikerj

  • Super Contributor
  • ***
  • Posts: 3240
  • Country: gb
Re: easy rotary encoder implementation + good easy to implement code?
« Reply #4 on: August 29, 2014, 07:42:37 am »
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.

This Arduino code shows 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).
« Last Edit: August 29, 2014, 08:21:30 am by mikerj »
 
The following users thanked this post: electronx

Offline firewalker

  • Super Contributor
  • ***
  • Posts: 2450
  • Country: gr
Re: easy rotary encoder implementation + good easy to implement code?
« Reply #5 on: August 29, 2014, 07:57:22 am »
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.html

The code above adopted for plain C.

https://mega.co.nz/#!m5hAkKLT!XqMBtjBwskuaeylIIU9tWytww2IzlVnNlR9bnQN3LaI

Become a realist, stay a dreamer.

 

Offline salbayeng

  • Frequent Contributor
  • **
  • Posts: 296
  • Country: au
Re: easy rotary encoder implementation + good easy to implement code?
« Reply #6 on: August 29, 2014, 08:10:37 am »
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_encoder
return


(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
 

Offline mikerj

  • Super Contributor
  • ***
  • Posts: 3240
  • Country: gb
Re: easy rotary encoder implementation + good easy to implement code?
« Reply #7 on: August 29, 2014, 08:20:54 am »
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.html

That's exactly the same state machine method as the Arduino link, it's the only truly reliable method for reading encoders.

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_encoder
return


(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

That's the way NOT to do it.  This method leaves you wide open to false counts from e.g. contact bounce on mechanical encoders.
 

Offline salbayeng

  • Frequent Contributor
  • **
  • Posts: 296
  • Country: au
Re: easy rotary encoder implementation + good easy to implement code?
« Reply #8 on: August 29, 2014, 08:56:37 am »
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.
 
 

Offline salbayeng

  • Frequent Contributor
  • **
  • Posts: 296
  • Country: au
Re: easy rotary encoder implementation + good easy to implement code?
« Reply #9 on: August 29, 2014, 11:57:51 am »
@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-arduino
I 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.
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: easy rotary encoder implementation + good easy to implement code?
« Reply #10 on: August 29, 2014, 12:46:17 pm »
Quote
This is what I do on an Atmega

Here 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.
« Last Edit: August 29, 2014, 01:39:27 pm by dannyf »
================================
https://dannyelectronics.wordpress.com/
 

Offline Artraze

  • Contributor
  • Posts: 29
  • Country: us
Re: easy rotary encoder implementation + good easy to implement code?
« Reply #11 on: August 29, 2014, 01:34:27 pm »
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.
 

Offline nessatse

  • Regular Contributor
  • *
  • Posts: 99
  • Country: za
Re: easy rotary encoder implementation + good easy to implement code?
« Reply #12 on: August 29, 2014, 02:07:03 pm »
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: easy rotary encoder implementation + good easy to implement code?
« Reply #13 on: August 29, 2014, 03:01:53 pm »
One way to achieve speed sensitive reading is to use a "pulse counter" to indicate how many consecutive movements the encoder has generated:

Code: [Select]
  if (encoder reading is non-zero)   //encoder rotation detected
    encoder_flag = (encoder_flag << 1) | 0x01; //add 1 bit
  else encoder_flag = 0; //encoder flag reset
  if (encoder_flag >= ENC_FAST) encoder reading *= 10; //increment at 10x of the normal speed

This allows the encoder to detect fast rotations without requiring any additional hardware.
================================
https://dannyelectronics.wordpress.com/
 

Offline salbayeng

  • Frequent Contributor
  • **
  • Posts: 296
  • Country: au
Re: easy rotary encoder implementation + good easy to implement code?
« Reply #14 on: August 30, 2014, 04:16:13 am »
@ DannyF
There 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 by
1) 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_stat

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


 
 

Offline SArepairmanTopic starter

  • Frequent Contributor
  • **
  • Posts: 885
  • Country: 00
  • wannabee bit hunter
Re: easy rotary encoder implementation + good easy to implement code?
« Reply #15 on: August 30, 2014, 06:49:02 am »
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
« Last Edit: August 30, 2014, 06:55:15 am by SArepairman »
 

Offline rs20

  • Super Contributor
  • ***
  • Posts: 2318
  • Country: au
Re: easy rotary encoder implementation + good easy to implement code?
« Reply #16 on: August 30, 2014, 07:37:06 am »
...
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.

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

Offline TerminalJack505

  • Super Contributor
  • ***
  • Posts: 1310
  • Country: 00
Re: easy rotary encoder implementation + good easy to implement code?
« Reply #17 on: August 30, 2014, 04:11:37 pm »
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

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

Offline FrankBuss

  • Supporter
  • ****
  • Posts: 2365
  • Country: de
    • Frank Buss
Re: easy rotary encoder implementation + good easy to implement code?
« Reply #18 on: August 30, 2014, 04:32:40 pm »
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.html

I 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.
So Long, and Thanks for All the Fish
Electronics, hiking, retro-computing, electronic music etc.: https://www.youtube.com/c/FrankBussProgrammer
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf