Skip to content

Commit 9dd52f1

Browse files
mkotounmkotoun
mkotoun
authored and
mkotoun
committed
Enable use of multiple software I2C master interfaces in core
1 parent 9fc5afd commit 9dd52f1

File tree

2 files changed

+95
-65
lines changed

2 files changed

+95
-65
lines changed

cores/esp8266/core_esp8266_si2c.cpp

Lines changed: 56 additions & 65 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
@@ -110,42 +105,15 @@ class Twi
110105

111106
// Allow not linking in the slave code if there is no call to setAddress
112107
bool _slaveEnabled = false;
113-
108+
114109
// Internal use functions
115-
void ICACHE_RAM_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);
122110
void ICACHE_RAM_ATTR onTwipEvent(uint8_t status);
123111

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-
141112
public:
142-
void setClock(unsigned int freq);
143-
void setClockStretchLimit(uint32_t limit);
113+
// custom version
144114
void init(unsigned char sda, unsigned char scl);
115+
145116
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();
149117
uint8_t transmit(const uint8_t* data, uint8_t length);
150118
void attachSlaveRxEvent(void (*function)(uint8_t*, size_t));
151119
void attachSlaveTxEvent(void (*function)(void));
@@ -154,13 +122,26 @@ class Twi
154122
void enableSlave();
155123
};
156124

157-
static Twi twi;
125+
static TwiMasterOrSlave twi;
158126

159127
#ifndef FCPU80
160128
#define FCPU80 80000000L
161129
#endif
162130

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

193-
void Twi::setClockStretchLimit(uint32_t limit)
174+
void TwiMaster::setClockStretchLimit(uint32_t limit)
194175
{
195176
twi_clockStretchLimit = limit;
196177
}
197178

198179

199180

200-
void Twi::init(unsigned char sda, unsigned char scl)
181+
void TwiMaster::init(unsigned char sda, unsigned char scl)
182+
{
183+
twi_sda = sda;
184+
twi_scl = scl;
185+
pinMode(twi_sda, INPUT_PULLUP);
186+
pinMode(twi_scl, INPUT_PULLUP);
187+
twi_setClock(preferred_si2c_clock);
188+
twi_setClockStretchLimit(150000L); // default value is 150 mS
189+
}
190+
191+
void TwiMasterOrSlave::init(unsigned char sda, unsigned char scl)
201192
{
202193
// set timer function
203194
ets_timer_setfn(&timer, onTimer, NULL);
@@ -213,13 +204,13 @@ void Twi::init(unsigned char sda, unsigned char scl)
213204
twi_setClockStretchLimit(150000L); // default value is 150 mS
214205
}
215206

216-
void Twi::setAddress(uint8_t address)
207+
void TwiMasterOrSlave::setAddress(uint8_t address)
217208
{
218209
// set twi slave address (skip over R/W bit)
219210
twi_addr = address << 1;
220211
}
221212

222-
void Twi::enableSlave()
213+
void TwiMasterOrSlave::enableSlave()
223214
{
224215
if (!_slaveEnabled)
225216
{
@@ -229,16 +220,16 @@ void Twi::enableSlave()
229220
}
230221
}
231222

232-
void ICACHE_RAM_ATTR Twi::busywait(unsigned int v)
223+
void ICACHE_RAM_ATTR TwiMaster::busywait(unsigned int v)
233224
{
234225
unsigned int i;
235226
for (i = 0; i < v; i++) // loop time is 5 machine cycles: 31.25ns @ 160MHz, 62.5ns @ 80MHz
236227
{
237-
__asm__ __volatile__("nop"); // minimum element to keep GCC from optimizing this function out.
228+
asm("nop"); // minimum element to keep GCC from optimizing this function out.
238229
}
239230
}
240231

241-
bool Twi::write_start(void)
232+
bool TwiMaster::write_start(void)
242233
{
243234
SCL_HIGH(twi_scl);
244235
SDA_HIGH(twi_sda);
@@ -252,7 +243,7 @@ bool Twi::write_start(void)
252243
return true;
253244
}
254245

255-
bool Twi::write_stop(void)
246+
bool TwiMaster::write_stop(void)
256247
{
257248
SCL_LOW(twi_scl);
258249
SDA_LOW(twi_sda);
@@ -265,7 +256,7 @@ bool Twi::write_stop(void)
265256
return true;
266257
}
267258

