@@ -52,10 +52,9 @@ type I2CConfig struct {
5252}
5353
5454type 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
5958}
6059
6160var (
@@ -236,7 +235,6 @@ func (i2c *I2C) init(config I2CConfig) error {
236235 if err := i2c .disable (); err != nil {
237236 return err
238237 }
239- i2c .restartOnNext = false
240238
241239 i2c .mode = config .Mode
242240
@@ -306,16 +304,17 @@ func (i2c *I2C) tx(addr uint8, tx, rx []byte, timeout_us uint64) (err error) {
306304 i2c .Bus .IC_TAR .Set (uint32 (addr ))
307305 i2c .enable ()
308306 abort := false
309- var abortReason uint32
307+ var abortReason i2cAbortError
308+ txStop := rxlen == 0
310309 for txCtr := 0 ; txCtr < txlen ; txCtr ++ {
311310 if abort {
312311 break
313312 }
314313 first := txCtr == 0
315314 last := txCtr == txlen - 1 && rxlen == 0
316315 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 ) |
319318 uint32 (tx [txCtr ]))
320319
321320 // 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) {
356355 // to take care of the abort.
357356 for ! i2c .interrupted (rp .I2C0_IC_RAW_INTR_STAT_STOP_DET ) {
358357 if ticks () > deadline {
358+ if abort {
359+ return abortReason
360+ }
359361 return errI2CWriteTimeout
360362 }
361363
@@ -365,6 +367,16 @@ func (i2c *I2C) tx(addr uint8, tx, rx []byte, timeout_us uint64) (err error) {
365367 }
366368 }
367369
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
368380 if rxlen > 0 && ! abort {
369381 for rxCtr := 0 ; rxCtr < rxlen ; rxCtr ++ {
370382 first := rxCtr == 0
@@ -373,14 +385,14 @@ func (i2c *I2C) tx(addr uint8, tx, rx []byte, timeout_us uint64) (err error) {
373385 gosched ()
374386 }
375387 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 |
377389 boolToBit (last )<< rp .I2C0_IC_DATA_CMD_STOP_Pos |
378390 rp .I2C0_IC_DATA_CMD_CMD ) // -> 1 for read
379391
380392 for ! abort && i2c .readAvailable () == 0 {
381393 abortReason = i2c .getAbortReason ()
382- i2c .clearAbortReason ()
383394 if abortReason != 0 {
395+ i2c .clearAbortReason ()
384396 abort = true
385397 }
386398 if ticks () > deadline {
@@ -407,7 +419,7 @@ func (i2c *I2C) tx(addr uint8, tx, rx []byte, timeout_us uint64) (err error) {
407419 // Address acknowledged, some data not acknowledged
408420 fallthrough
409421 default :
410- err = makeI2CAbortError ( abortReason )
422+ err = abortReason
411423 }
412424 }
413425 return err
@@ -534,8 +546,8 @@ func (i2c *I2C) clearAbortReason() {
534546// getAbortReason reads IC_TX_ABRT_SOURCE register.
535547//
536548//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 () )
539551}
540552
541553// returns true if RAW_INTR_STAT bits in mask are all set. performs:
@@ -554,9 +566,62 @@ func (b i2cAbortError) Error() string {
554566 return "i2c abort, reason " + itoa .Uitoa (uint (b ))
555567}
556568
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
560625}
561626
562627//go:inline
0 commit comments