Are Rotary Quad Encoders a write off?

Go To Last Post
41 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Several years ago I used a rotary quadrature encoder in a DDS frequenxcy generator with complete simplicity and success, but it was a quality unit pulled out of a radio or similar.

 

I'm trying to get one of the cheap little blues ones to work, using hardware debouncing, but as my debouncing gets more and more complex the nosiy little beast refuses to produce a reliable signal. I've tried continuous sampling until I get three readings the same in a row. (with a delay of 100ms before re-enabling the interrupt)

 

As I'm reading it in an ISR I don't want to spend too long on debouncing, has anyone found a reliable and relatively quick way of debouncing these beasts in software?

 

I would rather not fit a daughter board with hardware debounce on, if all else fails I will simply cycle through the available values, as there are only nine, but it won't be as elegant.

It's me again...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There's been plenty of discussions regarding this. One technique is to poll the pins regularly in a timer tick and debounce and decode.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There's hw debounce and sw debounce. Got a scope? Post the scope shot. Lets theorize in advance that the contacts bounce 3 times 2 ms apart. Knowing this, I'd look for an edge, read the other chan, inc or dec the count, and don't come back for 6ms. Done. I call this algorithm 'Read slower than the switch bounce settling time'. Maybe add an RC with 5 time constants longer than the bounce time.

 

 

Imagecraft compiler user

Last Edited: Wed. Jun 22, 2016 - 11:05 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Do you have a part number for "the little blue ones"?    I use the cheapest encoders that I can find: about $1 per piece. 

 

                    cap          resistors

I use:   gnd--||------+ --/\/\/-----+----/\/\/-----Vcc

                 0.1uF      |     10K      |     10K

                              to AVR       to enc left

                               IRQ           encoder center = gnd

 

    repeat components above and attach the point between 0.1uF and 10K to AVR encoder-direction pin.  Attach the point btn 10K and 10K to encoder right.

 

I believe that the cheap encoders are resistor/capacitor driven.  When the shaft is turned, the R/C makes little very short pulses on one pin.  The other pin will be either high or low (at the instant that the pulse gets generated) to indicate direction.  Both pulses only last a few milliseconds so you need an interrupt when one pin goes low.   The ISR routine reads the other encoder line and sets a direction variable (volatile) for the main code.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Simonetta wrote:

I believe that the cheap encoders are resistor/capacitor driven.  When the shaft is turned, the R/C makes little very short pulses on one pin.  The other pin will be either high or low (at the instant that the pulse gets generated) to indicate direction.  Both pulses only last a few milliseconds so you need an interrupt when one pin goes low.   The ISR routine reads the other encoder line and sets a direction variable (volatile) for the main code.

 

Sounds complicated, for a switch company ? - got any scope shots showing that action ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If the FW is made correct, then some noise don't matter, it will just be a extra -1 and then quickly +1, (you have a problem is both can shift at the same time but then even a analoge filter will have a problem)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This one worked well for me.  It increments by 4 I think.  Any glitches (debounces) are ignored unless they are perfectly aligned on both pins at the same time (i think), but you'd do well to hardware debounce all the same (using a scope to pick values) as everyone here will tell you.    EDIT:  Okay that part about the glitches getting ignored is bull.  Just read the article over again.  No such magic dice.  Apologies for the glimmer of non-existent hope.

 

This kind of reminds me when people complain about usart timings, trying to invent the world's greatest software timer, and everyone's response is always "SPEND 1 DOLLAR ON EXTRA HARDWARE!".  I agree.  Software may be eating the world, but it still need's some metal teeth to chew properly.

 

https://www.circuitsathome.com/m...

Last Edited: Thu. Jun 23, 2016 - 06:33 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

These are just cheap generic ones off eBay, about £1 for five. It doesn't have any built in debouncing, it isn't fitted to a board like some.

 

This screengrab from Sparkfun shows the extent of the problem, Now that's what I call bounce!

Bounce

 

