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 6 : 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 6 : AnalogMultiplex(pin_t analogPin, const Array<pin_t, N> &addressPins, 45 : pin_t enablePin = NO_PIN) 46 12 : : analogPin(analogPin), addressPins(addressPins), enablePin(enablePin) { 47 6 : } 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, uint8_t mode) override; 65 : 66 : /** 67 : * @brief The digitalWrite function is not implemented because writing an 68 : * output to a multiplexer is not useful. 69 : */ 70 : void digitalWrite(pin_t, uint8_t) override // LCOV_EXCL_LINE 71 : __attribute__((deprecated)) {} // LCOV_EXCL_LINE 72 : 73 : /** 74 : * @brief Read the digital state of the given input. 75 : * 76 : * @param pin 77 : * The multiplexer's pin number to read from. 78 : */ 79 : int digitalRead(pin_t pin) override; 80 : 81 : /** 82 : * @brief Read the analog value of the given input. 83 : * 84 : * @param pin 85 : * The multiplexer's pin number to read from. 86 : */ 87 : analog_t analogRead(pin_t pin) override; 88 : 89 : /** 90 : * @brief The analogWrite function is not implemented because writing an 91 : * output to a multiplexer is not useful. 92 : */ 93 : void analogWrite(pin_t, analog_t) override // LCOV_EXCL_LINE 94 : __attribute__((deprecated)) {} // LCOV_EXCL_LINE 95 : 96 : /** 97 : * @brief Initialize the multiplexer: set the pin mode of the address pins 98 : * and the enable pin to output mode. 99 : */ 100 : void begin() override; 101 : 102 : /** 103 : * @brief No periodic updating of the state is necessary, all actions are 104 : * carried out when the user calls analogRead or digitalRead. 105 : */ 106 : void update() override {} // LCOV_EXCL_LINE 107 : 108 : private: 109 : const pin_t analogPin; 110 : const Array<pin_t, N> addressPins; 111 : const pin_t enablePin; 112 : 113 : /** 114 : * @brief Write the pin number/address to the address pins of the 115 : * multiplexer. 116 : * 117 : * @param address 118 : * The address to select. 119 : */ 120 : void setMuxAddress(uint8_t address); 121 : 122 : /** 123 : * @brief Select the correct address and enable the multiplexer. 124 : * 125 : * @param address 126 : * The address to select. 127 : */ 128 : void prepareReading(uint8_t address); 129 : 130 : /** 131 : * @brief Disable the multiplexer. 132 : */ 133 : void afterReading(); 134 : 135 : // The enable pin is active low. 136 : constexpr static uint8_t MUX_ENABLED = LOW; 137 : constexpr static uint8_t MUX_DISABLED = HIGH; 138 : }; 139 : 140 : /** 141 : * @brief An alias for AnalogMultiplex<4> to use with CD74HC4067 analog 142 : * multiplexers. 143 : * 144 : * @ingroup AH_ExtIO 145 : */ 146 : using CD74HC4067 = AnalogMultiplex<4>; 147 : 148 : /** 149 : * @brief An alias for AnalogMultiplex<3> to use with CD74HC4051 analog 150 : * multiplexers. 151 : * 152 : * @ingroup AH_ExtIO 153 : */ 154 : using CD74HC4051 = AnalogMultiplex<3>; 155 : 156 : // -------------------------------------------------------------------------- // 157 : 158 : template <uint8_t N> 159 1 : void AnalogMultiplex<N>::pinMode(pin_t, uint8_t mode) { 160 1 : ExtIO::pinMode(analogPin, mode); 161 1 : } 162 : 163 : template <uint8_t N> 164 2 : int AnalogMultiplex<N>::digitalRead(pin_t pin) { 165 2 : prepareReading(pin); 166 2 : int result = ExtIO::digitalRead(analogPin); 167 2 : afterReading(); 168 2 : return result; 169 : } 170 : 171 : template <uint8_t N> 172 7 : analog_t AnalogMultiplex<N>::analogRead(pin_t pin) { 173 7 : prepareReading(pin); 174 7 : ExtIO::analogRead(analogPin); // Discard first reading 175 7 : analog_t result = ExtIO::analogRead(analogPin); 176 7 : afterReading(); 177 7 : return result; 178 : } 179 : 180 : template <uint8_t N> 181 6 : void AnalogMultiplex<N>::begin() { 182 29 : for (const pin_t &addressPin : addressPins) 183 23 : ExtIO::pinMode(addressPin, OUTPUT); 184 6 : if (enablePin != NO_PIN) { 185 4 : ExtIO::pinMode(enablePin, OUTPUT); 186 4 : ExtIO::digitalWrite(enablePin, MUX_DISABLED); 187 4 : } 188 6 : } 189 : 190 : template <uint8_t N> 191 9 : void AnalogMultiplex<N>::setMuxAddress(uint8_t address) { 192 9 : uint8_t mask = 1; 193 42 : for (const pin_t &addressPin : addressPins) { 194 33 : ExtIO::digitalWrite(addressPin, (address & mask) != 0); 195 33 : mask <<= 1; 196 : } 197 : #if !defined(__AVR__) && !defined(__x86_64__) 198 : delayMicroseconds(5); 199 : #endif 200 9 : } 201 : 202 : template <uint8_t N> 203 9 : void AnalogMultiplex<N>::prepareReading(uint8_t address) { 204 9 : setMuxAddress(address); 205 9 : if (enablePin != NO_PIN) 206 5 : ExtIO::digitalWrite(enablePin, MUX_ENABLED); 207 9 : } 208 : 209 : template <uint8_t N> 210 9 : void AnalogMultiplex<N>::afterReading() { 211 9 : if (enablePin != NO_PIN) 212 5 : ExtIO::digitalWrite(enablePin, MUX_DISABLED); 213 9 : } 214 : 215 : END_AH_NAMESPACE 216 : 217 : AH_DIAGNOSTIC_POP()