17
17
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
18
19
19
Modified 2012 by Todd Krein ([email protected] ) to implement repeated starts
20
+ Modified 2020 by Greyson Christoforo ([email protected] ) to implement timeouts
20
21
*/
21
22
22
23
#include <math.h>
23
24
#include <stdlib.h>
24
25
#include <inttypes.h>
25
26
#include <avr/io.h>
26
27
#include <avr/interrupt.h>
28
+ #include <util/delay.h>
27
29
#include <compat/twi.h>
28
30
#include "Arduino.h" // for digitalWrite and micros
29
31
@@ -42,7 +44,16 @@ static volatile uint8_t twi_state;
42
44
static volatile uint8_t twi_slarw ;
43
45
static volatile uint8_t twi_sendStop ; // should the transaction end with a stop
44
46
static volatile uint8_t twi_inRepStart ; // in the middle of a repeated start
47
+
48
+ // twi_timeout_us > 0 prevents the code from getting stuck in various while loops here
49
+ // if twi_timeout_us == 0 then timeout checking is disabled (the previous Wire lib behavior)
50
+ // at some point in the future, the default twi_timeout_us value could become 25000
51
+ // and twi_do_reset_on_timeout could become true
52
+ // to conform to the SMBus standard
53
+ // http://smbus.org/specs/SMBus_3_1_20180319.pdf
45
54
static volatile uint32_t twi_timeout_us = 0ul ;
55
+ static volatile bool twi_timed_out_flag = false; // a timeout has been seen
56
+ static volatile bool twi_do_reset_on_timeout = false; // reset the TWI registers on timeout
46
57
47
58
static void (* twi_onSlaveTransmit )(void );
48
59
static void (* twi_onSlaveReceive )(uint8_t * , int );
@@ -157,11 +168,10 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen
157
168
// wait until twi is ready, become master receiver
158
169
uint32_t startMicros = micros ();
159
170
while (TWI_READY != twi_state ){
160
- if ((twi_timeout_us > 0ul ) && (micros () - startMicros > twi_timeout_us )) {
161
- twi_handleTimeout ();
171
+ if ((twi_timeout_us > 0ul ) && (( micros () - startMicros ) > twi_timeout_us )) {
172
+ twi_handleTimeout (twi_do_reset_on_timeout );
162
173
return 0 ;
163
174
}
164
- continue ;
165
175
}
166
176
twi_state = TWI_MRX ;
167
177
twi_sendStop = sendStop ;
@@ -192,35 +202,35 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen
192
202
startMicros = micros ();
193
203
do {
194
204
TWDR = twi_slarw ;
195
- if ((twi_timeout_us > 0ul ) && (micros () - startMicros > twi_timeout_us )) {
196
- twi_handleTimeout ();
205
+ if ((twi_timeout_us > 0ul ) && (( micros () - startMicros ) > twi_timeout_us )) {
206
+ twi_handleTimeout (twi_do_reset_on_timeout );
197
207
return 0 ;
198
208
}
199
209
} while (TWCR & _BV (TWWC ));
200
210
TWCR = _BV (TWINT ) | _BV (TWEA ) | _BV (TWEN ) | _BV (TWIE ); // enable INTs, but not START
201
- }
202
- else
211
+ } else {
203
212
// send start condition
204
213
TWCR = _BV (TWEN ) | _BV (TWIE ) | _BV (TWEA ) | _BV (TWINT ) | _BV (TWSTA );
214
+ }
205
215
206
216
// wait for read operation to complete
207
217
startMicros = micros ();
208
218
while (TWI_MRX == twi_state ){
209
- if ((twi_timeout_us > 0ul ) && (micros () - startMicros > twi_timeout_us )) {
210
- twi_handleTimeout ();
219
+ if ((twi_timeout_us > 0ul ) && (( micros () - startMicros ) > twi_timeout_us )) {
220
+ twi_handleTimeout (twi_do_reset_on_timeout );
211
221
return 0 ;
212
222
}
213
- continue ;
214
223
}
215
224
216
- if (twi_masterBufferIndex < length )
225
+ if (twi_masterBufferIndex < length ) {
217
226
length = twi_masterBufferIndex ;
227
+ }
218
228
219
229
// copy twi buffer to data
220
230
for (i = 0 ; i < length ; ++ i ){
221
231
data [i ] = twi_masterBuffer [i ];
222
232
}
223
-
233
+
224
234
return length ;
225
235
}
226
236
@@ -238,6 +248,7 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen
238
248
* 2 .. address send, NACK received
239
249
* 3 .. data send, NACK received
240
250
* 4 .. other twi error (lost bus arbitration, bus error, ..)
251
+ * 5 .. timeout
241
252
*/
242
253
uint8_t twi_writeTo (uint8_t address , uint8_t * data , uint8_t length , uint8_t wait , uint8_t sendStop )
243
254
{
@@ -251,11 +262,10 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait
251
262
// wait until twi is ready, become master transmitter
252
263
uint32_t startMicros = micros ();
253
264
while (TWI_READY != twi_state ){
254
- if ((twi_timeout_us > 0ul ) && (micros () - startMicros > twi_timeout_us )) {
255
- twi_handleTimeout ();
256
- return 4 ;
265
+ if ((twi_timeout_us > 0ul ) && (( micros () - startMicros ) > twi_timeout_us )) {
266
+ twi_handleTimeout (twi_do_reset_on_timeout );
267
+ return ( 5 ) ;
257
268
}
258
- continue ;
259
269
}
260
270
twi_state = TWI_MTX ;
261
271
twi_sendStop = sendStop ;
@@ -289,25 +299,24 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait
289
299
startMicros = micros ();
290
300
do {
291
301
TWDR = twi_slarw ;
292
- if ((twi_timeout_us > 0ul ) && (micros () - startMicros > twi_timeout_us )) {
293
- twi_handleTimeout ();
294
- return 4 ;
302
+ if ((twi_timeout_us > 0ul ) && (( micros () - startMicros ) > twi_timeout_us )) {
303
+ twi_handleTimeout (twi_do_reset_on_timeout );
304
+ return ( 5 ) ;
295
305
}
296
306
} while (TWCR & _BV (TWWC ));
297
307
TWCR = _BV (TWINT ) | _BV (TWEA ) | _BV (TWEN ) | _BV (TWIE ); // enable INTs, but not START
298
- }
299
- else
308
+ } else {
300
309
// send start condition
301
310
TWCR = _BV (TWINT ) | _BV (TWEA ) | _BV (TWEN ) | _BV (TWIE ) | _BV (TWSTA ); // enable INTs
311
+ }
302
312
303
313
// wait for write operation to complete
304
314
startMicros = micros ();
305
315
while (wait && (TWI_MTX == twi_state )){
306
- if ((twi_timeout_us > 0ul ) && (micros () - startMicros > twi_timeout_us )) {
307
- twi_handleTimeout ();
308
- return 4 ;
316
+ if ((twi_timeout_us > 0ul ) && (( micros () - startMicros ) > twi_timeout_us )) {
317
+ twi_handleTimeout (twi_do_reset_on_timeout );
318
+ return ( 5 ) ;
309
319
}
310
- continue ;
311
320
}
312
321
313
322
if (twi_error == 0xFF )
@@ -387,7 +396,7 @@ void twi_reply(uint8_t ack)
387
396
if (ack ){
388
397
TWCR = _BV (TWEN ) | _BV (TWIE ) | _BV (TWINT ) | _BV (TWEA );
389
398
}else {
390
- TWCR = _BV (TWEN ) | _BV (TWIE ) | _BV (TWINT );
399
+ TWCR = _BV (TWEN ) | _BV (TWIE ) | _BV (TWINT );
391
400
}
392
401
}
393
402
@@ -404,15 +413,17 @@ void twi_stop(void)
404
413
405
414
// wait for stop condition to be exectued on bus
406
415
// TWINT is not set after a stop condition!
407
- volatile uint32_t counter = 0 ;
416
+ volatile uint32_t counter = twi_timeout_us / 10ul ; // approximate the timeout
408
417
while (TWCR & _BV (TWSTO )){
409
- counter ++ ;
410
- if ((twi_timeout_us > 0ul ) && (counter >= 25000 )) {
411
- twi_handleTimeout ();
412
- return ;
418
+ if (twi_timeout_us > 0ul ){
419
+ if (counter > 0ul ){
420
+ _delay_us (10 );
421
+ counter -- ;
422
+ } else {
423
+ twi_handleTimeout (twi_do_reset_on_timeout );
424
+ return ;
425
+ }
413
426
}
414
-
415
- continue ;
416
427
}
417
428
418
429
// update twi state
@@ -436,34 +447,55 @@ void twi_releaseBus(void)
436
447
437
448
/*
438
449
* Function twi_setTimeoutInMicros
439
- * Desc set a global timeout for while loops that we might get stuck in
440
- * Input timeout value in microseconds
450
+ * Desc set a timeout for while loops that twi might get stuck in
451
+ * Input timeout value in microseconds (0 means never time out)
452
+ * Input reset_with_timeout: true causes timeout events to reset twi
441
453
* Output none
442
454
*/
443
- void twi_setTimeoutInMicros (uint32_t timeout )
444
- {
455
+ void twi_setTimeoutInMicros (uint32_t timeout , bool reset_with_timeout ){
456
+ twi_timed_out_flag = false;
445
457
twi_timeout_us = timeout ;
458
+ twi_do_reset_on_timeout = reset_with_timeout ;
446
459
}
447
460
448
461
/*
449
462
* Function twi_handleTimeout
450
- * Desc do this stuff when a while loop here has run for longer than twi_timeout_us microseconds
451
- * Input none
463
+ * Desc this gets called whenever a while loop here has lasted longer than
464
+ * twi_timeout_us microseconds. always sets twi_timed_out_flag
465
+ * Input reset: true causes this function to reset the twi hardware interface
452
466
* Output none
453
467
*/
454
- void twi_handleTimeout (void )
455
- {
456
- // remember bitrate and address settings
457
- uint8_t previous_TWBR = TWBR ;
458
- uint8_t previous_TWAR = TWAR ;
468
+ void twi_handleTimeout (bool reset ){
469
+ twi_timed_out_flag = true;
470
+
471
+ if (reset ) {
472
+ // remember bitrate and address settings
473
+ uint8_t previous_TWBR = TWBR ;
474
+ uint8_t previous_TWAR = TWAR ;
475
+
476
+ // reset the interface
477
+ twi_disable ();
478
+ twi_init ();
459
479
460
- // reset the interface
461
- twi_disable ();
462
- twi_init ();
480
+ // reapply the previous register values
481
+ TWAR = previous_TWAR ;
482
+ TWBR = previous_TWBR ;
483
+ }
484
+ }
463
485
464
- // reapply the previous register values
465
- TWAR = previous_TWAR ;
466
- TWBR = previous_TWBR ;
486
+ /*
487
+ * Function twi_manageTimeoutFlag
488
+ * Desc returns true if twi has seen a timeout
489
+ * optionally clears the timeout flag
490
+ * Input clear_flag: true if we should reset the hardware
491
+ * Output none
492
+ */
493
+ bool twi_manageTimeoutFlag (bool clear_flag ){
494
+ bool flag = twi_timed_out_flag ;
495
+ if (clear_flag ){
496
+ twi_timed_out_flag = false;
497
+ }
498
+ return (flag );
467
499
}
468
500
469
501
ISR (TWI_vect )
@@ -486,16 +518,16 @@ ISR(TWI_vect)
486
518
TWDR = twi_masterBuffer [twi_masterBufferIndex ++ ];
487
519
twi_reply (1 );
488
520
}else {
489
- if (twi_sendStop )
521
+ if (twi_sendStop ){
490
522
twi_stop ();
491
- else {
492
- twi_inRepStart = true; // we're gonna send the START
493
- // don't enable the interrupt. We'll generate the start, but we
494
- // avoid handling the interrupt until we're in the next transaction,
495
- // at the point where we would normally issue the start.
496
- TWCR = _BV (TWINT ) | _BV (TWSTA )| _BV (TWEN ) ;
497
- twi_state = TWI_READY ;
498
- }
523
+ } else {
524
+ twi_inRepStart = true; // we're gonna send the START
525
+ // don't enable the interrupt. We'll generate the start, but we
526
+ // avoid handling the interrupt until we're in the next transaction,
527
+ // at the point where we would normally issue the start.
528
+ TWCR = _BV (TWINT ) | _BV (TWSTA )| _BV (TWEN ) ;
529
+ twi_state = TWI_READY ;
530
+ }
499
531
}
500
532
break ;
501
533
case TW_MT_SLA_NACK : // address sent, nack received
@@ -527,17 +559,17 @@ ISR(TWI_vect)
527
559
case TW_MR_DATA_NACK : // data received, nack sent
528
560
// put final byte into buffer
529
561
twi_masterBuffer [twi_masterBufferIndex ++ ] = TWDR ;
530
- if (twi_sendStop )
531
- twi_stop ();
532
- else {
533
- twi_inRepStart = true; // we're gonna send the START
534
- // don't enable the interrupt. We'll generate the start, but we
535
- // avoid handling the interrupt until we're in the next transaction,
536
- // at the point where we would normally issue the start.
537
- TWCR = _BV (TWINT ) | _BV (TWSTA )| _BV (TWEN ) ;
538
- twi_state = TWI_READY ;
539
- }
540
- break ;
562
+ if (twi_sendStop ){
563
+ twi_stop ();
564
+ } else {
565
+ twi_inRepStart = true; // we're gonna send the START
566
+ // don't enable the interrupt. We'll generate the start, but we
567
+ // avoid handling the interrupt until we're in the next transaction,
568
+ // at the point where we would normally issue the start.
569
+ TWCR = _BV (TWINT ) | _BV (TWSTA )| _BV (TWEN ) ;
570
+ twi_state = TWI_READY ;
571
+ }
572
+ break ;
541
573
case TW_MR_SLA_NACK : // address sent, nack received
542
574
twi_stop ();
543
575
break ;
0 commit comments