cs-midi/Submodules/Encoder/AHEncoder.cpp

103 lines
2.6 KiB
C++

#include "AHEncoder.hpp"
#include <AH/STL/utility>
#include "hardware/gpio.h"
BEGIN_CS_NAMESPACE
AHEncoder::AHEncoder(uint8_t pinA, uint8_t pinB)
: pins {pinA, pinB},
direct_pins {direct_pin_read(pinA), direct_pin_read(pinB)} {}
AHEncoder::AHEncoder(AHEncoder &&other)
: pins(other.pins), direct_pins(other.direct_pins) {
if (other.interrupts_in_use)
return; // can't move from initialized encoder
}
AHEncoder &AHEncoder::operator=(AHEncoder &&other) {
swap(*this, other);
return *this;
}
void swap(AHEncoder &a, AHEncoder &b) {
std::swap(a.interrupts_in_use, b.interrupts_in_use);
std::swap(a.pins, b.pins);
noInterrupts();
std::swap(a.state, b.state);
std::swap(a.direct_pins, b.direct_pins);
std::swap(a.position, b.position);
if (a.interrupts_in_use > 0) {
AHEncoder::instance_table[a.pins[0]] = &a;
AHEncoder::instance_table[a.pins[1]] = &a;
}
if (b.interrupts_in_use > 0) {
AHEncoder::instance_table[b.pins[0]] = &b;
AHEncoder::instance_table[b.pins[1]] = &b;
}
interrupts();
}
AHEncoder::~AHEncoder() { end(); }
void AHEncoder::begin() {
if (interrupts_in_use > 0)
return;
pinMode(pins[0], INPUT_PULLUP);
pinMode(pins[1], INPUT_PULLUP);
sleep_ms(2);
uint8_t s = 0;
if (direct_pins[0].read())
s |= 1;
if (direct_pins[1].read())
s |= 2;
state = s;
attachInterruptCtx(pins[0]);
attachInterruptCtx(pins[1]);
}
void AHEncoder::end() {
if (interrupts_in_use > 0) {
detachInterruptCtx(pins[0]);
detachInterruptCtx(pins[1]);
}
}
static bool gpio_callback_registered = false;
void encoder_gpio_callback(uint gpio, uint32_t) {
if (gpio < CS_ENCODER_ARGLIST_SIZE) {
if (auto *enc = AHEncoder::instance_table[gpio])
enc->update();
}
}
void AHEncoder::attachInterruptCtx(uint8_t pin) {
if (instance_table[pin] != nullptr)
return;
instance_table[pin] = this;
++interrupts_in_use;
if (!gpio_callback_registered) {
gpio_set_irq_enabled_with_callback(
pin, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true,
encoder_gpio_callback);
gpio_callback_registered = true;
} else {
gpio_set_irq_enabled(pin, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE,
true);
}
}
void AHEncoder::detachInterruptCtx(uint8_t pin) {
gpio_set_irq_enabled(pin, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, false);
--interrupts_in_use;
instance_table[pin] = nullptr;
}
AHEncoder *AHEncoder::instance_table[CS_ENCODER_ARGLIST_SIZE] {};
END_CS_NAMESPACE