quadrature decoder within microcontroller

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

quadrature decoder within microcontroller

23,758 Views
airswit
Contributor III
Hi, i was wondering if you could help me out with the software required to decode a quadrature encoded signal coming from an IR detector. I want to create a 'rotary switch' and need to make sense of the signal coming from the device (2 data lines). I am using a coldfire 5213 (i know its not 16 bit, but there isn't a 32 bit forum). as a side note, will i need any kind of amplifier, or will the square wave output from the detector be able to be connected straight to an input on the processor?

Thanks,
Trevor
Labels (1)
17 Replies

3,576 Views
mke_et
Contributor IV

OK, here's another thought...

What's the MAXIMUM rate of change of what you want to detect?  I mean, is it 1000 state changes per second?  Obviously with a detented dial it may be on the order 10 to 20 state changes per second, and then only if it's 'cranking along'.

 

If it's less than 100 per second, seriously consider a timer tick at 1/1000 sec to just look at two pins and walk a state machine to match...

 

 

0 Kudos

3,576 Views
airswit
Contributor III
thanks for the replies guys.
My application is simple, it is just an input device for a user to change a setting, much like a mouse's scroll wheel. I have never used timer inputs before, so if this is the way to go, any chance i could get some code or hints on how to use them? As for the detector, i havn't created the circuit yet, because i'm not really sure what to use. I have tried to search all the major parts catalogs (digi-key, mouser, jameco), and havn't really come up with anything. I would like to keep external component count to a minimum, which means that the detector should output a 'square' wave, if possible. From a mouse I have opened up, I see that it is possible to have a single module for the photo detector, with 3 pins. it is a common collectored photo transistor, i do believe. on this device, the output (i am pretty sure at least) goes straight into the microcontroller, but i think that ucontroller has comparater inputs.

Message Edited by airswit on 03-08-200609:13 PM

0 Kudos

3,576 Views
rocco
Senior Contributor II
Hi, Trevor:

You did not state, but I assume you need this to be bidirectional. If not, disregard all that I am about to post, as it is much simpler when you only need to count up (or down).

Take a look at Digi-Key for the GH3071-ND from Greyhill for under $5. It has detents, but is available without detents as well. The one I use has 36 counts per turn. It has three pins, A, B and Common. You connect A and B directly to the micro, and connect the Common to ground. If the micro's input pins do not have built-in pullup resistors, you would need to add them.

That one is mechanical, but there are also optical versions with much less friction and higher resolution. It all depend on the "feel" that you are after.

Depending on whether or not you have detents, I would suggest either the two-timer-channel approach, or the switch-debounce approach.

I would hesitate to use two-timer-channels if you do not have detents. The reason is that, if you land on an edge and it dithers, your interrupt rate can get very high, and tie up your processor. If you do have detents, the interrupt happens only with each position change, and the processor usage can be very low. If fact, it is zero if the input isn't turning.

If there is a possibility of dither, the switch-debounce method is better, because it samples at a fixed frequency regardless of what the input signal does. So its processor usage is constant, and proportional to your sampling frequency. An additional advantage is that the switch-debounce routine can handle more than one encoder and also handle other switches at the same time (my routine will debounce 16 input pins on two ports).

I can post some code if:
1) You let me know which routine you're most interested in, and
2) someone clues me in on how to post an attachment to this board.

In the meantime, back to the emulator!
0 Kudos

3,576 Views
rocco
Senior Contributor II
Hi, Trevor:

Mike makes some very good points; He's seems to have been there. Pay particular attention to his "creep" issue. If you only count on falling edges, the count will creep if you land on that edge. Many algorithms have the problem.

I have had to interface to encoders a lot, and have about six different algorithms, depending on desired resolution (1x, 2x or 4x), maximum frequency, noise immunity and direction-behavior. If I knew more about the application, I could suggest an algorithm.

My favorite algorithm is to use each input, A and B, into separate input-capture channels. I program the edge detect on the two channels to interrupt on both rising and falling edges, and then use a state sequencer to track position. It's pretty foolproof, as long as you don't exceed it's maximum frequency.

If your input device has detents (therefore, no creep problems), then the code is simpler for most of the algorithms.

If the input frequency is low, I have a debounce-timer algorithm that uses standard I/O pins for input. This works very well for encoder-pots that are part of an user-interface.