268-
bool Twi::write_bit(bool bit)
259+
bool TwiMaster::write_bit(bool bit)
269260
{
270261
SCL_LOW(twi_scl);
271262
if (bit)
@@ -283,7 +274,7 @@ bool Twi::write_bit(bool bit)
283274
return true;
284275
}
285276

286-
bool Twi::read_bit(void)
277+
bool TwiMaster::read_bit(void)
287278
{
288279
SCL_LOW(twi_scl);
289280
SDA_HIGH(twi_sda);
@@ -295,7 +286,7 @@ bool Twi::read_bit(void)
295286
return bit;
296287
}
297288

298-
bool Twi::write_byte(unsigned char byte)
289+
bool TwiMaster::write_byte(unsigned char byte)
299290
{
300291
unsigned char bit;
301292
for (bit = 0; bit < 8; bit++)
@@ -306,7 +297,7 @@ bool Twi::write_byte(unsigned char byte)
306297
return !read_bit();//NACK/ACK
307298
}
308299

309-
unsigned char Twi::read_byte(bool nack)
300+
unsigned char TwiMaster::read_byte(bool nack)
310301
{
311302
unsigned char byte = 0;
312303
unsigned char bit;
@@ -318,7 +309,7 @@ unsigned char Twi::read_byte(bool nack)
318309
return byte;
319310
}
320311

