In many panel units rotary encoders are used.

The type we are using has one central contact and 2 make/break contacts. If turned the A and B contact produce a 2 bits Gray Code. (see picture). If signal A and B are read as binary, where A is least significant bit, the 2 bit Gray code can be seen in the picture. Wave form A and B are shifted 90 degrees depending on the direction of rotation.

Rotary encoders can be have detents (holding positions of the shaft).

Detents can be placed on several ways:

• F – Full cycle. The detent is after every full cycle of both signals A and B
• H – Half cycle. The detent is on every position where both signals are equal.
• Q – Quarter cycle. There is a detent after every signal change A or B

Normaly we want one count per detent. The (micro)code to decode the Gray Code signal is slightly different depending on the position of the detents.

Rotary encoders are specified by 2 parameters:

• Number of pulses per rotation.
• Number of detents.

The number of pulses is the number of pulses from one contact. (A of B) for one full rotation.
If an encoder is specified having e.g. 20 pulses per rotation and 20 detents, the position of the detents is according to F (After every Full Cycle: 00-01-11-10-00)

This type will always detent (rest) at the 00 (open state). If encoders are used in a matrix scanning setup, this is a must otherwise on or the other contact will always be on.

Specified: 4 pulses/360 degrees and 16 detents, then the detents are according to Q (Quarter Cycle)

When an encoder has a high number of  pulses/360, the resulting pulses (when turned fast) are very short. The microcode should be able to handle that. Microcode should either be bases on interrupts from these signals or, when polling is used, have a high input polling frequency.

Let’s assume we have an encoder with specification: 20/360 and 20 detents.
Let’s assume that an encoder can be turned as at a rate of 5 revolution/sec. This will cause 100 pulses/input/sec. As we have to monitor 2 signals, this comes down to 200 signal changes per second. This means a signal change every 5mS

In order to be reliable detect all signal changes the probe frequency should be at least double that so 400 probes/second. Sure microcomputers can do that easily, but that rate should be maintained regardless of other tasks the processor is performing.

For our FSIO project, rotary encoders are on a chain of inputs with other buttons. This will limit the poll frequency of the inputs. At present we poll at a rate of 2mS.

We have done some testing with 20/20 rotary encoders. It works ok if dialed normal. In panic, fast yanking of the decoder, loses a noticeable amount of counts.

We would prefer to use encoders with less pulses. If you cannot find an encoder with less then 20 pulses, go for it.

Although the decode of the CTS 288 decoders with 4/16, 6/24, 32/8, 40/10 and 48/12 (Q) is slightly more complicated,  it delivers rock solid reliable counts.

## Rotary Encoder Configuration and Handling

Rotary encoders are handled by the firmware of the microprocessor. They can be connected to any 2 consecutive inputs of any input chip in the chain.

Below is how a rotary encoder is specified in the SIOC script:

`Var vvvv, name Rot1, Link IOCARD_ENCODER, Device dd, Input ii, Aceleration n, Type n`
• Although in FSIO all inputs are on the same microprocessor, we need to specify IOCARD_ENCODER so we know it’s an encoder and not a switch.
• The least significant input is specified as ‘Input’. The rotary encoder is connected to input ii and ii+1.
• Aceleration (spelling with one c, feature of SIOC). This is the amount that IOControl will send per pulse if IOControl detects sustained fast movement. see Note 1)
• Type 1: Quarter Cycle, 2: Half Cycle, 3: Full cycle inbetween detents.
or you can use:
Type Q: Quarter Cycle, H: Half Cycle, 3F: Full cycle inbetween detents.

In order to successfully decode the encoders, the microcode needs to know on what inputs the rotary encoders are connected and their types.

For software developers:
At initialization IOControl will send information about the ‘input positions’ and ‘type’ of the rotary encoders to the micro processor. This information is send in two 32 bytes buffers matching the 32 byte input chips.

• The first buffer will hold a 1 in bit positions that match the inputs where rotary encoders are connected.
• The second buffer has information about the encoder type (01, 10 or 11) in the 2 bit positions that match the bit positions of the encoder connections.

Default, if no Type is specified IOControl will assume Q type encoder. If the actual encoder is H or F, this will result in 2 or 4 pulses per detent.

