Skip to content

Commit f41e3a9

Browse files
committed
Merge branch 'i2c-multi-master'
2 parents 076a4ed + 3b8bd99 commit f41e3a9

File tree

4 files changed

+383
-195
lines changed

4 files changed

+383
-195
lines changed

cores/esp8266/core_esp8266_si2c.cpp

Lines changed: 71 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,12 @@ static inline __attribute__((always_inline)) bool SCL_READ(const int twi_scl)
5757
return (GPI & (1 << twi_scl)) != 0;
5858
}
5959

60-
6160
// Implement as a class to reduce code size by allowing access to many global variables with a single base pointer
62-
class Twi
61+
// Derived from TwiMaster which can be instantied multiple times
62+
class TwiMasterOrSlave : public TwiMaster
6363
{
6464
private:
65-
unsigned int preferred_si2c_clock = 100000;
66-
uint32_t twi_dcount = 18;
67-
unsigned char twi_sda = 0;
68-
unsigned char twi_scl = 0;
6965
unsigned char twi_addr = 0;
70-
uint32_t twi_clockStretchLimit = 0;
7166

7267
// These are int-wide, even though they could all fit in a byte, to reduce code size and avoid any potential
7368
// issues about RmW on packed bytes. The int-wide variations of asm instructions are smaller than the equivalent
@@ -93,8 +88,9 @@ class Twi
9388
uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH];
9489
volatile int twi_rxBufferIndex = 0;
9590

96-
void (*twi_onSlaveTransmit)(void);
97-
void (*twi_onSlaveReceive)(uint8_t*, size_t);
91+
void* twi_SlaveTargetObject;
92+
void (*twi_onSlaveTransmit)(void*);
93+
void (*twi_onSlaveReceive)(uint8_t*, size_t, void*);
9894

9995
// ETS queue/timer interfaces
10096
enum { EVENTTASK_QUEUE_SIZE = 1, EVENTTASK_QUEUE_PRIO = 2 };
@@ -108,59 +104,46 @@ class Twi
108104
static void eventTask(ETSEvent *e);
109105
static void IRAM_ATTR onTimer(void *unused);
110106

111-
// Allow not linking in the slave code if there is no call to setAddress
107+
// Allow not linking in the slave code if there is no call to enableSlave
112108
bool _slaveEnabled = false;
113109

114110
// Internal use functions
115-
void IRAM_ATTR busywait(unsigned int v);
116-
bool write_start(void);
117-
bool write_stop(void);
118-
bool write_bit(bool bit);
119-
bool read_bit(void);
120-
bool write_byte(unsigned char byte);
121-
unsigned char read_byte(bool nack);
122111
void IRAM_ATTR onTwipEvent(uint8_t status);
123112

124-
// Handle the case where a slave needs to stretch the clock with a time-limited busy wait
125-
inline void WAIT_CLOCK_STRETCH()
126-
{
127-
esp8266::polledTimeout::oneShotFastUs timeout(twi_clockStretchLimit);
128-
esp8266::polledTimeout::periodicFastUs yieldTimeout(5000);
129-
while (!timeout && !SCL_READ(twi_scl)) // outer loop is stretch duration up to stretch limit
130-
{
131-
if (yieldTimeout) // inner loop yields every 5ms
132-
{
133-
yield();
134-
}
135-
}
136-
}
137-
138-
// Generate a clock "valley" (at the end of a segment, just before a repeated start)
139-
void twi_scl_valley(void);
140-
141113
public:
142-
void setClock(unsigned int freq);
143-
void setClockStretchLimit(uint32_t limit);
114+
// custom version
144115
void init(unsigned char sda, unsigned char scl);
116+
145117
void setAddress(uint8_t address);
146-
unsigned char writeTo(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop);
147-
unsigned char readFrom(unsigned char address, unsigned char* buf, unsigned int len, unsigned char sendStop);
148-
uint8_t status();
149118
uint8_t transmit(const uint8_t* data, uint8_t length);
150-
void attachSlaveRxEvent(void (*function)(uint8_t*, size_t));
151-
void attachSlaveTxEvent(void (*function)(void));
119+
void attachSlaveRxEvent(void (*function)(uint8_t*, size_t, void*));
120+
void attachSlaveTxEvent(void (*function)(void*));
152121
void IRAM_ATTR reply(uint8_t ack);
153122
void IRAM_ATTR releaseBus(void);
154-
void enableSlave();
123+
void enableSlave(void* targetObject);
155124
};
156125

