diff options
author | Kenneth Bell <[email protected]> | 2023-03-25 15:25:50 +0000 |
---|---|---|
committer | Ron Evans <[email protected]> | 2023-04-04 09:36:42 +0200 |
commit | e0385e48d09907c028f5f5926a2523c62757474a (patch) | |
tree | 590f38f302eab4c875d02aa2f4bf894db5468b23 /src | |
parent | feadb9c85c67e119095a9751255f3b9bce676e39 (diff) | |
download | tinygo-e0385e48d09907c028f5f5926a2523c62757474a.tar.gz tinygo-e0385e48d09907c028f5f5926a2523c62757474a.zip |
nrf: new peripheral type for nrf528xx chips
Diffstat (limited to 'src')
-rw-r--r-- | src/machine/machine_nrf.go | 92 | ||||
-rw-r--r-- | src/machine/machine_nrf528xx.go | 102 | ||||
-rw-r--r-- | src/machine/machine_nrf5x.go | 101 |
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 +} |