@@ -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{
6464private:
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-
141113public:
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