2727#include " RingBuffer.h"
2828#include < stddef.h>
2929
30- // WIRE_HAS_END means Wire has end()
30+ // WIRE_HAS_END means Wire has end()
3131#define WIRE_HAS_END 1
3232
3333// NOTE: SAMD21/SAMD51 silicon errata: when I2C master uses SCLSM=1, CTRLB.CMD
3838
3939class TwoWire : public Stream
4040{
41- public:
42- TwoWire (SERCOM *s, uint8_t pinSDA, uint8_t pinSCL);
43- void begin ();
44- void begin (uint8_t , bool enableGeneralCall = false );
45- void begin (uint16_t , bool enableGeneralCall, uint8_t speed = 0x0 , bool enable10Bit = false );
46- void end ();
47- void setClock (uint32_t );
48-
49- void beginTransmission (uint8_t );
50- // If onComplete is nullptr, this blocks for legacy sync behavior.
51- // If onComplete is non-null, this enqueues and returns immediately (async).
52- uint8_t endTransmission (bool stopBit = true ,
53- void (*onComplete)(void *user, int status) = nullptr,
54- void *user = nullptr);
55-
56- // If onComplete is nullptr, this blocks for legacy sync behavior.
57- // If onComplete is non-null, this enqueues and returns immediately (async).
58- // If rxBuffer is nullptr, the internal buffer is used; otherwise rxBuffer is used.
59- uint8_t requestFrom (uint8_t address, size_t quantity, bool stopBit = true ,
60- uint8_t *rxBuffer = nullptr ,
61- void (*onComplete)(void *user, int status) = nullptr,
62- void *user = nullptr);
63-
64- size_t write (uint8_t data);
65- // 3-arg write: when setExternal=true, data is used directly (zero-copy) and
66- // quantity is treated as both length and capacity; subsequent write() calls return 0.
67- // For streaming > WIRE_BUFFER_LENGTH or async usage, call setTxBuffer() before write()
68- // on every transaction.
69- size_t write (const uint8_t *data, size_t quantity, bool setExternal = false );
41+ public:
42+ TwoWire (SERCOM *s, uint8_t pinSDA, uint8_t pinSCL);
43+ void begin ();
44+ void begin (uint8_t , bool enableGeneralCall = false );
45+ void begin (uint16_t , bool enableGeneralCall, uint8_t speed = 0x0 ,
46+ bool enable10Bit = false );
47+ void end ();
48+ void setClock (uint32_t );
49+
50+ void beginTransmission (uint8_t );
51+ // If onComplete is nullptr, this blocks for legacy sync behavior.
52+ // If onComplete is non-null, this enqueues and returns immediately (async).
53+ uint8_t endTransmission (bool stopBit = true ,
54+ void (*onComplete)(void *user, int status) = nullptr,
55+ void *user = nullptr);
56+
57+ // If onComplete is nullptr, this blocks for legacy sync behavior.
58+ // If onComplete is non-null, this enqueues and returns immediately (async).
59+ // If rxBuffer is nullptr, the internal buffer is used; otherwise rxBuffer is
60+ // used.
61+ uint8_t requestFrom (uint8_t address, size_t quantity, bool stopBit = true ,
62+ uint8_t *rxBuffer = nullptr ,
63+ void (*onComplete)(void *user, int status) = nullptr,
64+ void *user = nullptr);
65+
66+ size_t write (uint8_t data);
67+ // 3-arg write: when setExternal=true, data is used directly (zero-copy) and
68+ // quantity is treated as both length and capacity; subsequent write() calls
69+ // return 0. For streaming > WIRE_BUFFER_LENGTH or async usage, call
70+ // setTxBuffer() before write() on every transaction.
71+ size_t write (const uint8_t *data, size_t quantity, bool setExternal = false );
7072
7173 virtual int available (void );
7274 virtual int read (void );
@@ -91,8 +93,21 @@ class TwoWire : public Stream
9193
9294 inline void onService (void );
9395
94- private:
95- SERCOM *sercom;
96+ #ifdef _DEBUG_
97+ inline SercomTxn *getSlaveTxn (void ) { return &slaveTxn; }
98+ inline const SercomTxn *getSlaveTxn (void ) const { return &slaveTxn; }
99+ inline SercomTxn *getLoaderTxn (void ) { return &loader; }
100+ inline const SercomTxn *getLoaderTxn (void ) const { return &loader; }
101+ inline SercomTxn *getActiveTxn (void ) {
102+ return sercom ? sercom->getCurrentTxnWIRE () : nullptr ;
103+ }
104+ inline const SercomTxn *getActiveTxn (void ) const {
105+ return sercom ? sercom->getCurrentTxnWIRE () : nullptr ;
106+ }
107+ #endif // _DEBUG_
108+
109+ private:
110+ SERCOM *sercom;
96111 uint8_t _uc_pinSDA;
97112 uint8_t _uc_pinSCL;
98113
@@ -102,7 +117,7 @@ class TwoWire : public Stream
102117 static constexpr size_t WIRE_BUFFER_LENGTH = 255 ;
103118 uint8_t rxBuffer[WIRE_BUFFER_LENGTH];
104119 uint8_t txBuffer[WIRE_BUFFER_LENGTH];
105- uint8_t *rxBufferPtr;
120+ uint8_t *rxBufferPtr;
106121 size_t rxBufferCapacity;
107122 size_t rxLength;
108123 size_t rxIndex;
@@ -115,12 +130,12 @@ class TwoWire : public Stream
115130 int pendingReceiveLength;
116131 SercomTxn slaveTxn;
117132 SercomTxn loader; // Staging area for building transactions
118-
133+
119134 // Transaction pool for async operations (matches SERCOM queue depth)
120135 static constexpr size_t TXN_POOL_SIZE = 8 ;
121136 SercomTxn txnPool[TXN_POOL_SIZE];
122137 uint8_t txnPoolHead;
123-
138+
124139 SercomTxn *allocateTxn ();
125140 void freeTxn (SercomTxn *txn);
126141
@@ -136,22 +151,22 @@ class TwoWire : public Stream
136151};
137152
138153#if WIRE_INTERFACES_COUNT > 0
139- extern TwoWire Wire;
154+ extern TwoWire Wire;
140155#endif
141156#if WIRE_INTERFACES_COUNT > 1
142- extern TwoWire Wire1;
157+ extern TwoWire Wire1;
143158#endif
144159#if WIRE_INTERFACES_COUNT > 2
145- extern TwoWire Wire2;
160+ extern TwoWire Wire2;
146161#endif
147162#if WIRE_INTERFACES_COUNT > 3
148- extern TwoWire Wire3;
163+ extern TwoWire Wire3;
149164#endif
150165#if WIRE_INTERFACES_COUNT > 4
151- extern TwoWire Wire4;
166+ extern TwoWire Wire4;
152167#endif
153168#if WIRE_INTERFACES_COUNT > 5
154- extern TwoWire Wire5;
169+ extern TwoWire Wire5;
155170#endif
156171
157172inline void TwoWire::onService (void )
@@ -161,9 +176,9 @@ inline void TwoWire::onService(void)
161176 bool isMaster = sercom->isMasterWIRE ();
162177
163178 if ((!isMaster && !sercom->isSlaveWIRE ()) || flags == 0 ) {
164- sercom->clearINTFLAG ();
165- return ;
166- }
179+ sercom->clearINTFLAG ();
180+ return ;
181+ }
167182
168183 if (status & SERCOM_I2CM_STATUS_RXNACK) {
169184 sercom->prepareCommandBitsWIRE (WIRE_MASTER_ACT_STOP);
@@ -197,7 +212,7 @@ inline void TwoWire::onService(void)
197212 err = SercomWireError::LENGTH_ERROR;
198213 if (busState == 0x0 )
199214 err = SercomWireError::BUS_STATE_UNKNOWN;
200-
215+
201216 sercom->clearINTFLAG ();
202217 sercom->deferStopWIRE (err);
203218 return ;
@@ -206,9 +221,9 @@ inline void TwoWire::onService(void)
206221 bool isRead = (txn->config & I2C_CFG_READ);
207222
208223 if (sercom->getTxnIndexWIRE () < sercom->getTxnLengthWIRE ()) {
209- isRead ? sercom->readDataWIRE () : sercom->sendDataWIRE ();
224+ bool more = isRead ? sercom->readDataWIRE () : sercom->sendDataWIRE ();
210225 awaitingAddressAck = false ;
211- return ;
226+ if (!isRead || more) return ;
212227 }
213228
214229 if ((txn->config & I2C_CFG_STOP) && !isRead)
@@ -244,7 +259,7 @@ inline void TwoWire::onService(void)
244259 bool prec = (flags & SERCOM_I2CS_INTFLAG_PREC); // Stop detected
245260 bool amatch = (flags & SERCOM_I2CS_INTFLAG_AMATCH); // Address Match detected
246261 bool drdy = (flags & SERCOM_I2CS_INTFLAG_DRDY); // Data Ready detected
247-
262+
248263 // Stop or Restart detected - defer receive callback
249264 if (prec || (amatch && sr && !isMasterRead))
250265 {
@@ -253,24 +268,23 @@ inline void TwoWire::onService(void)
253268 sercom->deferReceiveWIRE (pendingReceiveLength);
254269 return ;
255270 }
256-
271+
257272 // Address Match - setup transaction
258273 // AACKEN enabled: address ACK is automatic, no manual ACK/clear needed
259- else if (amatch)
260- {
274+ else if (amatch) {
261275 if (isMasterRead) // Master Read / Slave TX
262276 {
263277 // onRequestCallback runs in ISR context here. Deferring to PendSV
264278 // would require stalling DRDY or returning 0xFF until the buffer is filled.
265279 // onRequestCallback is what will set TwoWire::slaveTxn for the transaction.
266280 if (onRequestCallback)
267281 onRequestCallback ();
268-
282+
269283 // Ensure callback actually set slaveTxn.length; if not, stall with 0-length txn
270- if (slaveTxn.length == 0 )
284+ if (slaveTxn.length == 0 )
271285 return ;
272286
273- if (!(slaveTxn.config & I2C_CFG_READ))
287+ if (!(slaveTxn.config & I2C_CFG_READ))
274288 slaveTxn.config |= I2C_CFG_READ;
275289 }
276290 else // Master Write / Slave RX
@@ -288,12 +302,12 @@ inline void TwoWire::onService(void)
288302
289303 // SCLSM=0 (Smart Mode disabled): AMATCH and DRDY never fire together
290304 // → return now, DRDY will fire in next interrupt
291- // SCLSM=1 (Smart Mode enabled) + Master Read: AMATCH+DRDY fire together
305+ // SCLSM=1 (Smart Mode enabled) + Master Read: AMATCH+DRDY fire together
292306 // → fall through to handle data immediately
293307 // SCLSM=1 + Master Write: DRDY not set yet
294308 // → return now, DRDY fires later
295309 if (!drdy)
296- return ;
310+ return ;
297311 // else: DRDY is set (SCLSM=1 Master Read case), fall through
298312 }
299313
@@ -303,7 +317,7 @@ inline void TwoWire::onService(void)
303317 isMasterRead ? sercom->sendDataWIRE () : sercom->readDataWIRE ();
304318
305319 if (!isMasterRead)
306- rxLength = sercom->getTxnIndexWIRE ();
320+ rxLength = sercom->getTxnIndexWIRE ();
307321 }
308322 }
309323}
0 commit comments