Line data Source code
1 : /* ✔ */
2 :
3 : #pragma once
4 :
5 : #include "ExtendedInputOutput.hpp"
6 : #include "StaticSizeExtendedIOElement.hpp"
7 : #include <AH/Containers/Array.hpp>
8 : #include <stdlib.h>
9 :
10 : BEGIN_AH_NAMESPACE
11 :
12 : /**
13 : * @brief A class for reading multiplexed analog inputs.
14 : * Supports 74HC4067, 74HC4051, etc.
15 : *
16 : * You can use many multiplexers on the same address lines if each of the
17 : * multiplexers has a different enable line.
18 : *
19 : * @tparam N
20 : * The number of address lines.
21 : *
22 : * @ingroup AH_ExtIO
23 : */
24 : template <uint8_t N>
25 : class AnalogMultiplex : public StaticSizeExtendedIOElement<1 << N> {
26 : public:
27 : /**
28 : * @brief Create a new AnalogMultiplex object on the given pins.
29 : *
30 : * @param analogPin
31 : * The analog input pin connected to the output of the multiplexer.
32 : * @param addressPins
33 : * An array of the pins connected to the address lines of the
34 : * multiplexer. (Labeled S0, S1, S2 ... in the datasheet.)
35 : * @param enablePin
36 : * The digital output pin connected to the enable pin of the
37 : * multiplexer. (Labeled Ē in the datasheet.)
38 : * If you don't need the enable pin, you can use NO_PIN, which is
39 : * the default.
40 : */
41 10 : AnalogMultiplex(pin_t analogPin, const Array<pin_t, N> &addressPins,
42 : pin_t enablePin = NO_PIN)
43 10 : : analogPin(analogPin), addressPins(addressPins), enablePin(enablePin) {
44 10 : }
45 :
46 : /**
47 : * @brief Set the pin mode of the analog input pin.
48 : * This allows you to enable the internal pull-up resistor, for
49 : * use with buttons or open-collector outputs.
50 : *
51 : * @note This applies to all pins of this multiplexer.
52 : * This affects all pins of the multiplexer, because it has only
53 : * a single common pin.
54 : *
55 : * @param pin
56 : * (Unused)
57 : * @param mode
58 : * The new mode of the input pin:
59 : * either INPUT or INPUT_PULLUP.
60 : */
61 : void pinMode(pin_int_t pin, PinMode_t mode) override;
62 :
63 : /**
64 : * @copydoc pinMode
65 : */
66 : void pinModeBuffered(pin_int_t pin, PinMode_t mode) override;
67 :
68 : /**
69 : * @brief The digitalWrite function is not implemented because writing an
70 : * output to a multiplexer is not useful.
71 : */
72 : void digitalWrite(pin_int_t, PinStatus_t) override // LCOV_EXCL_LINE
73 : __attribute__((deprecated)) {} // LCOV_EXCL_LINE
74 :
75 : /**
76 : * @copydoc digitalWrite
77 : */
78 : void digitalWriteBuffered(pin_int_t, PinStatus_t) override // LCOV_EXCL_LINE
79 : __attribute__((deprecated)) {} // LCOV_EXCL_LINE
80 :
81 : /**
82 : * @brief Read the digital state of the given input.
83 : *
84 : * @param pin
85 : * The multiplexer's pin number to read from.
86 : */
87 : PinStatus_t digitalRead(pin_int_t pin) override;
88 :
89 : /**
90 : * @copydoc digitalRead
91 : */
92 : PinStatus_t digitalReadBuffered(pin_int_t pin) override;
93 :
94 : /**
95 : * @brief Read the analog value of the given input.
96 : *
97 : * @param pin
98 : * The multiplexer's pin number to read from.
99 : */
100 : analog_t analogRead(pin_int_t pin) override;
101 :
102 : /**
103 : * @copydoc analogRead
104 : */
105 : analog_t analogReadBuffered(pin_int_t pin) override;
106 :
107 : /**
108 : * @brief The analogWrite function is not implemented because writing an
109 : * output to a multiplexer is not useful.
110 : */
111 : void analogWrite(pin_int_t, analog_t) override // LCOV_EXCL_LINE
112 : __attribute__((deprecated)) {} // LCOV_EXCL_LINE
113 :
114 : /**
115 : * @copydoc analogWrite
116 : */
117 : void analogWriteBuffered(pin_int_t, analog_t) override // LCOV_EXCL_LINE
118 : __attribute__((deprecated)) {} // LCOV_EXCL_LINE
119 :
120 : /**
121 : * @brief Initialize the multiplexer: set the pin mode of the address pins
122 : * and the enable pin to output mode.
123 : */
124 : void begin() override;
125 :
126 : /**
127 : * @brief No periodic updating of the state is necessary, all actions are
128 : * carried out when the user calls analogRead or digitalRead.
129 : */
130 : void updateBufferedOutputs() override {} // LCOV_EXCL_LINE
131 :
132 : /**
133 : * @brief No periodic updating of the state is necessary, all actions are
134 : * carried out when the user calls analogRead or digitalRead.
135 : */
136 : void updateBufferedInputs() override {} // LCOV_EXCL_LINE
137 :
138 : /**
139 : * @brief Specify whether to discard the first analog reading after
140 : * changing the address lines (enabled by default).
141 : */
142 1 : void discardFirstReading(bool discardFirstReading_) {
143 1 : this->discardFirstReading_ = discardFirstReading_;
144 1 : }
145 :
146 : protected:
147 : const pin_t analogPin;
148 : const Array<pin_t, N> addressPins;
149 : const pin_t enablePin;
150 : bool discardFirstReading_ = true;
151 :
152 : /**
153 : * @brief Write the pin number/address to the address pins of the
154 : * multiplexer.
155 : *
156 : * @param address
157 : * The address to select.
158 : */
159 : void setMuxAddress(uint8_t address);
160 :
161 : /**
162 : * @brief Select the correct address and enable the multiplexer.
163 : *
164 : * @param address
165 : * The address to select.
166 : */
167 : void prepareReading(uint8_t address);
168 :
169 : /**
170 : * @brief Disable the multiplexer.
171 : */
172 : void afterReading();
173 :
174 : // The enable pin is active low.
175 : constexpr static auto MUX_ENABLED = LOW;
176 : constexpr static auto MUX_DISABLED = HIGH;
177 : };
178 :
179 : /**
180 : * @brief An alias for AnalogMultiplex<4> to use with CD74HC4067 analog
181 : * multiplexers.
182 : *
183 : * @ingroup AH_ExtIO
184 : */
185 : using CD74HC4067 = AnalogMultiplex<4>;
186 :
187 : /**
188 : * @brief An alias for AnalogMultiplex<3> to use with CD74HC4051 analog
189 : * multiplexers.
190 : *
191 : * @ingroup AH_ExtIO
192 : */
193 : using CD74HC4051 = AnalogMultiplex<3>;
194 :
195 : // -------------------------------------------------------------------------- //
196 :
197 : template <uint8_t N>
198 2 : void AnalogMultiplex<N>::pinMode(pin_int_t, PinMode_t mode) {
199 2 : ExtIO::pinMode(analogPin, mode);
200 2 : }
201 :
202 : template <uint8_t N>
203 1 : void AnalogMultiplex<N>::pinModeBuffered(pin_int_t p, PinMode_t mode) {
204 1 : AnalogMultiplex<N>::pinMode(p, mode);
205 1 : }
206 :
207 : template <uint8_t N>
208 3 : PinStatus_t AnalogMultiplex<N>::digitalRead(pin_int_t pin) {
209 3 : prepareReading(static_cast<uint8_t>(pin));
210 3 : PinStatus_t result = ExtIO::digitalRead(analogPin);
211 3 : afterReading();
212 3 : return result;
213 : }
214 :
215 : template <uint8_t N>
216 1 : PinStatus_t AnalogMultiplex<N>::digitalReadBuffered(pin_int_t pin) {
217 1 : return AnalogMultiplex<N>::digitalRead(pin);
218 : }
219 :
220 : template <uint8_t N>
221 11 : analog_t AnalogMultiplex<N>::analogRead(pin_int_t pin) {
222 11 : prepareReading(static_cast<uint8_t>(pin));
223 11 : if (discardFirstReading_)
224 8 : (void)ExtIO::analogRead(analogPin); // Discard first reading
225 11 : analog_t result = ExtIO::analogRead(analogPin);
226 11 : afterReading();
227 11 : return result;
228 : }
229 :
230 : template <uint8_t N>
231 1 : analog_t AnalogMultiplex<N>::analogReadBuffered(pin_int_t pin) {
232 1 : return AnalogMultiplex<N>::analogRead(pin);
233 : }
234 :
235 : template <uint8_t N>
236 10 : void AnalogMultiplex<N>::begin() {
237 49 : for (const pin_t &addressPin : addressPins)
238 39 : ExtIO::pinMode(addressPin, OUTPUT);
239 10 : if (enablePin != NO_PIN) {
240 7 : ExtIO::pinMode(enablePin, OUTPUT);
241 7 : ExtIO::digitalWrite(enablePin, MUX_DISABLED);
242 : }
243 10 : }
244 :
245 : template <uint8_t N>
246 14 : void AnalogMultiplex<N>::setMuxAddress(uint8_t address) {
247 14 : uint8_t mask = 1;
248 67 : for (const pin_t &addressPin : addressPins) {
249 53 : ExtIO::digitalWrite(addressPin, (address & mask) != 0 ? HIGH : LOW);
250 53 : mask <<= 1;
251 : }
252 : #if !defined(__AVR__) && defined(ARDUINO)
253 : delayMicroseconds(SELECT_LINE_DELAY);
254 : #endif
255 14 : }
256 :
257 : template <uint8_t N>
258 14 : void AnalogMultiplex<N>::prepareReading(uint8_t address) {
259 14 : setMuxAddress(address);
260 14 : if (enablePin != NO_PIN)
261 7 : ExtIO::digitalWrite(enablePin, MUX_ENABLED);
262 14 : }
263 :
264 : template <uint8_t N>
265 14 : void AnalogMultiplex<N>::afterReading() {
266 14 : if (enablePin != NO_PIN)
267 7 : ExtIO::digitalWrite(enablePin, MUX_DISABLED);
268 14 : }
269 :
270 : END_AH_NAMESPACE
|