tinygo/src/machine/machine_nrf.go
Ron Evans 4f4d7976c6
Add core support for multiple UARTs (#152)
* 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
2019-01-25 22:09:13 +01:00

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
}