diff --git a/libraries/Wire/examples/master_reader/master_reader.ino b/libraries/Wire/examples/master_reader/master_reader.ino index e27cac32b..214ffe996 100644 --- a/libraries/Wire/examples/master_reader/master_reader.ino +++ b/libraries/Wire/examples/master_reader/master_reader.ino @@ -24,6 +24,7 @@ void loop() { char c = Wire.read(); // receive a byte as character Serial.print(c); // print the character } + Serial.println(); delay(500); } diff --git a/libraries/Wire/examples/master_reader_custombuffer/master_reader_custombuffer.ino b/libraries/Wire/examples/master_reader_custombuffer/master_reader_custombuffer.ino new file mode 100644 index 000000000..cf71e52e0 --- /dev/null +++ b/libraries/Wire/examples/master_reader_custombuffer/master_reader_custombuffer.ino @@ -0,0 +1,62 @@ +// Wire Master Reader Custom Buffer + +// Demonstrates use of the Wire library with customized buffers +// Reads data from an I2C/TWI slave device +// Refer to the "Wire Slave Sender Custom Buffer" example for use with this + +// Created 31 Dec 2024 + +// This example code is in the public domain. + + +#include +#include +#include "Arduino.h" + +// request 6 bytes from slave device #8 +constexpr size_t REQUESTED_BYTE_COUNT = 6; + +constexpr size_t RECEIVE_BUFFER_SIZE = REQUESTED_BYTE_COUNT; +constexpr size_t TRANSMIT_BUFFER_SIZE = 0; // There is no transmit in this sketch. + +SET_Wire_BUFFERS(RECEIVE_BUFFER_SIZE, TRANSMIT_BUFFER_SIZE, + true /* master buffers needed */, false /* no slave buffers needed */ ); + +void setup() { + Wire.begin(); // join I2C bus (address optional for master) + Serial.begin(9600); // start serial for output + + // This is just for curiosity and could be removed + printWireBuffersCapacity(Serial); +} + +void loop() { + Wire.requestFrom(8, REQUESTED_BYTE_COUNT); + + while (Wire.available()) { // slave may send less than requested + char c = Wire.read(); // receive a byte as character + Serial.print(c); // print the character + } + Serial.println(); + + delay(500); +} + +void printWireBuffersCapacity(Stream& stream) { + const auto& buffers = GET_Wire_BUFFERS(); + + stream.print("Wire transmit buffer size is "); + stream.println(buffers.txWireBufferCapacity()); + + stream.print("Wire receive buffer size is "); + stream.println(buffers.rxWireBufferCapacity()); + + stream.print("twi_masterBuffer size is "); + stream.println(buffers.twi_masterBufferCapacity()); + + stream.print("twi_rxBuffer size is "); + stream.println(buffers.twi_rxBufferCapacity()); + + stream.print("twi_txBuffer size is "); + stream.println(buffers.twi_txBufferCapacity()); +} diff --git a/libraries/Wire/examples/master_writer/master_writer.ino b/libraries/Wire/examples/master_writer/master_writer.ino index 7a1766874..09bf12d57 100644 --- a/libraries/Wire/examples/master_writer/master_writer.ino +++ b/libraries/Wire/examples/master_writer/master_writer.ino @@ -16,7 +16,7 @@ void setup() { Wire.begin(); // join I2C bus (address optional for master) } -byte x = 0; +static byte x = 0; void loop() { Wire.beginTransmission(8); // transmit to device #8 diff --git a/libraries/Wire/examples/master_writer_custombuffer/master_writer_custombuffer.ino b/libraries/Wire/examples/master_writer_custombuffer/master_writer_custombuffer.ino new file mode 100644 index 000000000..67126fb72 --- /dev/null +++ b/libraries/Wire/examples/master_writer_custombuffer/master_writer_custombuffer.ino @@ -0,0 +1,62 @@ +// Wire Master Writer Custom Buffer + +// Demonstrates use of the Wire library with customized buffers +// Writes data to an I2C/TWI slave device +// Refer to the "Wire Slave Receiver Custom Buffer" example for use with this + +// Created 31 Dec 2024 + +// This example code is in the public domain. + + +#include +#include +#include "Arduino.h" + +// The following text will not fit into the default buffer of 32 bytes. +static const char text[] = "You really won't believe it, but x is "; + +constexpr size_t RECEIVE_BUFFER_SIZE = 0; // There is no receive in this sketch. +constexpr size_t TRANSMIT_BUFFER_SIZE = 42; // Enhance the buffer to 42 characters. + +SET_Wire_BUFFERS(RECEIVE_BUFFER_SIZE, TRANSMIT_BUFFER_SIZE, + true /* master buffers needed */, false /* no slave buffers needed */ ); + +void setup() { + Wire.begin(); // join I2C bus (address optional for master) + + // This is just for curiosity and could be removed + Serial.begin(9600); // start serial for output + printWireBuffersCapacity(Serial); +} + +static byte x = 0; + +void loop() { + Wire.beginTransmission(8); // transmit to device #8 + Wire.write(text); // sends multiple bytes + Wire.write(x); // sends one byte + Wire.endTransmission(); // stop transmitting + + x++; + delay(500); +} + +void printWireBuffersCapacity(Stream& stream) { + const auto& buffers = GET_Wire_BUFFERS(); + + stream.print("Wire transmit buffer size is "); + stream.println(buffers.txWireBufferCapacity()); + + stream.print("Wire receive buffer size is "); + stream.println(buffers.rxWireBufferCapacity()); + + stream.print("twi_masterBuffer size is "); + stream.println(buffers.twi_masterBufferCapacity()); + + stream.print("twi_rxBuffer size is "); + stream.println(buffers.twi_rxBufferCapacity()); + + stream.print("twi_txBuffer size is "); + stream.println(buffers.twi_txBufferCapacity()); +} diff --git a/libraries/Wire/examples/slave_receiver_custombuffer/slave_receiver_custombuffer.ino b/libraries/Wire/examples/slave_receiver_custombuffer/slave_receiver_custombuffer.ino new file mode 100644 index 000000000..8e0421a63 --- /dev/null +++ b/libraries/Wire/examples/slave_receiver_custombuffer/slave_receiver_custombuffer.ino @@ -0,0 +1,69 @@ +// Wire Slave Receiver Custom Buffer + +// Demonstrates use of the Wire library with customized buffers +// Receives data as an I2C/TWI slave device +// Refer to the "Wire Master Writer Custom Buffer" example for use with this + +// Created 31 Dec 2024 + +// This example code is in the public domain. + + +#include +#include +#include "Arduino.h" + +constexpr size_t RECEIVE_BUFFER_SIZE = 42; // Be able receive up to 42 characters in one message. +constexpr size_t TRANSMIT_BUFFER_SIZE = 0; // There is no transmit in this sketch. + +SET_Wire_BUFFERS(RECEIVE_BUFFER_SIZE, TRANSMIT_BUFFER_SIZE, + false /* no master buffers needed */, true /* slave buffers needed */ ); + +void setup() { + Wire.begin(8); // join I2C bus with address #8 + Wire.onReceive(receiveEvent); // register event + Serial.begin(9600); // start serial for output + + // This is just for curiosity and could be removed + printWireBuffersCapacity(Serial); +} + +void loop() { + delay(100); +} + +// function that executes whenever data is received from master +// this function is registered as an event, see setup() +// +// Hint: This function is called within an interrupt context. +// That means, that there must be enough space in the Serial output +// buffer for the characters to be printed. Otherwise the +// Serial.print() call will lock up. +void receiveEvent(int howMany) { + while (1 < Wire.available()) { // loop through all but the last + const char c = Wire.read(); // receive byte as a character + Serial.print(c); // print the character + } + const int x = Wire.read(); // receive byte as an integer + Serial.println(x); // print the integer +} + +void printWireBuffersCapacity(Stream& stream) { + const auto& buffers = GET_Wire_BUFFERS(); + + stream.print("Wire transmit buffer size is "); + stream.println(buffers.txWireBufferCapacity()); + + stream.print("Wire receive buffer size is "); + stream.println(buffers.rxWireBufferCapacity()); + + stream.print("twi_masterBuffer size is "); + stream.println(buffers.twi_masterBufferCapacity()); + + stream.print("twi_rxBuffer size is "); + stream.println(buffers.twi_rxBufferCapacity()); + + stream.print("twi_txBuffer size is "); + stream.println(buffers.twi_txBufferCapacity()); + delay(250); // Give time to free up Serial output buffer. +} diff --git a/libraries/Wire/examples/slave_sender_custombuffer/slave_sender_custombuffer.ino b/libraries/Wire/examples/slave_sender_custombuffer/slave_sender_custombuffer.ino new file mode 100644 index 000000000..6da7ce083 --- /dev/null +++ b/libraries/Wire/examples/slave_sender_custombuffer/slave_sender_custombuffer.ino @@ -0,0 +1,61 @@ +// Wire Slave Sender Custom Buffer + +// Demonstrates use of the Wire library with customized buffers +// Sends data as an I2C/TWI slave device +// Refer to the "Wire Master Reader Custom Buffer" example for use with this + +// Created 31 Dec 2024 + +// This example code is in the public domain. + + +#include +#include +#include "Arduino.h" + +static const char text[] = "hello "; // respond with message of 6 bytes + +constexpr size_t RECEIVE_BUFFER_SIZE = 0; // There is no receive in this sketch. +constexpr size_t TRANSMIT_BUFFER_SIZE = sizeof(text)-1; // Don't need a byte for the \0 + +SET_Wire_BUFFERS(RECEIVE_BUFFER_SIZE, TRANSMIT_BUFFER_SIZE, + false /* no master buffers needed */, true /* slave buffers needed */ ); + +void setup() { + Wire.begin(8); // join I2C bus with address #8 + Wire.onRequest(requestEvent); // register event + + // This is just for curiosity and could be removed + Serial.begin(9600); + printWireBuffersCapacity(Serial); +} + +void loop() { + delay(100); +} + +// function that executes whenever data is requested by master +// this function is registered as an event, see setup() +void requestEvent() { + Wire.write(text); + // as expected by master +} + +void printWireBuffersCapacity(Stream& stream) { + const auto& buffers = GET_Wire_BUFFERS(); + + stream.print("Wire transmit buffer size is "); + stream.println(buffers.txWireBufferCapacity()); + + stream.print("Wire receive buffer size is "); + stream.println(buffers.rxWireBufferCapacity()); + + stream.print("twi_masterBuffer size is "); + stream.println(buffers.twi_masterBufferCapacity()); + + stream.print("twi_rxBuffer size is "); + stream.println(buffers.twi_rxBufferCapacity()); + + stream.print("twi_txBuffer size is "); + stream.println(buffers.twi_txBufferCapacity()); +} diff --git a/libraries/Wire/src/TwoWireBuffers.cpp b/libraries/Wire/src/TwoWireBuffers.cpp new file mode 100644 index 000000000..076513f90 --- /dev/null +++ b/libraries/Wire/src/TwoWireBuffers.cpp @@ -0,0 +1,29 @@ +/* + TwoWireBuffers.cpp - TWI/I2C library for Wiring & Arduino + Copyright (c) 2006 Nicholas Zambetti. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "TwoWireBuffers.h" + +constexpr size_t RX_BUFFER_DEFAULT_LENGTH = 32; +constexpr size_t TX_BUFFER_DEFAULT_LENGTH = 32; + +// Default buffers for the one and only Wire object +template<> __attribute__((weak)) TwoWireBuffers::Interface& WireBuffers<0>::instance() { \ + static TwoWireBuffers::Impl buffers; \ + return buffers; \ +} diff --git a/libraries/Wire/src/TwoWireBuffers.h b/libraries/Wire/src/TwoWireBuffers.h new file mode 100644 index 000000000..15162cca6 --- /dev/null +++ b/libraries/Wire/src/TwoWireBuffers.h @@ -0,0 +1,128 @@ +/* + TwoWireBuffers.h - TWI/I2C library for Wiring & Arduino + Copyright (c) 2006 Nicholas Zambetti. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once + +#ifndef TwiBuffers_h +#define TwiBuffers_h + +#include +#include +#include "Wire.h" + +namespace TwoWireBuffers { + +/* Template class that implements a compile time fixed size array. */ +template +class StaticBuffer { + uint8_t mByteArray[CAPACITY]; +public: + inline uint8_t capacity() const {return CAPACITY;} + inline uint8_t* storage() {return mByteArray;} +}; + +/* Specialization of StaticBuffer template class with zero size. */ +template<> +class StaticBuffer<0> { +public: + inline uint8_t capacity() const {return 0;} + inline uint8_t* storage() {return nullptr;} +}; + + +/* Interface that provides buffers for twi driver and TwoWire objects */ +class Interface { +public: + virtual uint8_t* twi_masterBuffer() = 0; + virtual size_t twi_masterBufferCapacity() = 0; + + virtual uint8_t* twi_rxBuffer() = 0; + virtual size_t twi_rxBufferCapacity() = 0; + + virtual uint8_t* twi_txBuffer() = 0; + virtual size_t twi_txBufferCapacity() = 0; + + virtual uint8_t* rxWireBuffer() = 0; + virtual size_t rxWireBufferCapacity() = 0; + + virtual uint8_t* txWireBuffer() = 0; + virtual size_t txWireBufferCapacity() = 0; +}; + +/* Template class implementing Interface with template parameter + * determined buffer sizes. + */ +template< + size_t RX_CAPACITY, // Receive buffer size. May be zero, if only transmitting data is needed + size_t TX_CAPACITY, // Transmit buffer size. May be zero, if only receiving data is needed + bool ENABLE_MASTER, // If master is disabled, it will save twi master buffer space + bool ENABLE_SLAVE // If slave is disabled, it will save twi slave buffer space + > +class Impl : public Interface { + static_assert(ENABLE_MASTER == true || ENABLE_SLAVE == true, + "You should not disable master and slave together."); + + static constexpr size_t TWI_MASTER_BUFFER_CAPACITY = + RX_CAPACITY > TX_CAPACITY ? RX_CAPACITY : TX_CAPACITY; + + // Set the twi master buffer capacity to 0, if master mode isn't needed. + TwoWireBuffers::StaticBuffer mTwiMasterBuffer; + + // Set the twi slave buffers capacity to 0, if slave mode isn't needed. + TwoWireBuffers::StaticBuffer mTwiRxBuffer; + TwoWireBuffers::StaticBuffer mTwiTxBuffer; + + // Set the capacity for a TwoWire object. + TwoWireBuffers::StaticBuffer mRxWireBuffer; + TwoWireBuffers::StaticBuffer mTxWireBuffer; + +public: + virtual uint8_t* twi_masterBuffer() override {return mTwiMasterBuffer.storage();} + virtual size_t twi_masterBufferCapacity() override {return mTwiMasterBuffer.capacity();} + + virtual uint8_t* twi_rxBuffer() override {return mTwiRxBuffer.storage();} + virtual size_t twi_rxBufferCapacity() override {return mTwiRxBuffer.capacity();} + + virtual uint8_t* twi_txBuffer() override {return mTwiTxBuffer.storage();} + virtual size_t twi_txBufferCapacity() override {return mTwiTxBuffer.capacity();} + + virtual uint8_t* rxWireBuffer() override {return mRxWireBuffer.storage();} + virtual size_t rxWireBufferCapacity() override {return mRxWireBuffer.capacity();} + + virtual uint8_t* txWireBuffer() override {return mTxWireBuffer.storage();} + virtual size_t txWireBufferCapacity() override {return mTxWireBuffer.capacity();} +}; + +} // namespace TwoWireBuffers + +// For avr there is currently only a single Wire object that is associated with wireNum 0 +template struct WireBuffers { // The buffers for the Wire object + static TwoWireBuffers::Interface& instance(); +}; + +#define SET_Wire_BUFFERS(rxBufferCapacity, txBufferCapacity, enableMaster, enableSlave) \ + template<> TwoWireBuffers::Interface& WireBuffers<0>::instance() { \ + static TwoWireBuffers::Impl buffers; \ + return buffers; \ + } + +#define GET_Wire_BUFFERS() \ + WireBuffers<0>::instance(); + +#endif /* TwiBuffers_h */ diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index 001d924df..b41ab5184 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -1,5 +1,5 @@ /* - TwoWire.cpp - TWI/I2C library for Wiring & Arduino + Wire.cpp - TWI/I2C library for Wiring & Arduino Copyright (c) 2006 Nicholas Zambetti. All right reserved. This library is free software; you can redistribute it and/or @@ -21,36 +21,48 @@ Modified 2020 by Greyson Christoforo (grey@christoforo.net) to implement timeouts */ -extern "C" { - #include - #include - #include - #include "utility/twi.h" -} +#include +#include +#include +#include "utility/twi.h" #include "Wire.h" +#include "TwoWireBuffers.h" -// Initialize Class Variables ////////////////////////////////////////////////// - -uint8_t TwoWire::rxBuffer[BUFFER_LENGTH]; -uint8_t TwoWire::rxBufferIndex = 0; -uint8_t TwoWire::rxBufferLength = 0; - -uint8_t TwoWire::txAddress = 0; -uint8_t TwoWire::txBuffer[BUFFER_LENGTH]; -uint8_t TwoWire::txBufferIndex = 0; -uint8_t TwoWire::txBufferLength = 0; -uint8_t TwoWire::transmitting = 0; -void (*TwoWire::user_onRequest)(void); -void (*TwoWire::user_onReceive)(int); // Constructors //////////////////////////////////////////////////////////////// TwoWire::TwoWire() + : rxBufferIndex(0) + , rxBufferLength(0) + , txAddress(0) + , txBufferIndex(0) + , txBufferLength(0) + , transmitting(0) + , user_onRequest(nullptr) + , user_onReceive(nullptr) { } +// Private Methods ///////////////////////////////////////////////////////////// + +uint8_t* TwoWire::rxBuffer() { + return WireBuffers<0>::instance().rxWireBuffer(); +} + +size_t TwoWire::rxBufferCapacity() { + return WireBuffers<0>::instance().rxWireBufferCapacity(); +} + +uint8_t* TwoWire::txBuffer() { + return WireBuffers<0>::instance().txWireBuffer(); +} + +size_t TwoWire::txBufferCapacity() { + return WireBuffers<0>::instance().txWireBufferCapacity(); +} + // Public Methods ////////////////////////////////////////////////////////////// void TwoWire::begin(void) @@ -62,8 +74,8 @@ void TwoWire::begin(void) txBufferLength = 0; twi_init(); - twi_attachSlaveTxEvent(onRequestService); // default callback must exist - twi_attachSlaveRxEvent(onReceiveService); // default callback must exist + twi_attachSlaveTxEvent(onRequestService_); // default callback must exist + twi_attachSlaveRxEvent(onReceiveService_); // default callback must exist } void TwoWire::begin(uint8_t address) @@ -72,11 +84,6 @@ void TwoWire::begin(uint8_t address) twi_setAddress(address); } -void TwoWire::begin(int address) -{ - begin((uint8_t)address); -} - void TwoWire::end(void) { twi_disable(); @@ -136,29 +143,34 @@ void TwoWire::clearWireTimeoutFlag(void){ uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint32_t iaddress, uint8_t isize, uint8_t sendStop) { if (isize > 0) { - // send internal address; this mode allows sending a repeated start to access - // some devices' internal registers. This function is executed by the hardware - // TWI module on other processors (for example Due's TWI_IADR and TWI_MMR registers) + // send internal address; this mode allows sending a repeated start to access + // some devices' internal registers. This function is executed by the hardware + // TWI module on other processors (for example Due's TWI_IADR and TWI_MMR registers) - beginTransmission(address); + beginTransmission(address); - // the maximum size of internal address is 3 bytes - if (isize > 3){ - isize = 3; - } + // the maximum size of internal address is 3 bytes + if (isize > 3){ + isize = 3; + } - // write internal register address - most significant byte first - while (isize-- > 0) - write((uint8_t)(iaddress >> (isize*8))); - endTransmission(false); + // write internal register address - most significant byte first + while (isize-- > 0) + { + write(static_cast(iaddress >> (isize*8))); + } + + endTransmission(false); } // clamp to buffer length - if(quantity > BUFFER_LENGTH){ - quantity = BUFFER_LENGTH; + const size_t capacity = rxBufferCapacity(); + if(quantity > capacity){ + quantity = capacity; } // perform blocking read into buffer - uint8_t read = twi_readFrom(address, rxBuffer, quantity, sendStop); + const uint8_t read = capacity > 0 ? + twi_readFrom(address, rxBuffer(), quantity, sendStop) : 0; // set rx buffer iterator vars rxBufferIndex = 0; rxBufferLength = read; @@ -166,25 +178,6 @@ uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint32_t iaddres return read; } -uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop) { - return requestFrom((uint8_t)address, (uint8_t)quantity, (uint32_t)0, (uint8_t)0, (uint8_t)sendStop); -} - -uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity) -{ - return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true); -} - -uint8_t TwoWire::requestFrom(int address, int quantity) -{ - return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true); -} - -uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop) -{ - return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)sendStop); -} - void TwoWire::beginTransmission(uint8_t address) { // indicate that we are transmitting @@ -196,11 +189,6 @@ void TwoWire::beginTransmission(uint8_t address) txBufferLength = 0; } -void TwoWire::beginTransmission(int address) -{ - beginTransmission((uint8_t)address); -} - // // Originally, 'endTransmission' was an f(void) function. // It has been modified to take one parameter indicating @@ -217,7 +205,7 @@ void TwoWire::beginTransmission(int address) uint8_t TwoWire::endTransmission(uint8_t sendStop) { // transmit buffer (blocking) - uint8_t ret = twi_writeTo(txAddress, txBuffer, txBufferLength, 1, sendStop); + const uint8_t ret = twi_writeTo(txAddress, txBuffer(), txBufferLength, 1, sendStop); // reset tx buffer iterator vars txBufferIndex = 0; txBufferLength = 0; @@ -242,12 +230,12 @@ size_t TwoWire::write(uint8_t data) if(transmitting){ // in master transmitter mode // don't bother if buffer is full - if(txBufferLength >= BUFFER_LENGTH){ + if(txBufferLength >= txBufferCapacity()){ setWriteError(); return 0; } // put byte in tx buffer - txBuffer[txBufferIndex] = data; + txBuffer()[txBufferIndex] = data; ++txBufferIndex; // update amount in buffer txBufferLength = txBufferIndex; @@ -294,7 +282,7 @@ int TwoWire::read(void) // get each successive byte on each call if(rxBufferIndex < rxBufferLength){ - value = rxBuffer[rxBufferIndex]; + value = rxBuffer()[rxBufferIndex]; ++rxBufferIndex; } @@ -309,7 +297,7 @@ int TwoWire::peek(void) int value = -1; if(rxBufferIndex < rxBufferLength){ - value = rxBuffer[rxBufferIndex]; + value = rxBuffer()[rxBufferIndex]; } return value; @@ -320,11 +308,16 @@ void TwoWire::flush(void) // XXX: to be implemented. } +void TwoWire::onReceiveService_(uint8_t* inBytes, int numBytes) +{ + Wire.onReceiveService(inBytes, numBytes); +} + // behind the scenes function that is called when data is received void TwoWire::onReceiveService(uint8_t* inBytes, int numBytes) { // don't bother if user hasn't registered a callback - if(!user_onReceive){ + if(user_onReceive == nullptr){ return; } // don't bother if rx buffer is in use by a master requestFrom() op @@ -335,8 +328,8 @@ void TwoWire::onReceiveService(uint8_t* inBytes, int numBytes) } // copy twi rx buffer into local read buffer // this enables new reads to happen in parallel - for(uint8_t i = 0; i < numBytes; ++i){ - rxBuffer[i] = inBytes[i]; + for(size_t i = 0; i < numBytes; ++i){ + rxBuffer()[i] = inBytes[i]; } // set rx iterator vars rxBufferIndex = 0; @@ -345,11 +338,16 @@ void TwoWire::onReceiveService(uint8_t* inBytes, int numBytes) user_onReceive(numBytes); } +void TwoWire::onRequestService_() +{ + Wire.onRequestService(); +} + // behind the scenes function that is called when data is requested void TwoWire::onRequestService(void) { // don't bother if user hasn't registered a callback - if(!user_onRequest){ + if(user_onRequest == nullptr){ return; } // reset tx buffer iterator vars diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index e70d72edb..41e2a7009 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -1,5 +1,5 @@ /* - TwoWire.h - TWI/I2C library for Arduino & Wiring + Wire.h - TWI/I2C library for Arduino & Wiring Copyright (c) 2006 Nicholas Zambetti. All right reserved. This library is free software; you can redistribute it and/or @@ -20,67 +20,90 @@ Modified 2020 by Greyson Christoforo (grey@christoforo.net) to implement timeouts */ +#pragma once + #ifndef TwoWire_h #define TwoWire_h #include #include "Stream.h" -#define BUFFER_LENGTH 32 - // WIRE_HAS_END means Wire has end() #define WIRE_HAS_END 1 +// Forward declaration of Twi::IStaticBuffer +namespace TwoWireBuffers { + class IStaticBuffer; +} + class TwoWire : public Stream { private: - static uint8_t rxBuffer[]; - static uint8_t rxBufferIndex; - static uint8_t rxBufferLength; - - static uint8_t txAddress; - static uint8_t txBuffer[]; - static uint8_t txBufferIndex; - static uint8_t txBufferLength; - - static uint8_t transmitting; - static void (*user_onRequest)(void); - static void (*user_onReceive)(int); - static void onRequestService(void); - static void onReceiveService(uint8_t*, int); + uint8_t rxBufferIndex; + uint8_t rxBufferLength; + + uint8_t txAddress; + uint8_t txBufferIndex; + uint8_t txBufferLength; + + uint8_t transmitting; + void (*user_onRequest)(void); + void (*user_onReceive)(int); + + static inline uint8_t* rxBuffer(); + static inline uint8_t* txBuffer(); + static void onRequestService_(void); + static void onReceiveService_(uint8_t*, int); + + void onRequestService(void); + void onReceiveService(uint8_t*, int); public: TwoWire(); void begin(); void begin(uint8_t); - void begin(int); + inline void begin(int address) {begin(static_cast(address));} void end(); void setClock(uint32_t); void setWireTimeout(uint32_t timeout = 25000, bool reset_with_timeout = false); bool getWireTimeoutFlag(void); void clearWireTimeoutFlag(void); void beginTransmission(uint8_t); - void beginTransmission(int); + void beginTransmission(int address) {beginTransmission(static_cast(address));} uint8_t endTransmission(void); uint8_t endTransmission(uint8_t); - uint8_t requestFrom(uint8_t, uint8_t); - uint8_t requestFrom(uint8_t, uint8_t, uint8_t); uint8_t requestFrom(uint8_t, uint8_t, uint32_t, uint8_t, uint8_t); - uint8_t requestFrom(int, int); - uint8_t requestFrom(int, int, int); - virtual size_t write(uint8_t); - virtual size_t write(const uint8_t *, size_t); - virtual int available(void); - virtual int read(void); - virtual int peek(void); - virtual void flush(void); + inline uint8_t requestFrom(uint8_t address, uint8_t quantity) { + return requestFrom(address, quantity, static_cast(true)); + } + inline uint8_t requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop) { + return requestFrom(address, quantity, static_cast(0) + , static_cast(0), static_cast(sendStop)); + } + inline uint8_t requestFrom(int address, int quantity) { + return requestFrom(static_cast(address), static_cast(quantity) + , static_cast(true)); + } + inline uint8_t requestFrom(int address, int quantity, int sendStop) { + return requestFrom(static_cast(address), static_cast(quantity) + , static_cast(sendStop)); + } + virtual size_t write(uint8_t) override; + virtual size_t write(const uint8_t *, size_t) override; + virtual int available(void) override; + virtual int read(void) override; + virtual int peek(void) override; + virtual void flush(void) override; void onReceive( void (*)(int) ); void onRequest( void (*)(void) ); - inline size_t write(unsigned long n) { return write((uint8_t)n); } - inline size_t write(long n) { return write((uint8_t)n); } - inline size_t write(unsigned int n) { return write((uint8_t)n); } - inline size_t write(int n) { return write((uint8_t)n); } + inline size_t write(unsigned long n) { return write(static_cast(n)); } + inline size_t write(long n) { return write(static_cast(n)); } + inline size_t write(unsigned int n) { return write(static_cast(n)); } + inline size_t write(int n) { return write(static_cast(n)); } using Print::write; + + static inline size_t rxBufferCapacity(); + static inline size_t txBufferCapacity(); }; extern TwoWire Wire; diff --git a/libraries/Wire/src/utility/twi.c b/libraries/Wire/src/utility/twi.cpp similarity index 88% rename from libraries/Wire/src/utility/twi.c rename to libraries/Wire/src/utility/twi.cpp index e09a33caf..43a403636 100644 --- a/libraries/Wire/src/utility/twi.c +++ b/libraries/Wire/src/utility/twi.cpp @@ -1,5 +1,5 @@ /* - twi.c - TWI/I2C library for Wiring & Arduino + twi.cpp - TWI/I2C library for Wiring & Arduino Copyright (c) 2006 Nicholas Zambetti. All right reserved. This library is free software; you can redistribute it and/or @@ -27,6 +27,9 @@ #include #include #include + +#include "../TwoWireBuffers.h" + #include "Arduino.h" // for digitalWrite and micros #ifndef cbi @@ -40,7 +43,19 @@ #include "pins_arduino.h" #include "twi.h" -static volatile uint8_t twi_state; +#ifndef TWI_FREQ +#define TWI_FREQ 100000L +#endif + +enum TWI_STATE : uint8_t { + TWI_READY = 0, + TWI_MRX = 1, + TWI_MTX = 2, + TWI_SRX = 3, + TWI_STX = 4, +}; + +static volatile TWI_STATE twi_state; static volatile uint8_t twi_slarw; static volatile uint8_t twi_sendStop; // should the transaction end with a stop static volatile uint8_t twi_inRepStart; // in the middle of a repeated start @@ -55,19 +70,24 @@ static volatile uint32_t twi_timeout_us = 0ul; static volatile bool twi_timed_out_flag = false; // a timeout has been seen static volatile bool twi_do_reset_on_timeout = false; // reset the TWI registers on timeout + static void (*twi_onSlaveTransmit)(void); static void (*twi_onSlaveReceive)(uint8_t*, int); -static uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH]; static volatile uint8_t twi_masterBufferIndex; static volatile uint8_t twi_masterBufferLength; +static inline uint8_t* twi_masterBuffer() {return WireBuffers<0>::instance().twi_masterBuffer();} +static inline size_t twi_masterBufferCapacity() {return WireBuffers<0>::instance().twi_masterBufferCapacity();} + -static uint8_t twi_txBuffer[TWI_BUFFER_LENGTH]; static volatile uint8_t twi_txBufferIndex; static volatile uint8_t twi_txBufferLength; +static inline uint8_t* twi_txBuffer() {return WireBuffers<0>::instance().twi_txBuffer();} +static inline size_t twi_txBufferCapacity() {return WireBuffers<0>::instance().twi_txBufferCapacity();} -static uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH]; static volatile uint8_t twi_rxBufferIndex; +static inline uint8_t* twi_rxBuffer() {return WireBuffers<0>::instance().twi_rxBuffer();} +static inline size_t twi_rxBufferCapacity() {return WireBuffers<0>::instance().twi_rxBufferCapacity();} static volatile uint8_t twi_error; @@ -77,7 +97,7 @@ static volatile uint8_t twi_error; * Input none * Output none */ -void twi_init(void) +void twi_init() { // initialize state twi_state = TWI_READY; @@ -158,13 +178,12 @@ void twi_setFrequency(uint32_t frequency) */ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sendStop) { - uint8_t i; - // ensure data will fit into buffer - if(TWI_BUFFER_LENGTH < length){ + if(twi_masterBufferCapacity() < length){ return 0; } + // wait until twi is ready, become master receiver uint32_t startMicros = micros(); while(TWI_READY != twi_state){ @@ -227,10 +246,12 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen } // copy twi buffer to data - for(i = 0; i < length; ++i){ - data[i] = twi_masterBuffer[i]; + { + uint8_t* const masterBuffer = twi_masterBuffer(); + for(size_t i = 0; i < length; ++i){ + data[i] = masterBuffer[i]; + } } - return length; } @@ -252,10 +273,8 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen */ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait, uint8_t sendStop) { - uint8_t i; - // ensure data will fit into buffer - if(TWI_BUFFER_LENGTH < length){ + if(twi_masterBufferCapacity() < length){ return 1; } @@ -277,8 +296,11 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait twi_masterBufferLength = length; // copy data to twi buffer - for(i = 0; i < length; ++i){ - twi_masterBuffer[i] = data[i]; + { + uint8_t* const masterBuffer = twi_masterBuffer(); + for(size_t i = 0; i < length; ++i){ + masterBuffer[i] = data[i]; + } } // build sla+w, slave device address + w bit @@ -341,10 +363,8 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait */ uint8_t twi_transmit(const uint8_t* data, uint8_t length) { - uint8_t i; - // ensure data will fit into buffer - if(TWI_BUFFER_LENGTH < (twi_txBufferLength+length)){ + if(twi_txBufferCapacity() < (twi_txBufferLength+length)){ return 1; } @@ -354,8 +374,11 @@ uint8_t twi_transmit(const uint8_t* data, uint8_t length) } // set length and copy data into tx buffer - for(i = 0; i < length; ++i){ - twi_txBuffer[twi_txBufferLength+i] = data[i]; + { + uint8_t* const txBuffer = twi_txBuffer(); + for(size_t i = 0; i < length; ++i){ + txBuffer[twi_txBufferLength+i] = data[i]; + } } twi_txBufferLength += length; @@ -493,7 +516,7 @@ void twi_handleTimeout(bool reset){ * Output the value of twi_timed_out_flag when the function was called */ bool twi_manageTimeoutFlag(bool clear_flag){ - bool flag = twi_timed_out_flag; + const bool flag = twi_timed_out_flag; if (clear_flag){ twi_timed_out_flag = false; } @@ -517,7 +540,8 @@ ISR(TWI_vect) // if there is data to send, send it, otherwise stop if(twi_masterBufferIndex < twi_masterBufferLength){ // copy data to output register and ack - TWDR = twi_masterBuffer[twi_masterBufferIndex++]; + uint8_t* const masterBuffer = twi_masterBuffer(); + TWDR = masterBuffer[twi_masterBufferIndex++]; twi_reply(1); }else{ if (twi_sendStop){ @@ -547,8 +571,11 @@ ISR(TWI_vect) // Master Receiver case TW_MR_DATA_ACK: // data received, ack sent - // put byte into buffer - twi_masterBuffer[twi_masterBufferIndex++] = TWDR; + { + // put byte into buffer + uint8_t* const masterBuffer = twi_masterBuffer(); + masterBuffer[twi_masterBufferIndex++] = TWDR; + } __attribute__ ((fallthrough)); case TW_MR_SLA_ACK: // address sent, ack received // ack if more bytes are expected, otherwise nack @@ -559,8 +586,10 @@ ISR(TWI_vect) } break; case TW_MR_DATA_NACK: // data received, nack sent + { // put final byte into buffer - twi_masterBuffer[twi_masterBufferIndex++] = TWDR; + uint8_t* const masterBuffer = twi_masterBuffer(); + masterBuffer[twi_masterBufferIndex++] = TWDR; if (twi_sendStop){ twi_stop(); } else { @@ -572,6 +601,7 @@ ISR(TWI_vect) twi_state = TWI_READY; } break; + } case TW_MR_SLA_NACK: // address sent, nack received twi_stop(); break; @@ -591,9 +621,10 @@ ISR(TWI_vect) case TW_SR_DATA_ACK: // data received, returned ack case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack // if there is still room in the rx buffer - if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){ + if(twi_rxBufferIndex < twi_rxBufferCapacity()){ // put byte in buffer and ack - twi_rxBuffer[twi_rxBufferIndex++] = TWDR; + uint8_t* const rxBuffer = twi_rxBuffer(); + rxBuffer[twi_rxBufferIndex++] = TWDR; twi_reply(1); }else{ // otherwise nack @@ -604,11 +635,15 @@ ISR(TWI_vect) // ack future responses and leave slave receiver state twi_releaseBus(); // put a null char after data if there's room - if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){ - twi_rxBuffer[twi_rxBufferIndex] = '\0'; + if(twi_rxBufferIndex < twi_rxBufferCapacity()){ + uint8_t* const rxBuffer = twi_rxBuffer(); + rxBuffer[twi_rxBufferIndex] = '\0'; } // callback to user defined callback - twi_onSlaveReceive(twi_rxBuffer, twi_rxBufferIndex); + { + uint8_t* const rxBuffer = twi_rxBuffer(); + twi_onSlaveReceive(rxBuffer, twi_rxBufferIndex); + } // since we submit rx buffer to "wire" library, we can reset it twi_rxBufferIndex = 0; break; @@ -632,14 +667,17 @@ ISR(TWI_vect) twi_onSlaveTransmit(); // if they didn't change buffer & length, initialize it if(0 == twi_txBufferLength){ + uint8_t* const txBuffer = twi_txBuffer(); twi_txBufferLength = 1; - twi_txBuffer[0] = 0x00; + txBuffer[0] = 0x00; } __attribute__ ((fallthrough)); // transmit first byte from buffer, fall case TW_ST_DATA_ACK: // byte sent, ack returned + { // copy data to output register - TWDR = twi_txBuffer[twi_txBufferIndex++]; + uint8_t* const txBuffer = twi_txBuffer(); + TWDR = txBuffer[twi_txBufferIndex++]; // if there is more to send, ack, otherwise nack if(twi_txBufferIndex < twi_txBufferLength){ twi_reply(1); @@ -647,6 +685,7 @@ ISR(TWI_vect) twi_reply(0); } break; + } case TW_ST_DATA_NACK: // received nack, we are done case TW_ST_LAST_DATA: // received ack, but we are done already! // ack future responses diff --git a/libraries/Wire/src/utility/twi.h b/libraries/Wire/src/utility/twi.h index 85b983794..416ed8855 100644 --- a/libraries/Wire/src/utility/twi.h +++ b/libraries/Wire/src/utility/twi.h @@ -19,6 +19,8 @@ Modified 2020 by Greyson Christoforo (grey@christoforo.net) to implement timeouts */ +#pragma once + #ifndef twi_h #define twi_h @@ -26,20 +28,6 @@ //#define ATMEGA8 - #ifndef TWI_FREQ - #define TWI_FREQ 100000L - #endif - - #ifndef TWI_BUFFER_LENGTH - #define TWI_BUFFER_LENGTH 32 - #endif - - #define TWI_READY 0 - #define TWI_MRX 1 - #define TWI_MTX 2 - #define TWI_SRX 3 - #define TWI_STX 4 - void twi_init(void); void twi_disable(void); void twi_setAddress(uint8_t); @@ -56,4 +44,4 @@ void twi_handleTimeout(bool); bool twi_manageTimeoutFlag(bool); -#endif +#endif // #ifndef twi_h