Skip to content

Allow customization of transmit and receive buffer sizes for I2C 'Wire' and 'Wire1' objects. #148

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 18 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions libraries/Wire/examples/master_reader/master_reader.ino
Original file line number Diff line number Diff line change
@@ -12,21 +12,19 @@

#include <Wire.h>

void setup()
{
Wire.begin(); // join i2c bus (address optional for master)
void setup() {
Wire.begin(); // join I2C bus (address optional for master)
Serial.begin(9600); // start serial for output
}

void loop()
{
Wire.requestFrom(2, 6); // request 6 bytes from slave device #2
void loop() {
Wire.requestFrom(8, 6); // request 6 bytes from slave device #8

while(Wire.available()) // slave may send less than requested
{
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);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// 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 <Wire.h>
#include <TwoWireBuffers.h>
#include "Arduino.h"

#define USE_WIRE1 false // Set to true for using Wire1

// 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.

#if not USE_WIRE1

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
const 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("Wire service buffer size is ");
stream.println(buffers.srvWireBufferCapacity());
}

#else

SET_Wire1_BUFFERS(RECEIVE_BUFFER_SIZE, TRANSMIT_BUFFER_SIZE,
true /* master buffers needed */, false /* no slave buffers needed */ );

void setup() {
Wire1.begin(); // join I2C bus (address optional for master)
Serial.begin(9600); // start serial for output

// This is just for curiosity and could be removed
printWire1BuffersCapacity(Serial);
}

void loop() {
Wire1.requestFrom(8, REQUESTED_BYTE_COUNT);

while (Wire1.available()) { // slave may send less than requested
const char c = Wire1.read(); // receive a byte as character
Serial.print(c); // print the character
}
Serial.println();

delay(500);
}

void printWire1BuffersCapacity(Stream& stream) {
const auto& buffers = GET_Wire1_BUFFERS();

stream.print("Wire1 transmit buffer size is ");
stream.println(buffers.txWireBufferCapacity());

stream.print("Wire1 receive buffer size is ");
stream.println(buffers.rxWireBufferCapacity());

stream.print("Wire1 service buffer size is ");
stream.println(buffers.srvWireBufferCapacity());
}

#endif
2 changes: 1 addition & 1 deletion libraries/Wire/examples/master_writer/master_writer.ino
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ void setup()
Wire.begin(); // join i2c bus (address optional for master)
}

byte x = 0;
static byte x = 0;

void loop()
{
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// 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 <Wire.h>
#include <TwoWireBuffers.h>
#include "Arduino.h"

#define USE_WIRE1 false // Set to true for using Wire1

// 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.

#if not USE_WIRE1

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("Wire service buffer size is ");
stream.println(buffers.srvWireBufferCapacity());
}

#else

SET_Wire1_BUFFERS(RECEIVE_BUFFER_SIZE, TRANSMIT_BUFFER_SIZE,
true /* master buffers needed */, false /* no slave buffers needed */ );

void setup() {
Wire1.begin(); // join I2C bus (address optional for master)

// This is just for curiosity and could be removed
Serial.begin(9600); // start serial for output
printWire1BuffersCapacity(Serial);
}

static byte x = 0;

void loop() {
Wire1.beginTransmission(8); // transmit to device #8
Wire1.write(text); // sends multiple bytes
Wire1.write(x); // sends one byte
Wire1.endTransmission(); // stop transmitting

x++;
delay(500);
}

void printWire1BuffersCapacity(Stream& stream) {
const auto& buffers = GET_Wire1_BUFFERS();

stream.print("Wire1 transmit buffer size is ");
stream.println(buffers.txWireBufferCapacity());

stream.print("Wire1 receive buffer size is ");
stream.println(buffers.rxWireBufferCapacity());

stream.print("Wire1 service buffer size is ");
stream.println(buffers.srvWireBufferCapacity());
}

#endif
5 changes: 5 additions & 0 deletions libraries/Wire/examples/slave_receiver/slave_receiver.ino
Original file line number Diff line number Diff line change
@@ -26,6 +26,11 @@ void loop()

// 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Wire1 connnected to Wire. (scl <-> scl1, sda <-> sda1)

// Demonstrates use of the Wire library on a single Arduino board
// with 2 Wire interfaces (like Arduino Due).
// Uses the option of customizing the buffers.
//
// Wire data to an I2C/TWI slave device
// Wire1 receives data as an I2C/TWI slave device

// Created 02 Jan 2025

// This example code is in the public domain.


#include <Wire.h>
#include <TwoWireBuffers.h>
#include "Arduino.h"

static_assert(WIRE_INTERFACES_COUNT > 1, "You need two I2C interfaces on the Arduino board to run this sketch");

static const char text[] = "You really won't believe it, but x is ";

// Wire is the master writer
constexpr size_t M_RECEIVE_BUFFER_SIZE = 0; // There is no receive in this sketch.
constexpr size_t M_TRANSMIT_BUFFER_SIZE = 42; // Enhance the buffer to 42 characters.
SET_Wire_BUFFERS(M_RECEIVE_BUFFER_SIZE, M_TRANSMIT_BUFFER_SIZE,
true /* master buffers needed */, false /* no slave buffers needed */ );

// Wire1 is the slave receiver
constexpr size_t S_RECEIVE_BUFFER_SIZE = 42; // Be able receive up to 42 characters in one message.
constexpr size_t S_TRANSMIT_BUFFER_SIZE = 0; // There is no transmit in this sketch.
SET_Wire1_BUFFERS(S_RECEIVE_BUFFER_SIZE, S_TRANSMIT_BUFFER_SIZE,
false /* no master buffers needed */, true /* slave buffers needed */ );

void setup() {
Serial.begin(9600); // start serial for output
Wire.begin(); // master joins I2C bus (address optional for master)
Wire1.begin(8); // slave joins I2C bus with address #8
Wire1.onReceive(receiveEvent); // register event

// This is just for curiosity and could be removed
printWireBuffersCapacity(Serial);
printWire1BuffersCapacity(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);
}

// 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 < Wire1.available()) { // loop through all but the last
const char c = Wire1.read(); // receive byte as a character
Serial.print(c); // print the character
}
const int x = Wire1.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("Wire service buffer size is ");
stream.println(buffers.srvWireBufferCapacity());
delay(250); // Give time to finalize the print out
}

