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