|
| 1 | +/* |
| 2 | + * The Wire Library (https://www.arduino.cc/en/Reference/Wire) |
| 3 | + * allows you to communicate with I2C/TWI devices. The general |
| 4 | + * TWI protocol supports one "master" device and many "slave" |
| 5 | + * devices that share the same two wires (SDA and SCL for data |
| 6 | + * and clock respectively). |
| 7 | + * |
| 8 | + * You initialize the library by calling begin() as a master or |
| 9 | + * begin(myAddress) as a slave (with an int from 8-127). In the |
| 10 | + * initial mock implementation we support only the master role. |
| 11 | + * |
| 12 | + * To send bytes from a master to a slave, start with |
| 13 | + * beginTransmission(slaveAddress), then use write(byte) to |
| 14 | + * enqueue data, and finish with endTransmission(). |
| 15 | + * |
| 16 | + * When a master wants to read, it starts with a call to |
| 17 | + * requestFrom(slaveAddress, quantity) which blocks until the |
| 18 | + * request finishes. The return value is either 0 (if the slave |
| 19 | + * does not respond) or the number of bytes requested (which |
| 20 | + * might be more than the number sent since reading is simply |
| 21 | + * looking at a pin value at each clock tick). |
| 22 | + * |
| 23 | + * A master can write to or read from two or more slaves in |
| 24 | + * quick succession (say, during one loop() function), so our |
| 25 | + * mock needs to support preloading data to be read from multiple |
| 26 | + * slaves and archive data sent to multiple slaves. |
| 27 | + * |
| 28 | + * In the mock, this is handled by having an array of wireData_t |
| 29 | + * structures, each of which contains a deque for input and a |
| 30 | + * deque for output. You can preload data to be read and you can |
| 31 | + * look at a log of data that has been written. |
| 32 | + */ |
1 | 33 |
|
2 | 34 | #pragma once
|
3 | 35 |
|
4 | 36 | #include <inttypes.h>
|
5 | 37 | #include "Stream.h"
|
| 38 | +#include <cassert> |
| 39 | +#include <deque> |
| 40 | +using std::deque; |
| 41 | + |
| 42 | +const size_t SLAVE_COUNT = 128; |
| 43 | +const size_t BUFFER_LENGTH = 32; |
| 44 | + |
| 45 | +struct wireData_t { |
| 46 | + uint8_t misoSize; // bytes remaining for this read |
| 47 | + uint8_t mosiSize; // bytes included in this write |
| 48 | + deque<uint8_t> misoBuffer; // master in, slave out |
| 49 | + deque<uint8_t> mosiBuffer; // master out, slave in |
| 50 | +}; |
| 51 | + |
| 52 | +// Some inspiration taken from |
| 53 | +// https://github.com/arduino/ArduinoCore-megaavr/blob/d2a81093ba66d22dbda14c30d146c231c5910734/libraries/Wire/src/Wire.cpp |
| 54 | +class TwoWire : public ObservableDataStream { |
| 55 | +private: |
| 56 | + bool _didBegin = false; |
| 57 | + wireData_t *in = nullptr; // pointer to current slave for writing |
| 58 | + wireData_t *out = nullptr; // pointer to current slave for reading |
| 59 | + wireData_t slaves[SLAVE_COUNT]; |
6 | 60 |
|
7 |
| -class TwoWire : public ObservableDataStream |
8 |
| -{ |
9 | 61 | public:
|
| 62 | + // constructor initializes internal data |
10 | 63 | TwoWire() {
|
| 64 | + for (int i = 0; i < SLAVE_COUNT; ++i) { |
| 65 | + slaves[i].misoSize = 0; |
| 66 | + slaves[i].mosiSize = 0; |
| 67 | + } |
11 | 68 | }
|
12 | 69 |
|
13 | 70 | // https://www.arduino.cc/en/Reference/WireBegin
|
14 |
| - // Initiate the Wire library and join the I2C bus as a master or slave. This should normally be called only once. |
15 |
| - void begin() { |
16 |
| - isMaster = true; |
17 |
| - } |
18 |
| - void begin(int address) { |
19 |
| - i2cAddress = address; |
20 |
| - isMaster = false; |
21 |
| - } |
| 71 | + // Initiate the Wire library and join the I2C bus as a master or slave. This |
| 72 | + // should normally be called only once. |
| 73 | + void begin() { begin(0); } |
22 | 74 | void begin(uint8_t address) {
|
23 |
| - begin((int)address); |
24 |
| - } |
25 |
| - void end() { |
26 |
| - // TODO: implement |
| 75 | + assert(address == 0); |
| 76 | + _didBegin = true; |
27 | 77 | }
|
| 78 | + void begin(int address) { begin((uint8_t)address); } |
| 79 | + // NOTE: end() is not part of the published API so we ignore it |
| 80 | + void end() {} |
28 | 81 |
|
29 | 82 | // https://www.arduino.cc/en/Reference/WireSetClock
|
30 |
| - // This function modifies the clock frequency for I2C communication. I2C slave devices have no minimum working |
31 |
| - // clock frequency, however 100KHz is usually the baseline. |
32 |
| - void setClock(uint32_t) { |
33 |
| - // TODO: implement? |
34 |
| - } |
| 83 | + // This function modifies the clock frequency for I2C communication. I2C slave |
| 84 | + // devices have no minimum working clock frequency, however 100KHz is usually |
| 85 | + // the baseline. |
| 86 | + // Since the mock does not actually write pins we ignore this. |
| 87 | + void setClock(uint32_t clock) {} |
35 | 88 |
|
36 | 89 | // https://www.arduino.cc/en/Reference/WireBeginTransmission
|
37 |
| - // Begin a transmission to the I2C slave device with the given address. Subsequently, queue bytes for |
38 |
| - // transmission with the write() function and transmit them by calling endTransmission(). |
39 |
| - void beginTransmission(int address) { |
40 |
| - // TODO: implement |
41 |
| - } |
| 90 | + // Begin a transmission to the I2C slave device with the given address. |
| 91 | + // Subsequently, queue bytes for transmission with the write() function and |
| 92 | + // transmit them by calling endTransmission(). |
| 93 | + // For the mock we update our output to the proper destination. |
42 | 94 | void beginTransmission(uint8_t address) {
|
43 |
| - beginTransmission((int)address); |
| 95 | + assert(_didBegin); |
| 96 | + assert(address > 0 && address < SLAVE_COUNT); |
| 97 | + assert(out == nullptr); |
| 98 | + out = &slaves[address]; |
| 99 | + out->mosiSize = 0; |
44 | 100 | }
|
| 101 | + void beginTransmission(int address) { beginTransmission((uint8_t)address); } |
45 | 102 |
|
46 | 103 | // https://www.arduino.cc/en/Reference/WireEndTransmission
|
47 |
| - // Ends a transmission to a slave device that was begun by beginTransmission() and transmits the bytes that were |
48 |
| - // queued by write(). |
49 |
| - uint8_t endTransmission(uint8_t sendStop) { |
50 |
| - // TODO: implement |
| 104 | + // Ends a transmission to a slave device that was begun by beginTransmission() |
| 105 | + // and transmits the bytes that were queued by write(). |
| 106 | + // In the mock we just leave the bytes there in the buffer |
| 107 | + // to be read by the testing API and we ignore the sendStop. |
| 108 | + uint8_t endTransmission(bool sendStop) { |
| 109 | + assert(_didBegin); |
| 110 | + assert(out); |
| 111 | + out = nullptr; |
51 | 112 | return 0; // success
|
52 | 113 | }
|
53 |
| - uint8_t endTransmission(void) { |
54 |
| - return endTransmission((uint8_t)true); |
55 |
| - } |
| 114 | + uint8_t endTransmission(void) { return endTransmission(true); } |
56 | 115 |
|
57 | 116 | // https://www.arduino.cc/en/Reference/WireRequestFrom
|
58 |
| - // Used by the master to request bytes from a slave device. The bytes may then be retrieved with the |
59 |
| - // available() and read() functions. |
60 |
| - uint8_t requestFrom(int address, int quantity, int stop) { |
61 |
| - // TODO: implement |
62 |
| - return 0; // number of bytes returned from the slave device |
| 117 | + // Used by the master to request bytes from a slave device. The bytes may then |
| 118 | + // be retrieved with the available() and read() functions. |
| 119 | + uint8_t requestFrom(uint8_t address, size_t quantity, bool stop) { |
| 120 | + assert(_didBegin); |
| 121 | + assert(address > 0 && address < SLAVE_COUNT); |
| 122 | + assert(quantity <= BUFFER_LENGTH); |
| 123 | + in = &slaves[address]; |
| 124 | + // do we have enough data in the input buffer |
| 125 | + if (quantity <= (in->misoBuffer).size()) { // enough data |
| 126 | + in->misoSize = quantity; |
| 127 | + return quantity; |
| 128 | + } else { // not enough data |
| 129 | + in->misoSize = 0; |
| 130 | + in = nullptr; |
| 131 | + return 0; |
| 132 | + } |
63 | 133 | }
|
64 | 134 | uint8_t requestFrom(int address, int quantity) {
|
65 |
| - int stop = true; |
66 |
| - return requestFrom(address, quantity, stop); |
67 |
| - } |
68 |
| - uint8_t requestFrom(uint8_t address, uint8_t quantity) { |
69 |
| - return requestFrom((int)address, (int)quantity); |
| 135 | + return requestFrom((uint8_t)address, (size_t)quantity, true); |
70 | 136 | }
|
71 |
| - uint8_t requestFrom(uint8_t address, uint8_t quantity, uint8_t stop) { |
72 |
| - return requestFrom((int)address, (int)quantity, (int)stop); |
73 |
| - } |
74 |
| - uint8_t requestFrom(uint8_t, uint8_t, uint32_t, uint8_t, uint8_t) { |
75 |
| - // TODO: implement |
76 |
| - return 0; |
| 137 | + uint8_t requestFrom(int address, int quantity, int stop) { |
| 138 | + return requestFrom((uint8_t)address, (size_t)quantity, (bool)stop); |
77 | 139 | }
|
78 | 140 |
|
79 | 141 | // https://www.arduino.cc/en/Reference/WireWrite
|
80 |
| - // Writes data from a slave device in response to a request from a master, or queues bytes for transmission from a |
81 |
| - // master to slave device (in-between calls to beginTransmission() and endTransmission()). |
| 142 | + // Writes data from a slave device in response to a request from a master, or |
| 143 | + // queues bytes for transmission from a master to slave device (in-between |
| 144 | + // calls to beginTransmission() and endTransmission()). |
82 | 145 | size_t write(uint8_t value) {
|
83 |
| - // TODO: implement |
84 |
| - return 0; // number of bytes written |
| 146 | + assert(out); |
| 147 | + assert(++(out->mosiSize) <= BUFFER_LENGTH); |
| 148 | + (out->mosiBuffer).push_back(value); |
| 149 | + return 1; // number of bytes written |
| 150 | + } |
| 151 | + size_t write(const char *str) { |
| 152 | + return str == NULL ? 0 : write((const uint8_t *)str, String(str).length()); |
85 | 153 | }
|
86 |
| - size_t write(const char *str) { return str == NULL ? 0 : write((const uint8_t *)str, String(str).length()); } |
87 | 154 | size_t write(const uint8_t *buffer, size_t size) {
|
88 | 155 | size_t n;
|
89 |
| - for (n = 0; size && write(*buffer++) && ++n; --size); |
| 156 | + for (n = 0; size && write(*buffer++) && ++n; --size) |
| 157 | + ; |
90 | 158 | return n;
|
91 | 159 | }
|
92 |
| - size_t write(const char *buffer, size_t size) { return write((const uint8_t *)buffer, size); } |
| 160 | + size_t write(const char *buffer, size_t size) { |
| 161 | + return write((const uint8_t *)buffer, size); |
| 162 | + } |
93 | 163 | size_t write(unsigned long n) { return write((uint8_t)n); }
|
94 | 164 | size_t write(long n) { return write((uint8_t)n); }
|
95 | 165 | size_t write(unsigned int n) { return write((uint8_t)n); }
|
96 | 166 | size_t write(int n) { return write((uint8_t)n); }
|
97 | 167 |
|
98 | 168 | // https://www.arduino.cc/en/Reference/WireAvailable
|
99 |
| - // Returns the number of bytes available for retrieval with read(). This should be called on a master device after a |
100 |
| - // call to requestFrom() or on a slave inside the onReceive() handler. |
| 169 | + // Returns the number of bytes available for retrieval with read(). This |
| 170 | + // should be called on a master device after a call to requestFrom() or on a |
| 171 | + // slave inside the onReceive() handler. |
101 | 172 | int available(void) {
|
102 |
| - // TODO: implement |
103 |
| - return 0; // number of bytes available for reading |
| 173 | + assert(in); |
| 174 | + return in->misoSize; |
104 | 175 | }
|
105 | 176 |
|
106 | 177 | // https://www.arduino.cc/en/Reference/WireRead
|
107 |
| - // Reads a byte that was transmitted from a slave device to a master after a call to requestFrom() or was transmitted |
108 |
| - // from a master to a slave. read() inherits from the Stream utility class. |
109 |
| - int read(void) { |
110 |
| - // TODO: implement |
111 |
| - return '\0'; // The next byte received |
| 178 | + // Reads a byte that was transmitted from a slave device to a master after a |
| 179 | + // call to requestFrom() or was transmitted from a master to a slave. read() |
| 180 | + // inherits from the Stream utility class. |
| 181 | + // In the mock we simply return the next byte from the input buffer. |
| 182 | + uint8_t read(void) { |
| 183 | + uint8_t value = peek(); |
| 184 | + --in->misoSize; |
| 185 | + in->misoBuffer.pop_front(); |
| 186 | + return value; // The next byte received |
112 | 187 | }
|
113 |
| - int peek(void) { |
114 |
| - // TODO: implement |
115 |
| - return 0; |
| 188 | + |
| 189 | + // part of the Stream API |
| 190 | + uint8_t peek(void) { |
| 191 | + assert(in); |
| 192 | + assert(0 < in->misoSize); |
| 193 | + return in->misoBuffer.front(); // The next byte received |
116 | 194 | }
|
| 195 | + |
| 196 | + // part of the Stream API |
117 | 197 | void flush(void) {
|
118 |
| - // TODO: implement |
| 198 | + // NOTE: commented out in the megaavr repository |
| 199 | + // data already at the (mock) destination |
119 | 200 | }
|
120 | 201 |
|
121 | 202 | // https://www.arduino.cc/en/Reference/WireOnReceive
|
122 |
| - // Registers a function to be called when a slave device receives a transmission from a master. |
123 |
| - void onReceive( void (*callback)(int) ) { |
124 |
| - // TODO: implement |
125 |
| - } |
| 203 | + // Registers a function to be called when a slave device receives a |
| 204 | + // transmission from a master. |
| 205 | + // We don't (yet) support the slave role in the mock |
| 206 | + void onReceive(void (*callback)(int)) { assert(false); } |
126 | 207 |
|
127 | 208 | // https://www.arduino.cc/en/Reference/WireOnRequest
|
128 |
| - // Register a function to be called when a master requests data from this slave device. |
129 |
| - void onRequest( void (*callback)(void) ) { |
130 |
| - // TODO: implement |
131 |
| - } |
| 209 | + // Register a function to be called when a master requests data from this |
| 210 | + // slave device. |
| 211 | + // We don't (yet) support the slave role in the mock |
| 212 | + void onRequest(void (*callback)(void)) { assert(false); } |
132 | 213 |
|
133 |
| -private: |
134 |
| - int i2cAddress; |
135 |
| - bool isMaster = false; |
| 214 | + // testing methods |
| 215 | + bool didBegin() { return _didBegin; } |
| 216 | + |
| 217 | + deque<uint8_t> *getMiso(uint8_t address) { |
| 218 | + return &slaves[address].misoBuffer; |
| 219 | + } |
| 220 | + deque<uint8_t> *getMosi(uint8_t address) { |
| 221 | + return &slaves[address].mosiBuffer; |
| 222 | + } |
136 | 223 | };
|
137 | 224 |
|
138 | 225 | extern TwoWire Wire;
|
0 commit comments