Quadrature Encoder Interface for motion control with resistance to jitter and chatter on AB signals and motor vibrations

Dependents:   QEIx4_Example realtimeMM_V3 realtimeMM_V3

Quadrature Encoder Interface for Motion Control.

A class to decode pulses on a rotary encoder with AB signals (quadrature encoder). It uses all 4 edges of the AB signals to increase the counter resolution 4 times of cycles per rotation/revolution (CPR) (e.g. an encoder with 500 CPR get 2000 counts per rotation)

In opposite to most common QEI implementation this is resistant to jitter and chatter on AB signals and motor vibrations. When using interrupts (IRQ_NO_JAMMING-mode) only the needed edge and pin is activated to prevent jamming CPU time with unnecessary interrupts. Whes reaching the next position the edge that triggerd this position (state) is ignored to aboid oscillating up/down counts.

It can also be used in polling mode i.g. in idle routines if interrupts are not desired. At this mode be sure that the sampling frequency is heigher than the maximum rotation speed (expeced counts per second)

The internal state machine is based on a look up table (LUT) to minimize interrupt retention time and get all necessary flags at once.

Additional the rotation speed of the encoder can be measured. The algorithm is based on the measuring time between the edges to get a very precise speed at very slow rotation.

The library is designed to support closed loop speed- and motion-controller for also slow and smooth motions like movie camera motion control.

Quadrature Encoder Signals:

/media/uploads/jocis/qeix4.png

(+) Count UP; (-) Count DOWN

QEIx4.cpp

Committer:
jocis
Date:
2014-09-02
Revision:
0:46b8d5680f66
Child:
1:ac6b7b1bf6c5

File content as of revision 0:46b8d5680f66:

#include "QEIx4.h"

// state machine for decoting - don't change!!!
short QEIx4::_modeLUT[16] = { 0, 21, 2, 47, 4, 5, 34, 27, 36, 9, 30, 11, 16, 41, 14, 15 };

// bit masks for state machine - don't change!!!
#define QEIx4_MASK  0xC
#define QEIx4_INC   0x10
#define QEIx4_DEC   0x20
#define QEIx4_CHG   0x30
#define QEIx4_A     1
#define QEIx4_B     2
#define QEIx4_S0    0x0
#define QEIx4_S1    0x4
#define QEIx4_S2    0x8
#define QEIx4_S3    0xC

//#define DEB(x) printf (x)
#define DEB(x)

///////////////////////////////////////////////////////////////////////////////

QEIx4::QEIx4 ( PinName pinA, PinName pinB, PinName pinI, bool bUseIRQ ) : _pinA(pinA), _pinB(pinB), _pinI(pinI)
{
    _bUseIRQ = bUseIRQ;
    _bZeroOnIndex = false;
    _cpr = 500;
    
    // synchronize state machine
    _counter = 0;
    ProcessISR ();
    _mode += QEIx4_S2;
    ProcessISR ();
    _counter = 0;
   
}

///////////////////////////////////////////////////////////////////////////////

QEIx4::~QEIx4()
{
    _pinA.rise ( NULL );
    _pinA.fall ( NULL );
    _pinB.rise ( NULL );
    _pinB.fall ( NULL );
}

///////////////////////////////////////////////////////////////////////////////

void QEIx4::ProcessISR ( void ) 
{
    DEB (".");
    _mode &= QEIx4_MASK;
    if ( _pinA ) _mode |= QEIx4_A;
    if ( _pinB ) _mode |= QEIx4_B;
        
    _mode = _modeLUT[_mode];
    
    if ( _mode & QEIx4_CHG )
    {
        bool bCounterChange = false;
        
        if ( _mode & QEIx4_INC )
        {
            _counter++;
            bCounterChange = true;
        }
        if ( _mode & QEIx4_DEC )
        {
            _counter--;
            bCounterChange = true;
        }
        if ( _bZeroOnIndex && _pinI != NC && _pinI == 1 )
        {
            _counter = 0;
            _bZeroOnIndex = false;
            CounterZero ();
            bCounterChange = true;
        }
            
        if ( _bUseIRQ )
        {
            switch ( _mode & QEIx4_MASK )
            {
            case QEIx4_S0:
                _pinB.rise ( NULL );
                _pinB.fall ( NULL );
                _pinA.rise ( this, &QEIx4::ProcessISR );
                DEB ("S0");
                break;
            case QEIx4_S1:
                _pinA.rise ( NULL );
                _pinA.fall ( NULL );
                _pinB.rise ( this, &QEIx4::ProcessISR );
                DEB ("S1");
                break;
            case QEIx4_S2:
                _pinB.rise ( NULL );
                _pinB.fall ( NULL );
                _pinA.fall ( this, &QEIx4::ProcessISR );
                DEB ("S2");
                break;
            case QEIx4_S3:
                _pinA.rise ( NULL );
                _pinA.fall ( NULL );
                _pinB.fall ( this, &QEIx4::ProcessISR );
                DEB ("S3");
                break;
            }
        }
        
        if ( bCounterChange )
        {
            CounterChange ();
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////