@@ -52,10 +52,9 @@ type I2CConfig struct {
52
52
}
53
53
54
54
type I2C struct {
55
- Bus * rp.I2C0_Type
56
- restartOnNext bool
57
- mode I2CMode
58
- txInProgress bool
55
+ Bus * rp.I2C0_Type
56
+ mode I2CMode
57
+ txInProgress bool
59
58
}
60
59
61
60
var (
@@ -236,7 +235,6 @@ func (i2c *I2C) init(config I2CConfig) error {
236
235
if err := i2c .disable (); err != nil {
237
236
return err
238
237
}
239
- i2c .restartOnNext = false
240
238
241
239
i2c .mode = config .Mode
242
240
@@ -306,16 +304,17 @@ func (i2c *I2C) tx(addr uint8, tx, rx []byte, timeout_us uint64) (err error) {
306
304
i2c .Bus .IC_TAR .Set (uint32 (addr ))
307
305
i2c .enable ()
308
306
abort := false
309
- var abortReason uint32
307
+ var abortReason i2cAbortError
308
+ txStop := rxlen == 0
310
309
for txCtr := 0 ; txCtr < txlen ; txCtr ++ {
311
310
if abort {
312
311
break
313
312
}
314
313
first := txCtr == 0
315
314
last := txCtr == txlen - 1 && rxlen == 0
316
315
i2c .Bus .IC_DATA_CMD .Set (
317
- (boolToBit (first && i2c . restartOnNext ) << rp .I2C0_IC_DATA_CMD_RESTART_Pos ) |
318
- (boolToBit (last ) << rp .I2C0_IC_DATA_CMD_STOP_Pos ) |
316
+ (boolToBit (first ) << rp .I2C0_IC_DATA_CMD_RESTART_Pos ) |
317
+ (boolToBit (last && txStop ) << rp .I2C0_IC_DATA_CMD_STOP_Pos ) |
319
318
uint32 (tx [txCtr ]))
320
319
321
320
// Wait until the transmission of the address/data from the internal
@@ -356,6 +355,9 @@ func (i2c *I2C) tx(addr uint8, tx, rx []byte, timeout_us uint64) (err error) {
356
355
// to take care of the abort.
357
356
for ! i2c .interrupted (rp .I2C0_IC_RAW_INTR_STAT_STOP_DET ) {
358
357
if ticks () > deadline {
358
+ if abort {
359
+ return abortReason
360
+ }
359
361
return errI2CWriteTimeout
360
362
}
361
363
@@ -365,6 +367,16 @@ func (i2c *I2C) tx(addr uint8, tx, rx []byte, timeout_us uint64) (err error) {
365
367
}
366
368
}
367
369
370
+ // Midway check for abort. Related issue https://github.com/tinygo-org/tinygo/issues/3671.
371
+ // The root cause for an abort after writing registers was "tx data no ack" (abort code=8).
372
+ // If the abort code was not registered then the whole peripheral would remain in disabled state forever.
373
+ abortReason = i2c .getAbortReason ()
374
+ if abortReason != 0 {
375
+ i2c .clearAbortReason ()
376
+ abort = true
377
+ }
378
+
379
+ rxStart := txlen == 0
368
380
if rxlen > 0 && ! abort {
369
381
for rxCtr := 0 ; rxCtr < rxlen ; rxCtr ++ {
370
382
first := rxCtr == 0
@@ -373,14 +385,14 @@ func (i2c *I2C) tx(addr uint8, tx, rx []byte, timeout_us uint64) (err error) {
373
385
gosched ()
374
386
}
375
387
i2c .Bus .IC_DATA_CMD .Set (
376
- boolToBit (first && i2c . restartOnNext )<< rp .I2C0_IC_DATA_CMD_RESTART_Pos |
388
+ boolToBit (first && rxStart )<< rp .I2C0_IC_DATA_CMD_RESTART_Pos |
377
389
boolToBit (last )<< rp .I2C0_IC_DATA_CMD_STOP_Pos |
378
390
rp .I2C0_IC_DATA_CMD_CMD ) // -> 1 for read
379
391
380
392
for ! abort && i2c .readAvailable () == 0 {
381
393
abortReason = i2c .getAbortReason ()
382
- i2c .clearAbortReason ()
383
394
if abortReason != 0 {
395
+ i2c .clearAbortReason ()
384
396
abort = true
385
397
}
386
398
if ticks () > deadline {
@@ -407,7 +419,7 @@ func (i2c *I2C) tx(addr uint8, tx, rx []byte, timeout_us uint64) (err error) {
407
419
// Address acknowledged, some data not acknowledged
408
420
fallthrough
409
421
default :
410
- err = makeI2CAbortError ( abortReason )
422
+ err = abortReason
411
423
}
412
424
}
413
425
return err
@@ -534,8 +546,8 @@ func (i2c *I2C) clearAbortReason() {
534
546
// getAbortReason reads IC_TX_ABRT_SOURCE register.
535
547
//
536
548
//go:inline
537
- func (i2c * I2C ) getAbortReason () uint32 {
538
- return i2c .Bus .IC_TX_ABRT_SOURCE .Get ()
549
+ func (i2c * I2C ) getAbortReason () i2cAbortError {
550
+ return i2cAbortError ( i2c .Bus .IC_TX_ABRT_SOURCE .Get () )
539
551
}
540
552
541
553
// returns true if RAW_INTR_STAT bits in mask are all set. performs:
@@ -554,9 +566,62 @@ func (b i2cAbortError) Error() string {
554
566
return "i2c abort, reason " + itoa .Uitoa (uint (b ))
555
567
}
556
568
557
- //go:inline
558
- func makeI2CAbortError (reason uint32 ) error {
559
- return i2cAbortError (reason )
569
+ func (b i2cAbortError ) Reasons () (reasons []string ) {
570
+ if b == 0 {
571
+ return nil
572
+ }
573
+ if b & rp .I2C0_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK != 0 {
574
+ reasons = append (reasons , "7-bit address no ack" )
575
+ }
576
+ if b & rp .I2C0_IC_TX_ABRT_SOURCE_ABRT_10ADDR1_NOACK != 0 {
577
+ reasons = append (reasons , "10-bit address first byte no ack" )
578
+ }
579
+ if b & rp .I2C0_IC_TX_ABRT_SOURCE_ABRT_10ADDR2_NOACK != 0 {
580
+ reasons = append (reasons , "10-bit address second byte no ack" )
581
+ }
582
+ if b & rp .I2C0_IC_TX_ABRT_SOURCE_ABRT_TXDATA_NOACK != 0 {
583
+ reasons = append (reasons , "tx data no ack" )
584
+ }
585
+ if b & rp .I2C0_IC_TX_ABRT_SOURCE_ABRT_GCALL_NOACK != 0 {
586
+ reasons = append (reasons , "general call no ack" )
587
+ }
588
+ if b & rp .I2C0_IC_TX_ABRT_SOURCE_ABRT_GCALL_READ != 0 {
589
+ reasons = append (reasons , "general call read" )
590
+ }
591
+ if b & rp .I2C0_IC_TX_ABRT_SOURCE_ABRT_HS_ACKDET != 0 {
592
+ reasons = append (reasons , "high speed ack detect" )
593
+ }
594
+ if b & rp .I2C0_IC_TX_ABRT_SOURCE_ABRT_SBYTE_ACKDET != 0 {
595
+ reasons = append (reasons , "start byte ack detect" )
596
+ }
597
+ if b & rp .I2C0_IC_TX_ABRT_SOURCE_ABRT_HS_NORSTRT != 0 {
598
+ reasons = append (reasons , "high speed no restart" )
599
+ }
600
+ if b & rp .I2C0_IC_TX_ABRT_SOURCE_ABRT_SBYTE_NORSTRT != 0 {
601
+ reasons = append (reasons , "start byte no restart" )
602
+ }
603
+ if b & rp .I2C0_IC_TX_ABRT_SOURCE_ABRT_10B_RD_NORSTRT != 0 {
604
+ reasons = append (reasons , "10-bit read no restart" )
605
+ }
606
+ if b & rp .I2C0_IC_TX_ABRT_SOURCE_ABRT_MASTER_DIS != 0 {
607
+ reasons = append (reasons , "master disabled" )
608
+ }
609
+ if b & rp .I2C0_IC_TX_ABRT_SOURCE_ARB_LOST != 0 {
610
+ reasons = append (reasons , "arbitration lost" )
611
+ }
612
+ if b & rp .I2C0_IC_TX_ABRT_SOURCE_ABRT_SLVFLUSH_TXFIFO != 0 {
613
+ reasons = append (reasons , "slave flush tx fifo" )
614
+ }
615
+ if b & rp .I2C0_IC_TX_ABRT_SOURCE_ABRT_SLV_ARBLOST != 0 {
616
+ reasons = append (reasons , "slave arbitration lost" )
617
+ }
618
+ if b & rp .I2C0_IC_TX_ABRT_SOURCE_ABRT_SLVRD_INTX != 0 {
619
+ reasons = append (reasons , "slave read while inactive" )
620
+ }
621
+ if b & rp .I2C0_IC_TX_ABRT_SOURCE_ABRT_USER_ABRT != 0 {
622
+ reasons = append (reasons , "user abort" )
623
+ }
624
+ return reasons
560
625
}
561
626
562
627
//go:inline
0 commit comments