Experiments with Rotary Encoders

Rotary encoders can be used similarly to potentiometers. The difference being that an encoder has full rotation without limits (It just goes round and round). They output gray code so that you can tell how much and in which direction the encoder has been turned. They’re great for navigating menu screens and things like that. (Core Electronics )

Trying one of these for a radio project – 3 rotary encoder pins only

I could not get it to work with the software discussed (there seem to be a lot of solutions around!)

With experimentation I found there were a number of issues

Pinouts

The datasheet for the device I started with did not have a pinout though I did find it elsewhere in the device description. The common pattern appears to be 3 pins – the centre pin common and the end pins A and B

However this device had one end pin as common and the other two as A and B.

Check the datasheet! (In this case it was incorrect)

A second device I tried had the usual pinout.

Mode of operation.

The datasheet (in part) for the first device I had showed that the output should be a Quadrature Gray code with rotation of the encoder.

But between detents, A and B were not connected to ground and were high due to a pullup I had installed If I watched the output on a CRO, I get a brief transition Hi=>Lo with each detent of the encoder of about 30msec for the A pin. Both pins are otherwise HIGH all the time. The B pin goes low immediately for CCW rotation – there did not appear to be a delay between A and B lines (as shown on the oscilloscope). With the typical noise requiring debouncing this could be an issue.

CRO Tek MDO3014 capturing one encoder movement detent to detent

For CW rotation the Hi=> Lo transition of the B pin is delayed by about 10-15msec

Mechanically this device was a switch with contacts between ground and each line as the device was rotated with a full momentary Gray code sequence between each detent.

I tried a second rotary encoder from Jaycar
The display below shows the output when rotating this rapidly
Screenshot (ignore the frequency readout )

The two seem to be roughly in quadrature – but with this device the lines stayed both high or both low between detents – ie a half Gray Code step per detent.

There was an interesting discussion on the Arduino Forum at at Purchased my first Rotary Encoders. faulty or not? – #10 by system – General Electronics – Arduino Forum
Encoders were behaving like the one I have described above – ie at each detent both switches open circuit but making brief contact between detents – these appear to follow a Gray Code sequence between detents but are open circuit at “rest” – presumably to save pullup current

Without fairly sophisticated test gear and knowledge a noob will not be able to work all this out
So it appears there are several Variants
(1) Different Pinouts
(2) Transient make/break between detents
(3) Both pins changing state between detents – they do seem to follow a Gray code in between – a half step per detent (the JayCar decoder)
(4) Encoders generating pulses rather than a true “Gray Code” sequence as in the ones above – this may a poor quality or faulty encoder.

And then the Software…

In practice the software will probably handle most of these variations.

The first problem to deal with is switch “bounce”. A typical mechanical contact may take 20msec or more to settle as it makes contact – there may be many on/off transitions as it does so. In this case the encoder continues to rotate – there may be more switch noise as it does so. Of course , a microcontroller is fast enough to see these transitions and may interpret them as “true” data.

There are various approaches to this problem

A Hardware Filter such as a lowpass RC network can be installed on the encoder data lines. This has the disadvantage of additional components and will slow the encoder response. A Delay in software will save the components but still has the problem of slowing the system.

The most durable approach appears to be a State Machine in software

A change in one of the data lines triggers an interrupt which reads the data lines

The routine then checks whether the transition is a valid Gray code sequence and then whether it is clockwise or anticlockwise – each step effectively filters invalid data transitions. This routine appears to work well. It is fast and appears to tolerate various decoders without error

Here is code for such a routine in C++ for the Arduino Uno

// Rotary encoder variables
// Indicates status of last ISR
volatile unsigned char state; 
  
// Lookup table whether transition is valid Gray Code
uint8_t rot_enc_table[] = {0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0};

/* ISR (Interrupt service routine) 
* for Rotary encoder
* for the Arduino must have no parameters
*  and no return
* Cannot call Class members it seems
*/
void encoder(void){

    // left shift last prevNextCode 2 bits
    prevNextCode <<= 2;

   // read in current state of input pins
   // to prevNextCode bits 1 and 0
    if (digitalRead(rotA)) prevNextCode |= 0x02;
    if (digitalRead(rotB)) prevNextCode |= 0x01;

    /* Mask off upper 4 bits
    * prevNext Code now holds
    * current and previous state of the input pins
    * in its lower 4 bits 
    */
    prevNextCode &= 0x0f;

    /* Use this as a key to rot_enc_table
    * valid transitions between this state
    *  and last will return 1  - invalid 0 
    * according to a Gray Code sequence
    * If valid then store as 8 bit data in store
    * store now holds this and the last valid transition
    */
    if  (rot_enc_table[prevNextCode]){
        store <<= 4;
        store |= prevNextCode;

    /* now decide whether the transition was 
    * clockwise or counterclockwise
    * effectively a further "debounce" stage
    * store encodes the last 2 transitions
    * in sequence of a complete cycle from 00 to 11
    * or vice versa
    * for encoders with a half cycle from 00 to 11
    *  and 11 to 00 between detents 
    * If the high bit changed first => CW
    */
        if ((store&0xff)==0xD4 | (store&0xff)==0x2B){
           state = 2;
     }
    //  If the low bit changed first => CCW 
        else if ((store&0xff)==0xE8 | (store&0xff)==0x17){
            state = 1;
   }
    // If no valid transitions return 0
    else{
       state = 0;
    }
  }
}


Author: Richard Hosking

GP Remote Health Music Electronics Amateur Radio

Leave a comment