Skip to content

Commit 412cee6

Browse files
committed
Reapply "allow readDataWIRE() to allow passing deferStopWIRE() on the final read."
This reverts commit 7f91df1.
1 parent 7f91df1 commit 412cee6

1 file changed

Lines changed: 71 additions & 57 deletions

File tree

libraries/Wire/Wire.h

Lines changed: 71 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
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
@@ -38,35 +38,37 @@
3838

3939
class 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

157172
inline 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

Comments
 (0)