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