Skip to content

Commit

Permalink
add copy of diff for arduino/ArduinoCore-avr#550 which adds a onReque…
Browse files Browse the repository at this point in the history
…stMore() callback to the usual Arduino Wire API
  • Loading branch information
jwagnerhki committed Mar 8, 2024
1 parent a9c9138 commit c691fe3
Show file tree
Hide file tree
Showing 2 changed files with 246 additions and 0 deletions.
237 changes: 237 additions & 0 deletions src/arduino-patch/ArduinoCore-avr-pullrequest_550.patch.txt
Original file line number Diff line number Diff line change
@@ -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 <Wire.h>
+
+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<sizeof(shared_memory); i++) {
+ shared_memory[i] = i;
+ }
+ memory_addr = 0;
+
+ // initialise Wire
+ Wire.begin(8); // join I2C bus with address #8
+ Wire.onReceive(receiveEvent); // register event
+ Wire.onRequest(requestEvent); // register event
+ Wire.onRequestMore(requestMoreEvent); // register event
+}
+
+void loop() {
+ delay(100);
+}
+
+// Helper to get next address within array bounds
+unsigned int next_addr(unsigned int curr_addr) {
+ return (curr_addr + 1) % (sizeof(shared_memory) + 1);
+}
+
+// Function that executes whenever data is sent to slave by master
+// This function is registered as an event, see setup()
+void receiveEvent(int nbytes) {
+ if(nbytes > 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);
9 changes: 9 additions & 0 deletions src/arduino-patch/info.txt
Original file line number Diff line number Diff line change
@@ -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.)"

0 comments on commit c691fe3

Please sign in to comment.