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);