Files
ESPEasy/lib/MechInputs/QEIx4.cpp
T

269 lines
7.5 KiB
C++

/*-------------------------------------------------------------------------
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
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#include "QEIx4.h"
#include <limits.h>
#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();
}
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////