As far as the hardware is concerned, is your IR detector a photo-diode, a photo-transistor or does it contain an output buffer? All can be interfaced with most microcontrollers with only a few passive components. You may need an amplifier only if the application is high-frequency.

Hope that helps.
0 Kudos

3,576 Views
bigmac
Specialist III

Hello Trevor,

Here is another quite simple possibility, that appears to avoid the "creep" issue. The idea is similar to the first proposal by Mike, where an interrupt only occurs on Channel A, and Channel B is monitored during the ISR. The difference is that the edge on which the next interrupt will occur is toggled, before exiting the ISR.

For example, if the current interrupt is on a positive edge, the next edge of Channel A has to be negative, either one half cycle later for continued rotation in the same direction, or sooner if the direction reverses, as detected by the state of Channel B. So any random movement back and forth on the edge of the Channel A transition, should result in the counter incrementing and decrementing in quick succession, with no significant net creep. If the toggling of the counter is unacceptable for the particular application, simply ignore the LS bit of the counter.

With this method, there are two transitions per cycle, giving twice the number of counter increments/decrements.

Here is some pseudo-code to implement this concept within the ISR -

if (A_edge == 1)
  if (Ch_B == 1) Count++;
  else Count--;
else
  if (Ch_B == 1) Count--;
  else Count++;
Toggle A_edge;
Clear interrupt flag;

It would seem feasible to use an input capture channel for channel A, and a general purpose input for channel B. So timing data would be available to allow for calculation velocity, if required.

To allow for de-bounce of a non-optical rotary encoder, I would simply inhibit further Channel A interrupts, and start a timer with 20-50 ms timeout, before exiting the ISR.  The timeout can then be monitored from within the main program loop, before the interrupt is re-enabled.

Regards,
Mac

Message Edited by bigmac on 03-09-200610:26 PM

0 Kudos

3,576 Views
rocco
Senior Contributor II
bigmac wrote:

Here is another quite simple possibility, that appears to avoid the "creep" issue. The idea is similar to the first proposal by Mike, where an interrupt only occurs on Channel A, and Channel B is monitored during the ISR. The difference is that the edge on which the next interrupt will occur is toggled, before exiting the ISR.



OK, I LIKE that approach. It would work. It does require that the ISR toggles the edge-sensitivity before the edge can change, but a filter cap could do that. Unfortunately (only for me) the HC08GP32's IRQ pin doesn't allow you to detect rising edges.

Just ignoring the LSB of the counter doesn't always help to keep the output from jittering. What if the count is between 0x0FF and 0x100? The way I eliminate the jitter is to keep two counters: The real one (internal to the ISR) and a dead-banded one (public to the other routines). I don't update the dead-banded one until it is different from the real one by two or more. There may be an easier way. If there is, please post it.
0 Kudos

3,576 Views
bigmac
Specialist III
Hello Rocco & Trevor,
 
Quote:
... It does require that the ISR toggles the edge-sensitivity before the edge can change, but a filter cap could do that. Unfortunately (only for me) the HC08GP32's IRQ pin doesn't allow you to detect rising edges.
 
For many of the HC(S)08 products, you may need to consume a timer channel (input capture) for the channel A input, to allow toggling the active interrupt edge.  Another possibility on some MCUs may be the keyboard interrupt, where edge selection is provided.  While I am not familiar with the Coldfire series, I would be most surprised if at least one of these methods were not available.
 
If the input can change again, within the ISR, this would not necessarily be a problem provided the interrupt flag is cleared just prior to leaving the ISR.  However, if this is due to switch bounce, disabling the interrupt for a de-bounce period should work.  Obviously, you would need to make sure that the interrupt flag is again cleared just prior to re-enabling the interrupt.
 
You are absolutely right about the jitter - I hadn't really given sufficient thought to that one.  What really needs to be done is to implement hysteresis of one-half cycle.  This turns out to be very simple - if the interrupt is due to a change of direction, still toggle the interrupt edge, but don't increment/decrement the counter.  So the pseudo-code might be modified thus -
 

if (A_edge == 1)
   if (Ch_B == 1) Count++;
else
   if (Ch_B == 1) Count--;
Toggle A_edge;
Clear interrupt flag;

Regards,
Mac

 

 

0 Kudos

