Skip to content

Commit 12e3bde

Browse files
committed
Implement SERCOM/Wire I2C timeout detection
1 parent b076b0a commit 12e3bde

File tree

4 files changed

+78
-6
lines changed

4 files changed

+78
-6
lines changed

Diff for: cores/arduino/SERCOM.cpp

+37-4
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.
@@ -547,6 +550,8 @@ bool SERCOM::startTransmissionWIRE(uint8_t address, SercomWireReadWriteFlag flag
547550
//sercom->I2CM.INTFLAG.bit.SB = 0x1ul;
548551
}
549552

553+
// check for timeout condition
554+
if ( didTimeout() ) return false;
550555

551556
//ACK received (0: ACK, 1: NACK)
552557
if(sercom->I2CM.STATUS.bit.RXNACK)
@@ -565,7 +570,8 @@ bool SERCOM::sendDataMasterWIRE(uint8_t data)
565570
sercom->I2CM.DATA.bit.DATA = data;
566571

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

570576
// If a bus error occurs, the MB bit may never be set.
571577
// Check the bus error bit and bail if it's set.
@@ -574,6 +580,9 @@ bool SERCOM::sendDataMasterWIRE(uint8_t data)
574580
}
575581
}
576582

583+
// check for timeout condition
584+
if ( didTimeout() ) return false;
585+
577586
//Problems on line? nack received?
578587
if(sercom->I2CM.STATUS.bit.RXNACK)
579588
return false;
@@ -665,7 +674,8 @@ uint8_t SERCOM::readDataWIRE( void )
665674
{
666675
if(isMasterWIRE())
667676
{
668-
while( sercom->I2CM.INTFLAG.bit.SB == 0 && sercom->I2CM.INTFLAG.bit.MB == 0 )
677+
initTimeout();
678+
while( sercom->I2CM.INTFLAG.bit.SB == 0 && sercom->I2CM.INTFLAG.bit.MB == 0 && !testTimeout() )
669679
{
670680
// Waiting complete receive
671681
}
@@ -739,3 +749,26 @@ void SERCOM::initClockNVIC( void )
739749
/* Wait for synchronization */
740750
}
741751
}
752+
753+
void SERCOM::setTimeout( uint16_t ms )
754+
{
755+
timeoutInterval = ms;
756+
}
757+
758+
bool SERCOM::didTimeout( void )
759+
{
760+
return timeoutOccurred;
761+
}
762+
763+
void SERCOM::initTimeout( void )
764+
{
765+
timeoutOccurred = false;
766+
timeoutRef = millis();
767+
}
768+
769+
bool SERCOM::testTimeout( void )
770+
{
771+
if (!timeoutInterval) return false;
772+
timeoutOccurred = (millis() - timeoutRef) > timeoutInterval;
773+
return timeoutOccurred;
774+
}

Diff for: cores/arduino/SERCOM.h

+12
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
#define SERCOM_FREQ_REF 48000000
2525
#define SERCOM_NVIC_PRIORITY ((1<<__NVIC_PRIO_BITS) - 1)
2626

27+
// timeout detection default length (zero is disabled)
28+
#define SERCOM_DEFAULT_I2C_OPERATION_TIMEOUT_MS 1000
29+
2730
typedef enum
2831
{
2932
UART_EXT_CLOCK = 0,
@@ -212,12 +215,21 @@ class SERCOM
212215
bool isRXNackReceivedWIRE( void ) ;
213216
int availableWIRE( void ) ;
214217
uint8_t readDataWIRE( void ) ;
218+
void setTimeout( uint16_t ms );
219+
bool didTimeout( void );
215220

216221
private:
217222
Sercom* sercom;
218223
uint8_t calculateBaudrateSynchronous(uint32_t baudrate) ;
219224
uint32_t division(uint32_t dividend, uint32_t divisor) ;
220225
void initClockNVIC( void ) ;
226+
227+
// timeout detection for I2C operations
228+
void initTimeout( void );
229+
bool testTimeout( void );
230+
uint16_t timeoutInterval;
231+
uint32_t timeoutRef;
232+
bool timeoutOccurred;
221233
};
222234

223235
#endif

