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
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = 0
|
||||
SPI0_MOSI_PIN = 0
|
||||
SPI0_MISO_PIN = 0
|
||||
)
|
||||
|
||||
// LED matrix pins
|
||||
const (
|
||||
LED_COL_1 = 4
|
||||
|
|
|
@ -23,3 +23,10 @@ const (
|
|||
SDA_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
|
||||
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
|
||||
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
|
||||
const (
|
||||
SDA_PIN = 26
|
||||
SCL_PIN = 27
|
||||
SDA_PIN = 26 // P0.26
|
||||
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
|
||||
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
|
||||
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.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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func InitADC() {
|
||||
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.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
|
||||
}
|
Загрузка…
Создание таблицы
Сослаться в новой задаче