3,576 Views
airswit
Contributor III
well, i did some more digging (reading of the 550page datasheet), and found out that the interrupt pins can be edge triggered. now, should i just trigger on 1 edge (the positive edge) or both positive and negative edges?

now i am thinking to have the a channel on an interrupt pin and trigger on the positive edge. inside the isr I will test the B channel (on a regular GPI) and adjust my count accordingly.

as a side question, if i want to create a button interrupt (for user input, or whatever), and I trigger on the edge, can i leave off debounce circuitry, or is it still necessary to put a filter capacitor on the line?
0 Kudos

3,576 Views
imajeff
Contributor III

airswit wrote:
well, i did some more digging (reading of the 550page datasheet), and found out that the interrupt pins can be edge triggered. now, should i just trigger on 1 edge (the positive edge) or both positive and negative edges?

now i am thinking to have the a channel on an interrupt pin and trigger on the positive edge. inside the isr I will test the B channel (on a regular GPI) and adjust my count accordingly.

as a side question, if i want to create a button interrupt (for user input, or whatever), and I trigger on the edge, can i leave off debounce circuitry, or is it still necessary to put a filter capacitor on the line?




1. You might want to trigger on both edges because of "creep" which was mentioned before. If the rotary happens to stop close to A rising, and that triggers an interrupt, then it may keep bouncing between A rising and falling (while B remains). Since your ISR would not detect A falling, it would assume it was continuing to turn in one direction. When it happened to me, it was sudden full volume, not even a slow creeping. Detent could help avoid the problem, but has not been reliable from my experience.

2. The solution for debouncing a button is rather opposite of *faster* response. If not done right, your ISR could actually capture many more bounces than polling without ISR. Frankly, there was never a need for added debounce circuitry, but you would need software to limit the response. In other words, I would use a timer (maybe 50 mS) so that the ISR is disabled (ignored) for that long and then enabled again.

The thing I dislike about calling the ISR every bounce and then determining whether to react is that ISR takes time away from other tasks, so it's better to not even call the ISR during time when you don't expect the button to be pressed again.
0 Kudos

3,577 Views
bigmac
Specialist III
Hello Trevor,
 
Quote:
Now, should i just trigger on 1 edge (the positive edge) or both positive and negative edges?

now i am thinking to have the a channel on an interrupt pin and trigger on the positive edge. inside the isr I will test the B channel (on a regular GPI) and adjust my count accordingly.
The method that I was proposing in my previous post would require that the interrupt pin be sensitive to either positive or negative edge of channel A signal, but not both at the same time.  I presume there is a control bit available in the MCU to select either positive edge or negative edge.  So each time the ISR is entered, this control bit would need to be toggled.
 
For the last variation posted, with the hysteresis, the counter would now increment on positive edges only, and decrement on negative edges only (with opposite rotation).  So the counter will change once per Channel A cycle (not twice), and if you are using an encoder with detents, you would need to check for one detent per cycle (not per half cycle).
As a side question, if i want to create a button interrupt (for user input, or whatever), and I trigger on the edge, can i leave off debounce circuitry, or is it still necessary to put a filter capacitor on the line?
Debounce arrangements would be required to prevent multiple interrupts from occurring (assuming use of keyboard interrupt, or similar) for a single pushbutton event.  I would generally consider the use of a simple capacitive filter inappropriate for this purpose.
 
A better method, as mentioned previously, would be to provide a timeout period of typically 20-50 milliseconds (using one of the timers).  Before exiting from the ISR, further interrupts for that particular input would be disabled, and the timer started.  Then from within the main program loop, the timer would be periodically examined for timeout.  When this is detected, the interrupt flag would be cleared, and the interrupt re-enabled for further pushbutton events.
 
This technique would also be necessary for the rotary encoder, if you decide to use mechanical switching (rather than an optical type).
 
Regards,
Mac
 
 
 
0 Kudos

3,577 Views
SteveRussell
Contributor III

Mac and Trevor,

 

I think that the timer method Mac proposes is more complicated than needed.

Sampling the inputs on a timer interrupt and applying one of the debounce methods discussed in the 01-26-2006 12:05 PM part of the "Switch Debounce Software" topic in this forum.

In particular the simplest method, which is just sampling at an interval longer than the bounce period should work pretty well for a human interface device.

If you sample at a 1 ms to 10 ms interval and do the decoding by comparing with the last state, you will be able to get velocity information easily. 

