@@ -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
@@ -93,8 +88,9 @@ class Twi
93
88
uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH];
94
89
volatile int twi_rxBufferIndex = 0 ;
95
90
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 *);
98
94
99
95
// ETS queue/timer interfaces
100
96
enum { EVENTTASK_QUEUE_SIZE = 1 , EVENTTASK_QUEUE_PRIO = 2 };
@@ -108,59 +104,46 @@ class Twi
108
104
static void eventTask (ETSEvent *e);
109
105
static void IRAM_ATTR onTimer (void *unused);
110
106
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
112
108
bool _slaveEnabled = false ;
113
109
114
110
// 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);
122
111
void IRAM_ATTR onTwipEvent (uint8_t status);
123
112
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
113
public:
142
- void setClock (unsigned int freq);
143
- void setClockStretchLimit (uint32_t limit);
114
+ // custom version
144
115
void init (unsigned char sda, unsigned char scl);
116
+
145
117
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
118
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 * ));
152
121
void IRAM_ATTR reply (uint8_t ack);
153
122
void IRAM_ATTR releaseBus (void );
154
- void enableSlave ();
123
+ void enableSlave (void * targetObject );
155
124
};
156
125
157
- static Twi twi;
126
+ static TwiMasterOrSlave twi;
127
+ TwiMaster& twiMasterSingleton = twi;
158
128
159
129
#ifndef FCPU80
160
130
#define FCPU80 80000000L
161
131
#endif
162
132
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)
164
147
{
165
148
if (freq < 1000 ) // minimum freq 1000Hz to minimize slave timeouts and WDT resets
166
149
{
@@ -190,14 +173,24 @@ void Twi::setClock(unsigned int freq)
190
173
#endif
191
174
}
192
175
193
- void Twi ::setClockStretchLimit (uint32_t limit)
176
+ void TwiMaster ::setClockStretchLimit (uint32_t limit)
194
177
{
195
178
twi_clockStretchLimit = limit;
196
179
}
197
180
198
181
199
182
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)
201
194
{
202
195
// set timer function
203
196
ets_timer_setfn (&timer, onTimer, NULL );
@@ -213,32 +206,33 @@ void Twi::init(unsigned char sda, unsigned char scl)
213
206
twi_setClockStretchLimit (150000L ); // default value is 150 mS
214
207
}
215
208
216
- void Twi ::setAddress (uint8_t address)
209
+ void TwiMasterOrSlave ::setAddress (uint8_t address)
217
210
{
218
211
// set twi slave address (skip over R/W bit)
219
212
twi_addr = address << 1 ;
220
213
}
221
214
222
- void Twi ::enableSlave ()
215
+ void TwiMasterOrSlave ::enableSlave (void * targetObject )
223
216
{
224
217
if (!_slaveEnabled)
225
218
{
219
+ twi_SlaveTargetObject = targetObject;
226
220
attachInterrupt (twi_scl, onSclChange, CHANGE);
227
221
attachInterrupt (twi_sda, onSdaChange, CHANGE);
228
222
_slaveEnabled = true ;
229
223
}
230
224
}
231
225
232
- void IRAM_ATTR Twi ::busywait (unsigned int v)
226
+ void IRAM_ATTR TwiMaster ::busywait (unsigned int v)
233
227
{
234
228
unsigned int i;
235
229
for (i = 0 ; i < v; i++) // loop time is 5 machine cycles: 31.25ns @ 160MHz, 62.5ns @ 80MHz
236
230
{
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.
238
232
}
239
233
}
240
234
241
- bool Twi ::write_start (void )
235
+ bool TwiMaster ::write_start (void )
242
236
{
243
237
SCL_HIGH (twi_scl);
244
238
SDA_HIGH (twi_sda);
@@ -252,7 +246,7 @@ bool Twi::write_start(void)
252
246
return true ;
253
247
}
254
248
255
- bool Twi ::write_stop (void )
249
+ bool TwiMaster ::write_stop (void )
256
250
{
257
251
SCL_LOW (twi_scl);
258
252
SDA_LOW (twi_sda);
@@ -265,7 +259,7 @@ bool Twi::write_stop(void)
265
259
return true ;
266
260
}
267
261
268
- bool Twi ::write_bit (bool bit)
262
+ bool TwiMaster ::write_bit (bool bit)
269
263
{
270
264
SCL_LOW (twi_scl);
271
265
if (bit)
@@ -283,7 +277,7 @@ bool Twi::write_bit(bool bit)
283
277
return true ;
284
278
}
285
279
286
- bool Twi ::read_bit (void )
280
+ bool TwiMaster ::read_bit (void )
287
281
{
288
282
SCL_LOW (twi_scl);
289
283
SDA_HIGH (twi_sda);
@@ -295,7 +289,7 @@ bool Twi::read_bit(void)
295
289
return bit;
296
290
}
297
291
298
- bool Twi ::write_byte (unsigned char byte)
292
+ bool TwiMaster ::write_byte (unsigned char byte)
299
293
{
300
294
unsigned char bit;
301
295
for (bit = 0 ; bit < 8 ; bit++)
@@ -306,7 +300,7 @@ bool Twi::write_byte(unsigned char byte)
306
300
return !read_bit ();// NACK/ACK
307
301
}
308
302
309
- unsigned char Twi ::read_byte (bool nack)
303
+ unsigned char TwiMaster ::read_byte (bool nack)
310
304
{
311
305
unsigned char byte = 0 ;
312
306
unsigned char bit;
@@ -318,7 +312,7 @@ unsigned char Twi::read_byte(bool nack)
318
312
return byte;
319
313
}
320
314
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)
322
316
{
323
317
unsigned int i;
324
318
if (!write_start ())
@@ -363,7 +357,7 @@ unsigned char Twi::writeTo(unsigned char address, unsigned char * buf, unsigned
363
357
return 0 ;
364
358
}
365
359
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)
367
361
{
368
362
unsigned int i;
369
363
if (!write_start ())
@@ -402,15 +396,15 @@ unsigned char Twi::readFrom(unsigned char address, unsigned char* buf, unsigned
402
396
return 0 ;
403
397
}
404
398
405
- void Twi ::twi_scl_valley (void )
399
+ void TwiMaster ::twi_scl_valley (void )
406
400
{
407
401
SCL_LOW (twi_scl);
408
402
busywait (twi_dcount);
409
403
SCL_HIGH (twi_scl);
410
404
WAIT_CLOCK_STRETCH ();
411
405
}
412
406
413
- uint8_t Twi ::status ()
407
+ uint8_t TwiMaster ::status ()
414
408
{
415
409
WAIT_CLOCK_STRETCH (); // wait for a slow slave to finish
416
410
if (!SCL_READ (twi_scl))
@@ -435,7 +429,7 @@ uint8_t Twi::status()
435
429
return I2C_OK;
436
430
}
437
431
438
- uint8_t Twi ::transmit (const uint8_t * data, uint8_t length)
432
+ uint8_t TwiMasterOrSlave ::transmit (const uint8_t * data, uint8_t length)
439
433
{
440
434
uint8_t i;
441
435
@@ -461,20 +455,20 @@ uint8_t Twi::transmit(const uint8_t* data, uint8_t length)
461
455
return 0 ;
462
456
}
463
457
464
- void Twi ::attachSlaveRxEvent (void (*function)(uint8_t *, size_t ))
458
+ void TwiMasterOrSlave ::attachSlaveRxEvent (void (*function)(uint8_t *, size_t , void * ))
465
459
{
466
460
twi_onSlaveReceive = function;
467
461
}
468
462
469
- void Twi ::attachSlaveTxEvent (void (*function)(void ))
463
+ void TwiMasterOrSlave ::attachSlaveTxEvent (void (*function)(void * ))
470
464
{
471
465
twi_onSlaveTransmit = function;
472
466
}
473
467
474
468
// DO NOT INLINE, inlining reply() in combination with compiler optimizations causes function breakup into
475
469
// parts and the IRAM_ATTR isn't propagated correctly to all parts, which of course causes crashes.
476
470
// 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)
478
472
{
479
473
// transmit master read ready signal, with or without ack
480
474
if (ack)
@@ -492,7 +486,7 @@ void IRAM_ATTR Twi::reply(uint8_t ack)
492
486
}
493
487
494
488
495
- void IRAM_ATTR Twi ::releaseBus (void )
489
+ void IRAM_ATTR TwiMasterOrSlave ::releaseBus (void )
496
490
{
497
491
// release bus
498
492
// TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT);
@@ -505,7 +499,7 @@ void IRAM_ATTR Twi::releaseBus(void)
505
499
}
506
500
507
501
508
- void IRAM_ATTR Twi ::onTwipEvent (uint8_t status)
502
+ void IRAM_ATTR TwiMasterOrSlave ::onTwipEvent (uint8_t status)
509
503
{
510
504
twip_status = status;
511
505
switch (status)
@@ -612,7 +606,7 @@ void IRAM_ATTR Twi::onTwipEvent(uint8_t status)
612
606
}
613
607
}
614
608
615
- void IRAM_ATTR Twi ::onTimer (void *unused)
609
+ void IRAM_ATTR TwiMasterOrSlave ::onTimer (void *unused)
616
610
{
617
611
(void )unused;
618
612
twi.releaseBus ();
@@ -621,7 +615,7 @@ void IRAM_ATTR Twi::onTimer(void *unused)
621
615
twi.twip_state = TWIP_BUS_ERR;
622
616
}
623
617
624
- void Twi ::eventTask (ETSEvent *e)
618
+ void TwiMasterOrSlave ::eventTask (ETSEvent *e)
625
619
{
626
620
627
621
if (e == NULL )
@@ -632,7 +626,7 @@ void Twi::eventTask(ETSEvent *e)
632
626
switch (e->sig )
633
627
{
634
628
case TWI_SIG_TX:
635
- twi.twi_onSlaveTransmit ();
629
+ twi.twi_onSlaveTransmit (twi. twi_SlaveTargetObject );
636
630
637
631
// if they didn't change buffer & length, initialize it
638
632
if (twi.twi_txBufferLength == 0 )
@@ -649,7 +643,7 @@ void Twi::eventTask(ETSEvent *e)
649
643
case TWI_SIG_RX:
650
644
// ack future responses and leave slave receiver state
651
645
twi.releaseBus ();
652
- twi.twi_onSlaveReceive (twi.twi_rxBuffer , e->par );
646
+ twi.twi_onSlaveReceive (twi.twi_rxBuffer , e->par , twi. twi_SlaveTargetObject );
653
647
break ;
654
648
}
655
649
}
@@ -662,7 +656,7 @@ void Twi::eventTask(ETSEvent *e)
662
656
// Shorthand for if the state is any of the or'd bitmask x
663
657
#define IFSTATE (x ) if (twip_state_mask & (x))
664
658
665
- void IRAM_ATTR Twi ::onSclChange (void )
659
+ void IRAM_ATTR TwiMasterOrSlave ::onSclChange (void )
666
660
{
667
661
unsigned int sda;
668
662
unsigned int scl;
@@ -860,7 +854,7 @@ void IRAM_ATTR Twi::onSclChange(void)
860
854
}
861
855
}
862
856
863
- void IRAM_ATTR Twi ::onSdaChange (void )
857
+ void IRAM_ATTR TwiMasterOrSlave ::onSdaChange (void )
864
858
{
865
859
unsigned int sda;
866
860
unsigned int scl;
@@ -998,16 +992,17 @@ extern "C" {
998
992
return twi.transmit (buf, len);
999
993
}
1000
994
1001
- void twi_attachSlaveRxEvent (void (*cb)(uint8_t *, size_t ))
995
+ void twi_attachSlaveRxEventWithTarget (void (*cb)(uint8_t *, size_t , void * ))
1002
996
{
1003
997
twi.attachSlaveRxEvent (cb);
1004
998
}
1005
999
1006
- void twi_attachSlaveTxEvent (void (*cb)(void ))
1000
+ void twi_attachSlaveTxEventWithTarget (void (*cb)(void * ))
1007
1001
{
1008
1002
twi.attachSlaveTxEvent (cb);
1009
1003
}
1010
1004
1005
+
1011
1006
void twi_reply (uint8_t r)
1012
1007
{
1013
1008
twi.reply (r);
@@ -1018,9 +1013,9 @@ extern "C" {
1018
1013
twi.releaseBus ();
1019
1014
}
1020
1015
1021
- void twi_enableSlaveMode (void )
1016
+ void twi_enableSlaveModeWithTarget (void * targetObject )
1022
1017
{
1023
- twi.enableSlave ();
1018
+ twi.enableSlave (targetObject );
1024
1019
}
1025
1020
1026
1021
};
0 commit comments