aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/examples/i2c-target/main.go130
-rw-r--r--src/examples/i2c-target/main_feather_nrf52840.go15
-rw-r--r--src/examples/i2c-target/main_feather_rp2040.go15
-rw-r--r--src/machine/i2c.go31
-rw-r--r--src/machine/machine_nrf.go21
-rw-r--r--src/machine/machine_nrf528xx.go120
-rw-r--r--src/machine/machine_nrf5x.go7
-rw-r--r--src/machine/machine_rp2040_i2c.go165
8 files changed, 477 insertions, 27 deletions
diff --git a/src/examples/i2c-target/main.go b/src/examples/i2c-target/main.go
new file mode 100644
index 000000000..cd984ba3c
--- /dev/null
+++ b/src/examples/i2c-target/main.go
@@ -0,0 +1,130 @@
+// Example demonstrating I2C controller / target comms.
+//
+// To use this example, physically connect I2C0 and I2C1.
+// I2C0 will be used as the controller and I2C1 used as
+// the target.
+//
+// In this example, the target implements a simple memory
+// map, with the controller able to read and write the
+// memory.
+
+package main
+
+import (
+ "machine"
+ "time"
+)
+
+const (
+ targetAddress = 0x11
+ maxTxSize = 16
+)
+
+func main() {
+ // Delay to enable USB monitor time to attach
+ time.Sleep(3 * time.Second)
+
+ // Controller uses default I2C pins and controller
+ // mode is default
+ err := controller.Configure(machine.I2CConfig{})
+ if err != nil {
+ panic("failed to config I2C0 as controller")
+ }
+
+ // Target uses alternate pins and target mode is
+ // explicit
+ err = target.Configure(machine.I2CConfig{
+ Mode: machine.I2CModeTarget,
+ SCL: TARGET_SCL,
+ SDA: TARGET_SDA,
+ })
+ if err != nil {
+ panic("failed to config I2C1 as target")
+ }
+
+ // Put welcome message directly into target memory
+ copy(mem[0:], []byte("Hello World!"))
+ err = target.Listen(targetAddress)
+ if err != nil {
+ panic("failed to listen as I2C target")
+ }
+
+ // Start the target
+ go targetMain()
+
+ // Read welcome message from target over I2C
+ buf := make([]byte, 12)
+ err = controller.Tx(targetAddress, []byte{0}, buf)
+ if err != nil {
+ println("failed to read welcome message over I2C")
+ panic(err)
+ }
+ println("message from target:", string(buf))
+
+ // Write (1,2,3) to the target starting at memory address 3
+ println("writing (1,2,3) @ 0x3")
+ err = controller.Tx(targetAddress, []byte{3, 1, 2, 3}, nil)
+ if err != nil {
+ println("failed to write to I2C target")
+ panic(err)
+ }
+
+ time.Sleep(100 * time.Millisecond) // Wait for target to process write
+ println("mem:", mem[0], mem[1], mem[2], mem[3], mem[4], mem[5])
+
+ // Read memory address 4 from target, which should be the value 2
+ buf = make([]byte, 1)
+ err = controller.Tx(targetAddress, []byte{4}, buf[:1])
+ if err != nil {
+ println("failed to read from I2C target")
+ panic(err)
+ }
+
+ if buf[0] != 2 {
+ panic("read incorrect value from I2C target")
+ }
+
+ println("all done!")
+ for {
+ time.Sleep(1 * time.Second)
+ }
+}
+
+// -- target ---
+
+var (
+ mem [256]byte
+)
+
+// targetMain implements the 'main loop' for an I2C target
+func targetMain() {
+ buf := make([]byte, maxTxSize)
+ var ptr uint8
+
+ for {
+ evt, n, err := target.WaitForEvent(buf)
+ if err != nil {
+ panic(err)
+ }
+
+ switch evt {
+ case machine.I2CReceive:
+ if n > 0 {
+ ptr = buf[0]
+ }
+
+ for o := 1; o < n; o++ {
+ mem[ptr] = buf[o]
+ ptr++
+ }
+
+ case machine.I2CRequest:
+ target.Reply(mem[ptr:256])
+
+ case machine.I2CFinish:
+ // nothing to do
+
+ default:
+ }
+ }
+}
diff --git a/src/examples/i2c-target/main_feather_nrf52840.go b/src/examples/i2c-target/main_feather_nrf52840.go
new file mode 100644
index 000000000..6c94cfe58
--- /dev/null
+++ b/src/examples/i2c-target/main_feather_nrf52840.go
@@ -0,0 +1,15 @@
+//go:build feather_nrf52840
+
+package main
+
+import "machine"
+
+const (
+ TARGET_SCL = machine.A5
+ TARGET_SDA = machine.A4
+)
+
+var (
+ controller = machine.I2C0
+ target = machine.I2C1
+)
diff --git a/src/examples/i2c-target/main_feather_rp2040.go b/src/examples/i2c-target/main_feather_rp2040.go
new file mode 100644
index 000000000..6f0581bba
--- /dev/null
+++ b/src/examples/i2c-target/main_feather_rp2040.go
@@ -0,0 +1,15 @@
+//go:build rp2040
+
+package main
+
+import "machine"
+
+const (
+ TARGET_SCL = machine.GPIO25
+ TARGET_SDA = machine.GPIO24
+)
+
+var (
+ controller = machine.I2C1
+ target = machine.I2C0
+)
diff --git a/src/machine/i2c.go b/src/machine/i2c.go
index 4e7a630b9..c8bd6d1b4 100644
--- a/src/machine/i2c.go
+++ b/src/machine/i2c.go
@@ -23,6 +23,37 @@ var (
errI2CSignalStopTimeout = errors.New("I2C timeout on signal stop")
errI2CAckExpected = errors.New("I2C error: expected ACK not NACK")
errI2CBusError = errors.New("I2C bus error")
+ errI2COverflow = errors.New("I2C receive buffer overflow")
+ errI2COverread = errors.New("I2C transmit buffer overflow")
+)
+
+// I2CTargetEvent reflects events on the I2C bus
+type I2CTargetEvent uint8
+
+const (
+ // I2CReceive indicates target has received a message from the controller.
+ I2CReceive I2CTargetEvent = iota
+
+ // I2CRequest indicates the controller is expecting a message from the target.
+ I2CRequest
+
+ // I2CFinish indicates the controller has ended the transaction.
+ //
+ // I2C controllers can chain multiple receive/request messages without
+ // relinquishing the bus by doing 'restarts'. I2CFinish indicates the
+ // bus has been relinquished by an I2C 'stop'.
+ I2CFinish
+)
+
+// I2CMode determines if an I2C peripheral is in Controller or Target mode.
+type I2CMode int
+
+const (
+ // I2CModeController represents an I2C peripheral in controller mode.
+ I2CModeController I2CMode = iota
+
+ // I2CModeTarget represents an I2C peripheral in target mode.
+ I2CModeTarget
)
// WriteRegister transmits first the register and then the data to the
diff --git a/src/machine/machine_nrf.go b/src/machine/machine_nrf.go
index e457db9db..979693a10 100644
--- a/src/machine/machine_nrf.go
+++ b/src/machine/machine_nrf.go
@@ -206,6 +206,7 @@ type I2CConfig struct {
Frequency uint32
SCL Pin
SDA Pin
+ Mode I2CMode
}
// Configure is intended to setup the I2C interface.
@@ -238,15 +239,21 @@ func (i2c *I2C) Configure(config I2CConfig) error {
(nrf.GPIO_PIN_CNF_DRIVE_S0D1 << nrf.GPIO_PIN_CNF_DRIVE_Pos) |
(nrf.GPIO_PIN_CNF_SENSE_Disabled << nrf.GPIO_PIN_CNF_SENSE_Pos))
- if config.Frequency >= 400*KHz {
- i2c.Bus.FREQUENCY.Set(nrf.TWI_FREQUENCY_FREQUENCY_K400)
- } else {
- i2c.Bus.FREQUENCY.Set(nrf.TWI_FREQUENCY_FREQUENCY_K100)
- }
-
i2c.setPins(config.SCL, config.SDA)
- i2c.enableAsController()
+ i2c.mode = config.Mode
+
+ if i2c.mode == I2CModeController {
+ if config.Frequency >= 400*KHz {
+ i2c.Bus.FREQUENCY.Set(nrf.TWI_FREQUENCY_FREQUENCY_K400)
+ } else {
+ i2c.Bus.FREQUENCY.Set(nrf.TWI_FREQUENCY_FREQUENCY_K100)
+ }
+
+ i2c.enableAsController()
+ } else {
+ i2c.enableAsTarget()
+ }
return nil
}
diff --git a/src/machine/machine_nrf528xx.go b/src/machine/machine_nrf528xx.go
index b3e95a253..019a66cf1 100644
--- a/src/machine/machine_nrf528xx.go
+++ b/src/machine/machine_nrf528xx.go
@@ -9,19 +9,25 @@ import (
// I2C on the NRF528xx.
type I2C struct {
- Bus *nrf.TWIM_Type
+ Bus *nrf.TWIM_Type // Called Bus to align with Bus field in nrf51
+ BusT *nrf.TWIS_Type
+ mode I2CMode
}
// There are 2 I2C interfaces on the NRF.
var (
- I2C0 = &I2C{Bus: nrf.TWIM0}
- I2C1 = &I2C{Bus: nrf.TWIM1}
+ I2C0 = &I2C{Bus: nrf.TWIM0, BusT: nrf.TWIS0}
+ I2C1 = &I2C{Bus: nrf.TWIM1, BusT: nrf.TWIS1}
)
func (i2c *I2C) enableAsController() {
i2c.Bus.ENABLE.Set(nrf.TWIM_ENABLE_ENABLE_Enabled)
}
+func (i2c *I2C) enableAsTarget() {
+ i2c.BusT.ENABLE.Set(nrf.TWIS_ENABLE_ENABLE_Enabled)
+}
+
func (i2c *I2C) disable() {
i2c.Bus.ENABLE.Set(0)
}
@@ -86,6 +92,99 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) (err error) {
return
}
+// Listen starts listening for I2C requests sent to specified address
+//
+// addr is the address to listen to
+func (i2c *I2C) Listen(addr uint8) error {
+ i2c.BusT.ADDRESS[0].Set(uint32(addr))
+ i2c.BusT.CONFIG.Set(nrf.TWIS_CONFIG_ADDRESS0_Enabled)
+
+ i2c.BusT.EVENTS_STOPPED.Set(0)
+ i2c.BusT.EVENTS_ERROR.Set(0)
+ i2c.BusT.EVENTS_RXSTARTED.Set(0)
+ i2c.BusT.EVENTS_TXSTARTED.Set(0)
+ i2c.BusT.EVENTS_WRITE.Set(0)
+ i2c.BusT.EVENTS_READ.Set(0)
+
+ return nil
+}
+
+// WaitForEvent blocks the current go-routine until an I2C event is received (when in Target mode).
+//
+// The passed buffer will be populated for receive events, with the number of bytes
+// received returned in count. For other event types, buf is not modified and a count
+// of zero is returned.
+//
+// For request events, the caller MUST call `Reply` to avoid hanging the i2c bus indefinitely.
+func (i2c *I2C) WaitForEvent(buf []byte) (evt I2CTargetEvent, count int, err error) {
+ i2c.BusT.RXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&buf[0]))))
+ i2c.BusT.RXD.MAXCNT.Set(uint32(len(buf)))
+
+ i2c.BusT.TASKS_PREPARERX.Set(nrf.TWIS_TASKS_PREPARERX_TASKS_PREPARERX_Trigger)
+
+ i2c.Bus.TASKS_RESUME.Set(1)
+
+ for i2c.BusT.EVENTS_STOPPED.Get() == 0 &&
+ i2c.BusT.EVENTS_READ.Get() == 0 {
+ gosched()
+
+ if i2c.BusT.EVENTS_ERROR.Get() != 0 {
+ i2c.BusT.EVENTS_ERROR.Set(0)
+ return I2CReceive, 0, twisError(i2c.BusT.ERRORSRC.Get())
+ }
+ }
+
+ count = 0
+ evt = I2CFinish
+ err = nil
+
+ if i2c.BusT.EVENTS_WRITE.Get() != 0 {
+ i2c.BusT.EVENTS_WRITE.Set(0)
+
+ // Data was sent to this target. We've waited for
+ // READ or STOPPED event, so transmission should be
+ // complete.
+ count = int(i2c.BusT.RXD.AMOUNT.Get())
+ evt = I2CReceive
+ } else if i2c.BusT.EVENTS_READ.Get() != 0 {
+ i2c.BusT.EVENTS_READ.Set(0)
+
+ // Data is requested from this target, hw will stretch
+ // the controller's clock until there is a reply to
+ // send
+ evt = I2CRequest
+ } else if i2c.BusT.EVENTS_STOPPED.Get() != 0 {
+ i2c.BusT.EVENTS_STOPPED.Set(0)
+ evt = I2CFinish
+ }
+
+ return
+}
+
+// Reply supplies the response data the controller.
+func (i2c *I2C) Reply(buf []byte) error {
+ i2c.BusT.TXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&buf[0]))))
+ i2c.BusT.TXD.MAXCNT.Set(uint32(len(buf)))
+
+ i2c.BusT.EVENTS_STOPPED.Set(0)
+
+ // Trigger Tx
+ i2c.BusT.TASKS_PREPARETX.Set(nrf.TWIS_TASKS_PREPARETX_TASKS_PREPARETX_Trigger)
+
+ // Block, waiting for Tx to complete
+ for i2c.BusT.EVENTS_STOPPED.Get() == 0 {
+ gosched()
+
+ if i2c.BusT.EVENTS_ERROR.Get() != 0 {
+ return twisError(i2c.BusT.ERRORSRC.Get())
+ }
+ }
+
+ i2c.BusT.EVENTS_STOPPED.Set(0)
+
+ return nil
+}
+
// twiCError converts an I2C controller error to Go
func twiCError(val uint32) error {
if val == 0 {
@@ -100,3 +199,18 @@ func twiCError(val uint32) error {
return errI2CBusError
}
+
+// twisError converts an I2C target error to Go
+func twisError(val uint32) error {
+ if val == 0 {
+ return nil
+ } else if val&nrf.TWIS_ERRORSRC_OVERFLOW_Msk == nrf.TWIS_ERRORSRC_OVERFLOW {
+ return errI2COverflow
+ } else if val&nrf.TWIS_ERRORSRC_DNACK_Msk == nrf.TWIS_ERRORSRC_DNACK {
+ return errI2CAckExpected
+ } else if val&nrf.TWIS_ERRORSRC_OVERREAD_Msk == nrf.TWIS_ERRORSRC_OVERREAD {
+ return errI2COverread
+ }
+
+ return errI2CBusError
+}
diff --git a/src/machine/machine_nrf5x.go b/src/machine/machine_nrf5x.go
index dbf2e1ccf..f36c7c93c 100644
--- a/src/machine/machine_nrf5x.go
+++ b/src/machine/machine_nrf5x.go
@@ -6,7 +6,8 @@ import "device/nrf"
// I2C on the NRF51 and NRF52.
type I2C struct {
- Bus *nrf.TWI_Type
+ Bus *nrf.TWI_Type
+ mode I2CMode
}
// There are 2 I2C interfaces on the NRF.
@@ -19,6 +20,10 @@ 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)
}
diff --git a/src/machine/machine_rp2040_i2c.go b/src/machine/machine_rp2040_i2c.go
index 0c5b688ef..4a4867c81 100644
--- a/src/machine/machine_rp2040_i2c.go
+++ b/src/machine/machine_rp2040_i2c.go
@@ -20,10 +20,13 @@ var (
}
)
+// The I2C target implementation is based on the C implementation from
+// here: https://github.com/vmilea/pico_i2c_slave
+
// Features: Taken from datasheet.
-// Default master mode, with slave mode available (not simulataneously).
-// Default slave address of RP2040: 0x055
-// Supports 10-bit addressing in Master mode
+// Default controller mode, with target mode available (not simulataneously).
+// Default target address of RP2040: 0x055
+// Supports 10-bit addressing in controller mode
// 16-element transmit buffer
// 16-element receive buffer
// Can be driven from DMA
@@ -45,20 +48,26 @@ type I2CConfig struct {
// SDA/SCL Serial Data and clock pins. Refer to datasheet to see
// which pins match the desired bus.
SDA, SCL Pin
+ Mode I2CMode
}
type I2C struct {
Bus *rp.I2C0_Type
restartOnNext bool
+ mode I2CMode
+ txInProgress bool
}
var (
- ErrInvalidI2CBaudrate = errors.New("invalid i2c baudrate")
- ErrInvalidTgtAddr = errors.New("invalid target i2c address not in 0..0x80 or is reserved")
- ErrI2CGeneric = errors.New("i2c error")
- ErrRP2040I2CDisable = errors.New("i2c rp2040 peripheral timeout in disable")
- errInvalidI2CSDA = errors.New("invalid I2C SDA pin")
- errInvalidI2CSCL = errors.New("invalid I2C SCL pin")
+ ErrInvalidI2CBaudrate = errors.New("invalid i2c baudrate")
+ ErrInvalidTgtAddr = errors.New("invalid target i2c address not in 0..0x80 or is reserved")
+ ErrI2CGeneric = errors.New("i2c error")
+ ErrRP2040I2CDisable = errors.New("i2c rp2040 peripheral timeout in disable")
+ errInvalidI2CSDA = errors.New("invalid I2C SDA pin")
+ errInvalidI2CSCL = errors.New("invalid I2C SCL pin")
+ ErrI2CAlreadyListening = errors.New("i2c already listening")
+ ErrI2CWrongMode = errors.New("i2c wrong mode")
+ ErrI2CUnderflow = errors.New("i2c underflow")
)
// Tx performs a write and then a read transfer placing the result in
@@ -75,11 +84,26 @@ var (
//
// Performs only a write transfer.
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
+ if i2c.mode != I2CModeController {
+ return ErrI2CWrongMode
+ }
+
// timeout in microseconds.
const timeout = 40 * 1000 // 40ms is a reasonable time for a real-time system.
return i2c.tx(uint8(addr), w, r, timeout)
}
+// Listen starts listening for I2C requests sent to specified address
+//
+// addr is the address to listen to
+func (i2c *I2C) Listen(addr uint16) error {
+ if i2c.mode != I2CModeTarget {
+ return ErrI2CWrongMode
+ }
+
+ return i2c.listen(uint8(addr))
+}
+
// Configure initializes i2c peripheral and configures I2C config's pins passed.
// Here's a list of valid SDA and SCL GPIO pins on bus I2C0 of the rp2040:
//
@@ -213,14 +237,22 @@ func (i2c *I2C) init(config I2CConfig) error {
return err
}
i2c.restartOnNext = false
- // Configure as a fast-mode master with RepStart support, 7-bit addresses
- i2c.Bus.IC_CON.Set((rp.I2C0_IC_CON_SPEED_FAST << rp.I2C0_IC_CON_SPEED_Pos) |
- rp.I2C0_IC_CON_MASTER_MODE | rp.I2C0_IC_CON_IC_SLAVE_DISABLE |
- rp.I2C0_IC_CON_IC_RESTART_EN | rp.I2C0_IC_CON_TX_EMPTY_CTRL) // sets TX_EMPTY_CTRL to enable TX_EMPTY interrupt status
+
+ i2c.mode = config.Mode
+
+ // Configure as fast-mode with RepStart support, 7-bit addresses
+ mode := uint32(rp.I2C0_IC_CON_SPEED_FAST<<rp.I2C0_IC_CON_SPEED_Pos) |
+ rp.I2C0_IC_CON_IC_RESTART_EN | rp.I2C0_IC_CON_TX_EMPTY_CTRL // sets TX_EMPTY_CTRL to enable TX_EMPTY interrupt status
+ if config.Mode == I2CModeController {
+ mode |= rp.I2C0_IC_CON_MASTER_MODE | rp.I2C0_IC_CON_IC_SLAVE_DISABLE
+ }
+ i2c.Bus.IC_CON.Set(mode)
// Set FIFO watermarks to 1 to make things simpler. This is encoded by a register value of 0.
- i2c.Bus.IC_TX_TL.Set(0)
- i2c.Bus.IC_RX_TL.Set(0)
+ if config.Mode == I2CModeController {
+ i2c.Bus.IC_TX_TL.Set(0)
+ i2c.Bus.IC_RX_TL.Set(0)
+ }
// Always enable the DREQ signalling -- harmless if DMA isn't listening
i2c.Bus.IC_DMA_CR.Set(rp.I2C0_IC_DMA_CR_TDMAE | rp.I2C0_IC_DMA_CR_RDMAE)
@@ -300,12 +332,14 @@ func (i2c *I2C) tx(addr uint8, tx, rx []byte, timeout_us uint64) (err error) {
// IC_ENABLE[0] is set to 0, the TX FIFO is flushed and held
// in reset. There the TX FIFO looks like it has no data within
// it, so this bit is set to 1, provided there is activity in the
- // master or slave state machines. When there is no longer
+ // controller or target state machines. When there is no longer
// any activity, then with ic_en=0, this bit is set to 0.
for !i2c.interrupted(rp.I2C0_IC_RAW_INTR_STAT_TX_EMPTY) {
if ticks() > deadline {
return errI2CWriteTimeout // If there was a timeout, don't attempt to do anything else.
}
+
+ gosched()
}
abortReason = i2c.getAbortReason()
@@ -324,6 +358,8 @@ func (i2c *I2C) tx(addr uint8, tx, rx []byte, timeout_us uint64) (err error) {
if ticks() > deadline {
return errI2CWriteTimeout
}
+
+ gosched()
}
i2c.Bus.IC_CLR_STOP_DET.Get()
}
@@ -334,6 +370,7 @@ func (i2c *I2C) tx(addr uint8, tx, rx []byte, timeout_us uint64) (err error) {
first := rxCtr == 0
last := rxCtr == rxlen-1
for i2c.writeAvailable() == 0 {
+ gosched()
}
i2c.Bus.IC_DATA_CMD.Set(
boolToBit(first && i2c.restartOnNext)<<rp.I2C0_IC_DATA_CMD_RESTART_Pos |
@@ -349,6 +386,8 @@ func (i2c *I2C) tx(addr uint8, tx, rx []byte, timeout_us uint64) (err error) {
if ticks() > deadline {
return errI2CReadTimeout // If there was a timeout, don't attempt to do anything else.
}
+
+ gosched()
}
if abort {
break
@@ -374,6 +413,100 @@ func (i2c *I2C) tx(addr uint8, tx, rx []byte, timeout_us uint64) (err error) {
return err
}
+// listen sets up for async handling of requests on the I2C bus.
+func (i2c *I2C) listen(addr uint8) error {
+ if addr >= 0x80 || isReservedI2CAddr(addr) {
+ return ErrInvalidTgtAddr
+ }
+
+ err := i2c.disable()
+ if err != nil {
+ return err
+ }
+
+ i2c.Bus.IC_SAR.Set(uint32(addr))
+
+ i2c.enable()
+
+ return nil
+}
+
+func (i2c *I2C) WaitForEvent(buf []byte) (evt I2CTargetEvent, count int, err error) {
+ rxPtr := 0
+ for {
+ stat := i2c.Bus.IC_RAW_INTR_STAT.Get()
+
+ if stat&rp.I2C0_IC_INTR_MASK_M_RX_FULL != 0 {
+ b := uint8(i2c.Bus.IC_DATA_CMD.Get())
+ if rxPtr < len(buf) {
+ buf[rxPtr] = b
+ rxPtr++
+ }
+ }
+
+ // Stop
+ if stat&rp.I2C0_IC_INTR_MASK_M_STOP_DET != 0 {
+ if rxPtr > 0 {
+ return I2CReceive, rxPtr, nil
+ }
+
+ i2c.Bus.IC_CLR_STOP_DET.Get() // clear
+ return I2CFinish, 0, nil
+ }
+
+ // Start or restart - ignore start, return on restart
+ if stat&rp.I2C0_IC_INTR_MASK_M_START_DET != 0 {
+ i2c.Bus.IC_CLR_START_DET.Get() // clear restart
+
+ // Restart
+ if rxPtr > 0 {
+ return I2CReceive, rxPtr, nil
+ }
+ }
+
+ // Read request - leave flag set until we start to reply.
+ if stat&rp.I2C0_IC_INTR_MASK_M_RD_REQ != 0 {
+ return I2CRequest, 0, nil
+ }
+
+ gosched()
+ }
+}
+
+func (i2c *I2C) Reply(buf []byte) error {
+ txPtr := 0
+
+ stat := i2c.Bus.IC_RAW_INTR_STAT.Get()
+
+ if stat&rp.I2C0_IC_INTR_MASK_M_RD_REQ == 0 {
+ return ErrI2CWrongMode
+ }
+ i2c.Bus.IC_CLR_RD_REQ.Get() // clear restart
+
+ // Clear any dangling TX abort
+ if stat&rp.I2C0_IC_INTR_MASK_M_TX_ABRT != 0 {
+ i2c.Bus.IC_CLR_TX_ABRT.Get()
+ }
+
+ for txPtr < len(buf) {
+ if stat&rp.I2C0_IC_INTR_MASK_M_TX_EMPTY != 0 {
+ i2c.Bus.IC_DATA_CMD.Set(uint32(buf[txPtr]))
+ txPtr++
+ }
+
+ // This Tx abort is a normal case - we're sending more
+ // data than controller wants to receive
+ if stat&rp.I2C0_IC_INTR_MASK_M_TX_ABRT != 0 {
+ i2c.Bus.IC_CLR_TX_ABRT.Get()
+ return nil
+ }
+
+ gosched()
+ }
+
+ return nil
+}
+
// writeAvailable determines non-blocking write space available
//
//go:inline