Skip to content

Commit adea3ac

Browse files
committed
Implement SERCOM/Wire I2C timeout detection
from SAMD PR 439 arduino/ArduinoCore-samd#439
1 parent d4a60a6 commit adea3ac

File tree

5 files changed

+82
-10
lines changed

5 files changed

+82
-10
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11

22
bootloaders/r34/.vs/samr3x_sam_ba/v14/.atsuo
3+
libraries/Wire/Wire.cpp.bak
4+
*.bak

cores/arduino/SERCOM.cpp

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
SERCOM::SERCOM(Sercom* s)
3030
{
3131
sercom = s;
32+
timeoutOccurred = false;
33+
timeoutInterval = SERCOM_DEFAULT_I2C_OPERATION_TIMEOUT_MS;
3234
}
3335

3436
/* =========================
@@ -517,9 +519,10 @@ bool SERCOM::startTransmissionWIRE(uint8_t address, SercomWireReadWriteFlag flag
517519
sercom->I2CM.ADDR.bit.ADDR = address;
518520

519521
// Address Transmitted
522+
initTimeout();
520523
if ( flag == WIRE_WRITE_FLAG ) // Write mode
521524
{
522-
while( !sercom->I2CM.INTFLAG.bit.MB )
525+
while( !sercom->I2CM.INTFLAG.bit.MB && !testTimeout() )
523526
{
524527
// Wait transmission complete
525528
}
@@ -532,7 +535,7 @@ bool SERCOM::startTransmissionWIRE(uint8_t address, SercomWireReadWriteFlag flag
532535
}
533536
else // Read mode
534537
{
535-
while( !sercom->I2CM.INTFLAG.bit.SB )
538+
while( !sercom->I2CM.INTFLAG.bit.SB && !testTimeout() )
536539
{
537540
// If the slave NACKS the address, the MB bit will be set.
538541
// In that case, send a stop condition and return false.
@@ -546,7 +549,8 @@ bool SERCOM::startTransmissionWIRE(uint8_t address, SercomWireReadWriteFlag flag
546549
// Clean the 'Slave on Bus' flag, for further usage.
547550
//sercom->I2CM.INTFLAG.bit.SB = 0x1ul;
548551
}
549-
552+
// check for timeout condition
553+
if ( didTimeout() ) return false;
550554

551555
//ACK received (0: ACK, 1: NACK)
552556
if(sercom->I2CM.STATUS.bit.RXNACK)
@@ -565,15 +569,18 @@ bool SERCOM::sendDataMasterWIRE(uint8_t data)
565569
sercom->I2CM.DATA.bit.DATA = data;
566570

567571
//Wait transmission successful
568-
while(!sercom->I2CM.INTFLAG.bit.MB) {
572+
initTimeout();
573+
while(!sercom->I2CM.INTFLAG.bit.MB && !testTimeout()) {
569574

570575
// If a bus error occurs, the MB bit may never be set.
571576
// Check the bus error bit and bail if it's set.
572577
if (sercom->I2CM.STATUS.bit.BUSERR) {
573578
return false;
574579
}
575580
}
576-
581+
// check for timeout condition
582+
if ( didTimeout() ) return false;
583+
577584
//Problems on line? nack received?
578585
if(sercom->I2CM.STATUS.bit.RXNACK)
579586
return false;
@@ -665,7 +672,8 @@ uint8_t SERCOM::readDataWIRE( void )
665672
{
666673
if(isMasterWIRE())
667674
{
668-
while( sercom->I2CM.INTFLAG.bit.SB == 0 && sercom->I2CM.INTFLAG.bit.MB == 0 )
675+
initTimeout();
676+
while( sercom->I2CM.INTFLAG.bit.SB == 0 && sercom->I2CM.INTFLAG.bit.MB == 0 && !testTimeout() )
669677
{
670678
// Waiting complete receive
671679
}
@@ -740,3 +748,26 @@ void SERCOM::initClockNVIC( void )
740748
// wait for sync
741749
}
742750
}
751+
752+
void SERCOM::setTimeout( uint16_t ms )
753+
{
754+
timeoutInterval = ms;
755+
}
756+
757+
bool SERCOM::didTimeout( void )
758+
{
759+
return timeoutOccurred;
760+
}
761+
762+
void SERCOM::initTimeout( void )
763+
{
764+
timeoutOccurred = false;
765+
timeoutRef = millis();
766+
}
767+
768+
bool SERCOM::testTimeout( void )
769+
{
770+
if (!timeoutInterval) return false;
771+
timeoutOccurred = (millis() - timeoutRef) > timeoutInterval;
772+
return timeoutOccurred;
773+
}

cores/arduino/SERCOM.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323

2424
#define SERCOM_FREQ_REF 48000000
2525
#define SERCOM_NVIC_PRIORITY ((1<<__NVIC_PRIO_BITS) - 1)
26-
26+
// timeout detection default length (zero is disabled)
27+
#define SERCOM_DEFAULT_I2C_OPERATION_TIMEOUT_MS 1000
2728
typedef enum
2829
{
2930
UART_EXT_CLOCK = 0,
@@ -218,6 +219,14 @@ class SERCOM
218219
uint8_t calculateBaudrateSynchronous(uint32_t baudrate) ;
219220
uint32_t division(uint32_t dividend, uint32_t divisor) ;
220221
void initClockNVIC( void ) ;
222+
223+
224+
// timeout detection for I2C operations
225+
void initTimeout( void );
226+
bool testTimeout( void );
227+
uint16_t timeoutInterval;
228+
uint32_t timeoutRef;
229+
bool timeoutOccurred;
221230
};
222231

223232
#endif

libraries/Wire/Wire.cpp

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ TwoWire::TwoWire(SERCOM * s, uint8_t pinSDA, uint8_t pinSCL)
3535
}
3636

3737
void TwoWire::begin(void) {
38+
39+
// track baud clock for auto-restarting bus in timeout condition
40+
activeBaudrate = TWI_CLOCK;
3841
//Master Mode
3942
sercom->initMasterWIRE(TWI_CLOCK);
4043
sercom->enableWIRE();
@@ -53,6 +56,10 @@ void TwoWire::begin(uint8_t address, bool enableGeneralCall) {
5356
}
5457

5558
void TwoWire::setClock(uint32_t baudrate) {
59+
60+
// track baud clock for auto-restarting bus in timeout condition
61+
activeBaudrate = baudrate;
62+
5663
sercom->disableWIRE();
5764
sercom->initMasterWIRE(baudrate);
5865
sercom->enableWIRE();
@@ -80,7 +87,8 @@ uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, bool stopBit)
8087

8188
bool busOwner;
8289
// Connected to slave
83-
for (byteRead = 1; byteRead < quantity && (busOwner = sercom->isBusOwnerWIRE()); ++byteRead)
90+
for (byteRead = 1; byteRead < quantity && !sercom->didTimeout() && (busOwner = sercom->isBusOwnerWIRE()); ++byteRead)
91+
8492
{
8593
sercom->prepareAckBitWIRE(); // Prepare Acknowledge
8694
sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_READ); // Prepare the ACK command for the slave
@@ -99,7 +107,15 @@ uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, bool stopBit)
99107
byteRead--; // because last read byte was garbage/invalid
100108
}
101109
}
102-
110+
111+
// catch timeout condition
112+
if (sercom->didTimeout())
113+
{
114+
// reset the bus
115+
setClock(activeBaudrate);
116+
transmissionBegun = false;
117+
return 0;
118+
}
103119
return byteRead;
104120
}
105121

@@ -121,7 +137,8 @@ void TwoWire::beginTransmission(uint8_t address) {
121137
// 1 : Data too long
122138
// 2 : NACK on transmit of address
123139
// 3 : NACK on transmit of data
124-
// 4 : Other error
140+
// 4 : Timeout
141+
// 5 : Other error
125142
uint8_t TwoWire::endTransmission(bool stopBit)
126143
{
127144
transmissionBegun = false ;
@@ -130,6 +147,7 @@ uint8_t TwoWire::endTransmission(bool stopBit)
130147
if ( !sercom->startTransmissionWIRE( txAddress, WIRE_WRITE_FLAG ) )
131148
{
132149
sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP);
150+
if (sercom->didTimeout()) return 4; // Timeout
133151
return 2 ; // Address error
134152
}
135153

@@ -140,6 +158,7 @@ uint8_t TwoWire::endTransmission(bool stopBit)
140158
if ( !sercom->sendDataMasterWIRE( txBuffer.read_char() ) )
141159
{
142160
sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP);
161+
if (sercom->didTimeout()) return 4; // Timeout
143162
return 3 ; // Nack or error
144163
}
145164
}

libraries/Wire/Wire.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828

2929
// WIRE_HAS_END means Wire has end()
3030
#define WIRE_HAS_END 1
31+
// WIRE_HAS_TIMEOUT means Wire implements timeout detection
32+
#define WIRE_HAS_TIMEOUT 1
33+
3134

3235
class TwoWire : public Stream
3336
{
@@ -45,6 +48,10 @@ class TwoWire : public Stream
4548
uint8_t requestFrom(uint8_t address, size_t quantity, bool stopBit);
4649
uint8_t requestFrom(uint8_t address, size_t quantity);
4750

51+
52+
bool didTimeout() { return sercom->didTimeout(); }
53+
bool setTimeout(uint16_t ms) { sercom->setTimeout(ms); }
54+
4855
size_t write(uint8_t data);
4956
size_t write(const uint8_t * data, size_t quantity);
5057

@@ -69,6 +76,10 @@ class TwoWire : public Stream
6976
uint8_t _uc_pinSCL;
7077

7178
bool transmissionBegun;
79+
80+
// Used to re-initialize the clock rate after a timeout
81+
uint32_t activeBaudrate;
82+
7283

7384
// RX Buffer
7485
RingBufferN<256> rxBuffer;

0 commit comments

Comments
 (0)