I don't know the details of the encoder, but most contact arrangements seem to bounce for less than 10 ms.  

Some systems of my experience don't promise an absolute dial position, but just take the dial motion as the indication of the velocity to move the indicator.  This has the advantage that missing counts at high velocity are not noticed by the user.

     Steve Russell

    Nohau Emulators

 

 

0 Kudos

3,577 Views
bigmac
Specialist III

Hello Steve,

I agree that the sampling method is potentially simpler and will provide integral de-bounce, but for the rotary encoder decoding it may not be adequate, depending on the type of encoder.  In particular, there would be no protection against "creep", as previously discussed in the thread.

I think it would be valid to summarize the situation as follows:

  • Optical encoder without detents -
    Could be subject to creep - interrupt method required, de-bounce not necessary.
  • Optical encoder with detents -
    Not subject to creep - either method may be used, de-bounce not an issue.
  • Mechanical switching encoder without detents -
    Could be subject to creep - interrupt method required, de-bounce required.
  • Mechanical switching encoder with detents -
    Not subject to creep - sampling or interrupt method, de-bounce required.

You are probably correct about bounce being typically less than 10 milliseconds, and minimising this is probably more important for the rotary encoder applications.  However, I do not know how much unit-to-unit variation there is, and how the switch is affected as it ages, so I think it better not to "skimp" too much.  Yes, I agree that a rotary encoder of this type cannot, in itself, provide absolute position control.

For individual pushbutton switch operation, I concur that the sampling method would be satisfactory, unless the operation of the switch requires the MCU to wake up from stop mode.  Perhaps I was partly reacting to other theads within this forum, where attempts have been made to utilize the KBI module, wihout proper debounce measures.

Regards,
Mac

 

3,577 Views
rocco
Senior Contributor II

bigmac wrote:

I agree that the sampling method is potentially simpler and will provide integral de-bounce, but for the rotary encoder decoding it may not be adequate, depending on the type of encoder. In particular, there would be no protection against "creep", as previously discussed in the thread.


Actually, the sampling method that I use has no creep and misses no transitions. But my debounce algorithm is not that simple.

First of all, I sample faster than the debounce period of my encoders and switches (the same routine does them all). My slowest switch is specified at 5 milliseconds, so I sample every 500 microseconds. My scheduler calls the debounce task when requested by a timer interrupt. I maintain a debounce counter, the last read state and the last reported state for each pin being debounced. That adds up to 16 bytes of ram for 16 input pins, plus a byte for an image of each i/o port.
It goes like this:

At each sample, I read the two ports, and XOR it with the value of the two ports saved from the previous sample. For any bit that has changed, I set its debounce counter to the debounce constant (for 5 milliseconds at 500 uSeconds-per-sample, I use 10).

I then decrement any debounce counter that is not already zero. When an input has been stable for the full 5 milliseconds, its debounce counter becomes zero, and I process the transition.

It's possible that the transition was just noise or jitter. So I then compare the new debounced state with the last-reported state, and discard the transition if they are the same. If they are not the same, I send a switch-event message to the routine that processes them.
A disadvantage of this approach is that there is no easy way to extract velocity data (that my meager brain can come up with on a Sunday).

Some advantages of this approach:
1) The overhead is low: about 30 microseconds every 500 microseconds on an 8MHz HC08.
2) If the jitter occurs in under 5 milliseconds, both edges are discarded. If the jitter is over 5 milliseconds, both edges are counted. So no counts are lost and there is no creep.
3) All encoder edges are counted, giving you x4 quadrature decode.

Additional advantages, not related to the encoder:
1) Other switches and encoders are debounced at the same time.
2) The routine also detects when a button has been held down for a predefined period (I use 1 second).
3) The routine can auto-repeat the switch after that hold-down time, at a predefined rate (I use 8-per-second).
4) It also reports when a switch has been released.

I will try to post the code later today, but it is written for HC08 (sorry).

Message Edited by rocco on 03-12-200603:14 PM

0 Kudos

3,577 Views
rocco
Senior Contributor II
I was hoping to have time to review this before posting, but that's not going to happen soon. So here it is. If it's missing anything or if there are any questions, you know where I am.

BTW: It's in HC08 assembler. It is not (as far as I know) specific to any particular family member.
0 Kudos

