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 9 : 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 9 : AnalogMultiplex(pin_t analogPin, const Array<pin_t, N> &addressPins, 45 : pin_t enablePin = NO_PIN) 46 18 : : analogPin(analogPin), addressPins(addressPins), enablePin(enablePin) { 47 9 : } 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 : int digitalRead(pin_t pin) override; 91 : 92 : /** 93 : * @copydoc digitalRead 94 : */ 95 : int 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 : private: 142 : const pin_t analogPin; 143 : const Array<pin_t, N> addressPins; 144 : const pin_t enablePin; 145 : 146 : /** 147 : * @brief Write the pin number/address to the address pins of the 148 : * multiplexer. 149 : * 150 : * @param address 151 : * The address to select. 152 : */ 153 : void setMuxAddress(uint8_t address); 154 : 155 : /** 156 : * @brief Select the correct address and enable the multiplexer. 157 : * 158 : * @param address 159 : * The address to select. 160 : */ 161 : void prepareReading(uint8_t address); 162 : 163 : /** 164 : * @brief Disable the multiplexer. 165 : */ 166 : void afterReading(); 167 : 168 : // The enable pin is active low. 169 : constexpr static auto MUX_ENABLED = LOW; 170 : constexpr static auto MUX_DISABLED = HIGH; 171 : }; 172 : 173 : /** 174 : * @brief An alias for AnalogMultiplex<4> to use with CD74HC4067 analog 175 : * multiplexers. 176 : * 177 : * @ingroup AH_ExtIO 178 : */ 179 : using CD74HC4067 = AnalogMultiplex<4>; 180 : 181 : /** 182 : * @brief An alias for AnalogMultiplex<3> to use with CD74HC4051 analog 183 : * multiplexers. 184 : * 185 : * @ingroup AH_ExtIO 186 : */ 187 : using CD74HC4051 = AnalogMultiplex<3>; 188 : 189 : // -------------------------------------------------------------------------- // 190 : 191 : template <uint8_t N> 192 2 : void AnalogMultiplex<N>::pinMode(pin_t, PinMode_t mode) { 193 2 : ExtIO::pinMode(analogPin, mode); 194 2 : } 195 : 196 : template <uint8_t N> 197 1 : void AnalogMultiplex<N>::pinModeBuffered(pin_t, PinMode_t mode) { 198 1 : AnalogMultiplex<N>::pinMode(analogPin, mode); 199 1 : } 200 : 201 : template <uint8_t N> 202 3 : int AnalogMultiplex<N>::digitalRead(pin_t pin) { 203 3 : prepareReading(pin); 204 3 : int result = ExtIO::digitalRead(analogPin); 205 3 : afterReading(); 206 6 : return result; 207 3 : } 208 : 209 : template <uint8_t N> 210 1 : int AnalogMultiplex<N>::digitalReadBuffered(pin_t pin) { 211 1 : return AnalogMultiplex<N>::digitalRead(pin); 212 : } 213 : 214 : template <uint8_t N> 215 8 : analog_t AnalogMultiplex<N>::analogRead(pin_t pin) { 216 8 : prepareReading(pin); 217 8 : ExtIO::analogRead(analogPin); // Discard first reading 218 8 : analog_t result = ExtIO::analogRead(analogPin); 219 8 : afterReading(); 220 16 : return result; 221 8 : } 222 : 223 : template <uint8_t N> 224 1 : analog_t AnalogMultiplex<N>::analogReadBuffered(pin_t pin) { 225 1 : return AnalogMultiplex<N>::analogRead(pin); 226 : } 227 : 228 : template <uint8_t N> 229 9 : void AnalogMultiplex<N>::begin() { 230 44 : for (const pin_t &addressPin : addressPins) 231 35 : ExtIO::pinMode(addressPin, OUTPUT); 232 9 : if (enablePin != NO_PIN) { 233 7 : ExtIO::pinMode(enablePin, OUTPUT); 234 7 : ExtIO::digitalWrite(enablePin, MUX_DISABLED); 235 7 : } 236 9 : } 237 : 238 : template <uint8_t N> 239 11 : void AnalogMultiplex<N>::setMuxAddress(uint8_t address) { 240 11 : uint8_t mask = 1; 241 52 : for (const pin_t &addressPin : addressPins) { 242 41 : ExtIO::digitalWrite(addressPin, (address & mask) != 0 ? HIGH : LOW); 243 41 : mask <<= 1; 244 41 : } 245 : #if !defined(__AVR__) && defined(ARDUINO) 246 : delayMicroseconds(5); 247 : #endif 248 11 : } 249 : 250 : template <uint8_t N> 251 11 : void AnalogMultiplex<N>::prepareReading(uint8_t address) { 252 11 : setMuxAddress(address); 253 11 : if (enablePin != NO_PIN) 254 7 : ExtIO::digitalWrite(enablePin, MUX_ENABLED); 255 11 : } 256 : 257 : template <uint8_t N> 258 11 : void AnalogMultiplex<N>::afterReading() { 259 11 : if (enablePin != NO_PIN) 260 7 : ExtIO::digitalWrite(enablePin, MUX_DISABLED); 261 11 : } 262 : 263 : END_AH_NAMESPACE 264 : 265 : AH_DIAGNOSTIC_POP()