I suspect the problem is chiefly noisy wiping rather than the noisy transitions - transitions are easy to debounce with delays or multiple polling, but random false transitions as the contacts rotate seem to be my problem, and I suspect this is why the output creates the illusion of the switch moving the wrong way or back and forth.

 

I like the idea of polling, but I have another idea I want to try first.

It's me again...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Now that's what I call bounce!

but it lasts less than 100uS! 

 

David 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

DAFlippers wrote:

Now that's what I call bounce!

but it lasts less than 100uS! 

 

David 

 

100uS is an eternity with a 16MHz clock

It's me again...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just some comments.

Okay that part about the glitches getting ignored is bull.

 

I guess is pointed at me, so can you tell me how there can be any error if you don't us a filter? you will just get:

 

34

35

34

35

35

...

Depending of how fast the code are perhaps more 34 35.

(if both change at the same time we have a problem, but both HW and SW solutions will have that problem.)

 

But I would use a timer ISR anyway (and main reason is that an AVR only have one level of ISR and I have a timer ISR anyway), and for a hand switch like this there is no hurry to scan.

 

 

About the link

I have not compiled the code, but I fear a lot of push and pop in that ISR, because Z is used and some other kind of index, so yes the code look nice in C but I don't think the ASM output is optimal.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

100uS is an eternity with a 16MHz clock

but very good for real world mechanical switches.  That is why there are whole topics (like this one) on debouncing switches. 

 

David 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I suspect your technique of using an external interrupt and disabling it for 100ms was flawed. Did you clear the external interrupt flag before re-enabling the interrupts?

Switches normally bounce for milliseconds.

Sample at 10ms, debounce and feed into a finite state machine. Takes very little cpu time.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Stub_Mandrel wrote:
100us is an eternity with a 16MHz clock

But the 16 MHz is entirely irrelevant - it's the time relative to the actual mechanical movement that matters!

 

ie, the speed that a real user can turn the real knob in real time!

 

As already noted, it's exactly the same as for any other switch...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:

I suspect your technique of using an external interrupt and disabling it for 100ms was flawed. Did you clear the external interrupt flag before re-enabling the interrupts?

Switches normally bounce for milliseconds.

Sample at 10ms, debounce and feed into a finite state machine. Takes very little cpu time.

 

 