void printWire1BuffersCapacity(Stream& stream) {
const auto& buffers = GET_Wire1_BUFFERS();

stream.print("Wire1 transmit buffer size is ");
stream.println(buffers.txWireBufferCapacity());

stream.print("Wire1 receive buffer size is ");
stream.println(buffers.rxWireBufferCapacity());

stream.print("Wire1 service buffer size is ");
stream.println(buffers.srvWireBufferCapacity());
delay(250); // Give time to free up Serial output buffer.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// 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 <Wire.h>
#include <TwoWireBuffers.h>
#include "Arduino.h"

#define USE_WIRE1 false // Set to true for using Wire1

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.

#if not USE_WIRE1

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("Wire service buffer size is ");
stream.println(buffers.srvWireBufferCapacity());
delay(250); // Give time to free up Serial output buffer.
}

#else

SET_Wire1_BUFFERS(RECEIVE_BUFFER_SIZE, TRANSMIT_BUFFER_SIZE,
false /* no master buffers needed */, true /* slave buffers needed */ );

void setup() {
Wire1.begin(8); // join I2C bus with address #8
Wire1.onReceive(receiveEvent); // register event
Serial.begin(9600); // start serial for output

// This is just for curiosity and could be removed
printWire1BuffersCapacity(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 < Wire1.available()) { // loop through all but the last
const char c = Wire1.read(); // receive byte as a character
Serial.print(c); // print the character
}
const int x = Wire1.read(); // receive byte as an integer
Serial.println(x); // print the integer
}

void printWire1BuffersCapacity(Stream& stream) {
const auto& buffers = GET_Wire1_BUFFERS();

stream.print("Wire1 transmit buffer size is ");
stream.println(buffers.txWireBufferCapacity());

stream.print("Wire1 receive buffer size is ");
stream.println(buffers.rxWireBufferCapacity());

stream.print("Wire1 service buffer size is ");
stream.println(buffers.srvWireBufferCapacity());
delay(250); // Give time to free up Serial output buffer.
}

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Wire1 connnected to Wire. (scl <-> scl1, sda <-> sda1)

