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