157-
static Twi twi;
126+
static TwiMasterOrSlave twi;
127+
TwiMaster& twiMasterSingleton = twi;
158128

159129
#ifndef FCPU80
160130
#define FCPU80 80000000L
161131
#endif
162132

163-
void Twi::setClock(unsigned int freq)
133+
inline void TwiMaster::WAIT_CLOCK_STRETCH()
134+
{
135+
esp8266::polledTimeout::oneShotFastUs timeout(twi_clockStretchLimit);
136+
esp8266::polledTimeout::periodicFastUs yieldTimeout(5000);
137+
while (!timeout && !SCL_READ(twi_scl)) // outer loop is stretch duration up to stretch limit
138+
{
139+
if (yieldTimeout) // inner loop yields every 5ms
140+
{
141+
yield();
142+
}
143+
}
144+
}
145+
146+
void TwiMaster::setClock(unsigned int freq)
164147
{
165148
if (freq < 1000) // minimum freq 1000Hz to minimize slave timeouts and WDT resets
166149
{
@@ -190,14 +173,24 @@ void Twi::setClock(unsigned int freq)
190173
#endif
191174
}
192175

193-
void Twi::setClockStretchLimit(uint32_t limit)
176+
void TwiMaster::setClockStretchLimit(uint32_t limit)
194177
{
195178
twi_clockStretchLimit = limit;
196179
}
197180

198181

199182

200-
void Twi::init(unsigned char sda, unsigned char scl)
183+
void TwiMaster::init(unsigned char sda, unsigned char scl)
184+
{
185+
twi_sda = sda;
186+
twi_scl = scl;
187+
pinMode(twi_sda, INPUT_PULLUP);
188+
pinMode(twi_scl, INPUT_PULLUP);
189+
twi_setClock(preferred_si2c_clock);
190+
twi_setClockStretchLimit(150000L); // default value is 150 mS
191+
}
192+
193+
void TwiMasterOrSlave::init(unsigned char sda, unsigned char scl)
201194
{
202195
// set timer function
203196
ets_timer_setfn(&timer, onTimer, NULL);
@@ -213,32 +206,33 @@ void Twi::init(unsigned char sda, unsigned char scl)
213206
twi_setClockStretchLimit(150000L); // default value is 150 mS
214207
}
215208

216-
void Twi::setAddress(uint8_t address)
209+
void TwiMasterOrSlave::setAddress(uint8_t address)
217210
{
218211
// set twi slave address (skip over R/W bit)
219212
twi_addr = address << 1;
220213
}
221214

222-
void Twi::enableSlave()
215+
void TwiMasterOrSlave::enableSlave(void* targetObject)
223216
{
224217
if (!_slaveEnabled)
225218
{
219+
twi_SlaveTargetObject = targetObject;
226220
attachInterrupt(twi_scl, onSclChange, CHANGE);
227221
attachInterrupt(twi_sda, onSdaChange, CHANGE);
228222
_slaveEnabled = true;
229223
}
230224
}
231225

232-
void IRAM_ATTR Twi::busywait(unsigned int v)
226+
void IRAM_ATTR TwiMaster::busywait(unsigned int v)
233227
{
234228
unsigned int i;
235229
for (i = 0; i < v; i++) // loop time is 5 machine cycles: 31.25ns @ 160MHz, 62.5ns @ 80MHz
236230
{
237-
__asm__ __volatile__("nop"); // minimum element to keep GCC from optimizing this function out.
231+
asm("nop"); // minimum element to keep GCC from optimizing this function out.
238232
}
239233
}
240234

241-
bool Twi::write_start(void)
235+
bool TwiMaster::write_start(void)
242236
{
243237
SCL_HIGH(twi_scl);
244238
SDA_HIGH(twi_sda);
@@ -252,7 +246,7 @@ bool Twi::write_start(void)
252246
return true;
253247
}
254248

255-
bool Twi::write_stop(void)
249+
bool TwiMaster::write_stop(void)
256250
{
257251
SCL_LOW(twi_scl);
258252
SDA_LOW(twi_sda);
@@ -265,7 +259,7 @@ bool Twi::write_stop(void)
265259
return true;
266260
}
267261

268-
bool Twi::write_bit(bool bit)
262+
bool TwiMaster::write_bit(bool bit)
269263
{
270264
SCL_LOW(twi_scl);
271265
if (bit)
@@ -283,7 +277,7 @@ bool Twi::write_bit(bool bit)
283277
return true;
284278
}
285279

286-
bool Twi::read_bit(void)
280+
bool TwiMaster::read_bit(void)
287281
{
288282
SCL_LOW(twi_scl);
289283
SDA_HIGH(twi_sda);
@@ -295,7 +289,7 @@ bool Twi::read_bit(void)
295289
return bit;
296290
}
297291

298-
bool Twi::write_byte(unsigned char byte)
292+
bool TwiMaster::write_byte(unsigned char byte)
299293
{
300294
unsigned char bit;
301295
for (bit = 0; bit < 8; bit++)
@@ -306,7 +300,7 @@ bool Twi::write_byte(unsigned char byte)
306300
return !read_bit();//NACK/ACK
307301
}
308302

309-
unsigned char Twi::read_byte(bool nack)
303+
unsigned char TwiMaster::read_byte(bool nack)
310304
{
311305
unsigned char byte = 0;
312306
unsigned char bit;
@@ -318,7 +312,7 @@ unsigned char Twi::read_byte(bool nack)
318312
return byte;
319313
}
320314

321-
unsigned char Twi::writeTo(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop)
315+
unsigned char TwiMaster::writeTo(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop)
322316
{
323317
unsigned int i;
324318
if (!write_start())
@@ -363,7 +357,7 @@ unsigned char Twi::writeTo(unsigned char address, unsigned char * buf, unsigned
363357
return 0;
364358
}
365359

366-
unsigned char Twi::readFrom(unsigned char address, unsigned char* buf, unsigned int len, unsigned char sendStop)
360+
unsigned char TwiMaster::readFrom(unsigned char address, unsigned char* buf, unsigned int len, unsigned char sendStop)
367361
{
368362
unsigned int i;
369363
if (!write_start())
@@ -402,15 +396,15 @@ unsigned char Twi::readFrom(unsigned char address, unsigned char* buf, unsigned
402396
return 0;
403397
}
404398

405-
void Twi::twi_scl_valley(void)
399+
void TwiMaster::twi_scl_valley(void)
406400
{
407401
SCL_LOW(twi_scl);
408402
busywait(twi_dcount);
409403
SCL_HIGH(twi_scl);
410404
WAIT_CLOCK_STRETCH();
411405
}
412406

413-
uint8_t Twi::status()
407+
uint8_t TwiMaster::status()
414408
{
415409
WAIT_CLOCK_STRETCH(); // wait for a slow slave to finish
416410
if (!SCL_READ(twi_scl))
@@ -435,7 +429,7 @@ uint8_t Twi::status()
435429
return I2C_OK;
436430
}
437431

438-
uint8_t Twi::transmit(const uint8_t* data, uint8_t length)
432+
uint8_t TwiMasterOrSlave::transmit(const uint8_t* data, uint8_t length)
439433
{
440434
uint8_t i;
441435

@@ -461,20 +455,20 @@ uint8_t Twi::transmit(const uint8_t* data, uint8_t length)
461455
return 0;
462456
}
463457

464-
void Twi::attachSlaveRxEvent(void (*function)(uint8_t*, size_t))
458+
void TwiMasterOrSlave::attachSlaveRxEvent(void (*function)(uint8_t*, size_t, void*))
465459
{
466460
twi_onSlaveReceive = function;
467461
}
468462

469-
void Twi::attachSlaveTxEvent(void (*function)(void))
463+
void TwiMasterOrSlave::attachSlaveTxEvent(void (*function)(void*))
470464
{
471465
twi_onSlaveTransmit = function;
472466
}
473467

474468
// DO NOT INLINE, inlining reply() in combination with compiler optimizations causes function breakup into
475469
// parts and the IRAM_ATTR isn't propagated correctly to all parts, which of course causes crashes.
476470
// TODO: test with gcc 9.x and if it still fails, disable optimization with -fdisable-ipa-fnsplit
477-
void IRAM_ATTR Twi::reply(uint8_t ack)
471+
void IRAM_ATTR TwiMasterOrSlave::reply(uint8_t ack)
478472
{
479473
// transmit master read ready signal, with or without ack
480474
if (ack)
@@ -492,7 +486,7 @@ void IRAM_ATTR Twi::reply(uint8_t ack)
492486
}
493487

494488

495-
void IRAM_ATTR Twi::releaseBus(void)
489+
void IRAM_ATTR TwiMasterOrSlave::releaseBus(void)
496490
{
497491
// release bus
498492
//TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT);
@@ -505,7 +499,7 @@ void IRAM_ATTR Twi::releaseBus(void)
505499
}
506500

507501

508-
void IRAM_ATTR Twi::onTwipEvent(uint8_t status)
502+
void IRAM_ATTR TwiMasterOrSlave::onTwipEvent(uint8_t status)
509503
{
510504
twip_status = status;
511505
switch (status)
@@ -612,7 +606,7 @@ void IRAM_ATTR Twi::onTwipEvent(uint8_t status)
612606
}
613607
}
614608

615-
void IRAM_ATTR Twi::onTimer(void *unused)
609+
void IRAM_ATTR TwiMasterOrSlave::onTimer(void *unused)
616610
{
617611
(void)unused;
618612
twi.releaseBus();
@@ -621,7 +615,7 @@ void IRAM_ATTR Twi::onTimer(void *unused)
621615
twi.twip_state = TWIP_BUS_ERR;
622616
}
623617

624-
void Twi::eventTask(ETSEvent *e)
618+
void TwiMasterOrSlave::eventTask(ETSEvent *e)
625619
{
626620

627621
if (e == NULL)
@@ -632,7 +626,7 @@ void Twi::eventTask(ETSEvent *e)
632626
switch (e->sig)
633627
{
634628
case TWI_SIG_TX:
635-
twi.twi_onSlaveTransmit();
629+
twi.twi_onSlaveTransmit(twi.twi_SlaveTargetObject);
636630

637631
// if they didn't change buffer & length, initialize it
638632
if (twi.twi_txBufferLength == 0)
@@ -649,7 +643,7 @@ void Twi::eventTask(ETSEvent *e)
649643
case TWI_SIG_RX:
650644
// ack future responses and leave slave receiver state
651645
twi.releaseBus();
652-
twi.twi_onSlaveReceive(twi.twi_rxBuffer, e->par);
646+
twi.twi_onSlaveReceive(twi.twi_rxBuffer, e->par, twi.twi_SlaveTargetObject);
653647
break;
654648
}
655649
}
@@ -662,7 +656,7 @@ void Twi::eventTask(ETSEvent *e)
662656
// Shorthand for if the state is any of the or'd bitmask x
663657
#define IFSTATE(x) if (twip_state_mask & (x))
664658

665-
void IRAM_ATTR Twi::onSclChange(void)
659+
void IRAM_ATTR TwiMasterOrSlave::onSclChange(void)
666660
{
667661
unsigned int sda;
668662
unsigned int scl;
@@ -860,7 +854,7 @@ void IRAM_ATTR Twi::onSclChange(void)
860854
}
861855
}
862856

863-
void IRAM_ATTR Twi::onSdaChange(void)
857+
void IRAM_ATTR TwiMasterOrSlave::onSdaChange(void)
864858
{
865859
unsigned int sda;
866860
unsigned int scl;
@@ -998,16 +992,17 @@ extern "C" {
998992
return twi.transmit(buf, len);
999993
}
1000994

1001-
void twi_attachSlaveRxEvent(void (*cb)(uint8_t*, size_t))
995+
void twi_attachSlaveRxEventWithTarget(void (*cb)(uint8_t*, size_t, void*))
1002996
{
1003997
twi.attachSlaveRxEvent(cb);
1004998
}
1005999

1006-
void twi_attachSlaveTxEvent(void (*cb)(void))
1000+
void twi_attachSlaveTxEventWithTarget(void (*cb)(void*))
10071001
{
10081002
twi.attachSlaveTxEvent(cb);
10091003
}
10101004

1005+
10111006
void twi_reply(uint8_t r)
10121007
{
10131008
twi.reply(r);
@@ -1018,9 +1013,9 @@ extern "C" {
10181013
twi.releaseBus();
10191014
}
10201015

1021-
void twi_enableSlaveMode(void)
1016+
void twi_enableSlaveModeWithTarget(void* targetObject)
10221017
{
1023-
twi.enableSlave();
1018+
twi.enableSlave(targetObject);
10241019
}
10251020

10261021
};

0 commit comments

Comments
 (0)