aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorKenneth Bell <[email protected]>2023-03-25 15:25:50 +0000
committerRon Evans <[email protected]>2023-04-04 09:36:42 +0200
commite0385e48d09907c028f5f5926a2523c62757474a (patch)
tree590f38f302eab4c875d02aa2f4bf894db5468b23 /src
parentfeadb9c85c67e119095a9751255f3b9bce676e39 (diff)
downloadtinygo-e0385e48d09907c028f5f5926a2523c62757474a.tar.gz
tinygo-e0385e48d09907c028f5f5926a2523c62757474a.zip
nrf: new peripheral type for nrf528xx chips
Diffstat (limited to 'src')
-rw-r--r--src/machine/machine_nrf.go92
-rw-r--r--src/machine/machine_nrf528xx.go102
-rw-r--r--src/machine/machine_nrf5x.go101
3 files changed, 205 insertions, 90 deletions
diff --git a/src/machine/machine_nrf.go b/src/machine/machine_nrf.go
index 3b07a897c..e457db9db 100644
--- a/src/machine/machine_nrf.go
+++ b/src/machine/machine_nrf.go
@@ -201,17 +201,6 @@ func (uart *UART) handleInterrupt(interrupt.Interrupt) {
}
}
-// I2C on the NRF.
-type I2C struct {
- Bus nrf.TWI_Type
-}
-
-// There are 2 I2C interfaces on the NRF.
-var (
- I2C0 = (*I2C)(unsafe.Pointer(nrf.TWI0))
- I2C1 = (*I2C)(unsafe.Pointer(nrf.TWI1))
-)
-
// I2CConfig is used to store config info for I2C.
type I2CConfig struct {
Frequency uint32
@@ -222,7 +211,7 @@ type I2CConfig struct {
// Configure is intended to setup the I2C interface.
func (i2c *I2C) Configure(config I2CConfig) error {
- i2c.Bus.ENABLE.Set(nrf.TWI_ENABLE_ENABLE_Disabled)
+ i2c.disable()
// Default I2C bus speed is 100 kHz.
if config.Frequency == 0 {
@@ -257,63 +246,11 @@ func (i2c *I2C) Configure(config I2CConfig) error {
i2c.setPins(config.SCL, config.SDA)
- i2c.Bus.ENABLE.Set(nrf.TWI_ENABLE_ENABLE_Enabled)
+ i2c.enableAsController()
return nil
}
-// 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)
- }
-
- // Stop explicitly when no reads were executed, stoping unconditionally would be a mistake.
- // It may execute after I2C peripheral has already been stopped by the shortcut in the read block,
- // so stop task will trigger first thing in a subsequent transaction, hanging it.
- if len(r) == 0 {
- i2c.signalStop()
- }
-
- return
-}
-
// signalStop sends a stop signal to the I2C peripheral and waits for confirmation.
func (i2c *I2C) signalStop() {
i2c.Bus.TASKS_STOP.Set(1)
@@ -322,31 +259,6 @@ func (i2c *I2C) signalStop() {
i2c.Bus.EVENTS_STOPPED.Set(0)
}
-// writeByte writes a single byte to the I2C bus and waits for confirmation.
-func (i2c *I2C) writeByte(data byte) error {
- 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
- }
- }
- 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) {
- 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
- }
- }
- i2c.Bus.EVENTS_RXDREADY.Set(0)
- return byte(i2c.Bus.RXD.Get()), nil
-}
-
var rngStarted = false
// GetRNG returns 32 bits of non-deterministic random data based on internal thermal noise.
diff --git a/src/machine/machine_nrf528xx.go b/src/machine/machine_nrf528xx.go
new file mode 100644
index 000000000..b3e95a253
--- /dev/null
+++ b/src/machine/machine_nrf528xx.go
@@ -0,0 +1,102 @@
+//go:build nrf52840 || nrf52833
+
+package machine
+
+import (
+ "device/nrf"
+ "unsafe"
+)
+
+// I2C on the NRF528xx.
+type I2C struct {
+ Bus *nrf.TWIM_Type
+}
+
+// There are 2 I2C interfaces on the NRF.
+var (
+ I2C0 = &I2C{Bus: nrf.TWIM0}
+ I2C1 = &I2C{Bus: nrf.TWIM1}
+)
+
+func (i2c *I2C) enableAsController() {
+ i2c.Bus.ENABLE.Set(nrf.TWIM_ENABLE_ENABLE_Enabled)
+}
+
+func (i2c *I2C) disable() {
+ i2c.Bus.ENABLE.Set(0)
+}
+
+// Tx does a single I2C transaction at the specified address (when in controller mode).
+//
+// 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) {
+ i2c.Bus.ADDRESS.Set(uint32(addr))
+
+ i2c.Bus.EVENTS_STOPPED.Set(0)
+ i2c.Bus.EVENTS_ERROR.Set(0)
+ i2c.Bus.EVENTS_RXSTARTED.Set(0)
+ i2c.Bus.EVENTS_TXSTARTED.Set(0)
+ i2c.Bus.EVENTS_LASTRX.Set(0)
+ i2c.Bus.EVENTS_LASTTX.Set(0)
+ i2c.Bus.EVENTS_SUSPENDED.Set(0)
+
+ // Configure for a single shot to perform both write and read (as applicable)
+ if len(w) != 0 {
+ i2c.Bus.TXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&w[0]))))
+ i2c.Bus.TXD.MAXCNT.Set(uint32(len(w)))
+
+ // If no read, immediately signal stop after TX
+ if len(r) == 0 {
+ i2c.Bus.SHORTS.Set(nrf.TWIM_SHORTS_LASTTX_STOP)
+ }
+ }
+ if len(r) != 0 {
+ i2c.Bus.RXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&r[0]))))
+ i2c.Bus.RXD.MAXCNT.Set(uint32(len(r)))
+
+ // Auto-start Rx after Tx and Stop after Rx
+ i2c.Bus.SHORTS.Set(nrf.TWIM_SHORTS_LASTTX_STARTRX | nrf.TWIM_SHORTS_LASTRX_STOP)
+ }
+
+ // Fire the transaction
+ i2c.Bus.TASKS_RESUME.Set(1)
+ if len(w) != 0 {
+ i2c.Bus.TASKS_STARTTX.Set(1)
+ } else if len(r) != 0 {
+ i2c.Bus.TASKS_STARTRX.Set(1)
+ }
+
+ // Wait until transaction stopped to ensure buffers fully processed
+ for i2c.Bus.EVENTS_STOPPED.Get() == 0 {
+ // Allow scheduler to run
+ gosched()
+
+ // Handle errors by ensuring STOP sent on bus
+ if i2c.Bus.EVENTS_ERROR.Get() != 0 {
+ if i2c.Bus.EVENTS_STOPPED.Get() == 0 {
+ // STOP cannot be sent during SUSPEND
+ i2c.Bus.TASKS_RESUME.Set(1)
+ i2c.Bus.TASKS_STOP.Set(1)
+ }
+ err = twiCError(i2c.Bus.ERRORSRC.Get())
+ }
+ }
+
+ return
+}
+
+// twiCError converts an I2C controller error to Go
+func twiCError(val uint32) error {
+ if val == 0 {
+ return nil
+ } else if val&nrf.TWIM_ERRORSRC_OVERRUN_Msk == nrf.TWIM_ERRORSRC_OVERRUN {
+ return errI2CBusError
+ } else if val&nrf.TWIM_ERRORSRC_ANACK_Msk == nrf.TWIM_ERRORSRC_ANACK {
+ return errI2CAckExpected
+ } else if val&nrf.TWIM_ERRORSRC_DNACK_Msk == nrf.TWIM_ERRORSRC_DNACK {
+ return errI2CAckExpected
+ }
+
+ return errI2CBusError
+}
diff --git a/src/machine/machine_nrf5x.go b/src/machine/machine_nrf5x.go
new file mode 100644
index 000000000..dbf2e1ccf
--- /dev/null
+++ b/src/machine/machine_nrf5x.go
@@ -0,0 +1,101 @@
+//go:build nrf51 || nrf52
+
+package machine
+
+import "device/nrf"
+
+// I2C on the NRF51 and NRF52.
+type I2C struct {
+ Bus *nrf.TWI_Type
+}
+
+// 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) 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)
+ }
+
+ // Stop explicitly when no reads were executed, stoping unconditionally would be a mistake.
+ // It may execute after I2C peripheral has already been stopped by the shortcut in the read block,
+ // so stop task will trigger first thing in a subsequent transaction, hanging it.
+ if len(r) == 0 {
+ i2c.signalStop()
+ }
+
+ return
+}
+
+// writeByte writes a single byte to the I2C bus and waits for confirmation.
+func (i2c *I2C) writeByte(data byte) error {
+ 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
+ }
+ }
+ 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) {
+ 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
+ }
+ }
+ i2c.Bus.EVENTS_RXDREADY.Set(0)
+ return byte(i2c.Bus.RXD.Get()), nil
+}