Skip to content

Commit da7d85a

Browse files
committed
- resetting the twi interface on timeouts is now optional
- timeouts in the ISR are no longer hardcoded and now obey the set timeout value - a user-readable flag is now set whenever a timeout occurs - the user can clear this flag whenever they like
1 parent c45ac98 commit da7d85a

File tree

4 files changed

+138
-78
lines changed

4 files changed

+138
-78
lines changed

Diff for: libraries/Wire/src/Wire.cpp

+26-3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
1919
Modified 2012 by Todd Krein ([email protected]) to implement repeated starts
2020
Modified 2017 by Chuck Todd ([email protected]) to correct Unconfigured Slave Mode reboot
21+
Modified 2020 by Greyson Christoforo ([email protected]) to implement timeouts
2122
*/
2223

2324
extern "C" {
@@ -86,9 +87,31 @@ void TwoWire::setClock(uint32_t clock)
8687
twi_setFrequency(clock);
8788
}
8889

89-
void TwoWire::setWireTimeoutUs(uint32_t timeout)
90-
{
91-
twi_setTimeoutInMicros(timeout);
90+
/***
91+
* Sets the TWI timeout.
92+
*
93+
* @param timeout a timeout value in microseconds, if zero then timeout checking is disabled
94+
* @param reset_with_timeout if true then TWI interface will be automatically reset on timeout
95+
* if false then TWI interface will not be reset on timeout
96+
*/
97+
void TwoWire::setWireTimeoutUs(uint32_t timeout, bool reset_with_timeout){
98+
twi_setTimeoutInMicros(timeout, reset_with_timeout);
99+
}
100+
101+
/***
102+
* Returns the TWI timeout flag.
103+
*
104+
* @return true if timeout has occured
105+
*/
106+
bool TwoWire::getWireTimeoutFlag(void){
107+
return(twi_manageTimeoutFlag(false));
108+
}
109+
110+
/***
111+
* Clears the TWI timeout flag.
112+
*/
113+
void TwoWire::clearWireTimeoutFlag(void){
114+
twi_manageTimeoutFlag(true);
92115
}
93116

94117
uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint32_t iaddress, uint8_t isize, uint8_t sendStop)

Diff for: libraries/Wire/src/Wire.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
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
#ifndef TwoWire_h
@@ -54,14 +55,16 @@ class TwoWire : public Stream
5455
void begin(int);
5556
void end();
5657
void setClock(uint32_t);
57-
void setWireTimeoutUs(uint32_t);
58+
void setWireTimeoutUs(uint32_t, bool);
59+
bool getWireTimeoutFlag(void);
60+
void clearWireTimeoutFlag(void);
5861
void beginTransmission(uint8_t);
5962
void beginTransmission(int);
6063
uint8_t endTransmission(void);
6164
uint8_t endTransmission(uint8_t);
6265
uint8_t requestFrom(uint8_t, uint8_t);
6366
uint8_t requestFrom(uint8_t, uint8_t, uint8_t);
64-
uint8_t requestFrom(uint8_t, uint8_t, uint32_t, uint8_t, uint8_t);
67+
uint8_t requestFrom(uint8_t, uint8_t, uint32_t, uint8_t, uint8_t);
6568
uint8_t requestFrom(int, int);
6669
uint8_t requestFrom(int, int, int);
6770
virtual size_t write(uint8_t);

Diff for: libraries/Wire/src/utility/twi.c

+102-70
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@
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;
4244
static volatile uint8_t twi_slarw;
4345
static volatile uint8_t twi_sendStop; // should the transaction end with a stop
4446
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
4554
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
4657

4758
static void (*twi_onSlaveTransmit)(void);
4859
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
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
*/
242253
uint8_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

469501
ISR(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

Comments
 (0)