Skip to content

Commit c691fe3

Browse files
committed
add copy of diff for arduino/ArduinoCore-avr#550 which adds a onRequestMore() callback to the usual Arduino Wire API
1 parent a9c9138 commit c691fe3

File tree

2 files changed

+246
-0
lines changed

2 files changed

+246
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
diff --git a/libraries/Wire/examples/slave_memory/slave_memory.ino b/libraries/Wire/examples/slave_memory/slave_memory.ino
2+
new file mode 100644
3+
index 000000000..942aa023f
4+
--- /dev/null
5+
+++ b/libraries/Wire/examples/slave_memory/slave_memory.ino
6+
@@ -0,0 +1,104 @@
7+
+// Wire Slave "256-Byte Memory" Device
8+
+// by Jan Wagner
9+
+
10+
+// Demonstrates use of the Wire library to emulate a 256-byte memory device,
11+
+// with sequential continued read and sequential continued write access.
12+
+// Operates as an I2C/TWI slave device.
13+
+//
14+
+// The protocol used in this example emulates typical small EEPROMs,
15+
+// or i2c-addressible register banks. The master controls the I2C transaction,
16+
+// doing one of two things within a single transaction:
17+
+//
18+
+// 1) master sends an address-byte and sends optional data-byte(s) that the
19+
+// slave shall store into memory starting from the given address,
20+
+//
21+
+// or
22+
+//
23+
+// 2) master sends an address-byte and immediately changes into receive
24+
+// mode, receiving data-byte(s) from slave memory starting from that address.
25+
+//
26+
+// The number of data bytes in the transaction is not known in advance.
27+
+// The master can at any time stop sending (1) or reading (2) data bytes.
28+
+// Either mode, (1) or (2), is carried out as a single multi-byte I2C transaction.
29+
+//
30+
+// Starting from a base address (between 0 and 255) sent by the master,
31+
+// the slave auto-increments the address for each byte sent or read.
32+
+// When the end of address space is reached it wraps back to 0.
33+
+
34+
+// Master writing address, then writing data (case 1) is handled simply in the
35+
+// onReceive event - receives starting address plus data bytes from master
36+
+
37+
+// Master writing address, then reading data (2) is handled in the
38+
+// onReceive event - receives starting address from master, no other data
39+
+// onRequest event - sends the first requested data to master
40+
+// onRequestMore event - sends more, continually reinvoked while master keeps reading
41+
+
42+
+// Created 18 December 2023
43+
+
44+
+// This example code is in the public domain.
45+
+
46+
+#include <Wire.h>
47+
+
48+
+static volatile byte shared_memory[256];
49+
+static volatile unsigned int memory_addr = 0;
50+
+
51+
+void setup() {
52+
+ unsigned int i;
53+
+
54+
+ // initialize memory with a simple pattern
55+
+ for(i=0; i<sizeof(shared_memory); i++) {
56+
+ shared_memory[i] = i;
57+
+ }
58+
+ memory_addr = 0;
59+
+
60+
+ // initialise Wire
61+
+ Wire.begin(8); // join I2C bus with address #8
62+
+ Wire.onReceive(receiveEvent); // register event
63+
+ Wire.onRequest(requestEvent); // register event
64+
+ Wire.onRequestMore(requestMoreEvent); // register event
65+
+}
66+
+
67+
+void loop() {
68+
+ delay(100);
69+
+}
70+
+
71+
+// Helper to get next address within array bounds
72+
+unsigned int next_addr(unsigned int curr_addr) {
73+
+ return (curr_addr + 1) % (sizeof(shared_memory) + 1);
74+
+}
75+
+
76+
+// Function that executes whenever data is sent to slave by master
77+
+// This function is registered as an event, see setup()
78+
+void receiveEvent(int nbytes) {
79+
+ if(nbytes > 0) {
80+
+
81+
+ // receive the memory address that the master wants to access
82+
+ memory_addr = Wire.read();
83+
+ memory_addr = memory_addr % (sizeof(shared_memory) + 1);
84+
+ nbytes--;
85+
+
86+
+ // receive optional data that master might be pushing into client memory
87+
+ while(nbytes > 0) {
88+
+ shared_memory[memory_addr] = Wire.read();
89+
+ memory_addr = next_addr(memory_addr);
90+
+ nbytes--;
91+
+ }
92+
+ }
93+
+}
94+
+
95+
+// Function that executes whenever data is first requested by master
96+
+// This function is registered as an event, see setup()
97+
+void requestEvent() {
98+
+ // master started reading: send back the first data byte
99+
+ Wire.write(shared_memory[memory_addr]);
100+
+ memory_addr = next_addr(memory_addr);
101+
+}
102+
+
103+
+// Function that executes each time the master in the current transaction
104+
+// tries to read more than initially provided in the onRequest handler above.
105+
+// This function is registered as an event, see setup()
106+
+void requestMoreEvent() {
107+
+ // master continues reading: send back the next data byte
108+
+ Wire.write(shared_memory[memory_addr]);
109+
+ memory_addr = next_addr(memory_addr);
110+
+}
111+
diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp
112+
index 001d924df..bdff763dc 100644
113+
--- a/libraries/Wire/src/Wire.cpp
114+
+++ b/libraries/Wire/src/Wire.cpp
115+
@@ -43,6 +43,7 @@ uint8_t TwoWire::txBufferLength = 0;
116+
117+
uint8_t TwoWire::transmitting = 0;
118+
void (*TwoWire::user_onRequest)(void);
119+
+void (*TwoWire::user_onRequestMore)(void);
120+
void (*TwoWire::user_onReceive)(int);
121+
122+
// Constructors ////////////////////////////////////////////////////////////////
123+
@@ -63,6 +64,7 @@ void TwoWire::begin(void)
124+
125+
twi_init();
126+
twi_attachSlaveTxEvent(onRequestService); // default callback must exist
127+
+ twi_attachSlaveTxMoreEvent(onRequestMoreService); // default callback must exist
128+
twi_attachSlaveRxEvent(onReceiveService); // default callback must exist
129+
}
130+
131+
@@ -360,6 +362,17 @@ void TwoWire::onRequestService(void)
132+
user_onRequest();
133+
}
134+
135+
+// behind the scenes function that is called when more data is requested
136+
+void TwoWire::onRequestMoreService(void)
137+
+{
138+
+ // don't bother if user hasn't registered a callback
139+
+ if(!user_onRequestMore){
140+
+ return;
141+
+ }
142+
+ // alert user program
143+
+ user_onRequestMore();
144+
+}
145+
+
146+
// sets function called on slave write
147+
void TwoWire::onReceive( void (*function)(int) )
148+
{
149+
@@ -372,6 +385,12 @@ void TwoWire::onRequest( void (*function)(void) )
150+
user_onRequest = function;
151+
}
152+
153+
+// sets function called on slave read
154+
+void TwoWire::onRequestMore( void (*function)(void) )
155+
+{
156+
+ user_onRequestMore = function;
157+
+}
158+
+
159+
// Preinstantiate Objects //////////////////////////////////////////////////////
160+
161+
TwoWire Wire = TwoWire();
162+
diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h
163+
index e70d72edb..e3620e241 100644
164+
--- a/libraries/Wire/src/Wire.h
165+
+++ b/libraries/Wire/src/Wire.h
166+
@@ -45,8 +45,10 @@ class TwoWire : public Stream
167+
168+
static uint8_t transmitting;
169+
static void (*user_onRequest)(void);
170+
+ static void (*user_onRequestMore)(void);
171+
static void (*user_onReceive)(int);
172+
static void onRequestService(void);
173+
+ static void onRequestMoreService(void);
174+
static void onReceiveService(uint8_t*, int);
175+
public:
176+
TwoWire();
177+
@@ -75,6 +77,7 @@ class TwoWire : public Stream
178+
virtual void flush(void);
179+
void onReceive( void (*)(int) );
180+
void onRequest( void (*)(void) );
181+
+ void onRequestMore( void (*)(void) );
182+
183+
inline size_t write(unsigned long n) { return write((uint8_t)n); }
184+
inline size_t write(long n) { return write((uint8_t)n); }
185+
diff --git a/libraries/Wire/src/utility/twi.c b/libraries/Wire/src/utility/twi.c
186+
index e09a33caf..391e09b1a 100644
187+
--- a/libraries/Wire/src/utility/twi.c
188+
+++ b/libraries/Wire/src/utility/twi.c
189+
@@ -56,6 +56,7 @@ static volatile bool twi_timed_out_flag = false; // a timeout has been seen
190+
static volatile bool twi_do_reset_on_timeout = false; // reset the TWI registers on timeout
191+
192+
static void (*twi_onSlaveTransmit)(void);
193+
+static void (*twi_onSlaveTransmitMore)(void);
194+
static void (*twi_onSlaveReceive)(uint8_t*, int);
195+
196+
static uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH];
197+
@@ -384,6 +385,17 @@ void twi_attachSlaveTxEvent( void (*function)(void) )
198+
twi_onSlaveTransmit = function;
199+
}
200+
201+
+/*
202+
+ * Function twi_attachSlaveTxMoreEvent
203+
+ * Desc sets function called before a slave cont'd sequential data write operation
204+
+ * Input function: callback function to use
205+
+ * Output none
206+
+ */
207+
+void twi_attachSlaveTxMoreEvent( void (*function)(void) )
208+
+{
209+
+ twi_onSlaveTransmitMore = function;
210+
+}
211+
+
212+
/*
213+
* Function twi_reply
214+
* Desc sends byte or readys receive line
215+
@@ -640,6 +652,10 @@ ISR(TWI_vect)
216+
case TW_ST_DATA_ACK: // byte sent, ack returned
217+
// copy data to output register
218+
TWDR = twi_txBuffer[twi_txBufferIndex++];
219+
+ // if the buffer emptied, request it to be topped up
220+
+ if (twi_txBufferIndex >= twi_txBufferLength) {
221+
+ twi_onSlaveTransmitMore();
222+
+ }
223+
// if there is more to send, ack, otherwise nack
224+
if(twi_txBufferIndex < twi_txBufferLength){
225+
twi_reply(1);
226+
diff --git a/libraries/Wire/src/utility/twi.h b/libraries/Wire/src/utility/twi.h
227+
index 85b983794..ea158d8ea 100644
228+
--- a/libraries/Wire/src/utility/twi.h
229+
+++ b/libraries/Wire/src/utility/twi.h
230+
@@ -49,6 +49,7 @@
231+
uint8_t twi_transmit(const uint8_t*, uint8_t);
232+
void twi_attachSlaveRxEvent( void (*)(uint8_t*, int) );
233+
void twi_attachSlaveTxEvent( void (*)(void) );
234+
+ void twi_attachSlaveTxMoreEvent( void (*)(void) );
235+
void twi_reply(uint8_t);
236+
void twi_stop(void);
237+
void twi_releaseBus(void);

src/arduino-patch/info.txt

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
See https://github.com/arduino/ArduinoCore-avr/pull/550
2+
3+
"Extend the Wire object by an onRequestMore callback. Example code is included.
4+
5+
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.
6+
7+
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.
8+
9+
(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 commit comments

Comments
 (0)