Browse Source

nrf: new peripheral type for nrf528xx chips

pull/3645/head
Kenneth Bell 2 years ago
committed by Ron Evans
parent
commit
e0385e48d0
  1. 92
      src/machine/machine_nrf.go
  2. 102
      src/machine/machine_nrf528xx.go
  3. 101
      src/machine/machine_nrf5x.go

92
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.

102
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
}

101
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
}
Loading…
Cancel
Save