The 100ms delay is intended to limit the maximum speed at which the variable can changes (it's for adjusting the slew speed of a telescope so the ability to make a quick spin and go from max to min in ~1 second or go one click at time is about right). My usual approach to software debouncing is simply to disable any re-triggering for a period and as a delay is usually needed to limit the rate at which things change for user convenience, it works well. The problem with this is that the signal seems to be so dirty that transients occur just as the contacts wipe across each other, not just at the transitions. That gives me two options - polling and hardware debounce, but even polling is likely to fall victim to the odd glitch without some hardware debounce. Assuming I can find a couple of schmidt trigger gates in my bits boxes I think I'll go the hardware route - although this is not a critical application, if I can get a reliable result I may use these actuators in many future projects. And if I fit the daughter board to the back of the switch it will look OK.

It's me again...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't see a problem... its hi, then 100us later its lo. If you spin it a half turn in 250ms, and you get 12 pulses, that's a pulse every 20ms. So read the thing every 10ms and you'll never miss a pulse, never get a bounce. Doesn't need an 'algorithm' or an interrupt, just an edge detect... 'its lo this pass and it was hi last pass 10ms ago'. Sounds just too simple? Want to see my small example?

 

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Exactly.  If you see a state change from a state that has been steady for 5-10mS then it is a state change otherwise ignore. 

 

David

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bobgardner wrote:

I don't see a problem... its hi, then 100us later its lo. If you spin it a half turn in 250ms, and you get 12 pulses, that's a pulse every 20ms. So read the thing every 10ms and you'll never miss a pulse, never get a bounce. Doesn't need an 'algorithm' or an interrupt, just an edge detect... 'its lo this pass and it was hi last pass 10ms ago'. Sounds just too simple? Want to see my small example?

 

 

That is the problem - when the contacts are open polling is fine, but if its moving when you poll it seems to be a complete lottery whether you get a high or a low. So you need to take multiple polls and take the majority view.

It's me again...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ignoring the details of this and just reading the  thread title...

 

Yup, they are a complete write off - totally unusable - which is why you never see them being used in any kind of commercial product....

 

.... oh, wait a minute

 

....

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Do you have a part number? 

 

Are you sure that they are normal A & B channel encoders, and not an other layout?

 

 

Perhaps show a picture with the two channels changing about 3 times. over something like 1/2 sec. 

 

I was just looking around and found this :

 

http://www.aliexpress.com/item/H...

Last Edited: Thu. Jun 23, 2016 - 02:37 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If in doubt RTFM.  Here is a rotary encoder and there are specific sections on chatter and bounce http://dlnmh9ip6v2uc.cloudfront....​. 

 

 

David

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Forget your time disable, it's causing you to miss important things.

 

Use the resistors and capacitors as your datasheet says.

 

Encoders are trouble if you don't follow the right scheme: Watch both edges of both lines. When A rises, if B is high, increment, else decrement. When A falls, if B is high, decrement, else increment. When B rises, if A is high, decrement, else increment. When B falls, if A is high, increment, else decrement.

 

If you don't debounce, the worse that will happen is you'll see a one count jitter. Usually not a disaster. Also remember: Converting any analog quantity, like the motion of your encoder or a voltage has a 1 count uncertainty: the number is 135, but the actual value might be 134 or 136. To know which, you would need another digit, then it would say 135.7, but might actually be 135.6 or 135.8.

The largest known prime number: 282589933-1

It's easy to stop breaking the 10th commandment! Break the 8th instead. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

And I forgot to mention: Pay attention to atomicity. If your count is more than 1 byte (in our 8 bit systems), you have to be sure it does not change between the time you read the first and second bytes or you can get values that are WAY off when you least expect them.

The largest known prime number: 282589933-1

It's easy to stop breaking the 10th commandment! Break the 8th instead. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@Clawson

 

As I posted at the start, I have had no trouble using a quality quad switch, it's when I tried to use teh ubiquitous blue-cheapies I got problems - and I suspect the ones off ebay may well be those that fail QC never get into commercial equipment (like teh nice waterproof press buttons that melt when you solder the terminals...)

 

@DAFLippers

 

That's useful, but without a part number I have looked at a lot of generic circuiits and none suggested using that layout without a schmitt trigger hanging of the end.

 

@torby

 

The coding isn't an issue, nor is atomicity in this case. Basically its shift left if clockwise, shift right if anticlockwise to give stepper speeds rising by powers of two.

 

I've made a little debounce board with a 74LS14 on it, still not working so off to check it with the scope.

 

It's me again...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well Stub, in msg 18 you said there was a problem figuring out the level while turning. If you look for the edge instead of the level, the problem is non existant. Lets examine your noisy waveform.... we know it was hi 10ms ago. You read it right in the middle of the noise burst, and its hi. No prob, no change, come back in 10ms. Now its lo. You have detected the edge. Increment the count. I swear there is an all sw way to do this and an all hw way. Want to see the all sw way yet?

 

 

 

Imagecraft compiler user

Last Edited: Thu. Jun 23, 2016 - 04:35 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Stub_Mandrel wrote:
I've made a little debounce board with a 74LS14 on it, still not working so off to check it with the scope.

 

Grr! the TTL seems to be duff. Year/week code is 9950, they must have been in a hurry to get home for Christmas, now where's the receipt?

 

@DAFlippers

 

the cope showed a quite nice waveform going into the 7414, so I'll desolder the chip & try with just the Rs & Cs. If that fails I've got another 7414.

It's me again...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bobgardner wrote:

Well Stub, in msg 18 you said there was a problem figuring out the level while turning. If you look for the edge instead of the level, the problem is non existant. Lets examine your noisy waveform.... we know it was hi 10ms ago. You read it right in the middle of the noise burst, and its hi. No prob, no change, come back in 10ms. Now its lo. You have detected the edge. Increment the count. I swear there is an all sw way to do this and an all hw way. Want to see the all sw way yet?

 

 

I can see that tells me it's a falling edge (as at least either the high or the lo is a 'stable' signal and that has to have been a change between them).

 

I can see that polling will work fine. I have a KHz tick that will probably do the job.

All the ways of doing it seem to suggest continuous polling, extra flags and having 'frankenstein code' to me, I assume you have an elegant solution?

It's me again...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Stub asks:

All the ways of doing it seem to suggest continuous polling, extra flags and having 'frankenstein code' to me, I assume you have an elegantsolution?

 

Bob replies:

Not continuous polling. There would be no time left to do anything else. You call 'readencoder()' every tick. Since the tick is greater than the bounce time, so far so good. You read the PIN containing A and B once at the top of the readencoder function. You compare the state now to the state last. Inc or Dec count accordingly. Save the state now for next pass. No extra flags. Frankenstine means a monster made out of dead people parts? That went Whoosh! over my head. There were no microcontrollers in Dr FrankenSchtine's day were there? My explanation was certainly elegant, in my humble opinion. Easy to understand, easy to implement, easy to maintain. Win Win Win.

 

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sometimes you kind of get the extra digit if the hole construction vibrate, then the AVG can give a better result than the encoder resolution at a stand still.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yo Stub... does your encoder have a detent? If so, lets say its in the detent right smack-a-dab in the middle of channel A, so if you 'rock' it in the detent, you get B on and off. This tells us that we should check for the edge on ch B and check the level of ch A, which is right in the middle when the edge arrives, to do the inc and dec. You can check if the detent is in the middle of which channel with a meter.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just test some numbers:

16 MHz 30Hz poll and an ISR take 50 clk.

that will take about 0.01% of the cpu time, that is a real killer!

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Which type of quadrature encoder is being discussed here?

 

  • Full cycle, detented, where a complete quadrature cycle occurs between detents, where both A & B contacts are typically open at detents.

Triggering an interrupt from one contact (A) and reading the other (B) will indicate direction of rotation.

 

  • Quarter cycle, detented or not, where a quarter quadrature cycle occurs between detents if existing, where (initially) the contact state is indeterminate. 

Triggering an interrupt from both contacts and reading the state of both contacts to be compared with the previous contact state will indicate the direction of rotation.

 

Manufacturer data sheets may suggest contact signal conditioning. The cheap blue Bourns 3315 series datasheet suggests a 5ms delay debouncer (MC14490).

 

Stan

Last Edited: Thu. Jun 23, 2016 - 09:16 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bobgardner wrote:

Yo Stub... does your encoder have a detent? If so, lets say its in the detent right smack-a-dab in the middle of channel A, so if you 'rock' it in the detent, you get B on and off. This tells us that we should check for the edge on ch B and check the level of ch A, which is right in the middle when the edge arrives, to do the inc and dec. You can check if the detent is in the middle of which channel with a meter.

 

Good thoughts! I'll look at that tomorrow. There's going to be some interesting radio on in 7 minutes when the polls close!

 

It's me again...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sbennett wrote:

Which type of quadrature encoder is being discussed here?

 

  • Full cycle, detented, where a complete quadrature cycle occurs between detents, where both A & B contacts are typically open at detents.

Triggering an interrupt from one contact (A) and reading the other (B) will indicate direction of rotation.

 

  • Quarter cycle, detented on not, where a quarter quadrature cycle occurs between detents if existing, where (initially) the contact state is indeterminate. 

Triggering an interrupt from both contacts and reading the state of both contacts to be compared with the previous contact state will indicate the direction of rotation.

 

Manufacturer data sheets may suggest contact signal conditioning. The cheap blue Bourns 3315 series datasheet suggests a 5ms delay debouncer (MC14490).

 

Stan

 

It's the cheap full cycle per detent type, the nice one I used in the past was 1/4 cycle per detent.

It's me again...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I can't help but think we're going over the same thing we done many times before. Can someone search out the relevant threads for mr mandrel as this thread is far from rivetting.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
I can't help but think we're going over the same thing we done many times before. Can someone search out the relevant threads for mr mandrel as this thread is far from rivetting.

 

I'm not forcing you to read it ;-)

 

P.S. You can call me Stub ;-)

