mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-06 20:13:40 +00:00
90b42799a2
This makes it possible to assign I2C objects (machine.I2C0, machine.I2C1, etc.) without needing to take a pointer. This is important especially in the future when I2C may be driven using DMA and the machine.I2C type needs to store some state.
198 lines
4.8 KiB
Go
198 lines
4.8 KiB
Go
// +build nrf51
|
|
|
|
package machine
|
|
|
|
import (
|
|
"device/nrf"
|
|
)
|
|
|
|
var (
|
|
UART0 = NRF_UART0
|
|
)
|
|
|
|
func CPUFrequency() uint32 {
|
|
return 16000000
|
|
}
|
|
|
|
// Get peripheral and pin number for this GPIO pin.
|
|
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
|
return nrf.GPIO, uint32(p)
|
|
}
|
|
|
|
func (uart UART) setPins(tx, rx Pin) {
|
|
nrf.UART0.PSELTXD.Set(uint32(tx))
|
|
nrf.UART0.PSELRXD.Set(uint32(rx))
|
|
}
|
|
|
|
func (i2c *I2C) setPins(scl, sda Pin) {
|
|
i2c.Bus.PSELSCL.Set(uint32(scl))
|
|
i2c.Bus.PSELSDA.Set(uint32(sda))
|
|
}
|
|
|
|
// SPI on the NRF.
|
|
type SPI struct {
|
|
Bus *nrf.SPI_Type
|
|
}
|
|
|
|
// There are 2 SPI interfaces on the NRF51.
|
|
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 Pin
|
|
SDO Pin
|
|
SDI Pin
|
|
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.Set(nrf.SPI_ENABLE_ENABLE_Disabled)
|
|
|
|
// set frequency
|
|
var freq uint32
|
|
|
|
if config.Frequency == 0 {
|
|
config.Frequency = 4000000 // 4MHz
|
|
}
|
|
|
|
switch {
|
|
case config.Frequency >= 8000000:
|
|
freq = nrf.SPI_FREQUENCY_FREQUENCY_M8
|
|
case config.Frequency >= 4000000:
|
|
freq = nrf.SPI_FREQUENCY_FREQUENCY_M4
|
|
case config.Frequency >= 2000000:
|
|
freq = nrf.SPI_FREQUENCY_FREQUENCY_M2
|
|
case config.Frequency >= 1000000:
|
|
freq = nrf.SPI_FREQUENCY_FREQUENCY_M1
|
|
case config.Frequency >= 500000:
|
|
freq = nrf.SPI_FREQUENCY_FREQUENCY_K500
|
|
case config.Frequency >= 250000:
|
|
freq = nrf.SPI_FREQUENCY_FREQUENCY_K250
|
|
default: // below 250kHz, default to the lowest speed available
|
|
freq = nrf.SPI_FREQUENCY_FREQUENCY_K125
|
|
}
|
|
spi.Bus.FREQUENCY.Set(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.Set(conf)
|
|
|
|
// set pins
|
|
if config.SCK == 0 && config.SDO == 0 && config.SDI == 0 {
|
|
config.SCK = SPI0_SCK_PIN
|
|
config.SDO = SPI0_SDO_PIN
|
|
config.SDI = SPI0_SDI_PIN
|
|
}
|
|
spi.Bus.PSELSCK.Set(uint32(config.SCK))
|
|
spi.Bus.PSELMOSI.Set(uint32(config.SDO))
|
|
spi.Bus.PSELMISO.Set(uint32(config.SDI))
|
|
|
|
// Re-enable bus now that it is configured.
|
|
spi.Bus.ENABLE.Set(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.Set(uint32(w))
|
|
for spi.Bus.EVENTS_READY.Get() == 0 {
|
|
}
|
|
r := spi.Bus.RXD.Get()
|
|
spi.Bus.EVENTS_READY.Set(0)
|
|
|
|
// TODO: handle SPI errors
|
|
return byte(r), nil
|
|
}
|
|
|
|
// Tx handles read/write operation for SPI interface. Since SPI is a syncronous write/read
|
|
// interface, there must always be the same number of bytes written as bytes read.
|
|
// The Tx method knows about this, and offers a few different ways of calling it.
|
|
//
|
|
// This form sends the bytes in tx buffer, putting the resulting bytes read into the rx buffer.
|
|
// Note that the tx and rx buffers must be the same size:
|
|
//
|
|
// spi.Tx(tx, rx)
|
|
//
|
|
// This form sends the tx buffer, ignoring the result. Useful for sending "commands" that return zeros
|
|
// until all the bytes in the command packet have been received:
|
|
//
|
|
// spi.Tx(tx, nil)
|
|
//
|
|
// This form sends zeros, putting the result into the rx buffer. Good for reading a "result packet":
|
|
//
|
|
// spi.Tx(nil, rx)
|
|
//
|
|
func (spi SPI) Tx(w, r []byte) error {
|
|
var err error
|
|
|
|
switch {
|
|
case len(w) == 0:
|
|
// read only, so write zero and read a result.
|
|
for i := range r {
|
|
r[i], err = spi.Transfer(0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
case len(r) == 0:
|
|
// write only
|
|
spi.Bus.TXD.Set(uint32(w[0]))
|
|
w = w[1:]
|
|
for _, b := range w {
|
|
spi.Bus.TXD.Set(uint32(b))
|
|
for spi.Bus.EVENTS_READY.Get() == 0 {
|
|
}
|
|
spi.Bus.EVENTS_READY.Set(0)
|
|
_ = spi.Bus.RXD.Get()
|
|
}
|
|
for spi.Bus.EVENTS_READY.Get() == 0 {
|
|
}
|
|
spi.Bus.EVENTS_READY.Set(0)
|
|
_ = spi.Bus.RXD.Get()
|
|
|
|
default:
|
|
// write/read
|
|
if len(w) != len(r) {
|
|
return ErrTxInvalidSliceSize
|
|
}
|
|
|
|
for i, b := range w {
|
|
r[i], err = spi.Transfer(b)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|