hybridgroup.gobot/drivers/spi/spi_connection.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
}