Diff for: libraries/Wire/Wire.cpp

+20-2
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+
// track baud clock for auto-restarting bus in timeout condition
39+
activeBaudrate = TWI_CLOCK;
40+
3841
//Master Mode
3942
sercom->initMasterWIRE(TWI_CLOCK);
4043
sercom->enableWIRE();
@@ -53,6 +56,9 @@ void TwoWire::begin(uint8_t address, bool enableGeneralCall) {
5356
}
5457

5558
void TwoWire::setClock(uint32_t baudrate) {
59+
// track baud clock for auto-restarting bus in timeout condition
60+
activeBaudrate = baudrate;
61+
5662
sercom->disableWIRE();
5763
sercom->initMasterWIRE(baudrate);
5864
sercom->enableWIRE();
@@ -80,7 +86,7 @@ uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, bool stopBit)
8086

8187
bool busOwner;
8288
// Connected to slave
83-
for (byteRead = 1; byteRead < quantity && (busOwner = sercom->isBusOwnerWIRE()); ++byteRead)
89+
for (byteRead = 1; byteRead < quantity && !sercom->didTimeout() && (busOwner = sercom->isBusOwnerWIRE()); ++byteRead)
8490
{
8591
sercom->prepareAckBitWIRE(); // Prepare Acknowledge
8692
sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_READ); // Prepare the ACK command for the slave
@@ -100,6 +106,15 @@ uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, bool stopBit)
100106
}
101107
}
102108

109+
// catch timeout condition
110+
if (sercom->didTimeout())
111+
{
112+
// reset the bus
113+
setClock(activeBaudrate);
114+
transmissionBegun = false;
115+
return 0;
116+
}
117+
103118
return byteRead;
104119
}
105120

@@ -121,7 +136,8 @@ void TwoWire::beginTransmission(uint8_t address) {
121136
// 1 : Data too long
122137
// 2 : NACK on transmit of address
123138
// 3 : NACK on transmit of data
124-
// 4 : Other error
139+
// 4 : Timeout
140+
// 5 : Other error
125141
uint8_t TwoWire::endTransmission(bool stopBit)
126142
{
127143
transmissionBegun = false ;
@@ -130,6 +146,7 @@ uint8_t TwoWire::endTransmission(bool stopBit)
130146
if ( !sercom->startTransmissionWIRE( txAddress, WIRE_WRITE_FLAG ) )
131147
{
132148
sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP);
149+
if (sercom->didTimeout()) return 4; // Timeout
133150
return 2 ; // Address error
134151
}
135152

@@ -140,6 +157,7 @@ uint8_t TwoWire::endTransmission(bool stopBit)
140157
if ( !sercom->sendDataMasterWIRE( txBuffer.read_char() ) )
141158
{
142159
sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP);
160+
if (sercom->didTimeout()) return 4; // Timeout
143161
return 3 ; // Nack or error
144162
}
145163
}

Diff for: libraries/Wire/Wire.h

+9
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
// WIRE_HAS_END means Wire has end()
3030
#define WIRE_HAS_END 1
3131

32+
// WIRE_HAS_TIMEOUT means Wire implements timeout detection
33+
#define WIRE_HAS_TIMEOUT 1
34+
3235
class TwoWire : public Stream
3336
{
3437
public:
@@ -44,6 +47,9 @@ class TwoWire : public Stream
4447

4548
uint8_t requestFrom(uint8_t address, size_t quantity, bool stopBit);
4649
uint8_t requestFrom(uint8_t address, size_t quantity);
50+
51+
bool didTimeout() { return sercom->didTimeout(); }
52+
bool setTimeout(uint16_t ms) { sercom->setTimeout(ms); }
4753

4854
size_t write(uint8_t data);
4955
size_t write(const uint8_t * data, size_t quantity);
@@ -70,6 +76,9 @@ class TwoWire : public Stream
7076

7177
bool transmissionBegun;
7278

79+
// Used to re-initialize the clock rate after a timeout
80+
uint32_t activeBaudrate;
81+
7382
// RX Buffer
7483
RingBufferN<256> rxBuffer;
7584

0 commit comments

Comments
 (0)