gobot.io/x/gobot/v2@v2.1.0/drivers/spi/spi_connection.go (about)

     1  package spi
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  
     7  	"gobot.io/x/gobot/v2"
     8  )
     9  
    10  const (
    11  	spiDebugByte  = false
    12  	spiDebugBlock = false
    13  )
    14  
    15  // spiConnection is the common implementation of the SPI bus interface.
    16  type spiConnection struct {
    17  	spiSystem gobot.SpiSystemDevicer
    18  	mutex     sync.Mutex
    19  }
    20  
    21  // NewConnection uses the given SPI system device and provides it as gobot.SpiOperations
    22  // and Implements gobot.BusOperations.
    23  func NewConnection(spiSystem gobot.SpiSystemDevicer) *spiConnection {
    24  	return &spiConnection{spiSystem: spiSystem}
    25  }
    26  
    27  // ReadCommandData uses the SPI device TX to send/receive data. Implements gobot.SpiOperations
    28  // On write command, the first byte normally contains the address and mode.
    29  // On read data, the return value is most likely one byte behind the command.
    30  // The length of command and data needs to be the same (except data is nil).
    31  func (c *spiConnection) ReadCommandData(command []byte, data []byte) error {
    32  	c.mutex.Lock()
    33  	defer c.mutex.Unlock()
    34  
    35  	return c.txRxAndCheckReadLength(command, data)
    36  }
    37  
    38  // Close connection to underlying SPI device.
    39  func (c *spiConnection) Close() error {
    40  	c.mutex.Lock()
    41  	defer c.mutex.Unlock()
    42  
    43  	return c.spiSystem.Close()
    44  }
    45  
    46  // ReadByteData reads a byte from the given register of SPI device. Implements gobot.BusOperations.
    47  func (c *spiConnection) ReadByteData(reg uint8) (uint8, error) {
    48  	c.mutex.Lock()
    49  	defer c.mutex.Unlock()
    50  
    51  	buf := []byte{0x0}
    52  	if err := c.readAlignedBlockData(reg, buf); err != nil {
    53  		return 0, err
    54  	}
    55  
    56  	if spiDebugByte {
    57  		fmt.Printf("ReadByteData: register 0x%02X/0x%02X : 0x%02X %dd\n", reg, reg&0x7F>>1, buf[0], buf[0])
    58  	}
    59  	return buf[0], nil
    60  }
    61  
    62  // ReadBlockData fills the given buffer with reads starting from the given register of SPI device.
    63  // Implements gobot.BusOperations.
    64  func (c *spiConnection) ReadBlockData(reg uint8, data []byte) error {
    65  	c.mutex.Lock()
    66  	defer c.mutex.Unlock()
    67  
    68  	if err := c.readAlignedBlockData(reg, data); err != nil {
    69  		return err
    70  	}
    71  
    72  	if spiDebugBlock {
    73  		fmt.Printf("ReadBlockData: register 0x%02X/0x%02X : %v\n", reg, reg&0x7F>>1, data)
    74  	}
    75  	return nil
    76  }
    77  
    78  // WriteByte writes the given byte value to the current register of SPI device. Implements gobot.BusOperations.
    79  func (c *spiConnection) WriteByte(val byte) error {
    80  	c.mutex.Lock()
    81  	defer c.mutex.Unlock()
    82  
    83  	return c.writeBytes([]byte{val})
    84  }
    85  
    86  // WriteByteData writes the given byte value to the given register of SPI device. Implements gobot.BusOperations.
    87  func (c *spiConnection) WriteByteData(reg byte, data byte) error {
    88  	c.mutex.Lock()
    89  	defer c.mutex.Unlock()
    90  
    91  	return c.writeBytes([]byte{reg, data})
    92  }
    93  
    94  // WriteBlockData writes the given data starting from the given register of SPI device. Implements gobot.BusOperations.
    95  func (c *spiConnection) WriteBlockData(reg byte, data []byte) error {
    96  	c.mutex.Lock()
    97  	defer c.mutex.Unlock()
    98  
    99  	buf := make([]byte, len(data)+1)
   100  	copy(buf[1:], data)
   101  	buf[0] = reg
   102  	return c.writeBytes(buf)
   103  }
   104  
   105  // WriteBytes writes the given data starting from the current register of bus device. Implements gobot.BusOperations.
   106  func (c *spiConnection) WriteBytes(data []byte) error {
   107  	c.mutex.Lock()
   108  	defer c.mutex.Unlock()
   109  
   110  	return c.writeBytes(data)
   111  }
   112  
   113  func (c *spiConnection) readAlignedBlockData(reg uint8, data []byte) error {
   114  	// length of TX needs to equal length of RX
   115  	// the read value is one cycle behind the write, so for n bytes to read, we need n+1 bytes (to read and write)
   116  	buflen := len(data) + 1
   117  	writeBuf := make([]byte, buflen)
   118  	readBuf := make([]byte, buflen)
   119  	writeBuf[0] = reg
   120  	if err := c.txRxAndCheckReadLength(writeBuf, readBuf); err != nil {
   121  		return err
   122  	}
   123  	copy(data, readBuf[1:])
   124  	return nil
   125  }
   126  
   127  func (c *spiConnection) writeBytes(data []byte) error {
   128  	return c.txRxAndCheckReadLength(data, nil)
   129  }
   130  
   131  func (c *spiConnection) txRxAndCheckReadLength(tx []byte, rx []byte) error {
   132  	dataLen := len(rx)
   133  	if err := c.spiSystem.TxRx(tx, rx); err != nil {
   134  		return err
   135  	}
   136  	if len(rx) != dataLen {
   137  		return fmt.Errorf("Read length (%d) differ to expected (%d)", len(rx), dataLen)
   138  	}
   139  	return nil
   140  }