Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

228 lines
6.8 KiB

/* ---------------------------- Original copyright -----------------------------
*
* Encoder Library, for measuring quadrature encoded signals
* http://www.pjrc.com/teensy/td_libs_Encoder.html
* Copyright (c) 2011,2013 PJRC.COM, LLC - Paul Stoffregen <paul@pjrc.com>
*
* Version 1.2 - fix -2 bug in C-only code
* Version 1.1 - expand to support boards with up to 60 interrupts
* Version 1.0 - initial release
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* -----------------------------------------------------------------------------
*
* Encoder.h, updated for ESP8266 use in ESPurna. Other hardware is not supported.
*
* - Added ESP-specific attributes to ISR handlers to place them in IRAM.
* - Reduced per-encoder structure sizes - only 5 Encoders can be used on ESP8266,
* and we can directly reference pin number instead of storing both register and bitmask
*
*/
#pragma once
// _______ _______
// Pin1 ______| |_______| |______ Pin1
// negative <--- _______ _______ __ --> positive
// Pin2 __| |_______| |_______| Pin2
// new new old old
// pin2 pin1 pin2 pin1 Result
// ---- ---- ---- ---- ------
// 0 0 0 0 no movement
// 0 0 0 1 +1
// 0 0 1 0 -1
// 0 0 1 1 +2 (assume pin1 edges only)
// 0 1 0 0 -1
// 0 1 0 1 no movement
// 0 1 1 0 -2 (assume pin1 edges only)
// 0 1 1 1 +1
// 1 0 0 0 +1
// 1 0 0 1 -2 (assume pin1 edges only)
// 1 0 1 0 no movement
// 1 0 1 1 -1
// 1 1 0 0 +2 (assume pin1 edges only)
// 1 1 0 1 -1
// 1 1 1 0 +1
// 1 1 1 1 no movement
namespace EncoderLibrary {
typedef struct {
uint8_t pin1;
uint8_t pin2;
uint8_t state;
int32_t position;
} encoder_values_t;
constexpr const unsigned char ENCODERS_MAXIMUM {5u};
encoder_values_t * EncoderValues[ENCODERS_MAXIMUM] = {nullptr};
uint8_t _encoderFindStorage() {
for (uint8_t i = 0; i < ENCODERS_MAXIMUM; i++) {
if (EncoderValues[i] == nullptr) {
return i;
}
}
return ENCODERS_MAXIMUM;
}
void _encoderCleanStorage(uint8_t pin1, uint8_t pin2) {
for (uint8_t i = 0; i < ENCODERS_MAXIMUM; i++) {
if (EncoderValues[i] == nullptr) continue;
if (((EncoderValues[i])->pin1 == pin1) && ((EncoderValues[i])->pin2 == pin2)) {
EncoderValues[i] = nullptr;
break;
}
}
}
// update() is not meant to be called from outside Encoder,
// but it is public to allow static interrupt routines.
void ICACHE_RAM_ATTR update(encoder_values_t *target) {
uint8_t p1val = GPIP(target->pin1);
uint8_t p2val = GPIP(target->pin2);
uint8_t state = target->state & 3;
if (p1val) state |= 4;
if (p2val) state |= 8;
target->state = (state >> 2);
switch (state) {
case 1: case 7: case 8: case 14:
target->position++;
return;
case 2: case 4: case 11: case 13:
target->position--;
return;
case 3: case 12:
target->position += 2;
return;
case 6: case 9:
target->position -= 2;
return;
}
}
// 2 pins per encoder, 1 isr per encoder
void ICACHE_RAM_ATTR isr0() { update(EncoderValues[0]); }
void ICACHE_RAM_ATTR isr1() { update(EncoderValues[1]); }
void ICACHE_RAM_ATTR isr2() { update(EncoderValues[2]); }
void ICACHE_RAM_ATTR isr3() { update(EncoderValues[3]); }
void ICACHE_RAM_ATTR isr4() { update(EncoderValues[4]); }
constexpr void (*_isr_funcs[5])() = {
isr0, isr1, isr2, isr3, isr4
};
class Encoder {
private:
encoder_values_t values;
public:
Encoder(uint8_t pin1, uint8_t pin2) {
values.pin1 = pin1;
values.pin2 = pin2;
pinMode(values.pin1, INPUT_PULLUP);
pinMode(values.pin2, INPUT_PULLUP);
values.position = 0;
// allow time for a passive R-C filter to charge
// through the pullup resistors, before reading
// the initial state
delayMicroseconds(2000);
uint8_t current = 0;
if (GPIP(values.pin1)) {
current |= 1;
}
if (GPIP(values.pin2)) {
current |= 2;
}
values.state = current;
attach();
}
~Encoder() {
detach();
}
uint8_t pin1() {
return values.pin1;
}
uint8_t pin2() {
return values.pin2;
}
int32_t read() {
noInterrupts();
update(&values);
int32_t ret = values.position;
interrupts();
return ret;
}
void write(int32_t position) {
noInterrupts();
values.position = position;
interrupts();
}
bool attach() {
uint8_t index = _encoderFindStorage();
if (index >= ENCODERS_MAXIMUM) return false;
EncoderValues[index] = &values;
attachInterrupt(values.pin1, _isr_funcs[index], CHANGE);
attachInterrupt(values.pin2, _isr_funcs[index], CHANGE);
return true;
}
void detach() {
noInterrupts();
_encoderCleanStorage(values.pin1, values.pin2);
detachInterrupt(values.pin1);
detachInterrupt(values.pin2);
interrupts();
}
};
}
using EncoderLibrary::Encoder;