141 lines
3.9 KiB
Go
141 lines
3.9 KiB
Go
package spi
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
|
|
"gobot.io/x/gobot/v2"
|
|
)
|
|
|
|
const (
|
|
spiDebugByte = false
|
|
spiDebugBlock = false
|
|
)
|
|
|
|
// spiConnection is the common implementation of the SPI bus interface.
|
|
type spiConnection struct {
|
|
spiSystem gobot.SpiSystemDevicer
|
|
mutex sync.Mutex
|
|
}
|
|
|
|
// NewConnection uses the given SPI system device and provides it as gobot.SpiOperations
|
|
// and Implements gobot.BusOperations.
|
|
func NewConnection(spiSystem gobot.SpiSystemDevicer) *spiConnection {
|
|
return &spiConnection{spiSystem: spiSystem}
|
|
}
|
|
|
|
// ReadCommandData uses the SPI device TX to send/receive data. Implements gobot.SpiOperations
|
|
// On write command, the first byte normally contains the address and mode.
|
|
// On read data, the return value is most likely one byte behind the command.
|
|
// The length of command and data needs to be the same (except data is nil).
|
|
func (c *spiConnection) ReadCommandData(command []byte, data []byte) error {
|
|
c.mutex.Lock()
|
|
defer c.mutex.Unlock()
|
|
|
|
return c.txRxAndCheckReadLength(command, data)
|
|
}
|
|
|
|
// Close connection to underlying SPI device.
|
|
func (c *spiConnection) Close() error {
|
|
c.mutex.Lock()
|
|
defer c.mutex.Unlock()
|
|
|
|
return c.spiSystem.Close()
|
|
}
|
|
|
|
// ReadByteData reads a byte from the given register of SPI device. Implements gobot.BusOperations.
|
|
func (c *spiConnection) ReadByteData(reg uint8) (uint8, error) {
|
|
c.mutex.Lock()
|
|
defer c.mutex.Unlock()
|
|
|
|
buf := []byte{0x0}
|
|
if err := c.readAlignedBlockData(reg, buf); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
if spiDebugByte {
|
|
fmt.Printf("ReadByteData: register 0x%02X/0x%02X : 0x%02X %dd\n", reg, reg&0x7F>>1, buf[0], buf[0])
|
|
}
|
|
return buf[0], nil
|
|
}
|
|
|
|
// ReadBlockData fills the given buffer with reads starting from the given register of SPI device.
|
|
// Implements gobot.BusOperations.
|
|
func (c *spiConnection) ReadBlockData(reg uint8, data []byte) error {
|
|
c.mutex.Lock()
|
|
defer c.mutex.Unlock()
|
|
|
|
if err := c.readAlignedBlockData(reg, data); err != nil {
|
|
return err
|
|
}
|
|
|
|
if spiDebugBlock {
|
|
fmt.Printf("ReadBlockData: register 0x%02X/0x%02X : %v\n", reg, reg&0x7F>>1, data)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// WriteByte writes the given byte value to the current register of SPI device. Implements gobot.BusOperations.
|
|
func (c *spiConnection) WriteByte(val byte) error {
|
|
c.mutex.Lock()
|
|
defer c.mutex.Unlock()
|
|
|
|
return c.writeBytes([]byte{val})
|
|
}
|
|
|
|
// WriteByteData writes the given byte value to the given register of SPI device. Implements gobot.BusOperations.
|
|
func (c *spiConnection) WriteByteData(reg byte, data byte) error {
|
|
c.mutex.Lock()
|
|
defer c.mutex.Unlock()
|
|
|
|
return c.writeBytes([]byte{reg, data})
|
|
}
|
|
|
|
// WriteBlockData writes the given data starting from the given register of SPI device. Implements gobot.BusOperations.
|
|
func (c *spiConnection) WriteBlockData(reg byte, data []byte) error {
|
|
c.mutex.Lock()
|
|
defer c.mutex.Unlock()
|
|
|
|
buf := make([]byte, len(data)+1)
|
|
copy(buf[1:], data)
|
|
buf[0] = reg
|
|
return c.writeBytes(buf)
|
|
}
|
|
|
|
// WriteBytes writes the given data starting from the current register of bus device. Implements gobot.BusOperations.
|
|
func (c *spiConnection) WriteBytes(data []byte) error {
|
|
c.mutex.Lock()
|
|
defer c.mutex.Unlock()
|
|
|
|
return c.writeBytes(data)
|
|
}
|
|
|
|
func (c *spiConnection) readAlignedBlockData(reg uint8, data []byte) error {
|
|
// length of TX needs to equal length of RX
|
|
// the read value is one cycle behind the write, so for n bytes to read, we need n+1 bytes (to read and write)
|
|
buflen := len(data) + 1
|
|
writeBuf := make([]byte, buflen)
|
|
readBuf := make([]byte, buflen)
|
|
writeBuf[0] = reg
|
|
if err := c.txRxAndCheckReadLength(writeBuf, readBuf); err != nil {
|
|
return err
|
|
}
|
|
copy(data, readBuf[1:])
|
|
return nil
|
|
}
|
|
|
|
func (c *spiConnection) writeBytes(data []byte) error {
|
|
return c.txRxAndCheckReadLength(data, nil)
|
|
}
|
|
|
|
func (c *spiConnection) txRxAndCheckReadLength(tx []byte, rx []byte) error {
|
|
dataLen := len(rx)
|
|
if err := c.spiSystem.TxRx(tx, rx); err != nil {
|
|
return err
|
|
}
|
|
if len(rx) != dataLen {
|
|
return fmt.Errorf("Read length (%d) differ to expected (%d)", len(rx), dataLen)
|
|
}
|
|
return nil
|
|
}
|