diff --git a/src/arduino-patch/ArduinoCore-avr-pullrequest_550.patch.txt b/src/arduino-patch/ArduinoCore-avr-pullrequest_550.patch.txt new file mode 100644 index 0000000..f49cd3a --- /dev/null +++ b/src/arduino-patch/ArduinoCore-avr-pullrequest_550.patch.txt @@ -0,0 +1,237 @@ +diff --git a/libraries/Wire/examples/slave_memory/slave_memory.ino b/libraries/Wire/examples/slave_memory/slave_memory.ino +new file mode 100644 +index 000000000..942aa023f +--- /dev/null ++++ b/libraries/Wire/examples/slave_memory/slave_memory.ino +@@ -0,0 +1,104 @@ ++// Wire Slave "256-Byte Memory" Device ++// by Jan Wagner ++ ++// Demonstrates use of the Wire library to emulate a 256-byte memory device, ++// with sequential continued read and sequential continued write access. ++// Operates as an I2C/TWI slave device. ++// ++// The protocol used in this example emulates typical small EEPROMs, ++// or i2c-addressible register banks. The master controls the I2C transaction, ++// doing one of two things within a single transaction: ++// ++// 1) master sends an address-byte and sends optional data-byte(s) that the ++// slave shall store into memory starting from the given address, ++// ++// or ++// ++// 2) master sends an address-byte and immediately changes into receive ++// mode, receiving data-byte(s) from slave memory starting from that address. ++// ++// The number of data bytes in the transaction is not known in advance. ++// The master can at any time stop sending (1) or reading (2) data bytes. ++// Either mode, (1) or (2), is carried out as a single multi-byte I2C transaction. ++// ++// Starting from a base address (between 0 and 255) sent by the master, ++// the slave auto-increments the address for each byte sent or read. ++// When the end of address space is reached it wraps back to 0. ++ ++// Master writing address, then writing data (case 1) is handled simply in the ++// onReceive event - receives starting address plus data bytes from master ++ ++// Master writing address, then reading data (2) is handled in the ++// onReceive event - receives starting address from master, no other data ++// onRequest event - sends the first requested data to master ++// onRequestMore event - sends more, continually reinvoked while master keeps reading ++ ++// Created 18 December 2023 ++ ++// This example code is in the public domain. ++ ++#include ++ ++static volatile byte shared_memory[256]; ++static volatile unsigned int memory_addr = 0; ++ ++void setup() { ++ unsigned int i; ++ ++ // initialize memory with a simple pattern ++ for(i=0; i 0) { ++ ++ // receive the memory address that the master wants to access ++ memory_addr = Wire.read(); ++ memory_addr = memory_addr % (sizeof(shared_memory) + 1); ++ nbytes--; ++ ++ // receive optional data that master might be pushing into client memory ++ while(nbytes > 0) { ++ shared_memory[memory_addr] = Wire.read(); ++ memory_addr = next_addr(memory_addr); ++ nbytes--; ++ } ++ } ++} ++ ++// Function that executes whenever data is first requested by master ++// This function is registered as an event, see setup() ++void requestEvent() { ++ // master started reading: send back the first data byte ++ Wire.write(shared_memory[memory_addr]); ++ memory_addr = next_addr(memory_addr); ++} ++ ++// Function that executes each time the master in the current transaction ++// tries to read more than initially provided in the onRequest handler above. ++// This function is registered as an event, see setup() ++void requestMoreEvent() { ++ // master continues reading: send back the next data byte ++ Wire.write(shared_memory[memory_addr]); ++ memory_addr = next_addr(memory_addr); ++} +diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp +index 001d924df..bdff763dc 100644 +--- a/libraries/Wire/src/Wire.cpp ++++ b/libraries/Wire/src/Wire.cpp +@@ -43,6 +43,7 @@ uint8_t TwoWire::txBufferLength = 0; + + uint8_t TwoWire::transmitting = 0; + void (*TwoWire::user_onRequest)(void); ++void (*TwoWire::user_onRequestMore)(void); + void (*TwoWire::user_onReceive)(int); + + // Constructors //////////////////////////////////////////////////////////////// +@@ -63,6 +64,7 @@ void TwoWire::begin(void) + + twi_init(); + twi_attachSlaveTxEvent(onRequestService); // default callback must exist ++ twi_attachSlaveTxMoreEvent(onRequestMoreService); // default callback must exist + twi_attachSlaveRxEvent(onReceiveService); // default callback must exist + } + +@@ -360,6 +362,17 @@ void TwoWire::onRequestService(void) + user_onRequest(); + } + ++// behind the scenes function that is called when more data is requested ++void TwoWire::onRequestMoreService(void) ++{ ++ // don't bother if user hasn't registered a callback ++ if(!user_onRequestMore){ ++ return; ++ } ++ // alert user program ++ user_onRequestMore(); ++} ++ + // sets function called on slave write + void TwoWire::onReceive( void (*function)(int) ) + { +@@ -372,6 +385,12 @@ void TwoWire::onRequest( void (*function)(void) ) + user_onRequest = function; + } + ++// sets function called on slave read ++void TwoWire::onRequestMore( void (*function)(void) ) ++{ ++ user_onRequestMore = function; ++} ++ + // Preinstantiate Objects ////////////////////////////////////////////////////// + + TwoWire Wire = TwoWire(); +diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h +index e70d72edb..e3620e241 100644 +--- a/libraries/Wire/src/Wire.h ++++ b/libraries/Wire/src/Wire.h +@@ -45,8 +45,10 @@ class TwoWire : public Stream + + static uint8_t transmitting; + static void (*user_onRequest)(void); ++ static void (*user_onRequestMore)(void); + static void (*user_onReceive)(int); + static void onRequestService(void); ++ static void onRequestMoreService(void); + static void onReceiveService(uint8_t*, int); + public: + TwoWire(); +@@ -75,6 +77,7 @@ class TwoWire : public Stream + virtual void flush(void); + void onReceive( void (*)(int) ); + void onRequest( void (*)(void) ); ++ void onRequestMore( 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); } +diff --git a/libraries/Wire/src/utility/twi.c b/libraries/Wire/src/utility/twi.c +index e09a33caf..391e09b1a 100644 +--- a/libraries/Wire/src/utility/twi.c ++++ b/libraries/Wire/src/utility/twi.c +@@ -56,6 +56,7 @@ 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_onSlaveTransmitMore)(void); + static void (*twi_onSlaveReceive)(uint8_t*, int); + + static uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH]; +@@ -384,6 +385,17 @@ void twi_attachSlaveTxEvent( void (*function)(void) ) + twi_onSlaveTransmit = function; + } + ++/* ++ * Function twi_attachSlaveTxMoreEvent ++ * Desc sets function called before a slave cont'd sequential data write operation ++ * Input function: callback function to use ++ * Output none ++ */ ++void twi_attachSlaveTxMoreEvent( void (*function)(void) ) ++{ ++ twi_onSlaveTransmitMore = function; ++} ++ + /* + * Function twi_reply + * Desc sends byte or readys receive line +@@ -640,6 +652,10 @@ ISR(TWI_vect) + case TW_ST_DATA_ACK: // byte sent, ack returned + // copy data to output register + TWDR = twi_txBuffer[twi_txBufferIndex++]; ++ // if the buffer emptied, request it to be topped up ++ if (twi_txBufferIndex >= twi_txBufferLength) { ++ twi_onSlaveTransmitMore(); ++ } + // if there is more to send, ack, otherwise nack + if(twi_txBufferIndex < twi_txBufferLength){ + twi_reply(1); +diff --git a/libraries/Wire/src/utility/twi.h b/libraries/Wire/src/utility/twi.h +index 85b983794..ea158d8ea 100644 +--- a/libraries/Wire/src/utility/twi.h ++++ b/libraries/Wire/src/utility/twi.h +@@ -49,6 +49,7 @@ + uint8_t twi_transmit(const uint8_t*, uint8_t); + void twi_attachSlaveRxEvent( void (*)(uint8_t*, int) ); + void twi_attachSlaveTxEvent( void (*)(void) ); ++ void twi_attachSlaveTxMoreEvent( void (*)(void) ); + void twi_reply(uint8_t); + void twi_stop(void); + void twi_releaseBus(void); diff --git a/src/arduino-patch/info.txt b/src/arduino-patch/info.txt new file mode 100644 index 0000000..067b24b --- /dev/null +++ b/src/arduino-patch/info.txt @@ -0,0 +1,9 @@ +See https://github.com/arduino/ArduinoCore-avr/pull/550 + +"Extend the Wire object by an onRequestMore callback. Example code is included. + +The pull request extends the existing onRequest callback mechanism in the I2C target mode. The existing onRequest callback provides some data into the transmit buffer. However, that callback is invoked only once at the very start of the I2C transaction. There was no mechanism to top up further data mid-way into a continued transaction. + +In more dynamic protocols, the actual number of bytes that the I2C controller is expecting is sometimes not known in advance - the controller tries to clock in as many bytes as it needs, before terminating the transaction. If the onRequest -provided initial data get depleted, the new onRequestMore is invoked and allows the user to top up the transmit buffer. The onRequestMore gets invoked each time the transmit buffer runs empty, as long as the I2C transaction is still active, i.e., until the I2C controller terminates it. + +(Note: if these additions are acceptable, it'll probably be necessary to update https://github.com/arduino/ArduinoCore-API/blob/master/api/HardwareI2C.h by adding a corresponding new line with "virtual void onRequestMore(void(*)(void)) = 0;' to it.)"