github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/machine_nrf5x.go (about)

     1  //go:build nrf51 || nrf52
     2  
     3  package machine
     4  
     5  import "device/nrf"
     6  
     7  // I2C on the NRF51 and NRF52.
     8  type I2C struct {
     9  	Bus  *nrf.TWI_Type
    10  	mode I2CMode
    11  }
    12  
    13  // There are 2 I2C interfaces on the NRF.
    14  var (
    15  	I2C0 = &I2C{Bus: nrf.TWI0}
    16  	I2C1 = &I2C{Bus: nrf.TWI1}
    17  )
    18  
    19  func (i2c *I2C) enableAsController() {
    20  	i2c.Bus.ENABLE.Set(nrf.TWI_ENABLE_ENABLE_Enabled)
    21  }
    22  
    23  func (i2c *I2C) enableAsTarget() {
    24  	// Not supported on this hardware
    25  }
    26  
    27  func (i2c *I2C) disable() {
    28  	i2c.Bus.ENABLE.Set(0)
    29  }
    30  
    31  // Tx does a single I2C transaction at the specified address.
    32  // It clocks out the given address, writes the bytes in w, reads back len(r)
    33  // bytes and stores them in r, and generates a stop condition on the bus.
    34  func (i2c *I2C) Tx(addr uint16, w, r []byte) (err error) {
    35  
    36  	// Tricky stop condition.
    37  	// After reads, the stop condition is generated implicitly with a shortcut.
    38  	// After writes not followed by reads and in the case of errors, stop must be generated explicitly.
    39  
    40  	i2c.Bus.ADDRESS.Set(uint32(addr))
    41  
    42  	if len(w) != 0 {
    43  		i2c.Bus.TASKS_STARTTX.Set(1) // start transmission for writing
    44  		for _, b := range w {
    45  			if err = i2c.writeByte(b); err != nil {
    46  				i2c.signalStop()
    47  				return
    48  			}
    49  		}
    50  	}
    51  
    52  	if len(r) != 0 {
    53  		// To trigger suspend task when a byte is received
    54  		i2c.Bus.SHORTS.Set(nrf.TWI_SHORTS_BB_SUSPEND)
    55  		i2c.Bus.TASKS_STARTRX.Set(1) // re-start transmission for reading
    56  		for i := range r {           // read each char
    57  			if i+1 == len(r) {
    58  				// To trigger stop task when last byte is received, set before resume task.
    59  				i2c.Bus.SHORTS.Set(nrf.TWI_SHORTS_BB_STOP)
    60  			}
    61  			if i > 0 {
    62  				i2c.Bus.TASKS_RESUME.Set(1) // re-start transmission for reading
    63  			}
    64  			if r[i], err = i2c.readByte(); err != nil {
    65  				i2c.Bus.SHORTS.Set(nrf.TWI_SHORTS_BB_SUSPEND_Disabled)
    66  				i2c.signalStop()
    67  				return
    68  			}
    69  		}
    70  		i2c.Bus.SHORTS.Set(nrf.TWI_SHORTS_BB_SUSPEND_Disabled)
    71  	}
    72  
    73  	if len(r) == 0 {
    74  		// Stop the I2C transaction after the write.
    75  		err = i2c.signalStop()
    76  	} else {
    77  		// The last byte read has already stopped the transaction, via
    78  		// TWI_SHORTS_BB_STOP. But we still need to wait until we receive the
    79  		// STOPPED event.
    80  		tries := 0
    81  		for i2c.Bus.EVENTS_STOPPED.Get() == 0 {
    82  			tries++
    83  			if tries >= i2cTimeout {
    84  				return errI2CSignalStopTimeout
    85  			}
    86  		}
    87  		i2c.Bus.EVENTS_STOPPED.Set(0)
    88  	}
    89  
    90  	return
    91  }
    92  
    93  // writeByte writes a single byte to the I2C bus and waits for confirmation.
    94  func (i2c *I2C) writeByte(data byte) error {
    95  	tries := 0
    96  	i2c.Bus.TXD.Set(uint32(data))
    97  	for i2c.Bus.EVENTS_TXDSENT.Get() == 0 {
    98  		if e := i2c.Bus.EVENTS_ERROR.Get(); e != 0 {
    99  			i2c.Bus.EVENTS_ERROR.Set(0)
   100  			return errI2CBusError
   101  		}
   102  		tries++
   103  		if tries >= i2cTimeout {
   104  			return errI2CWriteTimeout
   105  		}
   106  	}
   107  	i2c.Bus.EVENTS_TXDSENT.Set(0)
   108  	return nil
   109  }
   110  
   111  // readByte reads a single byte from the I2C bus when it is ready.
   112  func (i2c *I2C) readByte() (byte, error) {
   113  	tries := 0
   114  	for i2c.Bus.EVENTS_RXDREADY.Get() == 0 {
   115  		if e := i2c.Bus.EVENTS_ERROR.Get(); e != 0 {
   116  			i2c.Bus.EVENTS_ERROR.Set(0)
   117  			return 0, errI2CBusError
   118  		}
   119  		tries++
   120  		if tries >= i2cTimeout {
   121  			return 0, errI2CReadTimeout
   122  		}
   123  	}
   124  	i2c.Bus.EVENTS_RXDREADY.Set(0)
   125  	return byte(i2c.Bus.RXD.Get()), nil
   126  }