It's me again...

Last Edited: Thu. Jun 23, 2016 - 10:49 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Stub-san, i'm hoping that you can be directed to threads where the solution has been found so you can progress. The issue of debounce has been done to death and rotary encoders less so.
Google ganssle debounce
For the lowdown on debouncing and why your technique is not too effective.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Stub_Mandrel wrote:

I can see that polling will work fine. I have a KHz tick that will probably do the job.

All the ways of doing it seem to suggest continuous polling, extra flags and having 'frankenstein code' to me, I assume you have an elegant solution?

 

I'm not sure what you expect, but any MCU polling code to manage Quad-Decode is going to need at least a previous-copy (aka extra flags).

Even pure-hardware solutions do the same.

 

If you imagine you can do it with no extra flags or previous-copy, and something you call elegant,  then we look forward to your reveal :)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm a fan of the xor method...seems elegant enough. Only need to save the previous state and there's no lookup table or anything. Assuming the rotary encoder is on pins 0 and 1 of PORTB:

 

volatile uint8_t old_state = PINB & 0x03;
volatile int16_t position=0;

ISR()
{
  uint8_t new_state = PINB & 0x03;
  if(new_state == old_state) return;
  if((old_state>>1) ^ (new_state & 1)) //xor pin 1 of old state with pin 0 of new state
    position++;
  else
    position--;
  old_state = new_state;
}