3,577 Views
SteveRussell
Contributor III
Mac,
 
I think that putting creep protection in the period sampling method is not too hard.
 
First, assume that the sampling period is longer than the bounce time and shorter than the time between bit transitions that you want to measure accurately.
 
Second assume that there is only one contact bouncing at a time.
 
If you save the previous sampling of the two input bits and compare them with the current sample, you should see no difference if there is no motion, and only one bit difference if there is motion or bounce.
 
So you may have one bit wrong when you sample during the bounce, but the by the next sample the bit will be correct.  The effect of this is to delay the detection of the change by one sample time, but no creep will result, as long as only one contact is bouncing.
 
If both bits have changed from the previous state, you really don't know what's going on.  It could be electronic noise, both contacts bouncing because the shaft is being turned fast or the input actually changing a couple of times between samples.
 
However, if you detect this a good guess it that the shaft is turning in the same direction as the last turn and going at the maximum speed.
 
This will result in creep, all right, but if there are no calibration marks on the knob on the shaft, a human twisting the knob probably won't notice that the velocity deduced from the successive positions isn't right.
 
So if you use the "angular velocity" of the shaft to move a display, the user probably won't notice.  When the user slows down to accurately position a display, the bit rate will be low and the deduced velocity will be 0, +1 or -1 per period, and the display will appear to position accurately.
 
My back of the envelope calculations suggest that this method will be fast enough for a human-operated knob, but not fast enough for a motor shaft or high speed driving wheels.  It might work for large slow driving wheels.
 
    Steve Russell
    Nohau Emulators
 
0 Kudos

3,577 Views
airswit
Contributor III
an edge trigger would be ideal, but i don't have that option on my processor (if i am wrong on this, someone please tell me). I will likely have detents, which i understand to mean that it 'stops' at certain states, like most mouse wheels do.
Right now, I am planning on doing it like this: the A output will be hooked to an interrupt pin, while the B is hooked to a GPI pin. in the ISR, I will sample B, and if it is high, I will decrement. if it is low, I will increment.

Will this be sufficient for a very basic up/down 'switch', or are there other considerations? I am not sure if I will be using a pre-made encoder, or making my own (I am thinking that, because it will cost me about $0.50 for the electronics). In my design, I will not be implementing acceleration or velocity calculations, so i just need the bare minimum for a up/down control.

Thanks for all your help so far, though I have no experience with this type of input, it is interesting to know that there are many very different approaches to this problem! Thanks for all your feedback

Oh, and to rocco, you can attach code by the 'attachment' form on the reply page, or you can email it to me at airswit@aol.com if you don't want to post it here. Thanks in advance!
0 Kudos

3,577 Views
mke_et
Contributor IV

If it were me, I'd just use two pins on the processor and make sure you don't run over limit, like trying to feed 20v to them.  Make sure one is an interrupt. 

As to amplifiers or whatever, usually the rotary devices will already have signal shaping components in place and you can usually just tie them to input pins (a pullup MAY be required).  If you're talking direct from some kind of photodetector, then yes, you should have an amplifier of some kind with a schmidt trigger buffer in the way.

If you want a SIMPLE interface, just set the interrupt to be in one direction, and in the ISR if the other pin is low it's one direction, if it's high it means the other direction.  Use the direction to inc or dec a 'position' counter.  Then on a timer tick throw out low position count values to keep from 'creeping'.  Or use a 'double pass' routine where one routine passes the count to another with a limit based on time.

If your knob has no detents, and you can support it, use interrupts in BOTH directions on the pin.  (I don't know your processor)  Even if it means you need THREE pins. When you get a low-going interrupt, do the processing as before.  When you get a high interrupt, however the inc or dec will be opposite.  (Depending on interrupt direction).  If you do the interrupt in both directions, then you shouldn't get ANY creep on your position, even if you're sitting right on a boundary.

Actually, I didn't think.  If you just use 2 int pins...  Call them pin A and pin B.  Int on the A pin, get pin B state and if 0  dec your count, 1 means inc.  Int on the B pin and read pin A.  0 means inc and 1 means dec your count.  Sorry, had a brain fade.

Smart use of the timer in the routine can even give you 'acceleration' on your 'knob'.  Like 'fast tuning' on a radio if you spin the knob.

Message Edited by mke_et on 03-07-200604:50 PM

0 Kudos