Note 1: This parameters is different from the opencockpit defenition. Aceleration in Opencockpits is defined as ‘counts per click’.
FSIO implementation is different. FSIO’s acceleration is dynamic, meaning that the number of counts per click will be set to the Aceleration value if the code detects fast repetitive turning of the encoder.

# Special type of Rotary decoder

In some panels multy positon switches are used (e.g EFIS range). In Opencockpits this type of switch is connected to multiple input’s. Each of these inputs has to be configured its own Variable in SIOC. The script will then have to deal with that.

There is nothing against doing so with FSIO panel cards.

However. in FSIO we implemented a special rotary encoder type for these multyposition switches.

Look at the following line:

`Var 0400, Name Range,Link IOCARD_ENCODER, Device 10, Input 56, Aceleration 8, Type S`

This configures a muli position switch connected to inputs 56 -63. (The connections need to be made to consecutive input pins). If the switch is positioned on pin 56 it will send 1 to variable 400, if switched to position 57 it will send 2 to variable 400 end so on.

To save input pins one could leave out the first pin of the multi posion switch as this will leave the variable with 0 value.

In the statement above we ‘misuse’ the acceleration parameter for number of inputs.

An other example for the VOR/ADF select switch.

`Var 0401, Name Vor_Adf_Select_1, IOCARD_ENCODER, Device 10, Input 24, Aceleration 2, Type S`

The 3 position switch is connected to inputs 24 and 25. If set to 24 a 1 will be send to variable 24, if set in the middle (no connection) a 0 will be send to var 24 and if switched to input 25 a 2 will be set to var 24.
Thus, Var 24 will hold the position of this switch as one variable that van be 0,1, or 2.

If wired correctly with this special statement one can simplify the script for these switches.

# My Code to decode the rotary encoder

Below is a snipped from the code that I developed for FSIO rotary encoders. The code is simple
For the code to understand you need to understand above and this:
• The present values of the 2 input bits from the rotary encoders is stored in ‘now’ least significant bits. ( 0b000000XX )
• To understand the comment; I call bit 0 the reference bit and bit 1 the signal bit.
• The code assumes that you scan the input bits at the same time, or scan both bits before entering the code.
• The previous read of these bits are stored in ‘prev’ ( 0b000000pp)
• ‘EncoderTp’ will hold the decoder type (1,2 or 3)
• The code has a section for eacht encoder type. If you know your type you just need to copy the appropriate part between case: and break;  and forget about the switch() statement.
• The result +1 or -1 will be in ‘Button pressed’.
```.
.
.
.
//
// From here the encoder values will be evaluated according to type
// bits from input will be in: byte now;
//

switch (EncoderTp)
{
case 1:
// The decoder has a mechanical stop at every change Q cycle
// Ref=Bit0 Signal=Bit1
// Ref _/ and signal \_ or Ref \_ and signal _/ = Positive
// Ref _/ and signal _/ or Ref \_ and signal \_ = Negative
//
if ( ((now ^ prev) & 0X03) != 0) // Did one of the bits change?
{
//YES it did, so we check how the signal bit changed
if ( (((now << 1)^ prev) & 0X02 ) == 2)
ButtonPressed = 1;
else
ButtonPressed = -1;
}
break;

case 2:
// The routine below is for encoders with stops at every half signal cycle
// For this decoder type we take one of the signals as reference
// We detect a pulse if the reference signal changes

if ( ((now ^ prev) & 0X01) != 0) // Did the ref bit change?
{
if ( ((now & 0X02) >> 1) == (now & 0X01)) // Both bits equal ?
ButtonPressed = -1; // Yes, counter clockwise
else
ButtonPressed = 1; // No, Clockwise
}

break;

case 3:
// Encoder type = 3
// This routine is for the encoders that have a detent after every
// full cycle of both signals
// From one stop to the other we get A - AB - B, or B - AB - A
// 11 - 01 - 00 - 10 - 11
// 11 - 10 - 00 - 01 - 11
// We check for AB, if AB then the previous state gives direction
//
if (((now ^ prev) & 0X03) != 0) // Did one of the signals change
{
if (now == 0) // Yes, is it middle (AB) position ?
{
if (prev == 1 ) ButtonPressed = -1;
if (prev == 2 ) ButtonPressed =  1;
}
}
break;
}

prev = now;
.
.
.
.
.```

### 1 Response to About Rotary Encoders

1. Pingback: Quadrature encoder wisdom | Tahium