// Demonstrates use of the Wire library on a single Arduino board
// with 2 Wire interfaces (like Arduino Due).
// Uses the option of customizing the buffers.
//
// Wire reads data from an I2C/TWI slave device
// Wire1 sends data as an I2C/TWI slave device

// Created 02 Jan 2025

// This example code is in the public domain.


#include <Wire.h>
#include <TwoWireBuffers.h>
#include "Arduino.h"

static_assert(WIRE_INTERFACES_COUNT > 1, "You need two I2C interfaces on the Arduino board to run this sketch");

static const char text[] = "hello "; // respond with message of 6 bytes

// Wire is the master reader
constexpr size_t M_RECEIVE_BUFFER_SIZE = sizeof(text)-1; // Don't need a byte for the \0
constexpr size_t M_TRANSMIT_BUFFER_SIZE = 0; // There is no transmit in this sketch.
SET_Wire_BUFFERS(M_RECEIVE_BUFFER_SIZE, M_TRANSMIT_BUFFER_SIZE,
true /* master buffers needed */, false /* no slave buffers needed */ );

// Wire1 is the slave sender
constexpr size_t S_RECEIVE_BUFFER_SIZE = 0; // There is no receive in this sketch.
constexpr size_t S_TRANSMIT_BUFFER_SIZE = sizeof(text)-1; // Don't need a byte for the \0
SET_Wire1_BUFFERS(S_RECEIVE_BUFFER_SIZE, S_TRANSMIT_BUFFER_SIZE,
false /* no master buffers needed */, true /* slave buffers needed */ );

void setup() {
Serial.begin(9600); // start serial for output
Wire.begin(); // master joins I2C bus (address optional for master)
Wire1.begin(8); // slave joins I2C bus with address #8
Wire1.onRequest(requestEvent); // register slave event

// This is just for curiosity and could be removed
printWireBuffersCapacity(Serial);
printWire1BuffersCapacity(Serial);
}

void loop() {
Wire.requestFrom(8, M_RECEIVE_BUFFER_SIZE);

while (Wire.available()) {
const char c = Wire.read(); // receive a byte as character
Serial.print(c); // print the character
}
Serial.println();

delay(500);
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {
Wire1.write(text);
// as expected by master
}

// print Wire buffer sizes
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("Wire service buffer size is ");
stream.println(buffers.srvWireBufferCapacity());
}

// print Wire1 buffer sizes
void printWire1BuffersCapacity(Stream& stream) {
const auto& buffers = GET_Wire1_BUFFERS();

stream.print("Wire1 transmit buffer size is ");
stream.println(buffers.txWireBufferCapacity());

stream.print("Wire1 receive buffer size is ");
stream.println(buffers.rxWireBufferCapacity());

stream.print("Wire1 service buffer size is ");
stream.println(buffers.srvWireBufferCapacity());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// 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 <Wire.h>
#include <TwoWireBuffers.h>
#include "Arduino.h"

#define USE_WIRE1 false // Set to true for using Wire1

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

#if not USE_WIRE1

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("Wire service buffer size is ");
stream.println(buffers.srvWireBufferCapacity());
}

#else

SET_Wire1_BUFFERS(RECEIVE_BUFFER_SIZE, TRANSMIT_BUFFER_SIZE,
false /* no master buffers needed */, true /* slave buffers needed */ );

