/*------------------------------------------------------------------------- Arduino library to ... Written by Jochen Krapf, contributions by ... and other members of the open source community. ------------------------------------------------------------------------- This file is part of the MechInputs library. MechInputs is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. MechInputs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with MechInputs. If not, see . -------------------------------------------------------------------------*/ #include "QEIx4.h" #include #ifndef CORE_POST_3_0_0 #ifdef ESP8266 #define IRAM_ATTR ICACHE_RAM_ATTR #endif #endif #ifdef ESP32 // Special macros to disable interrupts from within an ISR function. // // See: // Re-added interrupt code again for ESP32: // https://github.com/espressif/arduino-esp32/commit/55d608e322443b7ac080b0ab62d5a93f26d2212f // However this code cannot be called from an interrupt service routine. // See comments at portDISABLE_INTERRUPTS # define ISR_noInterrupts() \ portMUX_TYPE updateMux = portMUX_INITIALIZER_UNLOCKED; \ portTRY_ENTER_CRITICAL_ISR(&updateMux, 1000); # define ISR_interrupts() portEXIT_CRITICAL(&updateMux); # endif // ifdef ESP32 # ifdef ESP8266 # define ISR_noInterrupts() noInterrupts(); # define ISR_interrupts() interrupts(); # endif // ifdef ESP8266 // bit masks for state machine - don't change!!! #define QEIx4_STATE 0xC #define QEIx4_MASK 0x1C #define QEIx4_1x_INC 0x0100 #define QEIx4_2x_INC 0x0200 #define QEIx4_4x_INC 0x0400 #define QEIx4_1x_DEC 0x1000 #define QEIx4_2x_DEC 0x2000 #define QEIx4_4x_DEC 0x4000 #define QEIx4_1x_MASK 0x11FF #define QEIx4_2x_MASK 0x33FF #define QEIx4_4x_MASK 0x77FF #define QEIx4_DIR 0x20 #define QEIx4_A 1 #define QEIx4_B 2 #define QEIx4_AB 3 #define QEIx4_S0 0x0 #define QEIx4_S1 0x4 #define QEIx4_S2 0x8 #define QEIx4_S3 0xC #define QEIx4_CCW 0 #define QEIx4_CW 0x10 #define QEIx4_IS_CHG 0x7700 #define QEIx4_IS_INC 0x0700 #define QEIx4_IS_DEC 0x7000 #define QEIx4_IS_DIR 0x20 // state machine for decoting - don't change!!! uint16_t QEIx4::__stateLUT[32] = { // act state S0 in CCW direction QEIx4_CCW | QEIx4_S0, QEIx4_CW | QEIx4_S1 | QEIx4_A | QEIx4_4x_INC | QEIx4_DIR, QEIx4_CCW | QEIx4_S0 | QEIx4_B, QEIx4_CCW | QEIx4_S3 | QEIx4_AB | QEIx4_1x_DEC, // act state S1 in CCW direction QEIx4_CCW | QEIx4_S1, QEIx4_CCW | QEIx4_S1 | QEIx4_A, QEIx4_CCW | QEIx4_S0 | QEIx4_B | QEIx4_4x_DEC, QEIx4_CW | QEIx4_S2 | QEIx4_AB | QEIx4_1x_INC | QEIx4_DIR, // act state S2 in CCW direction QEIx4_CCW | QEIx4_S1 | QEIx4_2x_DEC, QEIx4_CCW | QEIx4_S2 | QEIx4_A, QEIx4_CW | QEIx4_S3 | QEIx4_B | QEIx4_4x_INC | QEIx4_DIR, QEIx4_CCW | QEIx4_S2 | QEIx4_AB, // act state S3 in CCW direction QEIx4_CW | QEIx4_S0 | QEIx4_2x_INC | QEIx4_DIR, QEIx4_CCW | QEIx4_S2 | QEIx4_A | QEIx4_4x_DEC, QEIx4_CCW | QEIx4_S3 | QEIx4_B, QEIx4_CCW | QEIx4_S3 | QEIx4_AB, // act state S0 in CW direction QEIx4_CW | QEIx4_S0, QEIx4_CW | QEIx4_S1 | QEIx4_A | QEIx4_4x_INC, QEIx4_CW | QEIx4_S0 | QEIx4_B, QEIx4_CCW | QEIx4_S3 | QEIx4_AB | QEIx4_1x_DEC | QEIx4_DIR, // act state S1 in CW direction QEIx4_CW | QEIx4_S1, QEIx4_CW | QEIx4_S1 | QEIx4_A, QEIx4_CCW | QEIx4_S0 | QEIx4_B | QEIx4_4x_DEC | QEIx4_DIR, QEIx4_CW | QEIx4_S2 | QEIx4_AB | QEIx4_1x_INC, // act state S2 in CW direction QEIx4_CCW | QEIx4_S1 | QEIx4_2x_DEC | QEIx4_DIR, QEIx4_CW | QEIx4_S2 | QEIx4_A, QEIx4_CW | QEIx4_S3 | QEIx4_B | QEIx4_4x_INC, QEIx4_CW | QEIx4_S2 | QEIx4_AB, // act state S3 in CW direction QEIx4_CW | QEIx4_S0 | QEIx4_2x_INC, QEIx4_CCW | QEIx4_S2 | QEIx4_A | QEIx4_4x_DEC | QEIx4_DIR, QEIx4_CW | QEIx4_S3 | QEIx4_B, QEIx4_CW | QEIx4_S3 | QEIx4_AB }; // Helper for ISR call QEIx4* QEIx4::__instance[4] = { 0 }; //#define DEB(x) printf (x) #define DEB(x) /////////////////////////////////////////////////////////////////////////////// QEIx4::QEIx4() { for (uint8_t i=0; i<4; i++) if (__instance[i] == 0) { __instance[i] = this; DEB("::"); break; } _pinA = -1; _pinB = -1; _pinI = -1; _state = 0; _limitMin = LONG_MIN; _limitMax = LONG_MAX; } /////////////////////////////////////////////////////////////////////////////// QEIx4::~QEIx4() { for (uint8_t i=0; i<4; i++) if (__instance[i] == this) { __instance[i] = 0; } } /////////////////////////////////////////////////////////////////////////////// void QEIx4::begin(int16_t pinA, int16_t pinB, int16_t pinI, uint8_t mode) { if (_pinA >= 0) detachInterrupt(digitalPinToInterrupt(_pinA)); if (_pinB >= 0) detachInterrupt(digitalPinToInterrupt(_pinB)); _pinA = pinA; _pinB = pinB; _pinI = pinI; _counter = 0; _bHasChanged = false; if (mode == 1) _eventMask = QEIx4_1x_MASK; else if (mode == 2) _eventMask = QEIx4_2x_MASK; else _eventMask = QEIx4_4x_MASK; pinMode(_pinA, INPUT_PULLUP); pinMode(_pinB, INPUT_PULLUP); pinMode(_pinI, INPUT_PULLUP); if (_pinA >= 0) attachInterrupt(digitalPinToInterrupt(_pinA), ISR, CHANGE); if (_pinB >= 0) attachInterrupt(digitalPinToInterrupt(_pinB), ISR, CHANGE); } /////////////////////////////////////////////////////////////////////////////// long QEIx4::read() { ISR_noInterrupts(); _bHasChanged = false; long ret = _counter; ISR_interrupts(); return ret; } void QEIx4::loop() { ISR_noInterrupts(); processStateMachine(); ISR_interrupts(); } /////////////////////////////////////////////////////////////////////////////// void IRAM_ATTR QEIx4::processStateMachine() { DEB("."); _state &= QEIx4_MASK; if (digitalRead(_pinA)) _state |= QEIx4_A; if (digitalRead(_pinB)) _state |= QEIx4_B; _state = __stateLUT[_state]; // magic is done by lookup-table _state &= _eventMask; if (_state & QEIx4_IS_CHG) { // is any change? bool bCounterChange = false; if ((_state & QEIx4_IS_INC) && (_counter < _limitMax)) { // has moved foreward? _counter++; bCounterChange = true; DEB("+"); } if ((_state & QEIx4_IS_DEC) && (_counter > _limitMin)) { // has moved backward? _counter--; bCounterChange = true; DEB("-"); } if (_pinI >= 0 && _bIndexTrigger && bCounterChange && digitalRead(_pinI)) { // is index pin triggered? _bIndexTrigger = false; _counter = 0; DEB("I"); } if (bCounterChange) { // has counter changed? _bHasChanged = true; } } } void IRAM_ATTR QEIx4::ISR() { for (uint8_t i=0; i<4; i++) if (__instance[i]) { __instance[i]->processStateMachine(); } } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////