1717 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
1818
1919 Modified 2012 by Todd Krein ([email protected] ) to implement repeated starts 20+ Modified 2020 by Greyson Christoforo ([email protected] ) to implement timeouts 2021*/
2122
2223#include <math.h>
2324#include <stdlib.h>
2425#include <inttypes.h>
2526#include <avr/io.h>
2627#include <avr/interrupt.h>
28+ #include <util/delay.h>
2729#include <compat/twi.h>
2830#include "Arduino.h" // for digitalWrite and micros
2931
@@ -42,7 +44,16 @@ static volatile uint8_t twi_state;
4244static volatile uint8_t twi_slarw ;
4345static volatile uint8_t twi_sendStop ; // should the transaction end with a stop
4446static 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
4554static 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
4657
4758static void (* twi_onSlaveTransmit )(void );
4859static 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
157168 // wait until twi is ready, become master receiver
158169 uint32_t startMicros = micros ();
159170 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 );
162173 return 0 ;
163174 }
164- continue ;
165175 }
166176 twi_state = TWI_MRX ;
167177 twi_sendStop = sendStop ;
@@ -192,35 +202,35 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen
192202 startMicros = micros ();
193203 do {
194204 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 );
197207 return 0 ;
198208 }
199209 } while (TWCR & _BV (TWWC ));
200210 TWCR = _BV (TWINT ) | _BV (TWEA ) | _BV (TWEN ) | _BV (TWIE ); // enable INTs, but not START
201- }
202- else
211+ } else {
203212 // send start condition
204213 TWCR = _BV (TWEN ) | _BV (TWIE ) | _BV (TWEA ) | _BV (TWINT ) | _BV (TWSTA );
214+ }
205215
206216 // wait for read operation to complete
207217 startMicros = micros ();
208218 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 );
211221 return 0 ;
212222 }
213- continue ;
214223 }
215224
216- if (twi_masterBufferIndex < length )
225+ if (twi_masterBufferIndex < length ) {
217226 length = twi_masterBufferIndex ;
227+ }
218228
219229 // copy twi buffer to data
220230 for (i = 0 ; i < length ; ++ i ){
221231 data [i ] = twi_masterBuffer [i ];
222232 }
223-
233+
224234 return length ;
225235}
226236
@@ -238,6 +248,7 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen
238248 * 2 .. address send, NACK received
239249 * 3 .. data send, NACK received
240250 * 4 .. other twi error (lost bus arbitration, bus error, ..)
251+ * 5 .. timeout
241252 */
242253uint8_t twi_writeTo (uint8_t address , uint8_t * data , uint8_t length , uint8_t wait , uint8_t sendStop )
243254{
@@ -251,11 +262,10 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait
251262 // wait until twi is ready, become master transmitter
252263 uint32_t startMicros = micros ();
253264 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 ) ;
257268 }
258- continue ;
259269 }
260270 twi_state = TWI_MTX ;
261271 twi_sendStop = sendStop ;
@@ -289,25 +299,24 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait
289299 startMicros = micros ();
290300 do {
291301 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 ) ;
295305 }
296306 } while (TWCR & _BV (TWWC ));
297307 TWCR = _BV (TWINT ) | _BV (TWEA ) | _BV (TWEN ) | _BV (TWIE ); // enable INTs, but not START
298- }
299- else
308+ } else {
300309 // send start condition
301310 TWCR = _BV (TWINT ) | _BV (TWEA ) | _BV (TWEN ) | _BV (TWIE ) | _BV (TWSTA ); // enable INTs
311+ }
302312
303313 // wait for write operation to complete
304314 startMicros = micros ();
305315 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 ) ;
309319 }
310- continue ;
311320 }
312321
313322 if (twi_error == 0xFF )
@@ -387,7 +396,7 @@ void twi_reply(uint8_t ack)
387396 if (ack ){
388397 TWCR = _BV (TWEN ) | _BV (TWIE ) | _BV (TWINT ) | _BV (TWEA );
389398 }else {
390- TWCR = _BV (TWEN ) | _BV (TWIE ) | _BV (TWINT );
399+ TWCR = _BV (TWEN ) | _BV (TWIE ) | _BV (TWINT );
391400 }
392401}
393402
@@ -404,15 +413,17 @@ void twi_stop(void)
404413
405414 // wait for stop condition to be exectued on bus
406415 // 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
408417 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+ }
413426 }
414-
415- continue ;
416427 }
417428
418429 // update twi state
@@ -436,34 +447,55 @@ void twi_releaseBus(void)
436447
437448/*
438449 * 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
441453 * Output none
442454 */
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;
445457 twi_timeout_us = timeout ;
458+ twi_do_reset_on_timeout = reset_with_timeout ;
446459}
447460
448461/*
449462 * 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
452466 * Output none
453467 */
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 ();
459479
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+ }
463485
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 );
467499}
468500
469501ISR (TWI_vect )
@@ -486,16 +518,16 @@ ISR(TWI_vect)
486518 TWDR = twi_masterBuffer [twi_masterBufferIndex ++ ];
487519 twi_reply (1 );
488520 }else {
489- if (twi_sendStop )
521+ if (twi_sendStop ){
490522 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+ }
499531 }
500532 break ;
501533 case TW_MT_SLA_NACK : // address sent, nack received
@@ -527,17 +559,17 @@ ISR(TWI_vect)
527559 case TW_MR_DATA_NACK : // data received, nack sent
528560 // put final byte into buffer
529561 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 ;
541573 case TW_MR_SLA_NACK : // address sent, nack received
542574 twi_stop ();
543575 break ;
0 commit comments