void setup() {
Wire1.begin(8); // join I2C bus with address #8
Wire1.onRequest(requestEvent); // register event

// This is just for curiosity and could be removed
Serial.begin(9600);
printWire1BuffersCapacity(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() {
Wire1.write(text);
// as expected by master
}

void printWire1BuffersCapacity(Stream& stream) {
const auto& buffers = GET_Wire1_BUFFERS();

stream.print("Wire1 transmit buffer size is ");
stream.println(buffers.txWireBufferCapacity());

stream.print("Wire1 receive buffer size is ");
stream.println(buffers.rxWireBufferCapacity());

stream.print("Wire1 service buffer size is ");
stream.println(buffers.srvWireBufferCapacity());
}

#endif
40 changes: 40 additions & 0 deletions libraries/Wire/src/TwoWireBuffers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
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"
#include "variant.h"

constexpr size_t RX_BUFFER_DEFAULT_LENGTH = 32;
constexpr size_t TX_BUFFER_DEFAULT_LENGTH = 32;

#if WIRE_INTERFACES_COUNT > 0
// Default buffers for the Wire object
template<> __attribute__((weak)) TwoWireBuffers::Interface& WireBuffers<0>::instance() { \
static TwoWireBuffers::Impl<RX_BUFFER_DEFAULT_LENGTH, TX_BUFFER_DEFAULT_LENGTH> buffers; \
return buffers; \
}
#endif

#if WIRE_INTERFACES_COUNT > 1
// Default buffers for the Wire1 object
template<> __attribute__((weak)) TwoWireBuffers::Interface& WireBuffers<1>::instance() { \
static TwoWireBuffers::Impl<RX_BUFFER_DEFAULT_LENGTH, TX_BUFFER_DEFAULT_LENGTH> buffers; \
return buffers; \
}
#endif
121 changes: 121 additions & 0 deletions libraries/Wire/src/TwoWireBuffers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
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 <stdint.h>
#include <stddef.h>
#include <Wire.h>

namespace TwoWireBuffers {

/* Template class that implements an compile time fixed size array. */
template<unsigned CAPACITY>
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* rxWireBuffer() = 0;
virtual size_t rxWireBufferCapacity()const = 0;

virtual uint8_t* txWireBuffer() = 0;
virtual size_t txWireBufferCapacity()const = 0;

virtual uint8_t* srvWireBuffer() = 0;
virtual size_t srvWireBufferCapacity()const = 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
>
class Impl : public Interface {
// Service buffer is needed for transmit and receive.
static constexpr size_t SRV_CAPACITY =
RX_CAPACITY > TX_CAPACITY ? RX_CAPACITY : TX_CAPACITY;

// Set the capacity for a TwoWire object.
TwoWireBuffers::StaticBuffer<RX_CAPACITY> mRxWireBuffer;
TwoWireBuffers::StaticBuffer<TX_CAPACITY> mTxWireBuffer;
TwoWireBuffers::StaticBuffer<SRV_CAPACITY> mSrvWireBuffer;

public:
virtual uint8_t* rxWireBuffer() override {return mRxWireBuffer.storage();}
virtual size_t rxWireBufferCapacity()const override {return mRxWireBuffer.capacity();}

virtual uint8_t* txWireBuffer() override {return mTxWireBuffer.storage();}
virtual size_t txWireBufferCapacity()const override {return mTxWireBuffer.capacity();}

virtual uint8_t* srvWireBuffer() override {return mSrvWireBuffer.storage();}
virtual size_t srvWireBufferCapacity()const override {return mSrvWireBuffer.capacity();}
};

} // namespace TwoWireBuffers

template<size_t wireNum> struct WireBuffers { // The buffers for the Wire object
static TwoWireBuffers::Interface& instance();
};

// Note: enableMaster and enableSlave don't have any impact on sam architecture, but they
// are present to keep the macro compatible with the one on the avr architecture, where
// it makes a difference in regard to memory consumption.
#define SET_WIRE_BUFFERS_(wireNum, rxBufferCapacity, txBufferCapacity, enableMaster, enableSlave) \
template<> TwoWireBuffers::Interface& WireBuffers<wireNum>::instance() { \
static TwoWireBuffers::Impl<rxBufferCapacity, txBufferCapacity> buffers; \
return buffers; \
}

#define GET_WIRE_BUFFERS_(wireNum) WireBuffers<wireNum>::instance()

#if WIRE_INTERFACES_COUNT > 0
#define SET_Wire_BUFFERS(rxBufferCapacity, txBufferCapacity, enableMaster, enableSlave) \
SET_WIRE_BUFFERS_(0, rxBufferCapacity, txBufferCapacity, enableMaster, enableSlave)

#define GET_Wire_BUFFERS() GET_WIRE_BUFFERS_(0)
#endif

#if WIRE_INTERFACES_COUNT > 1
#define SET_Wire1_BUFFERS(rxBufferCapacity, txBufferCapacity, enableMaster, enableSlave) \
SET_WIRE_BUFFERS_(1, rxBufferCapacity, txBufferCapacity, enableMaster, enableSlave)

#define GET_Wire1_BUFFERS() GET_WIRE_BUFFERS_(1)
#endif

#endif /* TwiBuffers_h */
91 changes: 45 additions & 46 deletions libraries/Wire/src/Wire.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* TwoWire.h - TWI/I2C library for Arduino Due
* Wire.cpp - TWI/I2C library for Arduino Due
* Copyright (c) 2011 Cristian Maglie <c.maglie@arduino.cc>
* All rights reserved.
*
@@ -27,6 +27,7 @@ extern "C" {
}

#include "Wire.h"
#include "TwoWireBuffers.h"

static inline bool TWI_FailedAcknowledge(Twi *pTwi) {
return pTwi->TWI_SR & TWI_SR_NACK;
@@ -96,13 +97,20 @@ static inline bool TWI_STATUS_NACK(uint32_t status) {
return (status & TWI_SR_NACK) == TWI_SR_NACK;
}

TwoWire::TwoWire(Twi *_twi, void(*_beginCb)(void), void(*_endCb)(void)) :
twi(_twi), rxBufferIndex(0), rxBufferLength(0), txAddress(0),
TwoWire::TwoWire(TwoWireBuffers::Interface& _twbi, Twi *_twi, void(*_beginCb)(void), void(*_endCb)(void)) :
buffers(_twbi), twi(_twi), rxBufferIndex(0), rxBufferLength(0), txAddress(0),
txBufferLength(0), srvBufferIndex(0), srvBufferLength(0), status(
UNINITIALIZED), onBeginCallback(_beginCb),
onEndCallback(_endCb), twiClock(TWI_CLOCK) {
}

uint8_t* TwoWire::srvBuffer()const {return buffers.srvWireBuffer();}
size_t TwoWire::srvBufferCapacity()const {return buffers.srvWireBufferCapacity();}
uint8_t* TwoWire::rxBuffer()const {return buffers.rxWireBuffer();}
size_t TwoWire::rxBufferCapacity()const {return buffers.rxWireBufferCapacity();}
uint8_t* TwoWire::txBuffer()const {return buffers.txWireBuffer();}
size_t TwoWire::txBufferCapacity()const {return buffers.txWireBufferCapacity();}

void TwoWire::begin(void) {
if (onBeginCallback)
onBeginCallback();
@@ -147,19 +155,21 @@ void TwoWire::setClock(uint32_t frequency) {
}

uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint32_t iaddress, uint8_t isize, uint8_t sendStop) {
if (quantity > BUFFER_LENGTH)
quantity = BUFFER_LENGTH;
if (quantity > rxBufferCapacity())
quantity = rxBufferCapacity();

// perform blocking read into buffer
int readed = 0;
TWI_StartRead(twi, address, iaddress, isize);

uint8_t* const rxBuf = rxBuffer();
do {
// Stop condition must be set during the reception of last byte
if (readed + 1 == quantity)
TWI_SendSTOPCondition( twi);

if (TWI_WaitByteReceived(twi, RECV_TIMEOUT))
rxBuffer[readed++] = TWI_ReadByte(twi);
rxBuf[readed++] = TWI_ReadByte(twi);
else
break;
} while (readed < quantity);
@@ -172,22 +182,6 @@ uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint32_t iaddres
return readed;
}

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) {
status = MASTER_SEND;

@@ -196,10 +190,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
@@ -216,14 +206,15 @@ void TwoWire::beginTransmission(int address) {
uint8_t TwoWire::endTransmission(uint8_t sendStop) {
uint8_t error = 0;
// transmit buffer (blocking)
TWI_StartWrite(twi, txAddress, 0, 0, txBuffer[0]);
uint8_t* const txBuf = txBuffer();
TWI_StartWrite(twi, txAddress, 0, 0, txBuf[0]);
if (!TWI_WaitByteSent(twi, XMIT_TIMEOUT))
error = 2; // error, got NACK on address transmit

if (error == 0) {
uint16_t sent = 1;
while (sent < txBufferLength) {
TWI_WriteByte(twi, txBuffer[sent++]);
TWI_WriteByte(twi, txBuf[sent++]);
if (!TWI_WaitByteSent(twi, XMIT_TIMEOUT))
error = 3; // error, got NACK during data transmmit
}
@@ -250,30 +241,32 @@ uint8_t TwoWire::endTransmission(void)

size_t TwoWire::write(uint8_t data) {
if (status == MASTER_SEND) {
if (txBufferLength >= BUFFER_LENGTH)
if (txBufferLength >= txBufferCapacity())
return 0;
txBuffer[txBufferLength++] = data;
txBuffer()[txBufferLength++] = data;
return 1;
} else {
if (srvBufferLength >= BUFFER_LENGTH)
if (srvBufferLength >= srvBufferCapacity())
return 0;
srvBuffer[srvBufferLength++] = data;
srvBuffer()[srvBufferLength++] = data;
return 1;
}
}

size_t TwoWire::write(const uint8_t *data, size_t quantity) {
if (status == MASTER_SEND) {
uint8_t* const txBuf = txBuffer();
for (size_t i = 0; i < quantity; ++i) {
if (txBufferLength >= BUFFER_LENGTH)
if (txBufferLength >= txBufferCapacity())
return i;
txBuffer[txBufferLength++] = data[i];
txBuf[txBufferLength++] = data[i];
}
} else {
uint8_t* const srvBuf = srvBuffer();
for (size_t i = 0; i < quantity; ++i) {
if (srvBufferLength >= BUFFER_LENGTH)
if (srvBufferLength >= srvBufferCapacity())
return i;
srvBuffer[srvBufferLength++] = data[i];
srvBuf[srvBufferLength++] = data[i];
}
}
return quantity;
@@ -285,13 +278,13 @@ int TwoWire::available(void) {

int TwoWire::read(void) {
if (rxBufferIndex < rxBufferLength)
return rxBuffer[rxBufferIndex++];
return rxBuffer()[rxBufferIndex++];
return -1;
}

int TwoWire::peek(void) {
if (rxBufferIndex < rxBufferLength)
return rxBuffer[rxBufferIndex];
return rxBuffer()[rxBufferIndex];
return -1;
}

@@ -332,7 +325,7 @@ void TwoWire::onService(void) {
onRequestCallback();
else
// create a default 1-byte response
write((uint8_t) 0);
write(static_cast<uint8_t>(0));
}
}

@@ -341,8 +334,13 @@ void TwoWire::onService(void) {
// Copy data into rxBuffer
// (allows to receive another packet while the
// user program reads actual data)
for (uint8_t i = 0; i < srvBufferLength; ++i)
rxBuffer[i] = srvBuffer[i];

uint8_t* const srvBuff = srvBuffer();
uint8_t* const rxBuff = rxBuffer();
for (size_t i = 0; i < srvBufferLength; ++i) {
rxBuff[i] = srvBuff[i];
}

rxBufferIndex = 0;
rxBufferLength = srvBufferLength;

@@ -359,16 +357,16 @@ void TwoWire::onService(void) {

if (status == SLAVE_RECV) {
if (TWI_STATUS_RXRDY(sr)) {
if (srvBufferLength < BUFFER_LENGTH)
srvBuffer[srvBufferLength++] = TWI_ReadByte(twi);
if (srvBufferLength < srvBufferCapacity())
srvBuffer()[srvBufferLength++] = TWI_ReadByte(twi);
}
}

if (status == SLAVE_SEND) {
if (TWI_STATUS_TXRDY(sr) && !TWI_STATUS_NACK(sr)) {
uint8_t c = 'x';
if (srvBufferIndex < srvBufferLength)
c = srvBuffer[srvBufferIndex++];
c = srvBuffer()[srvBufferIndex++];
TWI_WriteByte(twi, c);
}
}
@@ -405,7 +403,7 @@ static void Wire_Deinit(void) {
// and pullups were not enabled
}

TwoWire Wire = TwoWire(WIRE_INTERFACE, Wire_Init, Wire_Deinit);
TwoWire Wire = TwoWire(WireBuffers<0>::instance(), WIRE_INTERFACE, Wire_Init, Wire_Deinit);

void WIRE_ISR_HANDLER(void) {
Wire.onService();
@@ -443,11 +441,12 @@ static void Wire1_Deinit(void) {
// and pullups were not enabled
}

TwoWire Wire1 = TwoWire(WIRE1_INTERFACE, Wire1_Init, Wire1_Deinit);
TwoWire Wire1 = TwoWire(WireBuffers<1>::instance(), WIRE1_INTERFACE, Wire1_Init, Wire1_Deinit);

void WIRE1_ISR_HANDLER(void) {
Wire1.onService();
}

#endif

#pragma GCC diagnostic pop
68 changes: 46 additions & 22 deletions libraries/Wire/src/Wire.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* TwoWire.h - TWI/I2C library for Arduino Due
* Wire.h - TWI/I2C library for Arduino Due
* Copyright (c) 2011 Cristian Maglie <c.maglie@arduino.cc>
* All rights reserved.
*
@@ -27,28 +27,43 @@
#include "Stream.h"
#include "variant.h"

#define BUFFER_LENGTH 32

// WIRE_HAS_END means Wire has end()
#define WIRE_HAS_END 1


namespace TwoWireBuffers {
class Interface;
}

class TwoWire : public Stream {
public:
TwoWire(Twi *twi, void(*begin_cb)(void), void(*end_cb)(void));
TwoWire(TwoWireBuffers::Interface& twbi, Twi *twi, void(*begin_cb)(void), void(*end_cb)(void));
void begin();
void begin(uint8_t);
void begin(int);
void end();
void setClock(uint32_t);
void beginTransmission(uint8_t);
void beginTransmission(int);
inline void beginTransmission(int address) {beginTransmission(static_cast<uint8_t>(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);
uint8_t endTransmission(uint8_t);
uint8_t requestFrom(uint8_t, uint8_t, uint32_t, uint8_t, uint8_t);
inline uint8_t requestFrom(uint8_t address, uint8_t quantity) {
return requestFrom(static_cast<uint8_t>(address), static_cast<uint8_t>(quantity),
static_cast<uint8_t>(true));
}
inline uint8_t requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop) {
return requestFrom(static_cast<uint8_t>(address), static_cast<uint8_t>(quantity),
static_cast<uint32_t>(0), static_cast<uint8_t>(0), static_cast<uint8_t>(sendStop));
}
inline uint8_t requestFrom(int address, int quantity) {
return requestFrom(static_cast<uint8_t>(address), static_cast<uint8_t>(quantity),
static_cast<uint8_t>(true));
}
inline uint8_t requestFrom(int address, int quantity, int sendStop) {
return requestFrom(static_cast<uint8_t>(address), static_cast<uint8_t>(quantity),
static_cast<uint8_t>(sendStop));
}
virtual size_t write(uint8_t);
virtual size_t write(const uint8_t *, size_t);
virtual int available(void);
@@ -58,27 +73,34 @@ class TwoWire : public Stream {
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); }
using Print::write;
inline size_t write(unsigned long n) { return write(static_cast<uint8_t>(n)); }
inline size_t write(long n) { return write(static_cast<uint8_t>(n)); }
inline size_t write(unsigned int n) { return write(static_cast<uint8_t>(n)); }
inline size_t write(int n) { return write(static_cast<uint8_t>(n)); }
using Print::write;

void onService(void);

inline size_t rxBufferCapacity()const;
inline size_t txBufferCapacity()const;
inline size_t srvBufferCapacity()const;

private:
// The buffers to be used for this Wire object.
TwoWireBuffers::Interface& buffers;

// RX Buffer
uint8_t rxBuffer[BUFFER_LENGTH];
inline uint8_t* rxBuffer()const;
uint8_t rxBufferIndex;
uint8_t rxBufferLength;

// TX Buffer
uint8_t txAddress;
uint8_t txBuffer[BUFFER_LENGTH];
inline uint8_t* txBuffer()const;
uint8_t txBufferLength;

// Service buffer
uint8_t srvBuffer[BUFFER_LENGTH];
inline uint8_t* srvBuffer()const;
uint8_t srvBufferIndex;
uint8_t srvBufferLength;

@@ -108,18 +130,20 @@ class TwoWire : public Stream {
TwoWireStatus status;

// TWI clock frequency
static const uint32_t TWI_CLOCK = 100000;
static constexpr uint32_t TWI_CLOCK = 100000;
uint32_t twiClock;

// Timeouts (
static const uint32_t RECV_TIMEOUT = 100000;
static const uint32_t XMIT_TIMEOUT = 100000;
static constexpr uint32_t RECV_TIMEOUT = 100000;
static constexpr uint32_t XMIT_TIMEOUT = 100000;
};

#if WIRE_INTERFACES_COUNT > 0
// The buffers for the Wire object
extern TwoWire Wire;
#endif
#if WIRE_INTERFACES_COUNT > 1
// The buffers for the Wire1 object
extern TwoWire Wire1;
#endif