mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-05 03:27:48 +00:00
14ddba8519
This commit adds I2C timeouts for nrf51 and nrf52 (but not yet for others like nrf52840). Tested on the PineTime, where I now got a timeout instead of hanging and resetting due to a watchdog reset.
127 lines
3.1 KiB
Go
127 lines
3.1 KiB
Go
//go:build nrf51 || nrf52
|
|
|
|
package machine
|
|
|
|
import "device/nrf"
|
|
|
|
// I2C on the NRF51 and NRF52.
|
|
type I2C struct {
|
|
Bus *nrf.TWI_Type
|
|
mode I2CMode
|
|
}
|
|
|
|
// There are 2 I2C interfaces on the NRF.
|
|
var (
|
|
I2C0 = &I2C{Bus: nrf.TWI0}
|
|
I2C1 = &I2C{Bus: nrf.TWI1}
|
|
)
|
|
|
|
func (i2c *I2C) enableAsController() {
|
|
i2c.Bus.ENABLE.Set(nrf.TWI_ENABLE_ENABLE_Enabled)
|
|
}
|
|
|
|
func (i2c *I2C) enableAsTarget() {
|
|
// Not supported on this hardware
|
|
}
|
|
|
|
func (i2c *I2C) disable() {
|
|
i2c.Bus.ENABLE.Set(0)
|
|
}
|
|
|
|
// Tx does a single I2C transaction at the specified address.
|
|
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
|
// bytes and stores them in r, and generates a stop condition on the bus.
|
|
func (i2c *I2C) Tx(addr uint16, w, r []byte) (err error) {
|
|
|
|
// Tricky stop condition.
|
|
// After reads, the stop condition is generated implicitly with a shortcut.
|
|
// After writes not followed by reads and in the case of errors, stop must be generated explicitly.
|
|
|
|
i2c.Bus.ADDRESS.Set(uint32(addr))
|
|
|
|
if len(w) != 0 {
|
|
i2c.Bus.TASKS_STARTTX.Set(1) // start transmission for writing
|
|
for _, b := range w {
|
|
if err = i2c.writeByte(b); err != nil {
|
|
i2c.signalStop()
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(r) != 0 {
|
|
// To trigger suspend task when a byte is received
|
|
i2c.Bus.SHORTS.Set(nrf.TWI_SHORTS_BB_SUSPEND)
|
|
i2c.Bus.TASKS_STARTRX.Set(1) // re-start transmission for reading
|
|
for i := range r { // read each char
|
|
if i+1 == len(r) {
|
|
// To trigger stop task when last byte is received, set before resume task.
|
|
i2c.Bus.SHORTS.Set(nrf.TWI_SHORTS_BB_STOP)
|
|
}
|
|
if i > 0 {
|
|
i2c.Bus.TASKS_RESUME.Set(1) // re-start transmission for reading
|
|
}
|
|
if r[i], err = i2c.readByte(); err != nil {
|
|
i2c.Bus.SHORTS.Set(nrf.TWI_SHORTS_BB_SUSPEND_Disabled)
|
|
i2c.signalStop()
|
|
return
|
|
}
|
|
}
|
|
i2c.Bus.SHORTS.Set(nrf.TWI_SHORTS_BB_SUSPEND_Disabled)
|
|
}
|
|
|
|
if len(r) == 0 {
|
|
// Stop the I2C transaction after the write.
|
|
err = i2c.signalStop()
|
|
} else {
|
|
// The last byte read has already stopped the transaction, via
|
|
// TWI_SHORTS_BB_STOP. But we still need to wait until we receive the
|
|
// STOPPED event.
|
|
tries := 0
|
|
for i2c.Bus.EVENTS_STOPPED.Get() == 0 {
|
|
tries++
|
|
if tries >= i2cTimeout {
|
|
return errI2CSignalStopTimeout
|
|
}
|
|
}
|
|
i2c.Bus.EVENTS_STOPPED.Set(0)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// writeByte writes a single byte to the I2C bus and waits for confirmation.
|
|
func (i2c *I2C) writeByte(data byte) error {
|
|
tries := 0
|
|
i2c.Bus.TXD.Set(uint32(data))
|
|
for i2c.Bus.EVENTS_TXDSENT.Get() == 0 {
|
|
if e := i2c.Bus.EVENTS_ERROR.Get(); e != 0 {
|
|
i2c.Bus.EVENTS_ERROR.Set(0)
|
|
return errI2CBusError
|
|
}
|
|
tries++
|
|
if tries >= i2cTimeout {
|
|
return errI2CWriteTimeout
|
|
}
|
|
}
|
|
i2c.Bus.EVENTS_TXDSENT.Set(0)
|
|
return nil
|
|
}
|
|
|
|
// readByte reads a single byte from the I2C bus when it is ready.
|
|
func (i2c *I2C) readByte() (byte, error) {
|
|
tries := 0
|
|
for i2c.Bus.EVENTS_RXDREADY.Get() == 0 {
|
|
if e := i2c.Bus.EVENTS_ERROR.Get(); e != 0 {
|
|
i2c.Bus.EVENTS_ERROR.Set(0)
|
|
return 0, errI2CBusError
|
|
}
|
|
tries++
|
|
if tries >= i2cTimeout {
|
|
return 0, errI2CReadTimeout
|
|
}
|
|
}
|
|
i2c.Bus.EVENTS_RXDREADY.Set(0)
|
|
return byte(i2c.Bus.RXD.Get()), nil
|
|
}
|