29
29
SERCOM::SERCOM (Sercom* s)
30
30
{
31
31
sercom = s;
32
+ timeoutOccurred = false ;
33
+ timeoutInterval = SERCOM_DEFAULT_I2C_OPERATION_TIMEOUT_MS;
32
34
}
33
35
34
36
/* =========================
@@ -393,11 +395,8 @@ void SERCOM::enableWIRE()
393
395
394
396
// Setting bus idle mode
395
397
sercom->I2CM .STATUS .bit .BUSSTATE = 1 ;
398
+ waitSyncWIRE ();
396
399
397
- while ( sercom->I2CM .SYNCBUSY .bit .SYSOP != 0 )
398
- {
399
- // Wait the SYSOP bit from SYNCBUSY coming back to 0
400
- }
401
400
}
402
401
403
402
void SERCOM::disableWIRE ()
@@ -433,10 +432,8 @@ void SERCOM::initSlaveWIRE( uint8_t ucAddress, bool enableGeneralCall )
433
432
SERCOM_I2CS_INTENSET_AMATCH | // Address Match
434
433
SERCOM_I2CS_INTENSET_DRDY ; // Data Ready
435
434
436
- while ( sercom->I2CM .SYNCBUSY .bit .SYSOP != 0 )
437
- {
438
- // Wait the SYSOP bit from SYNCBUSY to come back to 0
439
- }
435
+ waitSyncWIRE ();
436
+
440
437
}
441
438
442
439
void SERCOM::initMasterWIRE ( uint32_t baudrate )
@@ -453,12 +450,12 @@ void SERCOM::initMasterWIRE( uint32_t baudrate )
453
450
// Enable Smart mode and Quick Command
454
451
// sercom->I2CM.CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN /*| SERCOM_I2CM_CTRLB_QCEN*/ ;
455
452
456
-
457
453
// Enable all interrupts
458
454
// sercom->I2CM.INTENSET.reg = SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB | SERCOM_I2CM_INTENSET_ERROR ;
459
455
460
456
// Synchronous arithmetic baudrate
461
457
sercom->I2CM .BAUD .bit .BAUD = SystemCoreClock / ( 2 * baudrate) - 5 - (((SystemCoreClock / 1000000 ) * WIRE_RISE_TIME_NANOSECONDS) / (2 * 1000 ));
458
+
462
459
}
463
460
464
461
void SERCOM::prepareNackBitWIRE ( void )
@@ -485,68 +482,65 @@ void SERCOM::prepareCommandBitsWire(uint8_t cmd)
485
482
{
486
483
if (isMasterWIRE ()) {
487
484
sercom->I2CM .CTRLB .bit .CMD = cmd;
485
+ waitSyncWIRE ();
486
+ } else {
487
+ sercom->I2CS .CTRLB .bit .CMD = cmd;
488
+ }
489
+ }
488
490
491
+ void SERCOM::waitSyncWIRE () {
489
492
while (sercom->I2CM .SYNCBUSY .bit .SYSOP )
490
493
{
491
494
// Waiting for synchronization
492
495
}
493
- } else {
494
- sercom->I2CS .CTRLB .bit .CMD = cmd;
495
- }
496
496
}
497
497
498
498
bool SERCOM::startTransmissionWIRE (uint8_t address, SercomWireReadWriteFlag flag)
499
499
{
500
500
// 7-bits address + 1-bits R/W
501
501
address = (address << 0x1ul ) | flag;
502
502
503
- // If another master owns the bus or the last bus owner has not properly
504
- // sent a stop, return failure early. This will prevent some misbehaved
505
- // devices from deadlocking here at the cost of the caller being responsible
506
- // for retrying the failed transmission. See SercomWireBusState for the
507
- // possible bus states.
508
- if (!isBusOwnerWIRE ())
509
- {
510
- if ( isBusBusyWIRE () || (isArbLostWIRE () && !isBusIdleWIRE ()) )
511
- {
512
- return false ;
513
- }
514
- }
515
-
516
503
// Send start and address
517
504
sercom->I2CM .ADDR .bit .ADDR = address;
505
+ waitSyncWIRE ();
506
+
507
+ // wait Address Transmitted
508
+ initTimeout ();
518
509
519
- // Address Transmitted
520
510
if ( flag == WIRE_WRITE_FLAG ) // Write mode
521
511
{
522
- while ( !sercom->I2CM .INTFLAG .bit .MB )
523
- {
524
- // Wait transmission complete
525
- }
526
- // Check for loss of arbitration (multiple masters starting communication at the same time)
527
- if (!isBusOwnerWIRE ())
512
+ while (!sercom->I2CM .INTFLAG .bit .MB && !testTimeout ())
528
513
{
529
- // Restart communication
530
- startTransmissionWIRE (address >> 1 , flag);
514
+ // wait transmission complete
531
515
}
532
516
}
533
517
else // Read mode
534
518
{
535
- while ( !sercom->I2CM .INTFLAG .bit .SB )
519
+ while ( !sercom->I2CM .INTFLAG .bit .SB && ! testTimeout () )
536
520
{
537
- // If the slave NACKS the address, the MB bit will be set.
538
- // In that case, send a stop condition and return false.
539
- if (sercom->I2CM .INTFLAG .bit .MB ) {
540
- sercom->I2CM .CTRLB .bit .CMD = 3 ; // Stop condition
541
- return false ;
542
- }
543
- // Wait transmission complete
521
+ // wait transmission complete
544
522
}
545
-
546
- // Clean the 'Slave on Bus' flag, for further usage.
547
- // sercom->I2CM.INTFLAG.bit.SB = 0x1ul;
548
523
}
549
524
525
+ // Check for loss of arbitration (multiple masters starting communication at the same time)
526
+ // or timeout
527
+ if (!isBusOwnerWIRE () || didTimeout ()) {
528
+
529
+ restartTX_cnt++; // increment restart counter
530
+
531
+ if (restartTX_cnt >= restartTX_limit) {
532
+ // only allow limited restarts
533
+ restartTX_cnt = 0 ;
534
+ return false ;
535
+ } else {
536
+ // Restart communication after small delay
537
+ if (!didTimeout ()) delayMicroseconds (1000 ); // delay if not already timed out
538
+
539
+ // Restart
540
+ startTransmissionWIRE (address >> 1 , flag);
541
+ }
542
+
543
+ } else restartTX_cnt = 0 ;
550
544
551
545
// ACK received (0: ACK, 1: NACK)
552
546
if (sercom->I2CM .STATUS .bit .RXNACK )
@@ -563,17 +557,18 @@ bool SERCOM::sendDataMasterWIRE(uint8_t data)
563
557
{
564
558
// Send data
565
559
sercom->I2CM .DATA .bit .DATA = data;
560
+ waitSyncWIRE ();
566
561
567
562
// Wait transmission successful
568
- while (!sercom->I2CM .INTFLAG .bit .MB ) {
569
-
570
- // If a bus error occurs, the MB bit may never be set.
571
- // Check the bus error bit and bail if it's set.
572
- if (sercom->I2CM .STATUS .bit .BUSERR ) {
573
- return false ;
574
- }
563
+ initTimeout ();
564
+ while (!sercom->I2CM .INTFLAG .bit .MB && !testTimeout ())
565
+ {
566
+ // Wait transmission complete
575
567
}
576
568
569
+ // check for timeout condition
570
+ if ( didTimeout () ) return false ;
571
+
577
572
// Problems on line? nack received?
578
573
if (sercom->I2CM .STATUS .bit .RXNACK )
579
574
return false ;
@@ -665,9 +660,10 @@ uint8_t SERCOM::readDataWIRE( void )
665
660
{
666
661
if (isMasterWIRE ())
667
662
{
668
- while ( sercom->I2CM .INTFLAG .bit .SB == 0 && sercom->I2CM .INTFLAG .bit .MB == 0 )
663
+ initTimeout ();
664
+ while (!sercom->I2CM .INTFLAG .bit .SB && !testTimeout ())
669
665
{
670
- // Waiting complete receive
666
+ // wait receive
671
667
}
672
668
673
669
return sercom->I2CM .DATA .bit .DATA ;
@@ -739,3 +735,26 @@ void SERCOM::initClockNVIC( void )
739
735
/* Wait for synchronization */
740
736
}
741
737
}
738
+
739
+ void SERCOM::setTimeout ( uint16_t ms )
740
+ {
741
+ timeoutInterval = ms;
742
+ }
743
+
744
+ bool SERCOM::didTimeout ( void )
745
+ {
746
+ return timeoutOccurred;
747
+ }
748
+
749
+ void SERCOM::initTimeout ( void )
750
+ {
751
+ timeoutOccurred = false ;
752
+ timeoutRef = millis ();
753
+ }
754
+
755
+ bool SERCOM::testTimeout ( void )
756
+ {
757
+ if (!timeoutInterval) return false ;
758
+ timeoutOccurred = (millis () - timeoutRef) > timeoutInterval;
759
+ return timeoutOccurred;
760
+ }
0 commit comments