
* machine/uart: add core support for multiple UARTs by allowing for multiple RingBuffers * machine/uart: complete core support for multiple UARTs * machine/uart: no need to store pointer to UART, better to treat like I2C and SPI * machine/uart: increase ring buffer size to 128 bytes * machine/uart: improve godocs comments and use comma-ok idiom for buffer Put/Get methods
335 строки
9,3 КиБ
Go
335 строки
9,3 КиБ
Go
// +build nrf
|
|
|
|
package machine
|
|
|
|
import (
|
|
"device/arm"
|
|
"device/nrf"
|
|
)
|
|
|
|
type GPIOMode uint8
|
|
|
|
const (
|
|
GPIO_INPUT = (nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) | (nrf.GPIO_PIN_CNF_INPUT_Connect << nrf.GPIO_PIN_CNF_INPUT_Pos)
|
|
GPIO_INPUT_PULLUP = GPIO_INPUT | (nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos)
|
|
GPIO_INPUT_PULLDOWN = GPIO_INPUT | (nrf.GPIO_PIN_CNF_PULL_Pulldown << nrf.GPIO_PIN_CNF_PULL_Pos)
|
|
GPIO_OUTPUT = (nrf.GPIO_PIN_CNF_DIR_Output << nrf.GPIO_PIN_CNF_DIR_Pos) | (nrf.GPIO_PIN_CNF_INPUT_Disconnect << nrf.GPIO_PIN_CNF_INPUT_Pos)
|
|
)
|
|
|
|
// Configure this pin with the given configuration.
|
|
func (p GPIO) Configure(config GPIOConfig) {
|
|
cfg := config.Mode | nrf.GPIO_PIN_CNF_DRIVE_S0S1 | nrf.GPIO_PIN_CNF_SENSE_Disabled
|
|
port, pin := p.getPortPin()
|
|
port.PIN_CNF[pin] = nrf.RegValue(cfg)
|
|
}
|
|
|
|
// Set the pin to high or low.
|
|
// Warning: only use this on an output pin!
|
|
func (p GPIO) Set(high bool) {
|
|
port, pin := p.getPortPin()
|
|
if high {
|
|
port.OUTSET = 1 << pin
|
|
} else {
|
|
port.OUTCLR = 1 << pin
|
|
}
|
|
}
|
|
|
|
// Return the register and mask to enable a given GPIO pin. This can be used to
|
|
// implement bit-banged drivers.
|
|
func (p GPIO) PortMaskSet() (*uint32, uint32) {
|
|
port, pin := p.getPortPin()
|
|
return (*uint32)(&port.OUTSET), 1 << pin
|
|
}
|
|
|
|
// Return the register and mask to disable a given port. This can be used to
|
|
// implement bit-banged drivers.
|
|
func (p GPIO) PortMaskClear() (*uint32, uint32) {
|
|
port, pin := p.getPortPin()
|
|
return (*uint32)(&port.OUTCLR), 1 << pin
|
|
}
|
|
|
|
// Get returns the current value of a GPIO pin.
|
|
func (p GPIO) Get() bool {
|
|
port, pin := p.getPortPin()
|
|
return (port.IN>>pin)&1 != 0
|
|
}
|
|
|
|
// UART on the NRF.
|
|
type UART struct {
|
|
Buffer *RingBuffer
|
|
}
|
|
|
|
// UART
|
|
var (
|
|
// UART0 is the hardware serial port on the NRF.
|
|
UART0 = UART{Buffer: NewRingBuffer()}
|
|
)
|
|
|
|
// Configure the UART.
|
|
func (uart UART) Configure(config UARTConfig) {
|
|
// Default baud rate to 115200.
|
|
if config.BaudRate == 0 {
|
|
config.BaudRate = 115200
|
|
}
|
|
|
|
uart.SetBaudRate(config.BaudRate)
|
|
|
|
// Set TX and RX pins from board.
|
|
uart.setPins(UART_TX_PIN, UART_RX_PIN)
|
|
|
|
nrf.UART0.ENABLE = nrf.UART_ENABLE_ENABLE_Enabled
|
|
nrf.UART0.TASKS_STARTTX = 1
|
|
nrf.UART0.TASKS_STARTRX = 1
|
|
nrf.UART0.INTENSET = nrf.UART_INTENSET_RXDRDY_Msk
|
|
|
|
// Enable RX IRQ.
|
|
arm.SetPriority(nrf.IRQ_UART0, 0xc0) // low priority
|
|
arm.EnableIRQ(nrf.IRQ_UART0)
|
|
}
|
|
|
|
// SetBaudRate sets the communication speed for the UART.
|
|
func (uart UART) SetBaudRate(br uint32) {
|
|
// Magic: calculate 'baudrate' register from the input number.
|
|
// Every value listed in the datasheet will be converted to the
|
|
// correct register value, except for 192600. I suspect the value
|
|
// listed in the nrf52 datasheet (0x0EBED000) is incorrectly rounded
|
|
// and should be 0x0EBEE000, as the nrf51 datasheet lists the
|
|
// nonrounded value 0x0EBEDFA4.
|
|
// Some background:
|
|
// https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values/2046#2046
|
|
rate := uint32((uint64(br/400)*uint64(400*0xffffffff/16000000) + 0x800) & 0xffffff000)
|
|
|
|
nrf.UART0.BAUDRATE = nrf.RegValue(rate)
|
|
}
|
|
|
|
// WriteByte writes a byte of data to the UART.
|
|
func (uart UART) WriteByte(c byte) error {
|
|
nrf.UART0.EVENTS_TXDRDY = 0
|
|
nrf.UART0.TXD = nrf.RegValue(c)
|
|
for nrf.UART0.EVENTS_TXDRDY == 0 {
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (uart UART) handleInterrupt() {
|
|
if nrf.UART0.EVENTS_RXDRDY != 0 {
|
|
uart.Receive(byte(nrf.UART0.RXD))
|
|
nrf.UART0.EVENTS_RXDRDY = 0x0
|
|
}
|
|
}
|
|
|
|
// I2C on the NRF.
|
|
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}
|
|
)
|
|
|
|
// I2CConfig is used to store config info for I2C.
|
|
type I2CConfig struct {
|
|
Frequency uint32
|
|
SCL uint8
|
|
SDA uint8
|
|
}
|
|
|
|
// Configure is intended to setup the I2C interface.
|
|
func (i2c I2C) Configure(config I2CConfig) {
|
|
// Default I2C bus speed is 100 kHz.
|
|
if config.Frequency == 0 {
|
|
config.Frequency = TWI_FREQ_100KHZ
|
|
}
|
|
// Default I2C pins if not set.
|
|
if config.SDA == 0 && config.SCL == 0 {
|
|
config.SDA = SDA_PIN
|
|
config.SCL = SCL_PIN
|
|
}
|
|
|
|
// do config
|
|
sclPort, sclPin := GPIO{config.SCL}.getPortPin()
|
|
sclPort.PIN_CNF[sclPin] = (nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) |
|
|
(nrf.GPIO_PIN_CNF_INPUT_Connect << nrf.GPIO_PIN_CNF_INPUT_Pos) |
|
|
(nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos) |
|
|
(nrf.GPIO_PIN_CNF_DRIVE_S0D1 << nrf.GPIO_PIN_CNF_DRIVE_Pos) |
|
|
(nrf.GPIO_PIN_CNF_SENSE_Disabled << nrf.GPIO_PIN_CNF_SENSE_Pos)
|
|
|
|
sdaPort, sdaPin := GPIO{config.SDA}.getPortPin()
|
|
sdaPort.PIN_CNF[sdaPin] = (nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) |
|
|
(nrf.GPIO_PIN_CNF_INPUT_Connect << nrf.GPIO_PIN_CNF_INPUT_Pos) |
|
|
(nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos) |
|
|
(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 == TWI_FREQ_400KHZ {
|
|
i2c.Bus.FREQUENCY = nrf.TWI_FREQUENCY_FREQUENCY_K400
|
|
} else {
|
|
i2c.Bus.FREQUENCY = nrf.TWI_FREQUENCY_FREQUENCY_K100
|
|
}
|
|
|
|
i2c.Bus.ENABLE = nrf.TWI_ENABLE_ENABLE_Enabled
|
|
i2c.setPins(config.SCL, config.SDA)
|
|
}
|
|
|
|
// 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) error {
|
|
i2c.Bus.ADDRESS = nrf.RegValue(addr)
|
|
if len(w) != 0 {
|
|
i2c.Bus.TASKS_STARTTX = 1 // start transmission for writing
|
|
for _, b := range w {
|
|
i2c.writeByte(b)
|
|
}
|
|
}
|
|
if len(r) != 0 {
|
|
i2c.Bus.TASKS_STARTRX = 1 // re-start transmission for reading
|
|
for i := range r { // read each char
|
|
if i+1 == len(r) {
|
|
// The 'stop' signal must be sent before reading back the last
|
|
// byte, so that it will be sent by the I2C peripheral right
|
|
// after the last byte has been read.
|
|
r[i] = i2c.readLastByte()
|
|
} else {
|
|
r[i] = i2c.readByte()
|
|
}
|
|
}
|
|
} else {
|
|
// Nothing to read back. Stop the transmission.
|
|
i2c.signalStop()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// signalStop sends a stop signal when writing or tells the I2C peripheral that
|
|
// it must generate a stop condition after the next character is retrieved when
|
|
// reading.
|
|
func (i2c I2C) signalStop() {
|
|
i2c.Bus.TASKS_STOP = 1
|
|
for i2c.Bus.EVENTS_STOPPED == 0 {
|
|
}
|
|
i2c.Bus.EVENTS_STOPPED = 0
|
|
}
|
|
|
|
// writeByte writes a single byte to the I2C bus.
|
|
func (i2c I2C) writeByte(data byte) {
|
|
i2c.Bus.TXD = nrf.RegValue(data)
|
|
for i2c.Bus.EVENTS_TXDSENT == 0 {
|
|
}
|
|
i2c.Bus.EVENTS_TXDSENT = 0
|
|
}
|
|
|
|
// readByte reads a single byte from the I2C bus.
|
|
func (i2c I2C) readByte() byte {
|
|
for i2c.Bus.EVENTS_RXDREADY == 0 {
|
|
}
|
|
i2c.Bus.EVENTS_RXDREADY = 0
|
|
return byte(i2c.Bus.RXD)
|
|
}
|
|
|
|
// readLastByte reads a single byte from the I2C bus, sending a stop signal
|
|
// after it has been read.
|
|
func (i2c I2C) readLastByte() byte {
|
|
for i2c.Bus.EVENTS_RXDREADY == 0 {
|
|
}
|
|
i2c.Bus.EVENTS_RXDREADY = 0
|
|
i2c.signalStop() // signal 'stop' now, so it is sent when reading RXD
|
|
return byte(i2c.Bus.RXD)
|
|
}
|
|
|
|
// SPI on the NRF.
|
|
type SPI struct {
|
|
Bus *nrf.SPI_Type
|
|
}
|
|
|
|
// There are 2 SPI interfaces on the NRF5x.
|
|
var (
|
|
SPI0 = SPI{Bus: nrf.SPI0}
|
|
SPI1 = SPI{Bus: nrf.SPI1}
|
|
)
|
|
|
|
// SPIConfig is used to store config info for SPI.
|
|
type SPIConfig struct {
|
|
Frequency uint32
|
|
SCK uint8
|
|
MOSI uint8
|
|
MISO uint8
|
|
LSBFirst bool
|
|
Mode uint8
|
|
}
|
|
|
|
// Configure is intended to setup the SPI interface.
|
|
func (spi SPI) Configure(config SPIConfig) {
|
|
// Disable bus to configure it
|
|
spi.Bus.ENABLE = nrf.SPI_ENABLE_ENABLE_Disabled
|
|
|
|
// set frequency
|
|
var freq uint32
|
|
|
|
switch config.Frequency {
|
|
case 125000:
|
|
freq = nrf.SPI_FREQUENCY_FREQUENCY_K125
|
|
case 250000:
|
|
freq = nrf.SPI_FREQUENCY_FREQUENCY_K250
|
|
case 500000:
|
|
freq = nrf.SPI_FREQUENCY_FREQUENCY_K500
|
|
case 1000000:
|
|
freq = nrf.SPI_FREQUENCY_FREQUENCY_M1
|
|
case 2000000:
|
|
freq = nrf.SPI_FREQUENCY_FREQUENCY_M2
|
|
case 4000000:
|
|
freq = nrf.SPI_FREQUENCY_FREQUENCY_M4
|
|
case 8000000:
|
|
freq = nrf.SPI_FREQUENCY_FREQUENCY_M8
|
|
default:
|
|
freq = nrf.SPI_FREQUENCY_FREQUENCY_K500
|
|
}
|
|
spi.Bus.FREQUENCY = nrf.RegValue(freq)
|
|
|
|
var conf uint32
|
|
|
|
// set bit transfer order
|
|
if config.LSBFirst {
|
|
conf = (nrf.SPI_CONFIG_ORDER_LsbFirst << nrf.SPI_CONFIG_ORDER_Pos)
|
|
}
|
|
|
|
// set mode
|
|
switch config.Mode {
|
|
case 0:
|
|
conf &^= (nrf.SPI_CONFIG_CPOL_ActiveHigh << nrf.SPI_CONFIG_CPOL_Pos)
|
|
conf &^= (nrf.SPI_CONFIG_CPHA_Leading << nrf.SPI_CONFIG_CPHA_Pos)
|
|
case 1:
|
|
conf &^= (nrf.SPI_CONFIG_CPOL_ActiveHigh << nrf.SPI_CONFIG_CPOL_Pos)
|
|
conf |= (nrf.SPI_CONFIG_CPHA_Trailing << nrf.SPI_CONFIG_CPHA_Pos)
|
|
case 2:
|
|
conf |= (nrf.SPI_CONFIG_CPOL_ActiveLow << nrf.SPI_CONFIG_CPOL_Pos)
|
|
conf &^= (nrf.SPI_CONFIG_CPHA_Leading << nrf.SPI_CONFIG_CPHA_Pos)
|
|
case 3:
|
|
conf |= (nrf.SPI_CONFIG_CPOL_ActiveLow << nrf.SPI_CONFIG_CPOL_Pos)
|
|
conf |= (nrf.SPI_CONFIG_CPHA_Trailing << nrf.SPI_CONFIG_CPHA_Pos)
|
|
default: // to mode
|
|
conf &^= (nrf.SPI_CONFIG_CPOL_ActiveHigh << nrf.SPI_CONFIG_CPOL_Pos)
|
|
conf &^= (nrf.SPI_CONFIG_CPHA_Leading << nrf.SPI_CONFIG_CPHA_Pos)
|
|
}
|
|
spi.Bus.CONFIG = nrf.RegValue(conf)
|
|
|
|
// set pins
|
|
spi.setPins(config.SCK, config.MOSI, config.MISO)
|
|
|
|
// Re-enable bus now that it is configured.
|
|
spi.Bus.ENABLE = nrf.SPI_ENABLE_ENABLE_Enabled
|
|
}
|
|
|
|
// Transfer writes/reads a single byte using the SPI interface.
|
|
func (spi SPI) Transfer(w byte) (byte, error) {
|
|
spi.Bus.TXD = nrf.RegValue(w)
|
|
for spi.Bus.EVENTS_READY == 0 {
|
|
}
|
|
r := spi.Bus.RXD
|
|
spi.Bus.EVENTS_READY = 0
|
|
|
|
// TODO: handle SPI errors
|
|
return byte(r), nil
|
|
}
|