Might be some errors since I'm typing this from memory, and the position increment or decrement is 50/50 on being reversed depending on the hardware connections...for an explanation of the xor trickery see http://www.me.unm.edu/~starr/tea.... Just run the ISR every 10ms or so and any bouncing will just cause the position value to waver a bit before settling.  There's also an optimised assembly version at http://www.edn.com/design/system... if you're into overkill.  Both pages mention edge interrupts, but I believe they're using optical encoders which aren't prone to bouncing.  The algorithm works fine either way, but as others have stated it's best to avoid edge interrupts with noisy transitions.

Last Edited: Fri. Jun 24, 2016 - 12:48 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's a VERY nice way of doing it, I did't know about.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think I'm very poor at explaining myself sometimes.

 

I'm also very good at screwing up in interesting ways.

 

Yes, the schmitt inverter was completely duff, all o/p pins were stuck low.

 

I just removed the chip and wired up direct like in @DAFlippers circuit.

 

The interrupt routine then worked but only in one direction whatever I tried... then the election results started.

 

This morning  the multimeter showed odd results, I couldn't seem to get what I'd expect from a quad decoder. put it on the scope and got a strange thing - the same trace on both outputs, synchronised and it looked like a slow rise time s=pulse with a lower one superimposed in the middle.

 

Realisation dawned - if you use the two tags to secure the switch to the strip board, don't forget to cut the strips so they don't short both your outputs together....  <:-(

 

For the record using the circuit above 22K resistors and 47n caps you get a rise time of about a millisecond or two and the width of the (very nicely formed) pulses at a typical turning speed is 10ms (think about it, you can spin the switch a full turn in a fraction of a second which is 12 pulses, so that's about right, to me this it suggests it might be better to poll faster than every 10ms). I think a 10n cap might be better if you expect it to be turned fast.

 

My interrupt code works fine, the code I used for a test is (register saving omitted):

 

        sbrs	flags2,manual                   ;in manual move mode?
        rjmp	XINT0_OUT                       ;no
        lds		temp,SYNC_STEP

        sbis	PIND,QUAD1                      ;Clockwise?
        rjmp	QUAD1_LOW                       ;no
        inc	    temp
        rjmp	XINT0_OUT

QUAD1_LOW:
        dec	temp

XINT0_OUT:
        sts     SYNC_STEP,temp
        

 

 

 

It's me again...

Last Edited: Fri. Jun 24, 2016 - 10:18 AM