Sensors

Rotary encoders

A 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.

Models

There 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-5500

The 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):
 Pin #
 Info
 1  Ground
 2  NC
 3  Channel A
 4  Vcc
 5  Channel B
The C06 encoder disk has a 1/4" shaft and a resolution of 100 CPR (counts-per-revolution).

Links

Quadrature Decoders

Incremental 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:
There are a number of ICs that will take the quadrature signal and turn it into counts.  This is pretty cool, because it means that you can get very high fidelity encoding of position with almost no processing cost.

Quadrature IC - LS7366R

Hardware

I am implementing the LSI/CSI LS7366R (vendor) and using the SPI 4-wire communication protocol.  Example implementations of this chip can be found on a JHU class website and on this blog.

Connections between the IC and the various components are in the following table:

 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)
To make the LS7366 do something we have to send instructions to the IR register which has the following 8-bit structure:

 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 100 000
32  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:

Definitions

//Arduino pin setup 
#define SLAVESELECT 7//ss
#define DATAOUT 11//MOSI
#define DATAIN  12//MISO
#define SPICLOCK  13//sck
//LS7366R OP-Codes
#define CLEAR_COUNTER 32
#define CLEAR_STATUS 48
#define READ_COUNTER 96
#define READ_STATUS 112
#define WRITE_MODE0 136
#define WRITE_MODE1 144

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:

Change in encoder counts

int delta; //change in encoder counts
int maxSize = 256;
unsigned int old=0;
unsigned int count = encoder.readCounter();
byte count_status = encoder.readStatus();
bool isOverflow =
count_status & B10000000;
bool isUnderflow = count_status & B01000000;

delta = count - old;
if(isUnderflow)
{
    if(count>0)
    {
        delta -=
maxSize;
    }
}
if(isOverflow)
{
    if(count<255)
    {
        delta += maxSize;
    }
}
old = count;

An arduino library for interfacing with the LS7366R can be found here.

ċ
Encoder.zip
(7k)
Jeff Bingham,
Dec 22, 2013, 3:24 PM
Ċ
Jeff Bingham,
Jun 20, 2009, 5:57 PM
Ċ
Jeff Bingham,
Jun 20, 2009, 6:17 PM
Ċ
LS7166.pdf
(188k)
Jeff Bingham,
Jun 20, 2009, 6:40 PM
ċ
LS7366R.pdf
(227k)
Jeff Bingham,
Jul 29, 2009, 11:09 AM
ċ
arduino-quadrature-0.90.zip
(7k)
Jeff Bingham,
Jun 21, 2009, 8:52 AM
Ċ
Jeff Bingham,
Jun 20, 2009, 5:21 PM
Comments