321-
unsigned char Twi::writeTo(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop)
312+
unsigned char TwiMaster::writeTo(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop)
322313
{
323314
unsigned int i;
324315
if (!write_start())
@@ -363,7 +354,7 @@ unsigned char Twi::writeTo(unsigned char address, unsigned char * buf, unsigned
363354
return 0;
364355
}
365356

366-
unsigned char Twi::readFrom(unsigned char address, unsigned char* buf, unsigned int len, unsigned char sendStop)
357+
unsigned char TwiMaster::readFrom(unsigned char address, unsigned char* buf, unsigned int len, unsigned char sendStop)
367358
{
368359
unsigned int i;
369360
if (!write_start())
@@ -402,15 +393,15 @@ unsigned char Twi::readFrom(unsigned char address, unsigned char* buf, unsigned
402393
return 0;
403394
}
404395

405-
void Twi::twi_scl_valley(void)
396+
void TwiMaster::twi_scl_valley(void)
406397
{
407398
SCL_LOW(twi_scl);
408399
busywait(twi_dcount);
409400
SCL_HIGH(twi_scl);
410401
WAIT_CLOCK_STRETCH();
411402
}
412403

413-
uint8_t Twi::status()
404+
uint8_t TwiMaster::status()
414405
{
415406
WAIT_CLOCK_STRETCH(); // wait for a slow slave to finish
416407
if (!SCL_READ(twi_scl))
@@ -435,7 +426,7 @@ uint8_t Twi::status()
435426
return I2C_OK;
436427
}
437428

438-
uint8_t Twi::transmit(const uint8_t* data, uint8_t length)
429+
uint8_t TwiMasterOrSlave::transmit(const uint8_t* data, uint8_t length)
439430
{
440431
uint8_t i;
441432

@@ -461,20 +452,20 @@ uint8_t Twi::transmit(const uint8_t* data, uint8_t length)
461452
return 0;
462453
}
463454

464-
void Twi::attachSlaveRxEvent(void (*function)(uint8_t*, size_t))
455+
void TwiMasterOrSlave::attachSlaveRxEvent(void (*function)(uint8_t*, size_t))
465456
{
466457
twi_onSlaveReceive = function;
467458
}
468459

469-
void Twi::attachSlaveTxEvent(void (*function)(void))
460+
void TwiMasterOrSlave::attachSlaveTxEvent(void (*function)(void))
470461
{
471462
twi_onSlaveTransmit = function;
472463
}
473464

474465
// DO NOT INLINE, inlining reply() in combination with compiler optimizations causes function breakup into
475466
// parts and the ICACHE_RAM_ATTR isn't propagated correctly to all parts, which of course causes crashes.
476467
// TODO: test with gcc 9.x and if it still fails, disable optimization with -fdisable-ipa-fnsplit
477-
void ICACHE_RAM_ATTR Twi::reply(uint8_t ack)
468+
void ICACHE_RAM_ATTR TwiMasterOrSlave::reply(uint8_t ack)
478469
{
479470
// transmit master read ready signal, with or without ack
480471
if (ack)
@@ -492,7 +483,7 @@ void ICACHE_RAM_ATTR Twi::reply(uint8_t ack)
492483
}
493484

494485

495-
void ICACHE_RAM_ATTR Twi::releaseBus(void)
486+
void ICACHE_RAM_ATTR TwiMasterOrSlave::releaseBus(void)
496487
{
497488
// release bus
498489
//TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT);
@@ -505,7 +496,7 @@ void ICACHE_RAM_ATTR Twi::releaseBus(void)
505496
}
506497

507498

508-
void ICACHE_RAM_ATTR Twi::onTwipEvent(uint8_t status)
499+
void ICACHE_RAM_ATTR TwiMasterOrSlave::onTwipEvent(uint8_t status)
509500
{
510501
twip_status = status;
511502
switch (status)
@@ -612,7 +603,7 @@ void ICACHE_RAM_ATTR Twi::onTwipEvent(uint8_t status)
612603
}
613604
}
614605

615-
void ICACHE_RAM_ATTR Twi::onTimer(void *unused)
606+
void ICACHE_RAM_ATTR TwiMasterOrSlave::onTimer(void *unused)
616607
{
617608
(void)unused;
618609
twi.releaseBus();
@@ -621,7 +612,7 @@ void ICACHE_RAM_ATTR Twi::onTimer(void *unused)
621612
twi.twip_state = TWIP_BUS_ERR;
622613
}
623614

624-
void Twi::eventTask(ETSEvent *e)
615+
void TwiMasterOrSlave::eventTask(ETSEvent *e)
625616
{
626617

627618
if (e == NULL)
@@ -662,7 +653,7 @@ void Twi::eventTask(ETSEvent *e)
662653
// Shorthand for if the state is any of the or'd bitmask x
663654
#define IFSTATE(x) if (twip_state_mask & (x))
664655

665-
void ICACHE_RAM_ATTR Twi::onSclChange(void)
656+
void ICACHE_RAM_ATTR TwiMasterOrSlave::onSclChange(void)
666657
{
667658
unsigned int sda;
668659
unsigned int scl;
@@ -860,7 +851,7 @@ void ICACHE_RAM_ATTR Twi::onSclChange(void)
860851
}
861852
}
862853

863-
void ICACHE_RAM_ATTR Twi::onSdaChange(void)
854+
void ICACHE_RAM_ATTR TwiMasterOrSlave::onSdaChange(void)
864855
{
865856
unsigned int sda;
866857
unsigned int scl;

cores/esp8266/twi.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,43 @@ void twi_enableSlaveMode(void);
6060
}
6161
#endif
6262

63+
#ifdef __cplusplus
64+
65+
// this is a C++ class, so declare it only in C++ context
66+
67+
class TwiMaster
68+
{
69+
protected:
70+
unsigned int preferred_si2c_clock = 100000;
71+
uint32_t twi_dcount = 18;
72+
unsigned char twi_sda = 0;
73+
unsigned char twi_scl = 0;
74+
uint32_t twi_clockStretchLimit = 0;
75+
76+
// Internal use functions
77+
void ICACHE_RAM_ATTR busywait(unsigned int v);
78+
bool write_start(void);
79+
bool write_stop(void);
80+
bool write_bit(bool bit);
81+
bool read_bit(void);
82+
bool write_byte(unsigned char byte);
83+
unsigned char read_byte(bool nack);
84+
85+
// Handle the case where a slave needs to stretch the clock with a time-limited busy wait
86+
inline void WAIT_CLOCK_STRETCH();
87+
88+
// Generate a clock "valley" (at the end of a segment, just before a repeated start)
89+
void twi_scl_valley(void);
90+
91+
public:
92+
void setClock(unsigned int freq);
93+
void setClockStretchLimit(uint32_t limit);
94+
void init(unsigned char sda, unsigned char scl);
95+
unsigned char writeTo(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop);
96+
unsigned char readFrom(unsigned char address, unsigned char* buf, unsigned int len, unsigned char sendStop);
97+
uint8_t status();
98+
};
99+
100+
#endif
101+
63102
#endif

0 commit comments

Comments
 (0)