Control Surface pin-t-adl
MIDI Control Surface library for Arduino
AHEncoder.cpp
Go to the documentation of this file.
1#ifdef ARDUINO
2
3#include "AHEncoder.hpp"
4
5#include <AH/STL/utility> // std::swap
6
8
9AHEncoder::AHEncoder(uint8_t pinA, uint8_t pinB)
10 : pins {pinA, pinB}, direct_pins {direct_pin_read(pinA),
11 direct_pin_read(pinB)} {
12 // It's much faster to use the GPIO registers directly, rather than
13 // calling digitalRead every time we need to read a pin.
14 // digitalRead looks up the register and bitmasks every time it's called
15 // but here, we look them up once in the constructor.
16}
17
18AHEncoder::AHEncoder(AHEncoder &&other) { swap(*this, other); }
19
21 swap(*this, other);
22 return *this;
23}
24
25void swap(AHEncoder &a, AHEncoder &b) {
26 // First swap the normal member variables:
28 std::swap(a.pins, b.pins);
29
30 // Next, update the pointers in instance_table:
31 // When interrupts are in use, there is a global interrupt context
32 // variable that holds a pointer to the encoders that are being swapped
33 // or moved.
34 // After moving, this pointer would no longer be valid, so it has to be
35 // changed to point to the new encoder object.
36 // Calling attachInterrupt is not necessary, because this should already
37 // have happened in the begin method if interrupts_in_use is nonzero.
38 // Before messing with the state variables that can be changed or
39 // accessed from within an ISR, we have to disable interrupts.
40 noInterrupts();
41 std::swap(a.state, b.state);
44 if (a.interrupts_in_use > 0) {
45 int int1 = digitalPinToInterrupt(a.pins[0]);
46 if (int1 != NOT_AN_INTERRUPT)
48 int int2 = digitalPinToInterrupt(a.pins[1]);
49 if (int2 != NOT_AN_INTERRUPT)
51 }
52 if (b.interrupts_in_use > 0) {
53 int int1 = digitalPinToInterrupt(b.pins[0]);
54 if (int1 != NOT_AN_INTERRUPT)
56 int int2 = digitalPinToInterrupt(b.pins[1]);
57 if (int2 != NOT_AN_INTERRUPT)
59 }
60 interrupts();
61}
62
64 // If interrupts are in use, there are dangling pointers to the encoder
65 // state in the global interrupt contexts. These have to be detached
66 // when the encoder object is removed.
67 end();
68}
69
73 // allow time for a passive R-C filter to charge
74 // through the pullup resistors, before reading
75 // the initial state
76 delayMicroseconds(2000);
77 uint8_t s = 0;
78 if (direct_pins[0].read())
79 s |= 1;
80 if (direct_pins[1].read())
81 s |= 2;
82 state = s;
83
84 attachInterruptCtx(digitalPinToInterrupt(pins[0]));
85 attachInterruptCtx(digitalPinToInterrupt(pins[1]));
86#if defined(ARDUINO_ARCH_MBED) && !defined(ARDUINO_ARCH_RP2040)
87 // https://github.com/arduino/ArduinoCore-mbed/issues/253
90#endif
91}
92
94 if (interrupts_in_use > 0) {
95 detachInterruptCtx(digitalPinToInterrupt(pins[0]));
96 detachInterruptCtx(digitalPinToInterrupt(pins[1]));
97 }
98}
99
100void AHEncoder::attachInterruptCtx(int interrupt) {
101 if (interrupt != NOT_AN_INTERRUPT) {
102 if (instance_table[interrupt] != nullptr) {
103 FATAL_ERROR(F("Multiple encoders on the same pin"), 0x7283);
104 return;
105 }
106 instance_table[interrupt] = this;
108#ifdef ARDUINO_ARCH_RP2040
109 gpio_set_irq_enabled_with_callback(
110 interrupt, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true,
111 [](uint gpio, uint32_t) {
112 if (auto arg = instance_table[gpio])
113 arg->update();
114 });
115#else
116 attachInterrupt(interrupt, get_isr(interrupt), CHANGE);
117#endif
118 }
119}
120
121void AHEncoder::detachInterruptCtx(int interrupt) {
122 if (interrupt != NOT_AN_INTERRUPT) {
123#ifdef ARDUINO_ARCH_RP2040
124 gpio_set_irq_enabled(interrupt, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE,
125 false);
126#else
127 detachInterrupt(interrupt);
128#endif
130 instance_table[interrupt] = nullptr;
131 }
132}
133
135
137
138#endif
void swap(AHEncoder &a, AHEncoder &b)
Definition: AHEncoder.cpp:25
#define CS_ENCODER_ARGLIST_SIZE
Definition: AHEncoder.hpp:17
constexpr PinMode_t INPUT_PULLUP
DirectPinRead direct_pin_read(ArduinoPin_t pin)
#define END_CS_NAMESPACE
#define BEGIN_CS_NAMESPACE
Class for reading quadrature encoders, heavily influenced by http://www.pjrc.com/teensy/td_libs_Encod...
Definition: AHEncoder.hpp:21
uint8_t state
Definition: AHEncoder.hpp:82
AtomicPosition< int32_t > position
Definition: AHEncoder.hpp:84
static isr_func_t get_isr(unsigned interrupt)
Get a pointer to the interrupt handler function for the given interrupt.
static AHEncoder * instance_table[NUM_DIGITAL_PINS]
Array of pointers to all instances with active interrupts.
Definition: AHEncoder.hpp:91
void attachInterruptCtx(int interrupt)
Register the interrupt handler for this instance.
Definition: AHEncoder.cpp:100
void detachInterruptCtx(int interrupt)
Un-register the interrupt handler for this instance.
Definition: AHEncoder.cpp:121
ArduinoPin_t pins[2]
Definition: AHEncoder.hpp:80
~AHEncoder()
Destructor, detaches the interrupts.
Definition: AHEncoder.cpp:63
AHEncoder(ArduinoPin_t pinA, ArduinoPin_t pinB)
Constructor.
uint8_t interrupts_in_use
Definition: AHEncoder.hpp:81
int32_t read()
Read the current absolute position of the encoder.
Definition: AHEncoder.ipp:19
void end()
Disable the interrupts used by this encoder.
Definition: AHEncoder.cpp:93
void begin()
Initialize this encoder by enabling the pull-up resistors and attaching the interrupts handlers (if i...
Definition: AHEncoder.cpp:70
AHEncoder & operator=(const AHEncoder &)=delete
Copy assignment: copying an Encoder object is semantically meaningless, so it has been deleted.
DirectPinRead direct_pins[2]
Definition: AHEncoder.hpp:83
friend void swap(AHEncoder &a, AHEncoder &b)
Swap two Encoder objects.
Definition: AHEncoder.cpp:25
#define FATAL_ERROR(msg, errc)
Print the error message and error code, and stop the execution.
Definition: Error.hpp:60
void pinMode(pin_t pin, PinMode_t mode)
An ExtIO version of the Arduino function.