/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Writes/Reads a buffer out/in to/from an SPI Slave Device. /// /// Note that the TO/FROM's and IN/OUT's in the above line are there because /// the SPI protocol always reads a byte for every byte sent. If you send a /// byte you get a byte. If you do not sent a byte you will never receive /// a byte since this code operates as an SPI Master. Thus if you only /// wish to receive you must send an equivalent number of bytes. The /// bytes you send are determined by the Slave. Sometimes this is just /// 0x00 and sometimes it represents an address to read - exactly what you, /// send is entirely slave device implementation dependent. /// /// If you only wish to transmit, not receive, just use NULL for your /// rxBuffer. The SPI port will still receive, of course, but you will /// not be bothered by it. /// /// NOTE: the Slave Select is set when you opened the port. The rule is /// one slave to one SPISlaveDeviceHandle. /// /// </summary> /// <param name="ssHandle">The SPI Slave Device handle to write to</param> /// <param name="txByteBuf">The buffer with bytes to write</param> /// <param name="rxByteBuf">The buffer with bytes to receive. Can be NULL</param> /// <param name="numBytes">The number of bytes to send/receive /// <history> /// 21 Dec 14 Cynic - Originally written /// </history> public void SPITransfer(SPISlaveDeviceHandle ssHandle, byte[] txByteBuf, byte[] rxByteBuf, int numBytes) { int spiFileDescriptor = -1; int ioctlRetVal = -1; // sanity check if (ssHandle == null) { throw new Exception("Null Slave Device Handle"); } if (txByteBuf == null) { throw new Exception("Null tx buffer"); } if (numBytes <= 0) { throw new Exception("numBytes <= 0"); } // set up our file descriptor // are we an internal chip select type slave device? if ((ssHandle.SPISlaveDevice == SPISlaveDeviceEnum.SPI_SLAVEDEVICE_CS0) || (ssHandle.SPISlaveDevice == SPISlaveDeviceEnum.SPI_SLAVEDEVICE_CS1)) { // yes we are, just use this descriptor spiFileDescriptor = ssHandle.SpiDevFileDescriptor; } else if (ssHandle.SPISlaveDevice == SPISlaveDeviceEnum.SPI_SLAVEDEVICE_GPIO) { if (ssHandle.GpioSlaveSelect == null) { throw new Exception("No GPIO Slave Select device found."); } // use the descriptor of the first non-GPIO slave select // we find. // get first slave device. We need the file descriptor SPISlaveDeviceHandle firstHandle = GetFirstSlaveDeviceWithFD(); // sanity check if (firstHandle == null) { throw new Exception("At least one non GPIO Slave Device must be enabled."); } // use this descriptor spiFileDescriptor = firstHandle.SpiDevFileDescriptor; } else { // should never happen throw new Exception("unknown slave device type"); } // the data needs to be in unmanaged global memory // so the spidev driver can see it. This allocates // that memory. We MUST release this! IntPtr txBufPtr = Marshal.AllocHGlobal(numBytes + 1); IntPtr rxBufPtr = Marshal.AllocHGlobal(numBytes + 1); try { // copy the data from the tx buffer to our pointer Marshal.Copy(txByteBuf, 0, txBufPtr, numBytes); // create and fill in the contents of our transfer struct spi_ioc_transfer xfer = new spi_ioc_transfer(); xfer.tx_buf = txBufPtr; xfer.rx_buf = rxBufPtr; xfer.len = (UInt32)numBytes; xfer.speed_hz = (UInt16)ssHandle.SpeedInHz; xfer.delay_usecs = ssHandle.DelayUSecs; xfer.bits_per_word = ssHandle.BitsPerWord; xfer.cs_change = ssHandle.CSChange; xfer.pad = 0; if (ssHandle.SPISlaveDevice == SPISlaveDeviceEnum.SPI_SLAVEDEVICE_GPIO) { // lower the slave select ssHandle.GpioSlaveSelect.Write(false); try { // this is an external call to the libc.so.6 library ioctlRetVal = ExternalIoCtl(spiFileDescriptor, SPI_IOC_MESSAGE_1, ref xfer); } finally { // raise the slave select ssHandle.GpioSlaveSelect.Write(false); } } else { // this is an external call to the libc.so.6 library ioctlRetVal = ExternalIoCtl(spiFileDescriptor, SPI_IOC_MESSAGE_1, ref xfer); } // did the call succeed? if (ioctlRetVal < 0) { // it failed throw new Exception("ExternalIoCtl on device " + ssHandle.SPISlaveDevice + " failed. retval=" + ioctlRetVal.ToString()); } // did the caller supply a receive buffer if (rxByteBuf != null) { // yes they did, copy the returned data in Marshal.Copy(rxBufPtr, rxByteBuf, 0, numBytes); } } finally { // Free the unmanaged memory. Marshal.FreeHGlobal(txBufPtr); Marshal.FreeHGlobal(rxBufPtr); } }
static extern int ExternalIoCtl(int fd, uint request, ref spi_ioc_transfer xfer);
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Writes/Reads a buffer out/in to/from an SPI Slave Device. /// /// Note that the TO/FROM's and IN/OUT's in the above line are there because /// the SPI protocol always reads a byte for every byte sent. If you send a /// byte you get a byte. If you do not sent a byte you will never receive /// a byte since this code operates as an SPI Master. Thus if you only /// wish to receive you must send an equivalent number of bytes. The /// bytes you send are determined by the Slave. Sometimes this is just /// 0x00 and sometimes it represents an address to read - exactly what you, /// send is entirely slave device implementation dependent. /// /// If you only wish to transmit, not receive, just use NULL for your /// rxBuffer. The SPI port will still receive, of course, but you will /// not be bothered by it. /// /// NOTE: the Slave Select is set when you opened the port. The rule is /// one slave to one SPISlaveDeviceHandle. /// /// </summary> /// <param name="ssHandle">The SPI Slave Device handle to write to</param> /// <param name="txByteBuf">The buffer with bytes to write</param> /// <param name="rxByteBuf">The buffer with bytes to receive. Can be NULL</param> /// <param name="numBytes">The number of bytes to send/receive /// <history> /// 16 Sep 17 Cai Biesinger - Modified for Scarlet: switching from file descriptor GPIO to IDigitalOut. /// 21 Dec 14 Cynic - Originally written /// </history> public void SPITransfer(IDigitalOut output, byte[] txByteBuf, byte[] rxByteBuf, int numBytes) { int spiFileDescriptor = -1; int ioctlRetVal = -1; // sanity check if (output == null) { throw new Exception("Null IDigitalOut object"); } if (txByteBuf == null) { throw new Exception("Null tx buffer"); } if (numBytes <= 0) { throw new Exception("numBytes <= 0"); } // set up our file descriptor // use the descriptor of the first non-GPIO slave select // we find. // get first slave device. We need the file descriptor SPISlaveDeviceHandle firstHandle = PortDevice; // sanity check if (firstHandle == null) { throw new Exception("At least one non GPIO Slave Device must be enabled."); } // use this descriptor spiFileDescriptor = firstHandle.SpiDevFileDescriptor; // the data needs to be in unmanaged global memory // so the spidev driver can see it. This allocates // that memory. We MUST release this! IntPtr txBufPtr = Marshal.AllocHGlobal(numBytes + 1); IntPtr rxBufPtr = Marshal.AllocHGlobal(numBytes + 1); try { // copy the data from the tx buffer to our pointer Marshal.Copy(txByteBuf, 0, txBufPtr, numBytes); // create and fill in the contents of our transfer struct spi_ioc_transfer xfer = new spi_ioc_transfer(); xfer.tx_buf = txBufPtr; xfer.rx_buf = rxBufPtr; xfer.len = (UInt32)numBytes; xfer.speed_hz = 0; //(UInt16)ssHandle.SpeedInHz; xfer.delay_usecs = 0; // ssHandle.DelayUSecs; xfer.bits_per_word = 0; // ssHandle.BitsPerWord; xfer.cs_change = 0; // ssHandle.CSChange; xfer.pad = 0; // lower the slave select output.SetOutput(false); try { // this is an external call to the libc.so.6 library ioctlRetVal = ExternalIoCtl(spiFileDescriptor, SPI_IOC_MESSAGE_1, ref xfer); } finally { // raise the slave select // CaiB 2017-09-16: Chenged this to true, BBBCSIO never released SS. output.SetOutput(true); } // did the call succeed? if (ioctlRetVal < 0) { // it failed throw new Exception("ExternalIoCtl on device " + output + " failed. retval=" + ioctlRetVal.ToString()); } // did the caller supply a receive buffer if (rxByteBuf != null) { // yes they did, copy the returned data in Marshal.Copy(rxBufPtr, rxByteBuf, 0, numBytes); } } finally { // Free the unmanaged memory. Marshal.FreeHGlobal(txBufPtr); Marshal.FreeHGlobal(rxBufPtr); } }