@@ -57,17 +57,12 @@ static inline __attribute__((always_inline)) bool SCL_READ(const int twi_scl)
57
57
return (GPI & (1 << twi_scl)) != 0 ;
58
58
}
59
59
60
-
61
60
// 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
63
63
{
64
64
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 ;
69
65
unsigned char twi_addr = 0 ;
70
- uint32_t twi_clockStretchLimit = 0 ;
71
66
72
67
// These are int-wide, even though they could all fit in a byte, to reduce code size and avoid any potential
73
68
// issues about RmW on packed bytes. The int-wide variations of asm instructions are smaller than the equivalent
@@ -110,42 +105,15 @@ class Twi
110
105
111
106
// Allow not linking in the slave code if there is no call to setAddress
112
107
bool _slaveEnabled = false ;
113
-
108
+
114
109
// 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);
122
110
void ICACHE_RAM_ATTR onTwipEvent (uint8_t status);
123
111
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
-
141
112
public:
142
- void setClock (unsigned int freq);
143
- void setClockStretchLimit (uint32_t limit);
113
+ // custom version
144
114
void init (unsigned char sda, unsigned char scl);
115
+
145
116
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 ();
149
117
uint8_t transmit (const uint8_t * data, uint8_t length);
150
118
void attachSlaveRxEvent (void (*function)(uint8_t *, size_t ));
151
119
void attachSlaveTxEvent (void (*function)(void ));
@@ -154,13 +122,26 @@ class Twi
154
122
void enableSlave ();
155
123
};
156
124
157
- static Twi twi;
125
+ static TwiMasterOrSlave twi;
158
126
159
127
#ifndef FCPU80
160
128
#define FCPU80 80000000L
161
129
#endif
162
130
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)
164
145
{
165
146
if (freq < 1000 ) // minimum freq 1000Hz to minimize slave timeouts and WDT resets
166
147
{
@@ -190,14 +171,24 @@ void Twi::setClock(unsigned int freq)
190
171
#endif
191
172
}
192
173
193
- void Twi ::setClockStretchLimit (uint32_t limit)
174
+ void TwiMaster ::setClockStretchLimit (uint32_t limit)
194
175
{
195
176
twi_clockStretchLimit = limit;
196
177
}
197
178
198
179
199
180
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)
201
192
{
202
193
// set timer function
203
194
ets_timer_setfn (&timer, onTimer, NULL );
@@ -213,13 +204,13 @@ void Twi::init(unsigned char sda, unsigned char scl)
213
204
twi_setClockStretchLimit (150000L ); // default value is 150 mS
214
205
}
215
206
216
- void Twi ::setAddress (uint8_t address)
207
+ void TwiMasterOrSlave ::setAddress (uint8_t address)
217
208
{
218
209
// set twi slave address (skip over R/W bit)
219
210
twi_addr = address << 1 ;
220
211
}
221
212
222
- void Twi ::enableSlave ()
213
+ void TwiMasterOrSlave ::enableSlave ()
223
214
{
224
215
if (!_slaveEnabled)
225
216
{
@@ -229,16 +220,16 @@ void Twi::enableSlave()
229
220
}
230
221
}
231
222
232
- void ICACHE_RAM_ATTR Twi ::busywait (unsigned int v)
223
+ void ICACHE_RAM_ATTR TwiMaster ::busywait (unsigned int v)
233
224
{
234
225
unsigned int i;
235
226
for (i = 0 ; i < v; i++) // loop time is 5 machine cycles: 31.25ns @ 160MHz, 62.5ns @ 80MHz
236
227
{
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.
238
229
}
239
230
}
240
231
241
- bool Twi ::write_start (void )
232
+ bool TwiMaster ::write_start (void )
242
233
{
243
234
SCL_HIGH (twi_scl);
244
235
SDA_HIGH (twi_sda);
@@ -252,7 +243,7 @@ bool Twi::write_start(void)
252
243
return true ;
253
244
}
254
245
255
- bool Twi ::write_stop (void )
246
+ bool TwiMaster ::write_stop (void )
256
247
{
257
248
SCL_LOW (twi_scl);
258
249
SDA_LOW (twi_sda);
@@ -265,7 +256,7 @@ bool Twi::write_stop(void)
265
256
return true ;
266
257
}
267
258
268
- bool Twi ::write_bit (bool bit)
259
+ bool TwiMaster ::write_bit (bool bit)
269
260
{
270
261
SCL_LOW (twi_scl);
271
262
if (bit)
@@ -283,7 +274,7 @@ bool Twi::write_bit(bool bit)
283
274
return true ;
284
275
}
285
276
286
- bool Twi ::read_bit (void )
277
+ bool TwiMaster ::read_bit (void )
287
278
{
288
279
SCL_LOW (twi_scl);
289
280
SDA_HIGH (twi_sda);
@@ -295,7 +286,7 @@ bool Twi::read_bit(void)
295
286
return bit;
296
287
}
297
288
298
- bool Twi ::write_byte (unsigned char byte)
289
+ bool TwiMaster ::write_byte (unsigned char byte)
299
290
{
300
291
unsigned char bit;
301
292
for (bit = 0 ; bit < 8 ; bit++)
@@ -306,7 +297,7 @@ bool Twi::write_byte(unsigned char byte)
306
297
return !read_bit ();// NACK/ACK
307
298
}
308
299
309
- unsigned char Twi ::read_byte (bool nack)
300
+ unsigned char TwiMaster ::read_byte (bool nack)
310
301
{
311
302
unsigned char byte = 0 ;
312
303
unsigned char bit;
@@ -318,7 +309,7 @@ unsigned char Twi::read_byte(bool nack)
318
309
return byte;
319
310
}
320
311
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)
322
313
{
323
314
unsigned int i;
324
315
if (!write_start ())
@@ -363,7 +354,7 @@ unsigned char Twi::writeTo(unsigned char address, unsigned char * buf, unsigned
363
354
return 0 ;
364
355
}
365
356
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)
367
358
{
368
359
unsigned int i;
369
360
if (!write_start ())
@@ -402,15 +393,15 @@ unsigned char Twi::readFrom(unsigned char address, unsigned char* buf, unsigned
402
393
return 0 ;
403
394
}
404
395
405
- void Twi ::twi_scl_valley (void )
396
+ void TwiMaster ::twi_scl_valley (void )
406
397
{
407
398
SCL_LOW (twi_scl);
408
399
busywait (twi_dcount);
409
400
SCL_HIGH (twi_scl);
410
401
WAIT_CLOCK_STRETCH ();
411
402
}
412
403
413
- uint8_t Twi ::status ()
404
+ uint8_t TwiMaster ::status ()
414
405
{
415
406
WAIT_CLOCK_STRETCH (); // wait for a slow slave to finish
416
407
if (!SCL_READ (twi_scl))
@@ -435,7 +426,7 @@ uint8_t Twi::status()
435
426
return I2C_OK;
436
427
}
437
428
438
- uint8_t Twi ::transmit (const uint8_t * data, uint8_t length)
429
+ uint8_t TwiMasterOrSlave ::transmit (const uint8_t * data, uint8_t length)
439
430
{
440
431
uint8_t i;
441
432
@@ -461,20 +452,20 @@ uint8_t Twi::transmit(const uint8_t* data, uint8_t length)
461
452
return 0 ;
462
453
}
463
454
464
- void Twi ::attachSlaveRxEvent (void (*function)(uint8_t *, size_t ))
455
+ void TwiMasterOrSlave ::attachSlaveRxEvent (void (*function)(uint8_t *, size_t ))
465
456
{
466
457
twi_onSlaveReceive = function;
467
458
}
468
459
469
- void Twi ::attachSlaveTxEvent (void (*function)(void ))
460
+ void TwiMasterOrSlave ::attachSlaveTxEvent (void (*function)(void ))
470
461
{
471
462
twi_onSlaveTransmit = function;
472
463
}
473
464
474
465
// DO NOT INLINE, inlining reply() in combination with compiler optimizations causes function breakup into
475
466
// parts and the ICACHE_RAM_ATTR isn't propagated correctly to all parts, which of course causes crashes.
476
467
// 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)
478
469
{
479
470
// transmit master read ready signal, with or without ack
480
471
if (ack)
@@ -492,7 +483,7 @@ void ICACHE_RAM_ATTR Twi::reply(uint8_t ack)
492
483
}
493
484
494
485
495
- void ICACHE_RAM_ATTR Twi ::releaseBus (void )
486
+ void ICACHE_RAM_ATTR TwiMasterOrSlave ::releaseBus (void )
496
487
{
497
488
// release bus
498
489
// TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT);
@@ -505,7 +496,7 @@ void ICACHE_RAM_ATTR Twi::releaseBus(void)
505
496
}
506
497
507
498
508
- void ICACHE_RAM_ATTR Twi ::onTwipEvent (uint8_t status)
499
+ void ICACHE_RAM_ATTR TwiMasterOrSlave ::onTwipEvent (uint8_t status)
509
500
{
510
501
twip_status = status;
511
502
switch (status)
@@ -612,7 +603,7 @@ void ICACHE_RAM_ATTR Twi::onTwipEvent(uint8_t status)
612
603
}
613
604
}
614
605
615
- void ICACHE_RAM_ATTR Twi ::onTimer (void *unused)
606
+ void ICACHE_RAM_ATTR TwiMasterOrSlave ::onTimer (void *unused)
616
607
{
617
608
(void )unused;
618
609
twi.releaseBus ();
@@ -621,7 +612,7 @@ void ICACHE_RAM_ATTR Twi::onTimer(void *unused)
621
612
twi.twip_state = TWIP_BUS_ERR;
622
613
}
623
614
624
- void Twi ::eventTask (ETSEvent *e)
615
+ void TwiMasterOrSlave ::eventTask (ETSEvent *e)
625
616
{
626
617
627
618
if (e == NULL )
@@ -662,7 +653,7 @@ void Twi::eventTask(ETSEvent *e)
662
653
// Shorthand for if the state is any of the or'd bitmask x
663
654
#define IFSTATE (x ) if (twip_state_mask & (x))
664
655
665
- void ICACHE_RAM_ATTR Twi ::onSclChange (void )
656
+ void ICACHE_RAM_ATTR TwiMasterOrSlave ::onSclChange (void )
666
657
{
667
658
unsigned int sda;
668
659
unsigned int scl;
@@ -860,7 +851,7 @@ void ICACHE_RAM_ATTR Twi::onSclChange(void)
860
851
}
861
852
}
862
853
863
- void ICACHE_RAM_ATTR Twi ::onSdaChange (void )
854
+ void ICACHE_RAM_ATTR TwiMasterOrSlave ::onSdaChange (void )
864
855
{
865
856
unsigned int sda;
866
857
unsigned int scl;
0 commit comments