machine/nrf: SPI master implementation

Signed-off-by: Ron Evans <ron@hybridgroup.com>
Этот коммит содержится в:
Ron Evans 2018-11-30 21:34:28 +01:00 коммит произвёл Ayke van Laethem
родитель 23b283366d
коммит 06ab3a836f
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E97FF5335DFDFDED
12 изменённых файлов: 312 добавлений и 2 удалений

55
src/examples/mcp3008/mcp3008.go Обычный файл
Просмотреть файл

@ -0,0 +1,55 @@
// Connects to an MCP3008 ADC via SPI.
// Datasheet: https://www.microchip.com/wwwproducts/en/en010530
package main
import (
"errors"
"machine"
"time"
)
// CS_PIN is the pin used for Chip Select (CS). Change to whatever is in use on your board.
const CS_PIN = 3
var (
tx []byte
rx []byte
val, result uint16
cs machine.GPIO
)
func main() {
cs = machine.GPIO{CS_PIN}
cs.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
machine.SPI0.Configure(machine.SPIConfig{
Frequency: 4000000,
Mode: 3})
tx = make([]byte, 3)
rx = make([]byte, 3)
for {
val, _ = Read(0)
println(val)
time.Sleep(50 * time.Millisecond)
}
}
// Read analog data from channel
func Read(channel int) (uint16, error) {
if channel < 0 || channel > 7 {
return 0, errors.New("Invalid channel for read")
}
tx[0] = 0x01
tx[1] = byte(8+channel) << 4
tx[2] = 0x00
cs.Low()
machine.SPI0.Tx(tx, rx)
result = uint16((rx[1]&0x3))<<8 + uint16(rx[2])
cs.High()
return result, nil
}

Просмотреть файл

@ -36,6 +36,13 @@ const (
SCL_PIN = 0 // P19 on the board SCL_PIN = 0 // P19 on the board
) )
// SPI pins
const (
SPI0_SCK_PIN = 0
SPI0_MOSI_PIN = 0
SPI0_MISO_PIN = 0
)
// LED matrix pins // LED matrix pins
const ( const (
LED_COL_1 = 4 LED_COL_1 = 4

Просмотреть файл

@ -23,3 +23,10 @@ const (
SDA_PIN = 0xff SDA_PIN = 0xff
SCL_PIN = 0xff SCL_PIN = 0xff
) )
// SPI pins (unused)
const (
SPI0_SCK_PIN = 0
SPI0_MOSI_PIN = 0
SPI0_MISO_PIN = 0
)

Просмотреть файл

@ -30,3 +30,10 @@ const (
SDA_PIN = 0xff SDA_PIN = 0xff
SCL_PIN = 0xff SCL_PIN = 0xff
) )
// SPI pins (unused)
const (
SPI0_SCK_PIN = 0
SPI0_MOSI_PIN = 0
SPI0_MISO_PIN = 0
)

Просмотреть файл

@ -44,3 +44,10 @@ const (
SDA_PIN = 26 SDA_PIN = 26
SCL_PIN = 27 SCL_PIN = 27
) )
// SPI pins
const (
SPI0_SCK_PIN = 25
SPI0_MOSI_PIN = 23
SPI0_MISO_PIN = 24
)

Просмотреть файл

@ -30,6 +30,13 @@ const (
// I2C pins // I2C pins
const ( const (
SDA_PIN = 26 SDA_PIN = 26 // P0.26
SCL_PIN = 27 SCL_PIN = 27 // P0.27
)
// SPI pins
const (
SPI0_SCK_PIN = 47 // P1.15
SPI0_MOSI_PIN = 45 // P1.13
SPI0_MISO_PIN = 46 // P1.14
) )

Просмотреть файл

@ -33,3 +33,10 @@ const (
SDA_PIN = 26 SDA_PIN = 26
SCL_PIN = 27 SCL_PIN = 27
) )
// SPI pins
const (
SPI0_SCK_PIN = 47
SPI0_MOSI_PIN = 45
SPI0_MISO_PIN = 46
)

