// SPI device services using IO.Objects.SimpleIO // Copyright (C)2017-2023, Philip Munts dba Munts Technologies. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. using static IO.Objects.SimpleIO.mikroBUS.Shield; using System; namespace IO.Objects.SimpleIO.SPI { /// /// Encapsulates Linux SPI slave devices using libsimpleio. /// public class Device: IO.Interfaces.SPI.Device { private readonly int myfd; private readonly int myfdcs; private static int SlaveSelect(string devname, IO.Objects.SimpleIO.Device.Designator? cspin) { // Special processing for the Mikroelektronika BeagleBone click // SHIELD (MIKROE-1596): Socket 1 has CS connected to GPIO44 // instead of SPI1 CS0, so we have to do software slave select. if ((kind == Kinds.BeagleBoneClick2) && (devname.Equals("/dev/spidev1.0"))) { var SSS = new IO.Objects.SimpleIO.GPIO.Pin (IO.Objects.SimpleIO.Platforms.BeagleBone.GPIO44, IO.Interfaces.GPIO.Direction.Output, true); return SSS.fd; } // Special processing for the Mikroelektronika BeagleBone click // SHIELD (MIKROE-1596): Socket 2 has CS connected to GPIO46 // instead of SPI1 CS1, so we have to do software slave select. if ((kind == Kinds.BeagleBoneClick2) && (devname.Equals("/dev/spidev1.1"))) { var SSS = new IO.Objects.SimpleIO.GPIO.Pin (IO.Objects.SimpleIO.Platforms.BeagleBone.GPIO46, IO.Interfaces.GPIO.Direction.Output, true); return SSS.fd; } // Explicit software slave select. if (cspin is IO.Objects.SimpleIO.Device.Designator desg) { var SSS = new IO.Objects.SimpleIO.GPIO.Pin(desg, IO.Interfaces.GPIO.Direction.Output, true); return SSS.fd; } // Implicit hardware slave select return IO.Bindings.libsimpleio.SPI_AUTO_CS; } /// /// Constructor for a single SPI device. /// /// SPI slave device designator. /// SPI clock mode. /// SPI transfer word size. /// SPI transfer speed. /// SPI software slave select GPIO pin designator, /// or null. public Device(IO.Objects.SimpleIO.Device.Designator desg, int mode, int wordsize, int speed, IO.Objects.SimpleIO.Device.Designator? cspin = null) { // Validate the SPI slave designator if ((desg.chip == IO.Objects.SimpleIO.Device.Designator.Unavailable.chip) || (desg.chan == IO.Objects.SimpleIO.Device.Designator.Unavailable.chan)) { throw new Exception("Invalid designator"); } System.String devname = System.String.Format("/dev/spidev{0}.{1}", desg.chip, desg.chan); IO.Bindings.libsimpleio.SPI_open(devname, mode, wordsize, speed, out this.myfd, out int error); if (error != 0) { throw new Exception("SPI_open() failed, " + errno.strerror(error)); } this.myfdcs = SlaveSelect(devname, cspin); } /// /// Read bytes from an SPI slave device. /// /// Response buffer. /// Number of bytes to read. public void Read(byte[] resp, int resplen) { if ((resplen < 0) || (resplen > resp.Length)) { throw new Exception("Invalid response length"); } IO.Bindings.libsimpleio.SPI_transaction(this.myfd, this.myfdcs, null, 0, 0, resp, resp.Length, out int error); if (error != 0) { throw new Exception("SPI_transaction() failed, " + errno.strerror(error)); } } /// /// Write bytes to an SPI slave device. /// /// Command buffer. /// Number of bytes to write. public void Write(byte[] cmd, int cmdlen) { if ((cmdlen < 0) || (cmdlen > cmd.Length)) { throw new Exception("Invalid command length"); } IO.Bindings.libsimpleio.SPI_transaction(this.myfd, this.myfdcs, cmd, cmdlen, 0, null, 0, out int error); if (error != 0) { throw new Exception("SPI_transaction() failed, " + errno.strerror(error)); } } /// /// Write and read bytes to and from an SPI slave device. /// /// Command buffer. /// Number of bytes to write. /// Delay in microseconds between write and read /// operations. /// Response buffer. /// Number of bytes to read. public void Transaction(byte[] cmd, int cmdlen, byte[] resp, int resplen, int delayus = 0) { if ((cmd == null) && (resp == null)) throw new Exception("Command buffer and response buffer are both null"); if ((cmdlen == 0) && (resplen == 0)) throw new Exception("Command length and response length are both zero"); if ((cmd == null) && (cmdlen != 0)) throw new Exception("Command buffer is null but command length is nonzero"); if ((cmd != null) && (cmdlen == 0)) throw new Exception("Command buffer is not null but command length is zero"); if ((resp == null) && (resplen != 0)) throw new Exception("Response buffer is null but response length is nonzero"); if ((resp != null) && (resplen == 0)) throw new Exception("Response buffer is not null but response length is zero"); if (cmd != null) if ((cmdlen < 1) || (cmdlen > 56) || (cmd.Length < cmdlen)) throw new Exception("Invalid command length parameter"); if (resp != null) if ((resplen < 1) || (resplen > 60) || (resp.Length < resplen)) throw new Exception("Invalid response length parameter"); if ((delayus < 0) || (delayus > 65535)) throw new Exception("Invalid delay parameter"); IO.Bindings.libsimpleio.SPI_transaction(this.myfd, this.myfdcs, cmd, cmdlen, delayus, resp, resplen, out int error); if (error != 0) { throw new Exception("SPI_transaction() failed, " + errno.strerror(error)); } } /// /// Read-only property returning the Linux file descriptor for the /// SPI slave device. /// public int fd { get { return this.myfd; } } } }