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

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers QEIx4.h Source File

QEIx4.h

00001 /*
00002 * @author Jochen Krapf
00003 * parts by Andy Kirkham
00004 *
00005 * @section LICENSE
00006 *
00007 * Copyright (c) 2014 Jochen Krapf, MIT License
00008 *
00009 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
00010 * and associated documentation files (the "Software"), to deal in the Software without restriction,
00011 * including without limitation the rights to use, copy, modify, merge, publish, distribute,
00012 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
00013 * furnished to do so, subject to the following conditions:
00014 *
00015 * The above copyright notice and this permission notice shall be included in all copies or
00016 * substantial portions of the Software.
00017 *
00018 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
00019 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00020 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
00021 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00022 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00023 *
00024 * @section DESCRIPTION
00025 * mbed QEI (quadrature encoder interface) library, for decoding AB signals from a rotary encoder
00026 *
00027 * Use cases:
00028 * - Rotary encoder in closed loop motor regulation
00029 * - Hand wheel
00030 * - Input device for motion control (MoCo)
00031 */
00032 
00033 #include "mbed.h"
00034 #include "FPointer_vi.h"
00035 
00036 /** Quadrature Encoder Interface for Motion Control.
00037 *
00038 * A class to decode pulses on a rotary encoder with AB signals (quadrature encoder). 
00039 * 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)
00040 *
00041 * In opposite to most common QEI implementation this is resistant to jitter and chatter on AB signals and motor vibrations. 
00042 * When using interrupts (IRQ_NO_JAMMING-mode) only the needed edge and pin is activated to prevent jamming CPU time with unnecessary interrupts.
00043 * Whes reaching the next position the edge that triggerd this position (state) is ignored to aboid oscillating up/down counts.
00044 *
00045 * It can also be used in polling mode i.g. in idle routines if interrupts are not desired. 
00046 * At this mode be sure that the sampling frequency is heigher than the maximum rotation speed (expeced counts per second)
00047 *
00048 * The internal state machine is based on a look up table (LUT) to minimize interrupt retention time and get all necessary flags at once.
00049 *
00050 * 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.
00051 *
00052 * The library is designed to support closed loop speed- and motion-controller for also slow and smooth motions like movie camera motion control.
00053 *
00054 * Example:
00055 *
00056 * @code
00057 #include "mbed.h"
00058 #include "QEIx4.h"
00059 
00060 DigitalOut LEDalive(LED1);
00061 DigitalOut LEDzero(LED2);
00062 DigitalOut LEDup(LED4);
00063 DigitalOut LEDdown(LED3);
00064 
00065 Timer t;   // timer for polling
00066 
00067 // ports for nxp LPC 1768
00068 QEIx4 qei1(p30, p29, p28, (QEIx4::EMODE)(QEIx4::IRQ | QEIx4::SPEED));   // QEI with index signal for zeroing
00069 QEIx4 qei2(p21, p22, NC,  QEIx4::IRQ_NO_JAMMING);                       // QEI with AB signals only
00070 QEIx4 qei3(p25, p24, NC,  QEIx4::POLLING);                              // QEI without interrups in polling mode
00071 
00072 // The callback functions
00073 void myCounterChangeCallback(int value)
00074 {
00075     static int valueLast=-1;
00076 
00077     if ( value > valueLast ) {
00078         LEDup = !LEDup;
00079         LEDdown = 0;
00080     } else {
00081         LEDdown = !LEDdown;
00082         LEDup = 0;
00083     }
00084     valueLast = value;
00085 }
00086 
00087 void myIndexTriggerCallback(int value)
00088 {
00089     qei1 = 0;   // reset counter
00090     LEDzero = 1;
00091 }
00092 
00093 int main()
00094 {
00095     t.start();
00096 
00097     qei1.setIndexTrigger(true);     // set the flag to zero counter on next index signal rises
00098     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, ...)
00099     qei3.attachIndexTrigger(myIndexTriggerCallback);
00100     
00101     qei3.attachCounterChange(myCounterChangeCallback);
00102 
00103     while(1) {
00104         qei3.poll();   // poll manually without interrupt - sampling in this loop with about 2kHz
00105 
00106         if ( t.read_ms() > 250 ) { // every quater second (4 Hz)
00107             t.reset();
00108             t.start();
00109             LEDalive = !LEDalive;
00110 
00111             printf ( "\r\n%6d  %6d  %6d  %10.3f", (int)qei1, (int)qei2, (int)qei3, (float)qei1.getSpeed() );   // print counter values
00112         }
00113 
00114         wait_us(20);   // for about 50kHz polling
00115     }
00116 }
00117 * @endcode
00118 */
00119 class QEIx4
00120 {
00121 public:
00122 
00123     typedef enum EMODE {
00124         POLLING = 0,
00125         IRQ = 1,
00126         IRQ_NO_JAMMING = 2,
00127         SPEED = 4,
00128     } EMODE;
00129 
00130     /** constructor of QEIx4 object
00131     *
00132     * @param pinA     Pin number of input/interrupt pin for encoder line A. All port pins are possible except p19 and p20
00133     * @param pinB     Pin number of input/interrupt pin for encoder line B. All port pins are possible except p19 and p20
00134     * @param pinI     Pin number of input pin for optional encoder index or reference switch.
00135     * @param eMode    Flag to use interrups to detect changes on line A and B. For none interrupt use mode POLLING and call the function poll() frequently. For optional speed calculation the mode SPEED can be ored
00136     */
00137     QEIx4 ( PinName pinA, PinName pinB, PinName pinI=NC, EMODE eMode=IRQ );
00138 
00139     /** destructor of QEIx4 object
00140     */
00141     ~QEIx4();
00142 
00143     /** Gets the actual counter value.
00144     *
00145     * @return        Actual counter value
00146     */
00147     int read() {
00148         return _counter;
00149     }
00150 
00151     /** Gets the actual counter value as int operator.
00152     *
00153     * @return        Actual counter value as int operator
00154     */
00155     operator int () {   // int-Operator
00156         return _counter;
00157     }
00158 
00159     /** Sets the counter value at actual encoder position to given value.
00160     *
00161     * @param        Counter value
00162     */
00163     void write ( int counter ) {
00164         _counter = counter;
00165     }
00166 
00167     /** Sets the counter value at actual encoder position to given value as assign operator.
00168     *
00169     * @param        Counter value
00170     */
00171     int operator= ( int counter ) {   // Assign-Operator
00172         write(counter);
00173         return counter;
00174     }
00175 
00176     /** Polls the state machine manually and updates the counter value.
00177     */
00178     void poll () {
00179         ProcessISR();
00180     }
00181 
00182     /** 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
00183     *
00184     * @param        Flag for triggering. Set to 1 for call the attached callback. It is reseted after this call
00185     */
00186     void setIndexTrigger ( bool bIndexTrigger ) {
00187         _bIndexTrigger = bIndexTrigger;
00188     }
00189 
00190     /** attach - Overloaded attachment function.
00191     *
00192     * Attach a C type function pointer as the callback.
00193     *
00194     * Note, the callback function prototype must be:-
00195     * @code
00196     * void myCallbackFunction(int);
00197     * @endcode
00198     * @param A C function pointer to call.
00199     */
00200     void attachCounterChange(void (*function)(int) = 0) {
00201         fPointerCounterChange.attach (function);
00202     }
00203 
00204     /** attachCounterChange - Overloaded attachment function.
00205      *
00206      * Attach a C++ type object/method pointer as the callback.
00207      *
00208      * Note, the callback method prototype must be:-
00209      * @code
00210      *     public:
00211      *         static void myCallbackFunction(int);
00212      * @endcode
00213      * @param A C++ object pointer.
00214      * @param A C++ method within the object to call.
00215      */
00216     template<class T>
00217     void attachCounterChange(T* item, void (T::*method)(int)) {
00218         fPointerCounterChange.attach( item, method);
00219     }
00220 
00221     /** attachDirectionChange - Overloaded attachment function.
00222     *
00223     * Attach a C type function pointer as the callback.
00224     *
00225     * Note, the callback function prototype must be:-
00226     * @code
00227     * void myCallbackFunction(int);
00228     * @endcode
00229     * @param A C function pointer to call.
00230     */
00231     void attachDirectionChange(void (*function)(int) = 0) {
00232         fPointerDirectionChange.attach (function);
00233     }
00234 
00235     /** attachDirectionChange - Overloaded attachment function.
00236      *
00237      * Attach a C++ type object/method pointer as the callback.
00238      *
00239      * Note, the callback method prototype must be:-
00240      * @code
00241      *     public:
00242      *         static void myCallbackFunction(int);
00243      * @endcode
00244      * @param A C++ object pointer.
00245      * @param A C++ method within the object to call.
00246      */
00247     template<class T>
00248     void attachDirectionChange(T* item, void (T::*method)(int)) {
00249         fPointerDirectionChange.attach( item, method);
00250     }
00251 
00252     /** attachIndexTrigger - Overloaded attachment function.
00253     *
00254     * Attach a C type function pointer as the callback.
00255     *
00256     * Note, the callback function prototype must be:-
00257     * @code
00258     * void myCallbackFunction(int);
00259     * @endcode
00260     * @param A C function pointer to call.
00261     */
00262     void attachIndexTrigger(void (*function)(int) = 0) {
00263         fPointerIndexTrigger.attach (function);
00264     }
00265 
00266     /** attachIndexTrigger - Overloaded attachment function.
00267      *
00268      * Attach a C++ type object/method pointer as the callback.
00269      *
00270      * Note, the callback method prototype must be:-
00271      * @code
00272      *     public:
00273      *         static void myCallbackFunction(int);
00274      * @endcode
00275      * @param A C++ object pointer.
00276      * @param A C++ method within the object to call.
00277      */
00278     template<class T>
00279     void attachIndexTrigger(T* item, void (T::*method)(int)) {
00280         fPointerIndexTrigger.attach( item, method);
00281     }
00282 
00283     /** 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, ...)
00284     *
00285     * @param fSpeedFactor - factor to scale from Hz (edges per second = 4 * CPS) to user units
00286     */
00287     void setSpeedFactor(float fSpeedFactor) {
00288         _fSpeedFactor = fSpeedFactor;
00289     }
00290 
00291     /** Gets the actual speed as float value. The value is scales by the facor set by setPositionFactor()
00292     *
00293     * @return        Actual encoder speed as float
00294     */
00295     float getSpeed();
00296 
00297     /** Sets the factor for the getter-functions to convert in another unit (e.g. CPR (cycles per rotation) * 4.0 to get 1.0 for a full rotation)
00298     *
00299     * @param fPositionFactor  Factor to scale from counts to user unit
00300     */
00301     void setPositionFactor ( float fPositionFactor ) {
00302         _fPositionFactor = fPositionFactor;
00303     }
00304 
00305     /** Gets the actual counter value as float value. The value is scales by the facor set by setSpeedFactor()
00306     *
00307     * @return        Actual encoder position as float
00308     */
00309     float getPosition () {
00310         return (float)_counter * _fPositionFactor;
00311     }
00312 
00313 
00314 protected:
00315     InterruptIn _pinA, _pinB;
00316     DigitalIn _pinI;
00317     FPointer_vi fPointerCounterChange;
00318     FPointer_vi fPointerDirectionChange;
00319     FPointer_vi fPointerIndexTrigger;
00320 
00321     int _counter;
00322     short _state;
00323     short _eMode;
00324     bool _bIndexTrigger;
00325 
00326     Timer _SpeedTimer;
00327 
00328     unsigned int _nSpeedLastTimer;
00329     unsigned int _nSpeedTimeoutMax;
00330     unsigned int _nSpeedTimeoutCount;
00331     int _nSpeedAvrTimeSum;
00332     int _nSpeedAvrTimeCount;
00333     float _fLastSpeed;
00334 
00335     void ProcessISR ( void );
00336     void callback_timeout();
00337 
00338     float _fPositionFactor;
00339     float _fSpeedFactor;
00340 
00341 private:
00342     static short _modeLUT[32];
00343 };