Просмотреть файл

@ -232,3 +232,98 @@ func (i2c I2C) readLastByte() byte {
i2c.signalStop() // signal 'stop' now, so it is sent when reading RXD i2c.signalStop() // signal 'stop' now, so it is sent when reading RXD
return byte(i2c.Bus.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
}

Просмотреть файл

@ -25,3 +25,19 @@ func (i2c I2C) setPins(scl, sda uint8) {
i2c.Bus.PSELSCL = nrf.RegValue(scl) i2c.Bus.PSELSCL = nrf.RegValue(scl)
i2c.Bus.PSELSDA = nrf.RegValue(sda) i2c.Bus.PSELSDA = nrf.RegValue(sda)
} }
// SPI
func (spi SPI) setPins(sck, mosi, miso uint8) {
if sck == 0 {
sck = SPI0_SCK_PIN
}
if mosi == 0 {
mosi = SPI0_MOSI_PIN
}
if miso == 0 {
miso = SPI0_MISO_PIN
}
spi.Bus.PSELSCK = nrf.RegValue(sck)
spi.Bus.PSELMOSI = nrf.RegValue(mosi)
spi.Bus.PSELMISO = nrf.RegValue(miso)
}

Просмотреть файл

@ -27,6 +27,22 @@ func (i2c I2C) setPins(scl, sda uint8) {
i2c.Bus.PSELSDA = nrf.RegValue(sda) i2c.Bus.PSELSDA = nrf.RegValue(sda)
} }
// SPI
func (spi SPI) setPins(sck, mosi, miso uint8) {
if sck == 0 {
sck = SPI0_SCK_PIN
}
if mosi == 0 {
mosi = SPI0_MOSI_PIN
}
if miso == 0 {
miso = SPI0_MISO_PIN
}
spi.Bus.PSEL.SCK = nrf.RegValue(sck)
spi.Bus.PSEL.MOSI = nrf.RegValue(mosi)
spi.Bus.PSEL.MISO = nrf.RegValue(miso)
}
// InitADC initializes the registers needed for ADC. // InitADC initializes the registers needed for ADC.
func InitADC() { func InitADC() {
return // no specific setup on nrf52 machine. return // no specific setup on nrf52 machine.

Просмотреть файл

@ -29,3 +29,19 @@ func (i2c I2C) setPins(scl, sda uint8) {
i2c.Bus.PSEL.SCL = nrf.RegValue(scl) i2c.Bus.PSEL.SCL = nrf.RegValue(scl)
i2c.Bus.PSEL.SDA = nrf.RegValue(sda) i2c.Bus.PSEL.SDA = nrf.RegValue(sda)
} }
// SPI
func (spi SPI) setPins(sck, mosi, miso uint8) {
if sck == 0 {
sck = SPI0_SCK_PIN
}
if mosi == 0 {
mosi = SPI0_MOSI_PIN
}
if miso == 0 {
miso = SPI0_MISO_PIN
}
spi.Bus.PSEL.SCK = nrf.RegValue(sck)
spi.Bus.PSEL.MOSI = nrf.RegValue(mosi)
spi.Bus.PSEL.MISO = nrf.RegValue(miso)
}

70
src/machine/spi.go Обычный файл
Просмотреть файл

@ -0,0 +1,70 @@
// +build nrf
package machine
import "errors"
var (
ErrTxSlicesRequired = errors.New("SPI Tx requires a write or read slice, or both")
ErrTxInvalidSliceSize = errors.New("SPI write and read slices must be same size")
)
// 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 {
if w == nil && r == nil {
return ErrTxSlicesRequired
}
var err error
switch {
case w == nil:
// 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 r == nil:
// write only
for _, b := range w {
_, err = spi.Transfer(b)
if err != nil {
return err
}
}
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
}