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 <AH/Containers/Updatable.hpp> 10 : #include <AH/Hardware/Hardware-Types.hpp> 11 : 12 : BEGIN_AH_NAMESPACE 13 : 14 : /** 15 : * @brief An abstract base class for Extended Input/Output elements. 16 : * 17 : * The limited number of IO pins of the Arduino can be extended by 18 : * adding multiplexers, shift registers, IO expanders, etc. 19 : * ExtendedIOElement serves as a base class for all these expanders. 20 : * 21 : * The pins of each extended IO element are mapped to a pin number 22 : * greater than the greatest Arduino pin number. 23 : * You can supply this pin number to the IO functions in the ExtIO 24 : * namespace. 25 : * If the pin number corresponds to an actual Arduino pin, 26 : * the default Arduino IO function (digitalRead, digitalWrite, ...) 27 : * will be called. 28 : * If the pin is not an Arduino pin, it is an extended IO pin number, 29 : * so the extended IO element that this pin belongs to is looked up, 30 : * and the IO function for this element is executed with the correct 31 : * pin number. 32 : * 33 : * For example: 34 : * Imagine an Arduino with 20 pins (e.g. the Arduino UNO). 35 : * Pins 0 - 19 will correspond to the Arduino pins, and 36 : * `ExtIO::digitalRead(pin)` will have the exact same effect as 37 : * the standard `digitalRead(pin)` function (albeit a little slower). 38 : * Now, we'll add two 8-channel analog multiplexers, let's call them 39 : * `mux1` and `mux2`. 40 : * The first pin (pin 0) of `mux1` will be extended IO pin number 20, 41 : * the last pin (pin 7) of `mux1` will be extended IO pin number 27, 42 : * etc. 43 : * The first pin of `mux2` will be extended IO pin number 28, you get 44 : * the idea. 45 : * If you now call `ExtIO::digitalRead(mux1.#pin (7))` or 46 : * `ExtIO::digitalRead(27)`, both will be 47 : * translated to `mux1.digitalRead(7)`. 48 : * 49 : * The number of extended IO elements is limited only by the size of 50 : * `pin_t`. However, looking up the extended IO element for a given 51 : * extended IO pin number uses linear search, so that might add 52 : * some noticable overhead for large pin numbers. 53 : * 54 : * The design here is a compromise: saving a pointer to each extended IO element 55 : * to find it directly would be much faster than having to search all elements 56 : * each time. On the other hand, it would require each `pin_t` variable to be 57 : * at least one byte larger. Since almost all other classes in this library 58 : * store pin variables, the memory penalty would be too large, especially on AVR 59 : * microcontrollers. 60 : * Another reason to do it this way, is that this approach is still fast enough 61 : * to make sure it is not noticable to human users. 62 : */ 63 34 : class ExtendedIOElement : public UpdatableCRTP<ExtendedIOElement> { 64 : protected: 65 : /** 66 : * @brief Create an ExtendedIOElement with the given number of pins. 67 : * 68 : * @param length 69 : * The number of pins this element has. 70 : */ 71 : ExtendedIOElement(pin_t length); 72 : 73 : public: 74 : /** 75 : * @brief Set the mode of a given pin. 76 : * 77 : * @note This function might not be implemented by all subclasses. 78 : * Some extended IO types, such as shift registers, can only be 79 : * used as outputs. 80 : * On others, it might be implemented, but it could impact all pins 81 : * of the IO element. For example, enabling the internal pull-up 82 : * resistor on an analog multiplexer affects all pins of the mux. 83 : * 84 : * @param pin 85 : * The (zero-based) pin of this IO element. 86 : * @param mode 87 : * The mode to set the pin to (e.g. `INPUT`, `OUTPUT` or 88 : * `INPUT_PULLUP`). 89 : */ 90 1 : virtual void pinMode(pin_t pin, PinMode_t mode) { 91 1 : pinModeBuffered(pin, mode); 92 1 : updateBufferedOutputs(); 93 1 : } 94 : 95 : /** 96 : * @brief Set the mode of a given pin in the software buffer. 97 : * The buffer is written to the ExtIO device when @ref updateBufferedOutputs 98 : * is called. 99 : * @copydetails pinMode 100 : */ 101 : virtual void pinModeBuffered(pin_t pin, PinMode_t mode) = 0; 102 : 103 : /** 104 : * @brief Set the output of the given pin to the given state. 105 : * 106 : * @param pin 107 : * The (zero-based) pin of this IO element. 108 : * @param state 109 : * The new state to set the pin to. 110 : */ 111 1 : virtual void digitalWrite(pin_t pin, PinStatus_t state) { 112 1 : digitalWriteBuffered(pin, state); 113 1 : updateBufferedOutputs(); 114 1 : } 115 : 116 : /** 117 : * @brief Set the output of a given pin in the software buffer. 118 : * The buffer is written to the ExtIO device when @ref updateBufferedOutputs 119 : * is called. 120 : * @copydetails digitalWrite 121 : */ 122 : virtual void digitalWriteBuffered(pin_t pin, PinStatus_t state) = 0; 123 : 124 : /** 125 : * @brief Read the state of the given pin. 126 : * 127 : * @param pin 128 : * The (zero-based) pin of this IO element. 129 : * @return The state of the given pin. 130 : */ 131 1 : virtual int digitalRead(pin_t pin) { 132 1 : updateBufferedInputs(); 133 1 : return digitalReadBuffered(pin); 134 : } 135 : 136 : /** 137 : * @brief Read the state of the given pin from the software buffer. 138 : * To update the buffer, you have to call @ref updateBufferedInputs first. 139 : * @copydetails digitalRead 140 : */ 141 : virtual int digitalReadBuffered(pin_t pin) = 0; 142 : 143 : /** 144 : * @brief Write an analog (or PWM) value to the given pin. 145 : * 146 : * @param pin 147 : * The (zero-based) pin of this IO element. 148 : * @param val 149 : * The new analog value to set the pin to. 150 : */ 151 1 : virtual void analogWrite(pin_t pin, analog_t val) { 152 1 : analogWriteBuffered(pin, val); 153 1 : updateBufferedOutputs(); 154 1 : } 155 : 156 : /** 157 : * @brief Write an analog (or PWM) value to the software buffer given pin. 158 : * The buffer is written to the ExtIO device when @ref updateBufferedOutputs 159 : * is called. 160 : * @copydetails analogWrite 161 : */ 162 : virtual void analogWriteBuffered(pin_t pin, analog_t val) = 0; 163 : 164 : /** 165 : * @brief Read the analog value of the given pin. 166 : * 167 : * @param pin 168 : * The (zero-based) pin of this IO element. 169 : * @return The new analog value of pin. 170 : */ 171 1 : virtual analog_t analogRead(pin_t pin) { 172 1 : updateBufferedInputs(); 173 1 : return analogReadBuffered(pin); 174 : } 175 : 176 : /** 177 : * @brief Read the analog value of the given pin from the software buffer. 178 : * To update the buffer, you have to call @ref updateBufferedInputs first. 179 : * @copydetails analogRead 180 : */ 181 : virtual analog_t analogReadBuffered(pin_t pin) = 0; 182 : 183 : /** 184 : * @brief Initialize the extended IO element. 185 : */ 186 : virtual void begin() = 0; 187 : 188 : /** 189 : * @brief Initialize all extended IO elements. 190 : */ 191 : static void beginAll(); 192 : 193 : /** 194 : * @brief Write the internal state to the physical outputs. 195 : */ 196 : virtual void updateBufferedOutputs() = 0; 197 : 198 : /** 199 : * @brief Write the internal states to the physical outputs for all 200 : * extended IO elements. 201 : */ 202 : static void updateAllBufferedOutputs(); 203 : 204 : /** 205 : * @brief Read the physical state into the input buffers. 206 : */ 207 : virtual void updateBufferedInputs() = 0; 208 : 209 : /** 210 : * @brief Read the physical state into the input buffers for all extended 211 : * IO elements. 212 : */ 213 : static void updateAllBufferedInputs(); 214 : 215 : /** 216 : * @brief Get the extended IO pin number of a given physical pin of this 217 : * extended IO element. 218 : * @param pin 219 : * The zero-based physical pin number of this IO element. 220 : * @return The global, unique extended IO pin number for the given pin. 221 : */ 222 : pin_t pin(pin_t pin) const; 223 : 224 : /** 225 : * @brief Get the extended IO pin number of a given physical pin of this 226 : * extended IO element. 227 : * It is alias for `ExtendedIOElement::pin`. 228 : * @param pin 229 : * The zero-based physical pin number of this IO element. 230 : * @return The global, unique extended IO pin number for the given pin. 231 : */ 232 : pin_t operator[](pin_t pin) const; 233 : 234 : /** 235 : * @brief Get the number of pins this IO element has. 236 : * 237 : * @return The number of pins this IO element has. 238 : */ 239 : pin_t getLength() const; 240 : 241 : /** 242 : * @brief Get the largest global extended IO pin number that belongs to 243 : * this extended IO element. 244 : */ 245 : pin_t getEnd() const; 246 : 247 : /** 248 : * @brief Get the smallest global extended IO pin number that belongs to 249 : * this extended IO element. 250 : */ 251 : pin_t getStart() const; 252 : 253 : /** 254 : * @brief Get the list of all Extended IO elements. 255 : */ 256 : static DoublyLinkedList<ExtendedIOElement> &getAll(); 257 : 258 : private: 259 : const pin_t length; 260 : const pin_t start; 261 : const pin_t end; 262 : static pin_t offset; 263 : }; 264 : 265 : END_AH_NAMESPACE 266 : 267 : AH_DIAGNOSTIC_POP()