/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Enables the SPI slave device which uses a GPIO pin as a slave select. /// /// NOTE on how this works. When using a GPIO pin as a slave select line /// we still need to open a SPIDev device because we need a device to /// send the data to. The way SPIDev works each spidev device is /// fundamentally associated with a particular slave select line and /// this cannot be changed. The GPIO line will be used as a separate /// slave select but the spidev device specific slave select will also /// be asserted whenever the device is writtent to. /// /// In order to use a GPIO as a slave select you must ignore and not /// electrically attach anything to the slave select pin the SPIDEV /// device uses as it will be asserted on each write. /// /// In other words, the SPIDev device is needed to shift the data in /// and out, however you must ignore its internal slave select line /// entirely if you wish to use GPIO based slave selects. Otherwise /// any device attached to it will be receive every write to the /// SPIPort no matter which GPIO slave select is also asserted. /// /// </summary> /// <returns>ssHandle - the handle for the Slave Device or null for fail</returns> /// <param name="spiSlaveDeviceIn">The GPIO of the pin we use as the slave select</param> /// <history> /// 01 Dec 16 Cynic - Originally written /// </history> public SPISlaveDeviceHandle EnableSPIGPIOSlaveDevice(GpioEnum gpioEnum) { // get first slave device. We need to check we have one SPISlaveDeviceHandle ssHandle = GetFirstSlaveDeviceWithFD(); // sanity check if (ssHandle == null) { throw new Exception("At least one non GPIO Slave Device must be enabled first."); } // create a new output port OutputPortMM gpioSlaveSelect = new OutputPortMM(gpioEnum); // set this high by default, most modes have slave selects high and go low to activate gpioSlaveSelect.Write(true); // create a new slave device handle SPISlaveDeviceHandle outHandle = new SPISlaveDeviceHandle(SPISlaveDeviceEnum.SPI_SLAVEDEVICE_GPIO, gpioSlaveSelect); if (outHandle == null) { throw new Exception("Could not create GPIO based slave select device"); } //Console.WriteLine("SPIPort GPIO Slave Device Enabled: "+ gpioEnum.ToString()); // record that we opened this slave device (so we can close it later) openSlaveDevices.Add(outHandle); // return the slave device handle return(outHandle); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Enables the SPI slave device. Will throw exceptions if it cannot open the /// device /// </summary> /// <returns>ssHandle - the handle for the Slave Device or null for fail</returns> /// <param name="spiSlaveDeviceIn">The SPI Slave Device we are configuring</param> /// <history> /// 01 Dec 16 Cynic - Originally written /// </history> public SPISlaveDeviceHandle EnableSPISlaveDevice(SPISlaveDeviceEnum spiSlaveDeviceIn) { string deviceFileName; // set up now deviceFileName = RPIDefinitions.SPIDEV_FILENAME; // set up the spi device number, this is based off the port if (SPIPort == SPIPortEnum.SPIPORT_0) { deviceFileName = deviceFileName.Replace("%device%", "0"); } else { // should never happen throw new Exception("Unknown SPI Port:" + SPIPort.ToString()); } // set up the spi slave number if (spiSlaveDeviceIn == SPISlaveDeviceEnum.SPI_SLAVEDEVICE_CE0) { deviceFileName = deviceFileName.Replace("%slave%", "0"); } else if (spiSlaveDeviceIn == SPISlaveDeviceEnum.SPI_SLAVEDEVICE_CE1) { deviceFileName = deviceFileName.Replace("%slave%", "1"); } else { // should never happen throw new Exception("Unknown SPI Slave Device:" + spiSlaveDeviceIn.ToString()); } // we open the file. We have to have an open file descriptor // note this is an external call. It has to be because the // ioctl needs an open file descriptor it can use int fd = ExternalFileOpen(deviceFileName, O_RDWR | O_NONBLOCK); if (fd <= 0) { throw new Exception("Could not open spidev file:" + deviceFileName); } //Console.WriteLine("SPIPort Slave Device Enabled: "+ deviceFileName); // create a new slave device handle SPISlaveDeviceHandle outHandle = new SPISlaveDeviceHandle(spiSlaveDeviceIn, fd); // record that we opened this slave device (so we can close it later) openSlaveDevices.Add(outHandle); // return the slave device handle return(outHandle); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Disables a SPI slave device. /// </summary> /// <param name="ssHandle">The SPI Slave Device handle to disable</param> /// <history> /// 01 Dec 16 Cynic - Originally written /// </history> private void DisableSPISlaveDevice(SPISlaveDeviceHandle ssHandle) { if (ssHandle == null) { return; } // are we an internal chip select type slave device? if ((ssHandle.SPISlaveDevice == SPISlaveDeviceEnum.SPI_SLAVEDEVICE_CE0) || (ssHandle.SPISlaveDevice == SPISlaveDeviceEnum.SPI_SLAVEDEVICE_CE1)) { // yes we are, close it this way // does it have a valid file descriptor if (ssHandle.SpiDevFileDescriptor < 0) { return; } // do an external close ExternalFileClose(ssHandle.SpiDevFileDescriptor); // mark it as having been closed ssHandle.Reset(); } else if (ssHandle.SPISlaveDevice == SPISlaveDeviceEnum.SPI_SLAVEDEVICE_GPIO) { // yes we are, close it this way // does it have a valid file descriptor if (ssHandle.GpioSlaveSelect == null) { return; } // 06 Dec 16 GPIOs are outputs. Lets leave it low ssHandle.GpioSlaveSelect.Write(false); // close it ssHandle.GpioSlaveSelect.ClosePort(); ssHandle.GpioSlaveSelect.Dispose(); ssHandle.GpioSlaveSelect = null; // mark it as having been closed ssHandle.Reset(); } else { // unknown type of slave device should never happen throw new Exception("Unknown SPI Slave Device:" + ssHandle.SPISlaveDevice.ToString()); } }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Sets the Default SPI speed in Hertz. This value can be overridden on /// a per slave basis by setting the SpeedInHz in the SPISlaveDeviceHandle /// </summary> /// <param name="spiSpeedInHz">The speed in Hertz to set</param> /// <history> /// 01 Dec 16 Cynic - Originally written /// </history> public void SetDefaultSpeedInHz(uint spiSpeedInHz) { // get first slave device. We need the file descriptor SPISlaveDeviceHandle ssHandle = GetFirstSlaveDeviceWithFD(); // sanity check if (ssHandle == null) { throw new Exception("At least one non GPIO Slave Device must be enabled."); } // this is an external call to the libc.so.6 library uint speedInHz = (uint)spiSpeedInHz; int retVal = ExternalIoCtl(ssHandle.SpiDevFileDescriptor, SPI_IOC_WR_MAX_SPEED_HZ, ref speedInHz); // did the call succeed? if (retVal < 0) { // it failed throw new Exception("ExternalIoCtl on device " + ssHandle.SPISlaveDevice + " failed. retval=" + retVal.ToString()); } }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Gets the SPI mode. This is used for all Slave Devices on the port /// It cannot be individually set on a per slave basis. At least one /// non-GPIO slave must be enabled /// </summary> /// <param name="ssHandle">The SPI Slave Device handle</param> /// <returns>the spi mode</returns> /// <history> /// 01 Dec 16 Cynic - Originally written /// </history> public SPIModeEnum GetMode() { // get first slave device. We need the file descriptor SPISlaveDeviceHandle ssHandle = GetFirstSlaveDeviceWithFD(); // sanity check if (ssHandle == null) { throw new Exception("At least one non GPIO Slave Device must be enabled."); } uint mode = 0; // this is an external call to the libc.so.6 library int retVal = ExternalIoCtl(ssHandle.SpiDevFileDescriptor, SPI_IOC_RD_MODE, ref mode); // did the call succeed? if (retVal < 0) { // it failed throw new Exception("ExternalIoCtl on device " + ssHandle.SPISlaveDevice + " failed. retval=" + retVal.ToString()); } return((SPIModeEnum)mode); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <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> /// 01 Dec 16 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_CE0) || (ssHandle.SPISlaveDevice == SPISlaveDeviceEnum.SPI_SLAVEDEVICE_CE1)) { // 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 { // 06 Dec 16 - bug here was (false) // raise the slave select ssHandle.GpioSlaveSelect.Write(true); } } 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); } }