From bb61617cba292e1e31f58e016e2103468052d514 Mon Sep 17 00:00:00 2001 From: Lucas Saca Date: Sun, 18 Oct 2020 21:55:22 -0700 Subject: [PATCH 01/12] Added crude high level Wire implementation + Wire unit tests. --- cpp/arduino/Wire.h | 52 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/cpp/arduino/Wire.h b/cpp/arduino/Wire.h index c85e816e..66583a76 100644 --- a/cpp/arduino/Wire.h +++ b/cpp/arduino/Wire.h @@ -2,8 +2,12 @@ #pragma once #include +#include +#include #include "Stream.h" + +// Some inspiration taken from https://github.com/arduino/ArduinoCore-megaavr/blob/d2a81093ba66d22dbda14c30d146c231c5910734/libraries/Wire/src/Wire.cpp class TwoWire : public ObservableDataStream { public: @@ -14,9 +18,10 @@ class TwoWire : public ObservableDataStream // Initiate the Wire library and join the I2C bus as a master or slave. This should normally be called only once. void begin() { isMaster = true; + txAddress = 0; } void begin(int address) { - i2cAddress = address; + txAddress = address; isMaster = false; } void begin(uint8_t address) { @@ -24,13 +29,15 @@ class TwoWire : public ObservableDataStream } void end() { // TODO: implement + // NOTE: unnecessary for current high level implementation } // https://www.arduino.cc/en/Reference/WireSetClock // This function modifies the clock frequency for I2C communication. I2C slave devices have no minimum working // clock frequency, however 100KHz is usually the baseline. - void setClock(uint32_t) { + void setClock(uint32_t clock) { // TODO: implement? + // NOTE: unnecessary for current high level implementation } // https://www.arduino.cc/en/Reference/WireBeginTransmission @@ -38,6 +45,9 @@ class TwoWire : public ObservableDataStream // transmission with the write() function and transmit them by calling endTransmission(). void beginTransmission(int address) { // TODO: implement + assert(isMaster); + txAddress = address; + txBuffer.clear(); } void beginTransmission(uint8_t address) { beginTransmission((int)address); @@ -47,7 +57,10 @@ class TwoWire : public ObservableDataStream // Ends a transmission to a slave device that was begun by beginTransmission() and transmits the bytes that were // queued by write(). uint8_t endTransmission(uint8_t sendStop) { - // TODO: implement + assert(isMaster); + txAddress = 0; + writeData = txBuffer; + txBuffer.clear(); return 0; // success } uint8_t endTransmission(void) { @@ -59,6 +72,8 @@ class TwoWire : public ObservableDataStream // available() and read() functions. uint8_t requestFrom(int address, int quantity, int stop) { // TODO: implement + // NOTE: deemed unnecessary for current high level implementation + assert(isMaster); return 0; // number of bytes returned from the slave device } uint8_t requestFrom(int address, int quantity) { @@ -81,7 +96,8 @@ class TwoWire : public ObservableDataStream // master to slave device (in-between calls to beginTransmission() and endTransmission()). size_t write(uint8_t value) { // TODO: implement - return 0; // number of bytes written + txBuffer.push_back(value); + return 1; // number of bytes written } size_t write(const char *str) { return str == NULL ? 0 : write((const uint8_t *)str, String(str).length()); } size_t write(const uint8_t *buffer, size_t size) { @@ -100,7 +116,9 @@ class TwoWire : public ObservableDataStream // call to requestFrom() or on a slave inside the onReceive() handler. int available(void) { // TODO: implement - return 0; // number of bytes available for reading + // NOTE: probably unnecessary for current high level implementation + + return rxBuffer.size(); // number of bytes available for reading } // https://www.arduino.cc/en/Reference/WireRead @@ -108,31 +126,51 @@ class TwoWire : public ObservableDataStream // from a master to a slave. read() inherits from the Stream utility class. int read(void) { // TODO: implement - return '\0'; // The next byte received + int value = -1; + value = rxBuffer.at(0); + rxBuffer.erase(rxBuffer.begin()); + return value; // The next byte received } int peek(void) { // TODO: implement + int value = -1; + value = rxBuffer.at(0); return 0; } void flush(void) { // TODO: implement + // NOTE: commented out in the megaavr repository + txBuffer.clear(); + rxBuffer.clear(); } // https://www.arduino.cc/en/Reference/WireOnReceive // Registers a function to be called when a slave device receives a transmission from a master. void onReceive( void (*callback)(int) ) { // TODO: implement + user_onReceive = callback; } // https://www.arduino.cc/en/Reference/WireOnRequest // Register a function to be called when a master requests data from this slave device. void onRequest( void (*callback)(void) ) { // TODO: implement + user_onRequest = callback; } + // testing methods + bool getIsMaster() { return isMaster; } + int getAddress() { return txAddress; } + vector getTxBuffer() { return txBuffer; } + vector getWriteData() { return writeData; } + private: - int i2cAddress; bool isMaster = false; + uint8_t txAddress; + static void (*user_onReceive)(int); + static void (*user_onRequest)(void); + // Consider queue data structure for a more "buffer-like" implementation with HEAD/TAIL + vector txBuffer, rxBuffer, writeData; }; extern TwoWire Wire; From 4989505a3a828b394310bc634e59c66cd7dcff48 Mon Sep 17 00:00:00 2001 From: Lucas Saca Date: Mon, 19 Oct 2020 00:20:32 -0700 Subject: [PATCH 02/12] Forgot to add new wire test file --- SampleProjects/TestSomething/test/wire.cpp | 50 ++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 SampleProjects/TestSomething/test/wire.cpp diff --git a/SampleProjects/TestSomething/test/wire.cpp b/SampleProjects/TestSomething/test/wire.cpp new file mode 100644 index 00000000..6e63e910 --- /dev/null +++ b/SampleProjects/TestSomething/test/wire.cpp @@ -0,0 +1,50 @@ +#include +#include +#include + +#include + +unittest(beginAsMaster) { + Wire.begin(); + assertTrue(Wire.getIsMaster()); +} + +unittest(beginAsSlave) { + Wire.begin(13); + assertFalse(Wire.getIsMaster()); +} + +unittest(getMasterAddress) { + Wire.begin(); + assertEqual(0, Wire.getAddress()); +} + +unittest(getSlaveAddress) { + Wire.begin(13); + assertEqual(13, Wire.getAddress()); +} + +unittest(begin_write_end) { + Wire.begin(); + Wire.beginTransmission(14); + assertEqual(14, txAddress); + + assertTrue(Wire.getTxBuffer().empty()); + + Wire.write(0x07); + Wire.write(0x0E); + assertEqual(0x07, getTxBuffer().at(0)); + assertEqual(0x0E, getTxBuffer().at(1)); + + Wire.endTransmission(true); + assertTrue(txBuffer.empty()); + assertEqual(0x07, getWriteData.at(0)); + assertEqual(0x0E, getWriteData.at(1)); +} + +// want to add read test, though it seems to depend on requestFrom + + + + +unittest_main() \ No newline at end of file From 13862bc6af88200a7c451d4a826c3ea74c5c4747 Mon Sep 17 00:00:00 2001 From: Lucas Saca Date: Mon, 19 Oct 2020 18:34:38 -0700 Subject: [PATCH 03/12] me dumb and fixed wire tests, though there's an anomaly in compilation --- SampleProjects/TestSomething/test/wire.cpp | 15 ++++++++------- cpp/arduino/Wire.h | 6 ++++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/SampleProjects/TestSomething/test/wire.cpp b/SampleProjects/TestSomething/test/wire.cpp index 6e63e910..3f3cd366 100644 --- a/SampleProjects/TestSomething/test/wire.cpp +++ b/SampleProjects/TestSomething/test/wire.cpp @@ -3,6 +3,7 @@ #include #include +using namespace std; unittest(beginAsMaster) { Wire.begin(); @@ -27,19 +28,19 @@ unittest(getSlaveAddress) { unittest(begin_write_end) { Wire.begin(); Wire.beginTransmission(14); - assertEqual(14, txAddress); + assertEqual(14, Wire.getAddress()); - assertTrue(Wire.getTxBuffer().empty()); + assertTrue(Wire.isTxBufferEmpty()); Wire.write(0x07); Wire.write(0x0E); - assertEqual(0x07, getTxBuffer().at(0)); - assertEqual(0x0E, getTxBuffer().at(1)); + assertEqual(0x07, Wire.getTxBufferElement(0)); + assertEqual(0x0E, Wire.getTxBufferElement(1)); Wire.endTransmission(true); - assertTrue(txBuffer.empty()); - assertEqual(0x07, getWriteData.at(0)); - assertEqual(0x0E, getWriteData.at(1)); + assertTrue(Wire.isTxBufferEmpty()); + assertEqual(0x07, Wire.getWriteDataElement(0)); + assertEqual(0x0E, Wire.getWriteDataElement(1)); } // want to add read test, though it seems to depend on requestFrom diff --git a/cpp/arduino/Wire.h b/cpp/arduino/Wire.h index 66583a76..325d67ca 100644 --- a/cpp/arduino/Wire.h +++ b/cpp/arduino/Wire.h @@ -6,6 +6,7 @@ #include #include "Stream.h" +using std::vector; // Some inspiration taken from https://github.com/arduino/ArduinoCore-megaavr/blob/d2a81093ba66d22dbda14c30d146c231c5910734/libraries/Wire/src/Wire.cpp class TwoWire : public ObservableDataStream @@ -161,8 +162,9 @@ class TwoWire : public ObservableDataStream // testing methods bool getIsMaster() { return isMaster; } int getAddress() { return txAddress; } - vector getTxBuffer() { return txBuffer; } - vector getWriteData() { return writeData; } + bool isTxBufferEmpty() { return txBuffer.empty(); } + uint8_t getTxBufferElement(int index) { return txBuffer.at(index); } + uint8_t getWriteDataElement(int index) { return writeData.at(index); } private: bool isMaster = false; From 226bc18f43775f5ee26a972ccdd422e6061d26b7 Mon Sep 17 00:00:00 2001 From: Lucas Saca Date: Mon, 26 Oct 2020 03:36:34 -0700 Subject: [PATCH 04/12] Completed wire implementation with address tracking via maps + tests --- SampleProjects/TestSomething/test/wire.cpp | 35 +++++++++++-- cpp/arduino/Wire.h | 58 ++++++++++++++++------ 2 files changed, 75 insertions(+), 18 deletions(-) diff --git a/SampleProjects/TestSomething/test/wire.cpp b/SampleProjects/TestSomething/test/wire.cpp index 3f3cd366..79d11619 100644 --- a/SampleProjects/TestSomething/test/wire.cpp +++ b/SampleProjects/TestSomething/test/wire.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include using namespace std; @@ -37,15 +38,41 @@ unittest(begin_write_end) { assertEqual(0x07, Wire.getTxBufferElement(0)); assertEqual(0x0E, Wire.getTxBufferElement(1)); - Wire.endTransmission(true); + Wire.endTransmission(); assertTrue(Wire.isTxBufferEmpty()); - assertEqual(0x07, Wire.getWriteDataElement(0)); - assertEqual(0x0E, Wire.getWriteDataElement(1)); + + vector finalData = {0x07, 0x0E}; + assert(finalData == Wire.getDataWritten(14)); } -// want to add read test, though it seems to depend on requestFrom +unittest(readTwo_writeOne) { + Wire.begin(); + + vector data1 = {0x07, 0x0E}, data2 = {1, 4, 7}; + Wire.setDataToRead(19, data1); + Wire.setDataToRead(34, data2); + assertEqual(2, Wire.requestFrom(19, 1)); + assertEqual(3, Wire.requestFrom(34, 1)); + assertEqual(data1.size() + data2.size(), Wire.getRxBufferSize()); + Wire.beginTransmission(47); + assertEqual(47, Wire.getAddress()); + assertTrue(Wire.isTxBufferEmpty()); + for (int i = 0; i < 5; i++) { + Wire.write(Wire.read()); + } + assertEqual(0x07, Wire.getTxBufferElement(0)); + assertEqual(0x0E, Wire.getTxBufferElement(1)); + assertEqual(1, Wire.getTxBufferElement(2)); + assertEqual(4, Wire.getTxBufferElement(3)); + assertEqual(7, Wire.getTxBufferElement(4)); + + Wire.endTransmission(); + assertTrue(Wire.isTxBufferEmpty()); + vector finalData = {0x07, 0x0E, 1, 4, 7}; + assert(finalData == Wire.getDataWritten(47)); +} unittest_main() \ No newline at end of file diff --git a/cpp/arduino/Wire.h b/cpp/arduino/Wire.h index 325d67ca..ebbc2537 100644 --- a/cpp/arduino/Wire.h +++ b/cpp/arduino/Wire.h @@ -2,11 +2,38 @@ #pragma once #include + +// https://github.com/Arduino-CI/arduino_ci/issues/165 +#ifdef max +#undef max +#ifdef __cplusplus +template +auto max(const T &a, const L &b) -> decltype((b < a) ? b : a) { + return (a < b) ? b : a; +} +#else +#define max(a, b) \ + ({ \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a > _b ? _a : _b; \ + }) +#endif +#endif + +// map is already defined in AvrMath.h. Need to use C++'s map. +#ifdef map +#undef map +#endif + + #include +#include #include #include "Stream.h" using std::vector; +using std::map; // Some inspiration taken from https://github.com/arduino/ArduinoCore-megaavr/blob/d2a81093ba66d22dbda14c30d146c231c5910734/libraries/Wire/src/Wire.cpp class TwoWire : public ObservableDataStream @@ -45,7 +72,6 @@ class TwoWire : public ObservableDataStream // Begin a transmission to the I2C slave device with the given address. Subsequently, queue bytes for // transmission with the write() function and transmit them by calling endTransmission(). void beginTransmission(int address) { - // TODO: implement assert(isMaster); txAddress = address; txBuffer.clear(); @@ -59,9 +85,12 @@ class TwoWire : public ObservableDataStream // queued by write(). uint8_t endTransmission(uint8_t sendStop) { assert(isMaster); - txAddress = 0; - writeData = txBuffer; + int bufferSize = txBuffer.size(); + dataWritten[txAddress] = txBuffer; txBuffer.clear(); + // ensure separate objects + assert(bufferSize == dataWritten[txAddress].size()); + txAddress = 0; return 0; // success } uint8_t endTransmission(void) { @@ -72,10 +101,13 @@ class TwoWire : public ObservableDataStream // Used by the master to request bytes from a slave device. The bytes may then be retrieved with the // available() and read() functions. uint8_t requestFrom(int address, int quantity, int stop) { - // TODO: implement - // NOTE: deemed unnecessary for current high level implementation + // TODO: implement quantity and stop? assert(isMaster); - return 0; // number of bytes returned from the slave device + + int oldRxBufferLength = rxBuffer.size(); + // append vector to vector + rxBuffer.insert(rxBuffer.end(), dataToRead[address].begin(), dataToRead[address].end()); + return rxBuffer.size()-oldRxBufferLength; // number of bytes returned from the slave device } uint8_t requestFrom(int address, int quantity) { int stop = true; @@ -96,7 +128,6 @@ class TwoWire : public ObservableDataStream // Writes data from a slave device in response to a request from a master, or queues bytes for transmission from a // master to slave device (in-between calls to beginTransmission() and endTransmission()). size_t write(uint8_t value) { - // TODO: implement txBuffer.push_back(value); return 1; // number of bytes written } @@ -116,9 +147,6 @@ class TwoWire : public ObservableDataStream // Returns the number of bytes available for retrieval with read(). This should be called on a master device after a // call to requestFrom() or on a slave inside the onReceive() handler. int available(void) { - // TODO: implement - // NOTE: probably unnecessary for current high level implementation - return rxBuffer.size(); // number of bytes available for reading } @@ -163,16 +191,18 @@ class TwoWire : public ObservableDataStream bool getIsMaster() { return isMaster; } int getAddress() { return txAddress; } bool isTxBufferEmpty() { return txBuffer.empty(); } - uint8_t getTxBufferElement(int index) { return txBuffer.at(index); } - uint8_t getWriteDataElement(int index) { return writeData.at(index); } + int getTxBufferElement(int index) { return txBuffer.at(index); } + vector getDataWritten(int address) { return dataWritten.at(address); } + int getRxBufferSize() { return rxBuffer.size(); } + void setDataToRead(int address, vector data) { dataToRead[address] = data; } private: bool isMaster = false; uint8_t txAddress; static void (*user_onReceive)(int); static void (*user_onRequest)(void); - // Consider queue data structure for a more "buffer-like" implementation with HEAD/TAIL - vector txBuffer, rxBuffer, writeData; + vector txBuffer, rxBuffer; + map> dataToRead, dataWritten; }; extern TwoWire Wire; From 42aaa39b193fa9e809e3159a4ba824610d1f622c Mon Sep 17 00:00:00 2001 From: Lucas Saca Date: Mon, 2 Nov 2020 18:19:42 -0800 Subject: [PATCH 05/12] Wire implementation converted from map to deque, courtesy of James Foster --- SampleProjects/TestSomething/test/wire.cpp | 98 +++---- cpp/arduino/Wire.h | 299 ++++++++++++--------- 2 files changed, 222 insertions(+), 175 deletions(-) diff --git a/SampleProjects/TestSomething/test/wire.cpp b/SampleProjects/TestSomething/test/wire.cpp index 79d11619..724b5db0 100644 --- a/SampleProjects/TestSomething/test/wire.cpp +++ b/SampleProjects/TestSomething/test/wire.cpp @@ -1,78 +1,68 @@ #include #include -#include -#include - #include using namespace std; -unittest(beginAsMaster) { - Wire.begin(); - assertTrue(Wire.getIsMaster()); -} - -unittest(beginAsSlave) { - Wire.begin(13); - assertFalse(Wire.getIsMaster()); -} - -unittest(getMasterAddress) { - Wire.begin(); - assertEqual(0, Wire.getAddress()); -} - -unittest(getSlaveAddress) { - Wire.begin(13); - assertEqual(13, Wire.getAddress()); -} - unittest(begin_write_end) { + deque* mosi = Wire.getMosi(14); + assertEqual(0, mosi->size()); Wire.begin(); Wire.beginTransmission(14); - assertEqual(14, Wire.getAddress()); - - assertTrue(Wire.isTxBufferEmpty()); - Wire.write(0x07); Wire.write(0x0E); - assertEqual(0x07, Wire.getTxBufferElement(0)); - assertEqual(0x0E, Wire.getTxBufferElement(1)); - Wire.endTransmission(); - assertTrue(Wire.isTxBufferEmpty()); - - vector finalData = {0x07, 0x0E}; - assert(finalData == Wire.getDataWritten(14)); + assertEqual(2, mosi->size()); + assertEqual(0x07, mosi->front()); + mosi->pop_front(); + assertEqual(0x0E, mosi->front()); + mosi->pop_front(); + assertEqual(0, mosi->size()); } unittest(readTwo_writeOne) { Wire.begin(); - - vector data1 = {0x07, 0x0E}, data2 = {1, 4, 7}; - Wire.setDataToRead(19, data1); - Wire.setDataToRead(34, data2); + deque* miso; + miso = Wire.getMiso(19); + miso->push_back(0x07); + miso->push_back(0x0E); + miso = Wire.getMiso(34); + miso->push_back(1); + miso->push_back(4); + miso->push_back(7); - assertEqual(2, Wire.requestFrom(19, 1)); - assertEqual(3, Wire.requestFrom(34, 1)); - assertEqual(data1.size() + data2.size(), Wire.getRxBufferSize()); + assertEqual(0, Wire.requestFrom(19, 3)); + assertEqual(2, Wire.requestFrom(19, 2)); + assertEqual(2, Wire.available()); + assertEqual(0x07, Wire.read()); + assertEqual(1, Wire.available()); + assertEqual(0x0E, Wire.read()); + assertEqual(0, Wire.available()); + assertEqual(3, Wire.requestFrom(34, 3)); + assertEqual(3, Wire.available()); + assertEqual(1, Wire.read()); + assertEqual(2, Wire.available()); + assertEqual(4, Wire.read()); + assertEqual(1, Wire.available()); + assertEqual(7, Wire.read()); + assertEqual(0, Wire.available()); Wire.beginTransmission(47); - assertEqual(47, Wire.getAddress()); - assertTrue(Wire.isTxBufferEmpty()); - for (int i = 0; i < 5; i++) { - Wire.write(Wire.read()); + for (int i = 1; i < 4; i++) { + Wire.write(i * 2); } - assertEqual(0x07, Wire.getTxBufferElement(0)); - assertEqual(0x0E, Wire.getTxBufferElement(1)); - assertEqual(1, Wire.getTxBufferElement(2)); - assertEqual(4, Wire.getTxBufferElement(3)); - assertEqual(7, Wire.getTxBufferElement(4)); - Wire.endTransmission(); - assertTrue(Wire.isTxBufferEmpty()); + deque* mosi = Wire.getMosi(47); - vector finalData = {0x07, 0x0E, 1, 4, 7}; - assert(finalData == Wire.getDataWritten(47)); + assertEqual(3, mosi->size()); + assertEqual(2, mosi->front()); + mosi->pop_front(); + assertEqual(2, mosi->size()); + assertEqual(4, mosi->front()); + mosi->pop_front(); + assertEqual(1, mosi->size()); + assertEqual(6, mosi->front()); + mosi->pop_front(); + assertEqual(0, mosi->size()); } unittest_main() \ No newline at end of file diff --git a/cpp/arduino/Wire.h b/cpp/arduino/Wire.h index ebbc2537..41ec2e36 100644 --- a/cpp/arduino/Wire.h +++ b/cpp/arduino/Wire.h @@ -1,3 +1,35 @@ +/* + * The Wire Library (https://www.arduino.cc/en/Reference/Wire) + * allows you to communicate with I2C/TWI devices. The general + * TWI protocol supports one "master" device and many "slave" + * devices that share the same two wires (SDA and SCL for data + * and clock respectively). + * + * You initialize the library by calling begin() as a master or + * begin(myAddress) as a slave (with an int from 8-127). In the + * initial mock implementation we support only the master role. + * + * To send bytes from a master to a slave, start with + * beginTransmission(slaveAddress), then use write(byte) to + * enqueue data, and finish with endTransmission(). + * + * When a master wants to read, it starts with a call to + * requestFrom(slaveAddress, quantity) which blocks until the + * request finishes. The return value is either 0 (if the slave + * does not respond) or the number of bytes requested (which + * might be more than the number sent since reading is simply + * looking at a pin value at each clock tick). + * + * A master can write to or read from two or more slaves in + * quick succession (say, during one loop() function), so our + * mock needs to support preloading data to be read from multiple + * slaves and archive data sent to multiple slaves. + * + * In the mock, this is handled by having an array of wireData_t + * structures, each of which contains a deque for input and a + * deque for output. You can preload data to be read and you can + * look at a log of data that has been written. + */ #pragma once @@ -21,188 +53,213 @@ auto max(const T &a, const L &b) -> decltype((b < a) ? b : a) { #endif #endif -// map is already defined in AvrMath.h. Need to use C++'s map. -#ifdef map -#undef map +#ifdef min +#undef min +#ifdef __cplusplus +template +auto min(const T &a, const L &b) -> decltype((b > a) ? b : a) { + return (a > b) ? b : a; +} +#else +#define min(a, b) \ + ({ \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a < _b ? _a : _b; \ + }) +#endif #endif -#include -#include -#include #include "Stream.h" +#include +#include +using std::deque; -using std::vector; -using std::map; +const size_t SLAVE_COUNT = 128; +const size_t BUFFER_LENGTH = 32; + +struct wireData_t { + uint8_t misoSize; // bytes remaining for this read + uint8_t mosiSize; // bytes included in this write + deque misoBuffer; // master in, slave out + deque mosiBuffer; // master out, slave in +}; + +// Some inspiration taken from +// https://github.com/arduino/ArduinoCore-megaavr/blob/d2a81093ba66d22dbda14c30d146c231c5910734/libraries/Wire/src/Wire.cpp +class TwoWire : public ObservableDataStream { +private: + bool _didBegin = false; + wireData_t *in = nullptr; // pointer to current slave for writing + wireData_t *out = nullptr; // pointer to current slave for reading + wireData_t slaves[SLAVE_COUNT]; -// Some inspiration taken from https://github.com/arduino/ArduinoCore-megaavr/blob/d2a81093ba66d22dbda14c30d146c231c5910734/libraries/Wire/src/Wire.cpp -class TwoWire : public ObservableDataStream -{ public: + // constructor initializes internal data TwoWire() { + for (int i = 0; i < SLAVE_COUNT; ++i) { + slaves[i].misoSize = 0; + slaves[i].mosiSize = 0; + } } // https://www.arduino.cc/en/Reference/WireBegin - // Initiate the Wire library and join the I2C bus as a master or slave. This should normally be called only once. - void begin() { - isMaster = true; - txAddress = 0; - } - void begin(int address) { - txAddress = address; - isMaster = false; - } + // Initiate the Wire library and join the I2C bus as a master or slave. This + // should normally be called only once. + void begin() { begin(0); } void begin(uint8_t address) { - begin((int)address); - } - void end() { - // TODO: implement - // NOTE: unnecessary for current high level implementation + assert(address == 0); + _didBegin = true; } + void begin(int address) { begin((uint8_t)address); } + // NOTE: end() is not part of the published API so we ignore it + void end() {} // https://www.arduino.cc/en/Reference/WireSetClock - // This function modifies the clock frequency for I2C communication. I2C slave devices have no minimum working - // clock frequency, however 100KHz is usually the baseline. - void setClock(uint32_t clock) { - // TODO: implement? - // NOTE: unnecessary for current high level implementation - } + // This function modifies the clock frequency for I2C communication. I2C slave + // devices have no minimum working clock frequency, however 100KHz is usually + // the baseline. + // Since the mock does not actually write pins we ignore this. + void setClock(uint32_t clock) {} // https://www.arduino.cc/en/Reference/WireBeginTransmission - // Begin a transmission to the I2C slave device with the given address. Subsequently, queue bytes for - // transmission with the write() function and transmit them by calling endTransmission(). - void beginTransmission(int address) { - assert(isMaster); - txAddress = address; - txBuffer.clear(); - } + // Begin a transmission to the I2C slave device with the given address. + // Subsequently, queue bytes for transmission with the write() function and + // transmit them by calling endTransmission(). + // For the mock we update our output to the proper destination. void beginTransmission(uint8_t address) { - beginTransmission((int)address); + assert(_didBegin); + assert(address > 0 && address < SLAVE_COUNT); + assert(out == nullptr); + out = &slaves[address]; + out->mosiSize = 0; } + void beginTransmission(int address) { beginTransmission((uint8_t)address); } // https://www.arduino.cc/en/Reference/WireEndTransmission - // Ends a transmission to a slave device that was begun by beginTransmission() and transmits the bytes that were - // queued by write(). - uint8_t endTransmission(uint8_t sendStop) { - assert(isMaster); - int bufferSize = txBuffer.size(); - dataWritten[txAddress] = txBuffer; - txBuffer.clear(); - // ensure separate objects - assert(bufferSize == dataWritten[txAddress].size()); - txAddress = 0; + // Ends a transmission to a slave device that was begun by beginTransmission() + // and transmits the bytes that were queued by write(). + // In the mock we just leave the bytes there in the buffer + // to be read by the testing API and we ignore the sendStop. + uint8_t endTransmission(bool sendStop) { + assert(_didBegin); + assert(out); + out = nullptr; return 0; // success } - uint8_t endTransmission(void) { - return endTransmission((uint8_t)true); - } + uint8_t endTransmission(void) { return endTransmission(true); } // https://www.arduino.cc/en/Reference/WireRequestFrom - // Used by the master to request bytes from a slave device. The bytes may then be retrieved with the - // available() and read() functions. - uint8_t requestFrom(int address, int quantity, int stop) { - // TODO: implement quantity and stop? - assert(isMaster); - - int oldRxBufferLength = rxBuffer.size(); - // append vector to vector - rxBuffer.insert(rxBuffer.end(), dataToRead[address].begin(), dataToRead[address].end()); - return rxBuffer.size()-oldRxBufferLength; // number of bytes returned from the slave device + // Used by the master to request bytes from a slave device. The bytes may then + // be retrieved with the available() and read() functions. + uint8_t requestFrom(uint8_t address, size_t quantity, bool stop) { + assert(_didBegin); + assert(address > 0 && address < SLAVE_COUNT); + assert(quantity <= BUFFER_LENGTH); + in = &slaves[address]; + // do we have enough data in the input buffer + if (quantity <= (in->misoBuffer).size()) { // enough data + in->misoSize = quantity; + return quantity; + } else { // not enough data + in->misoSize = 0; + in = nullptr; + return 0; + } } - uint8_t requestFrom(int address, int quantity) { - int stop = true; - return requestFrom(address, quantity, stop); + uint8_t requestFrom(uint8_t address, size_t quantity) { + return requestFrom(address, quantity, true); } - uint8_t requestFrom(uint8_t address, uint8_t quantity) { - return requestFrom((int)address, (int)quantity); - } - uint8_t requestFrom(uint8_t address, uint8_t quantity, uint8_t stop) { - return requestFrom((int)address, (int)quantity, (int)stop); + uint8_t requestFrom(int address, int quantity) { + return requestFrom((uint8_t)address, (size_t)quantity); } - uint8_t requestFrom(uint8_t, uint8_t, uint32_t, uint8_t, uint8_t) { - // TODO: implement - return 0; + uint8_t requestFrom(int address, int quantity, int stop) { + return requestFrom((uint8_t)address, (size_t)quantity, (bool)stop); } // https://www.arduino.cc/en/Reference/WireWrite - // Writes data from a slave device in response to a request from a master, or queues bytes for transmission from a - // master to slave device (in-between calls to beginTransmission() and endTransmission()). + // Writes data from a slave device in response to a request from a master, or + // queues bytes for transmission from a master to slave device (in-between + // calls to beginTransmission() and endTransmission()). size_t write(uint8_t value) { - txBuffer.push_back(value); + assert(out); + assert(++(out->mosiSize) <= BUFFER_LENGTH); + (out->mosiBuffer).push_back(value); return 1; // number of bytes written } - size_t write(const char *str) { return str == NULL ? 0 : write((const uint8_t *)str, String(str).length()); } + size_t write(const char *str) { + return str == NULL ? 0 : write((const uint8_t *)str, String(str).length()); + } size_t write(const uint8_t *buffer, size_t size) { size_t n; - for (n = 0; size && write(*buffer++) && ++n; --size); + for (n = 0; size && write(*buffer++) && ++n; --size) + ; return n; } - size_t write(const char *buffer, size_t size) { return write((const uint8_t *)buffer, size); } + size_t write(const char *buffer, size_t size) { + return write((const uint8_t *)buffer, size); + } size_t write(unsigned long n) { return write((uint8_t)n); } size_t write(long n) { return write((uint8_t)n); } size_t write(unsigned int n) { return write((uint8_t)n); } size_t write(int n) { return write((uint8_t)n); } // https://www.arduino.cc/en/Reference/WireAvailable - // Returns the number of bytes available for retrieval with read(). This should be called on a master device after a - // call to requestFrom() or on a slave inside the onReceive() handler. + // Returns the number of bytes available for retrieval with read(). This + // should be called on a master device after a call to requestFrom() or on a + // slave inside the onReceive() handler. int available(void) { - return rxBuffer.size(); // number of bytes available for reading + assert(in); + return in->misoSize; } // https://www.arduino.cc/en/Reference/WireRead - // Reads a byte that was transmitted from a slave device to a master after a call to requestFrom() or was transmitted - // from a master to a slave. read() inherits from the Stream utility class. - int read(void) { - // TODO: implement - int value = -1; - value = rxBuffer.at(0); - rxBuffer.erase(rxBuffer.begin()); + // Reads a byte that was transmitted from a slave device to a master after a + // call to requestFrom() or was transmitted from a master to a slave. read() + // inherits from the Stream utility class. + // In the mock we simply return the next byte from the input buffer. + uint8_t read(void) { + uint8_t value = peek(); + --in->misoSize; + in->misoBuffer.pop_front(); return value; // The next byte received } - int peek(void) { - // TODO: implement - int value = -1; - value = rxBuffer.at(0); - return 0; + + // part of the Stream API + uint8_t peek(void) { + assert(in); + assert(0 < in->misoSize); + return in->misoBuffer.front(); // The next byte received } + + // part of the Stream API void flush(void) { - // TODO: implement // NOTE: commented out in the megaavr repository - txBuffer.clear(); - rxBuffer.clear(); + // data already at the (mock) destination } // https://www.arduino.cc/en/Reference/WireOnReceive - // Registers a function to be called when a slave device receives a transmission from a master. - void onReceive( void (*callback)(int) ) { - // TODO: implement - user_onReceive = callback; - } + // Registers a function to be called when a slave device receives a + // transmission from a master. + // We don't (yet) support the slave role in the mock + void onReceive(void (*callback)(int)) { assert(false); } // https://www.arduino.cc/en/Reference/WireOnRequest - // Register a function to be called when a master requests data from this slave device. - void onRequest( void (*callback)(void) ) { - // TODO: implement - user_onRequest = callback; - } + // Register a function to be called when a master requests data from this + // slave device. + // We don't (yet) support the slave role in the mock + void onRequest(void (*callback)(void)) { assert(false); } // testing methods - bool getIsMaster() { return isMaster; } - int getAddress() { return txAddress; } - bool isTxBufferEmpty() { return txBuffer.empty(); } - int getTxBufferElement(int index) { return txBuffer.at(index); } - vector getDataWritten(int address) { return dataWritten.at(address); } - int getRxBufferSize() { return rxBuffer.size(); } - void setDataToRead(int address, vector data) { dataToRead[address] = data; } + bool didBegin() { return _didBegin; } -private: - bool isMaster = false; - uint8_t txAddress; - static void (*user_onReceive)(int); - static void (*user_onRequest)(void); - vector txBuffer, rxBuffer; - map> dataToRead, dataWritten; + deque *getMiso(uint8_t address) { + return &slaves[address].misoBuffer; + } + deque *getMosi(uint8_t address) { + return &slaves[address].mosiBuffer; + } }; extern TwoWire Wire; From 6ddd254464e9eaca06333728aebed125ad4bb63c Mon Sep 17 00:00:00 2001 From: Lucas Saca Date: Mon, 2 Nov 2020 19:37:37 -0800 Subject: [PATCH 06/12] Here's a newline for you: --- SampleProjects/TestSomething/test/wire.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SampleProjects/TestSomething/test/wire.cpp b/SampleProjects/TestSomething/test/wire.cpp index 724b5db0..dd5df1c7 100644 --- a/SampleProjects/TestSomething/test/wire.cpp +++ b/SampleProjects/TestSomething/test/wire.cpp @@ -1,7 +1,7 @@ #include #include #include -using namespace std; +using std::deque; unittest(begin_write_end) { deque* mosi = Wire.getMosi(14); @@ -65,4 +65,4 @@ unittest(readTwo_writeOne) { assertEqual(0, mosi->size()); } -unittest_main() \ No newline at end of file +unittest_main() From d6c3bb8b299b30a19e6988d19be28c5779635690 Mon Sep 17 00:00:00 2001 From: Lucas Saca Date: Sat, 7 Nov 2020 23:27:03 -0800 Subject: [PATCH 07/12] Cleaned up crude fixes for name clashes in wire header. --- cpp/arduino/Wire.h | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/cpp/arduino/Wire.h b/cpp/arduino/Wire.h index 41ec2e36..d1546502 100644 --- a/cpp/arduino/Wire.h +++ b/cpp/arduino/Wire.h @@ -34,43 +34,6 @@ #pragma once #include - -// https://github.com/Arduino-CI/arduino_ci/issues/165 -#ifdef max -#undef max -#ifdef __cplusplus -template -auto max(const T &a, const L &b) -> decltype((b < a) ? b : a) { - return (a < b) ? b : a; -} -#else -#define max(a, b) \ - ({ \ - __typeof__(a) _a = (a); \ - __typeof__(b) _b = (b); \ - _a > _b ? _a : _b; \ - }) -#endif -#endif - -#ifdef min -#undef min -#ifdef __cplusplus -template -auto min(const T &a, const L &b) -> decltype((b > a) ? b : a) { - return (a > b) ? b : a; -} -#else -#define min(a, b) \ - ({ \ - __typeof__(a) _a = (a); \ - __typeof__(b) _b = (b); \ - _a < _b ? _a : _b; \ - }) -#endif -#endif - - #include "Stream.h" #include #include From 2821146b80f1382b8968afbac4614be099b7af40 Mon Sep 17 00:00:00 2001 From: Lucas Saca Date: Tue, 10 Nov 2020 09:15:44 -0800 Subject: [PATCH 08/12] Removed requestFrom() overload to reduce ambiguity. Added wire test comments. --- SampleProjects/TestSomething/test/wire.cpp | 5 +++++ cpp/arduino/Wire.h | 5 +---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/SampleProjects/TestSomething/test/wire.cpp b/SampleProjects/TestSomething/test/wire.cpp index dd5df1c7..8f9d81df 100644 --- a/SampleProjects/TestSomething/test/wire.cpp +++ b/SampleProjects/TestSomething/test/wire.cpp @@ -7,10 +7,12 @@ unittest(begin_write_end) { deque* mosi = Wire.getMosi(14); assertEqual(0, mosi->size()); Wire.begin(); + // write some random values to random slave Wire.beginTransmission(14); Wire.write(0x07); Wire.write(0x0E); Wire.endTransmission(); + // check values assertEqual(2, mosi->size()); assertEqual(0x07, mosi->front()); mosi->pop_front(); @@ -22,6 +24,7 @@ unittest(begin_write_end) { unittest(readTwo_writeOne) { Wire.begin(); deque* miso; + // place some values on random slaves' read buffers miso = Wire.getMiso(19); miso->push_back(0x07); miso->push_back(0x0E); @@ -46,11 +49,13 @@ unittest(readTwo_writeOne) { assertEqual(7, Wire.read()); assertEqual(0, Wire.available()); + // write some values to different random slave Wire.beginTransmission(47); for (int i = 1; i < 4; i++) { Wire.write(i * 2); } Wire.endTransmission(); + // check master write buffer deque* mosi = Wire.getMosi(47); assertEqual(3, mosi->size()); diff --git a/cpp/arduino/Wire.h b/cpp/arduino/Wire.h index d1546502..bedf9d58 100644 --- a/cpp/arduino/Wire.h +++ b/cpp/arduino/Wire.h @@ -131,11 +131,8 @@ class TwoWire : public ObservableDataStream { return 0; } } - uint8_t requestFrom(uint8_t address, size_t quantity) { - return requestFrom(address, quantity, true); - } uint8_t requestFrom(int address, int quantity) { - return requestFrom((uint8_t)address, (size_t)quantity); + return requestFrom((uint8_t)address, (size_t)quantity, true); } uint8_t requestFrom(int address, int quantity, int stop) { return requestFrom((uint8_t)address, (size_t)quantity, (bool)stop); From 01eeaf30af5fc5c3b663cbc1edc204d05f0c5cf0 Mon Sep 17 00:00:00 2001 From: Lucas Saca Date: Tue, 10 Nov 2020 09:17:13 -0800 Subject: [PATCH 09/12] Forgot to add test file to last commit. This is the real commit adding test comments. --- SampleProjects/TestSomething/test/wire.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/SampleProjects/TestSomething/test/wire.cpp b/SampleProjects/TestSomething/test/wire.cpp index 8f9d81df..cf8db986 100644 --- a/SampleProjects/TestSomething/test/wire.cpp +++ b/SampleProjects/TestSomething/test/wire.cpp @@ -4,15 +4,18 @@ using std::deque; unittest(begin_write_end) { + // master write buffer should be empty deque* mosi = Wire.getMosi(14); assertEqual(0, mosi->size()); - Wire.begin(); + // write some random values to random slave + Wire.begin(); Wire.beginTransmission(14); Wire.write(0x07); Wire.write(0x0E); Wire.endTransmission(); - // check values + + // check master write buffer values assertEqual(2, mosi->size()); assertEqual(0x07, mosi->front()); mosi->pop_front(); @@ -33,6 +36,7 @@ unittest(readTwo_writeOne) { miso->push_back(4); miso->push_back(7); + // check read buffers and read-related functions assertEqual(0, Wire.requestFrom(19, 3)); assertEqual(2, Wire.requestFrom(19, 2)); assertEqual(2, Wire.available()); @@ -55,6 +59,7 @@ unittest(readTwo_writeOne) { Wire.write(i * 2); } Wire.endTransmission(); + // check master write buffer deque* mosi = Wire.getMosi(47); From 7593ab8341de525b5084f8bd43cb89add773db46 Mon Sep 17 00:00:00 2001 From: Lucas Saca Date: Wed, 18 Nov 2020 03:40:17 -0800 Subject: [PATCH 10/12] Updated wire tests to be more readable. Added additional assertion for too much data requested case. --- SampleProjects/TestSomething/test/wire.cpp | 49 ++++++++++++---------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/SampleProjects/TestSomething/test/wire.cpp b/SampleProjects/TestSomething/test/wire.cpp index cf8db986..b4c3b41c 100644 --- a/SampleProjects/TestSomething/test/wire.cpp +++ b/SampleProjects/TestSomething/test/wire.cpp @@ -8,18 +8,20 @@ unittest(begin_write_end) { deque* mosi = Wire.getMosi(14); assertEqual(0, mosi->size()); - // write some random values to random slave + // write some random data to random slave + const uint8_t randomSlaveAddr = 14; + const uint8_t randomData[] = { 0x07, 0x0E }; Wire.begin(); - Wire.beginTransmission(14); - Wire.write(0x07); - Wire.write(0x0E); + Wire.beginTransmission(randomSlaveAddr); + Wire.write(randomData[0]); + Wire.write(randomData[1]); Wire.endTransmission(); // check master write buffer values assertEqual(2, mosi->size()); - assertEqual(0x07, mosi->front()); + assertEqual(randomData[0], mosi->front()); mosi->pop_front(); - assertEqual(0x0E, mosi->front()); + assertEqual(randomData[1], mosi->front()); mosi->pop_front(); assertEqual(0, mosi->size()); } @@ -28,29 +30,34 @@ unittest(readTwo_writeOne) { Wire.begin(); deque* miso; // place some values on random slaves' read buffers - miso = Wire.getMiso(19); - miso->push_back(0x07); - miso->push_back(0x0E); - miso = Wire.getMiso(34); - miso->push_back(1); - miso->push_back(4); - miso->push_back(7); + const int randomSlaveAddr = 19, anotherRandomSlave = 34; + const uint8_t randomData[] = { 0x07, 0x0E }, moreRandomData[] = { 1, 4, 7 }; + miso = Wire.getMiso(randomSlaveAddr); + miso->push_back(randomData[0]); + miso->push_back(randomData[1]); + miso = Wire.getMiso(anotherRandomSlave); + miso->push_back(moreRandomData[0]); + miso->push_back(moreRandomData[1]); + miso->push_back(moreRandomData[2]); // check read buffers and read-related functions - assertEqual(0, Wire.requestFrom(19, 3)); - assertEqual(2, Wire.requestFrom(19, 2)); + // request more data than is in input buffer + assertEqual(0, Wire.requestFrom(randomSlaveAddr, 3)); + assertEqual(0, Wire.available()); + // normal use cases + assertEqual(2, Wire.requestFrom(randomSlaveAddr, 2)); assertEqual(2, Wire.available()); - assertEqual(0x07, Wire.read()); + assertEqual(randomData[0], Wire.read()); assertEqual(1, Wire.available()); - assertEqual(0x0E, Wire.read()); + assertEqual(randomData[1], Wire.read()); assertEqual(0, Wire.available()); - assertEqual(3, Wire.requestFrom(34, 3)); + assertEqual(3, Wire.requestFrom(anotherRandomSlave, 3)); assertEqual(3, Wire.available()); - assertEqual(1, Wire.read()); + assertEqual(moreRandomData[0], Wire.read()); assertEqual(2, Wire.available()); - assertEqual(4, Wire.read()); + assertEqual(moreRandomData[1], Wire.read()); assertEqual(1, Wire.available()); - assertEqual(7, Wire.read()); + assertEqual(moreRandomData[2], Wire.read()); assertEqual(0, Wire.available()); // write some values to different random slave From f2fd95a18b73c529bbf46d6f377ddb8ce0a58104 Mon Sep 17 00:00:00 2001 From: Lucas Saca Date: Wed, 18 Nov 2020 03:45:33 -0800 Subject: [PATCH 11/12] Forgot to scroll down and update half of the last test for last commit. Don't code at sleeping hours. --- SampleProjects/TestSomething/test/wire.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/SampleProjects/TestSomething/test/wire.cpp b/SampleProjects/TestSomething/test/wire.cpp index b4c3b41c..8d2873fa 100644 --- a/SampleProjects/TestSomething/test/wire.cpp +++ b/SampleProjects/TestSomething/test/wire.cpp @@ -30,7 +30,7 @@ unittest(readTwo_writeOne) { Wire.begin(); deque* miso; // place some values on random slaves' read buffers - const int randomSlaveAddr = 19, anotherRandomSlave = 34; + const int randomSlaveAddr = 19, anotherRandomSlave = 34, yetAnotherSlave = 47; const uint8_t randomData[] = { 0x07, 0x0E }, moreRandomData[] = { 1, 4, 7 }; miso = Wire.getMiso(randomSlaveAddr); miso->push_back(randomData[0]); @@ -60,24 +60,25 @@ unittest(readTwo_writeOne) { assertEqual(moreRandomData[2], Wire.read()); assertEqual(0, Wire.available()); - // write some values to different random slave - Wire.beginTransmission(47); + // write some arbitrary values to a third slave + Wire.beginTransmission(yetAnotherSlave); for (int i = 1; i < 4; i++) { Wire.write(i * 2); } Wire.endTransmission(); // check master write buffer - deque* mosi = Wire.getMosi(47); + deque* mosi = Wire.getMosi(yetAnotherSlave); + const uint8_t expectedValues[] = { 2, 4, 6 }; assertEqual(3, mosi->size()); - assertEqual(2, mosi->front()); + assertEqual(expectedValues[0], mosi->front()); mosi->pop_front(); assertEqual(2, mosi->size()); - assertEqual(4, mosi->front()); + assertEqual(expectedValues[1], mosi->front()); mosi->pop_front(); assertEqual(1, mosi->size()); - assertEqual(6, mosi->front()); + assertEqual(expectedValues[2], mosi->front()); mosi->pop_front(); assertEqual(0, mosi->size()); } From e4fd2e46680d59a96eafea034b50baccb9551d7d Mon Sep 17 00:00:00 2001 From: Lucas Saca Date: Wed, 18 Nov 2020 04:00:27 -0800 Subject: [PATCH 12/12] Removed the assertion added 3 commits ago. Didn't realize failed request results in no read buffer. --- SampleProjects/TestSomething/test/wire.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/SampleProjects/TestSomething/test/wire.cpp b/SampleProjects/TestSomething/test/wire.cpp index 8d2873fa..720afb8c 100644 --- a/SampleProjects/TestSomething/test/wire.cpp +++ b/SampleProjects/TestSomething/test/wire.cpp @@ -43,7 +43,6 @@ unittest(readTwo_writeOne) { // check read buffers and read-related functions // request more data than is in input buffer assertEqual(0, Wire.requestFrom(randomSlaveAddr, 3)); - assertEqual(0, Wire.available()); // normal use cases assertEqual(2, Wire.requestFrom(randomSlaveAddr, 2)); assertEqual(2, Wire.available());