Rotary encodersA rotary encoder is a sensor that provides info about mechanical position. In this case I am interested in knowing the angular position of the pendulum links. They come in a number of flavors: contact, magnetic and optical. I am interested in optical, "transmissive" (light shines through as opposed to being reflected), incremental encoders. A pretty good FAQ about rotary encoders is provided by Avago.ModelsThere appear to be a lot of different rotary encoders out there. The price range I have been looking at is around $30-$50. US Digital has a number of items and a conversion chart. However it appears more straightforward to go with the Avago parts, specifically the HEDS-5500-C06 (datasheet - vendor) since they are slightly cheaper and appear to have better documentation.HEDS-5500The supply voltage required is 5 V with allowable ripple of 100 mV-pp. Pulses have peak at 2.4 V and trough at 0.4 V nominally. The pinout for the device is as follows (Pin 1 is to the left when looking through the spyhole of the shroud):
Links
Quadrature DecodersIncremental transmissive rotary encoders often spit out signals in quadrature. This is essentially two square waves 90° out of phase giving four (hence the quad) distinct states. See picture below, ripped off from wikipedia. The cool thing here is that this allows for 4x resolution compared to the actual number of divisions of the encoder wheel. This also poses somewhat of a hassle as it requires additional methods to decode the quadrature signal.![]() You have two options to "decode" the quadrature signal, either through a software algorithm or through a dedicated IC. Software is probably the path of least resistance, but it has the detriment of adding additional load to the processor. Algorithms specific to the Arduino include:
Quadrature IC - LS7366R
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| IC Pin |
IC Pin Info |
Connection |
| 1 | fCKO - filter clock output |
Arduino - XTAL2 (#9) |
| 2 | fCKI - filter clock input |
Arduino - XTAL1 (#10) |
| 3 | Vss - V- |
Arduino - GND |
| 4 | SS/ - slave select |
Arduino Jumper 7 |
| 5 | SCK - serial clock |
Arduino Jumper 13 |
| 6 | MISO - output to CPU |
Arduino Jumper 12 |
| 7 | MOSI - input from CPU |
Arduino Jumper 11 |
| 8 | DFLAG/ - latched flag |
|
| 9 | LFLAG/ - push/pull flag |
|
| 10 | INDEX/ - reset count |
|
| 11 | B - quadrature input |
HEDS5500 #5 |
| 12 | A - quadrature input |
HEDS5500 #3 |
| 13 | CNT_EN - enable count |
|
| 14 | Vdd - V+ |
Arduino - 5V+ |
I am using the Arduino's oscillator (running at 16 MHz) to drive fCKI. Since the Arduino Jumper 8 (IC#14=CLKO) is not available for getting the clock signal, it requires hacking some leads directly to the IC to siphon off the clock from terminals 9 & 10.
For the digital filters to work the timing signal to fCKI must obey Fquad<fCKI/4. Setting a bit on the internal register of the LS7366 sets the divisor of the clock signal to one (MDR0 <B7>=0) giving my upper limit 16/4/1=4 MHz. The motor can spin at a max of 7000 rpm and 4x quadrature gives 400 CPR, so 400x7000/60=~47kHz and I am well under my 4 MHz limit.
The keywords for the control registers are mapped out in a header file located somewhere like ../hardware/tools/avr/avr/include/avr/iom328p.h in the Arduino software installation directory. The LS7366 appears to operate in SPI mode 0, so the settings required for the Arduino control register are as follows: SPCR = 0101 0000
| SPCR | ||
|---|---|---|
| 7 | 0 | Disable SPI Interupt |
| 6 | 1 | Enable SPI |
| 5 | 0 | Most significant bit first |
| 4 | 1 | Master |
| 3 | 0 | Idle when low |
| 2 | 0 | Sample on rising edge |
| 1 | 0 | 4 MHz com speed |
| 0 | 0 | 4 MHz com speed |
Similarly, there are two control registers on the LS7366 that are used to setup the chip. The first control register is for the initial operation of the chip: MDR0 = 0000 0011
| MDR0 | ||
|---|---|---|
| 7 | 0 | Filter divisor = 1 |
| 6 | 0 | Asynchronous index |
| 5 | 0 | Disable index |
| 4 | 0 | Disable index |
| 3 | 0 | Free running count |
| 2 | 0 | Free running count |
| 1 | 1 | 4x quadrature count |
| 0 | 1 | 4x quadrature count |
The second control register allows for further options for the operation of the chip: MDR1 = 0000 0011
| MDR1 | ||
|---|---|---|
| 7 | 0 | No operation |
| 6 | 0 | No operation |
| 5 | 0 | No operation |
| 4 | 0 | No operation |
| 3 | 0 | Not used |
| 2 | 0 | Enable counting |
| 1 | 1 | 1-byte counter mode |
| 0 | 1 | 1-byte counter mode |
The Arduino and LS7366 should now be setup. To check the status of the LS7366, we can poll the status register STR, which has the following layout:
| 7 | 6 |
5 | 4 | 3 | 2 | 1 | 0 |
| CY | BW | CMP | IDX | CEN | PLS | U/D | S |
- CY - Carry (CNTR overflow) latch
- BW - Borrow (CNTR underflow) latch
- CMP - Compare (CNTR = DTR) latch
- IDX - Index latch
- CEN - Count enable status (Enabled=1, Disabled=0)
- PLS - Power loss indicator
- U/D - Count direction (Up=1, Down=0)
- S - Sign bit (Negative=1, Positive=0)
| 7 | 6 |
5 | 4 | 3 | 2 | 1 | 0 |
| Operations | Selected register | Not used | |||||
The operations can have the following values:
| 7 | 6 | Operations |
| 0 | 0 | Clear register |
| 0 | 1 | Read register |
| 1 | 0 | Write register |
| 1 | 1 | Load register |
And the registers are defined by:
| 5 | 4 | 3 | Register |
| 0 | 0 | 0 | None |
| 0 | 0 | 1 | MDR0 |
| 0 | 1 | 0 | MDR1 |
| 0 | 1 | 1 | DTR |
| 1 | 0 | 0 | CNTR |
| 1 | 0 | 1 | OTR |
| 1 | 1 | 0 | STR |
| 1 | 1 | 1 | None |
This leads to some useful op-codes that will be employed for setup, operation and status:
| Binary (MSB) |
Dec. |
Operation |
| 00 001 000 |
8 |
Clear CNTR to zero |
| 00 110 000 |
48 |
Clear STR to zero |
| 01 100 000 |
96 |
Read CNTR |
| 01 110 000 |
112 |
Read STR |
| 10 001 000 |
136 |
Write to MDR0 |
| 10 010 000 |
144 |
Write to MDR1 |
Software
The necessary code in the Arduino developer environment follows a similar project for implementing EEPROM. The first step is to define some constants for the necessary port assignments and op-codes that will be used for manipulating the quadrature decoder:For the counter in 1-byte mode, status register clear on overflow and free-running there are the following possible cases for counting:
| n |
n+1 |
delta |
DIR |
SIGN | Overflow |
Underflow |
| 235 | 245 |
10 |
up |
pos |
||
| 245 |
255 | 10 |
up |
pos |
X |
|
| 255 |
20 |
21 |
up |
pos |
X |
|
| 20 |
10 |
-10 |
down |
pos |
||
| 10 |
0 |
-10 |
down |
pos |
X |
|
| 0 |
246 |
-10 |
down |
neg |
X |
|
| 246 |
236 |
-10 |
down |
neg |
X |
|
| 236 |
246 |
10 |
up |
neg |
X |
|
| 246 |
255 |
9 |
up |
neg |
X |
X |
| 255 | 9 |
10 |
up |
pos |
X |
Need to make a decision tree about the new number of counts and determine if it should be adjusted:
if pos
if up
if new > old //evaluated as if they were signed values delta = new - old
else
delta = new + maxSize - old //Overflow condition
end
else //moving down
if new > old //Can this happen?
else
delta = new - old
end
end
else //neg
if up
if new > old //Can this happen?
else
delta = old - new
end
else //moving down
if new > old
delta = old - new
else //underflow condition
delta = -old - (maxsize + new)
end end
end
Assuming that now there is a reset on both overflow and underflow the actual "change" in encoder counts can be determined with the following code:
