machine/nrf: SPI master implementation
Signed-off-by: Ron Evans <ron@hybridgroup.com>
Этот коммит содержится в:
родитель
23b283366d
коммит
06ab3a836f
12 изменённых файлов: 312 добавлений и 2 удалений
55
src/examples/mcp3008/mcp3008.go
Обычный файл
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
Обычный файл
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
|
||||||
|
}
|
Загрузка…
Создание таблицы
Сослаться в новой задаче