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

Revision:
2:c0b87b11b9cd
Parent:
1:ac6b7b1bf6c5
--- a/QEIx4.h	Tue Sep 30 12:34:07 2014 +0000
+++ b/QEIx4.h	Wed Oct 01 10:23:21 2014 +0000
@@ -33,13 +33,17 @@
 #include "mbed.h"
 #include "FPointer_vi.h"
 
-/** === Quadrature Encoder Interface. ===
+/** Quadrature Encoder Interface for Motion Control.
 *
-* A class to decode pulses on a AB rotary encoder. It uses all 4 edges of the AB signals to increase resolution 4 times of cycles per rotation (CPR) (e.g. an encoder with 500 CPR get 2000 counts per rotation)
+* 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 on AB signals and motor vibration. When using interrupts only (IRQ_NO_JAMMING-mode) the needed edge and pin is activated to prevent jamming CPU time with unnecessary interrupts.
+* 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.
+* 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.
 *
@@ -53,33 +57,61 @@
 #include "mbed.h"
 #include "QEIx4.h"
 
-DigitalOut myled(LED1);
-Timer t;
+DigitalOut LEDalive(LED1);
+DigitalOut LEDzero(LED2);
+DigitalOut LEDup(LED4);
+DigitalOut LEDdown(LED3);
+
+Timer t;   // timer for polling
 
 // ports for nxp LPC 1768
-QEIx4 qei1(p30, p29, p28);          // QEI with index signal for zeroing
-QEIx4 qei2(p27, p26, NC);           // QEI only with AB signals
-QEIx4 qei3(p25, p24, NC, QEIx4::POLLING);    // QEI without interrups for polling mode
+QEIx4 qei1(p30, p29, p28, (QEIx4::EMODE)(QEIx4::IRQ | QEIx4::SPEED));   // QEI with index signal for zeroing
+QEIx4 qei2(p21, p22, NC,  QEIx4::IRQ_NO_JAMMING);                       // QEI with AB signals only
+QEIx4 qei3(p25, p24, NC,  QEIx4::POLLING);                              // QEI without interrups in polling mode
+
+// The callback functions
+void myCounterChangeCallback(int value)
+{
+    static int valueLast=-1;
 
-int main() {
+    if ( value > valueLast ) {
+        LEDup = !LEDup;
+        LEDdown = 0;
+    } else {
+        LEDdown = !LEDdown;
+        LEDup = 0;
+    }
+    valueLast = value;
+}
+
+void myIndexTriggerCallback(int value)
+{
+    qei1 = 0;   // reset counter
+    LEDzero = 1;
+}
+
+int main()
+{
     t.start();
 
-    qei1.SetZeroOnIndex(true);      // Set the flag to zero counter on next index signal rises
+    qei1.setIndexTrigger(true);     // set the flag to zero counter on next index signal rises
+    qei1.setSpeedFactor(1.0f);      // factor to scale from Hz (edges pe second = 4 * CPS) to user units (1.0=Hz, 1/(4*CPR)=rps, 1/(60*4*CPR)=rpm, 360/(4*CPR)=°/s, ...)
+    qei3.attachIndexTrigger(myIndexTriggerCallback);
+    
+    qei3.attachCounterChange(myCounterChangeCallback);
 
-    while(1)
-    {
+    while(1) {
         qei3.poll();   // poll manually without interrupt - sampling in this loop with about 2kHz
 
-        if ( t.read_ms() > 500 )   // every half second
-        {
+        if ( t.read_ms() > 250 ) { // every quater second (4 Hz)
             t.reset();
             t.start();
-            myled = !myled;
+            LEDalive = !LEDalive;
 
-            printf ( "\r\n%6d %6d %6d", (int)qei1, (int)qei2, (int)qei3 );   // print counter values
+            printf ( "\r\n%6d  %6d  %6d  %10.3f", (int)qei1, (int)qei2, (int)qei3, (float)qei1.getSpeed() );   // print counter values
         }
 
-    wait_us(20);   // for about 50kHz
+        wait_us(20);   // for about 50kHz polling
     }
 }
 * @endcode
@@ -147,22 +179,14 @@
         ProcessISR();
     }
 
-    /** Sets the flag for zeroing on next high on index pin while AB lines triggers next counting. The trigger forces the counter set to zero
+    /** Sets the flag for zeroing on next high on index pin while AB lines triggers next counting. The trigger calls tha callback function in which the counter can be set to zero or the actual counter can be latched in for later offset calculation
     *
-    * @param        Flag for triggering. Is reset on next zeroing
+    * @param        Flag for triggering. Set to 1 for call the attached callback. It is reseted after this call
     */
-    void setZeroOnIndex ( bool bZeroOnIndex ) {
-        _bZeroOnIndex = bZeroOnIndex;
+    void setIndexTrigger ( bool bIndexTrigger ) {
+        _bIndexTrigger = bIndexTrigger;
     }
 
-    /** Callback in derived classes to act on counter change
-    */
-    virtual void CounterChange ( void ) {};
-
-    /** Callback in derived classes to act on zeroing trigger
-    */
-    virtual void CounterZero ( void ) {};
-
     /** attach - Overloaded attachment function.
     *
     * Attach a C type function pointer as the callback.
@@ -225,6 +249,37 @@
         fPointerDirectionChange.attach( item, method);
     }
 
+    /** attachIndexTrigger - Overloaded attachment function.
+    *
+    * Attach a C type function pointer as the callback.
+    *
+    * Note, the callback function prototype must be:-
+    * @code
+    * void myCallbackFunction(int);
+    * @endcode
+    * @param A C function pointer to call.
+    */
+    void attachIndexTrigger(void (*function)(int) = 0) {
+        fPointerIndexTrigger.attach (function);
+    }
+
+    /** attachIndexTrigger - Overloaded attachment function.
+     *
+     * Attach a C++ type object/method pointer as the callback.
+     *
+     * Note, the callback method prototype must be:-
+     * @code
+     *     public:
+     *         static void myCallbackFunction(int);
+     * @endcode
+     * @param A C++ object pointer.
+     * @param A C++ method within the object to call.
+     */
+    template<class T>
+    void attachIndexTrigger(T* item, void (T::*method)(int)) {
+        fPointerIndexTrigger.attach( item, method);
+    }
+
     /** Sets the factor for the getter-functions to convert in another unit (1.0=Hz, 1/(4*CPR)=rps, 1/(60*4*CPR)=rpm, 360/(4*CPR)=°/s, ...)
     *
     * @param fSpeedFactor - factor to scale from Hz (edges per second = 4 * CPS) to user units
@@ -261,11 +316,12 @@
     DigitalIn _pinI;
     FPointer_vi fPointerCounterChange;
     FPointer_vi fPointerDirectionChange;
+    FPointer_vi fPointerIndexTrigger;
 
     int _counter;
     short _state;
     short _eMode;
-    bool _bZeroOnIndex;
+    bool _bIndexTrigger;
 
     Timer _SpeedTimer;