using Unosquare.RaspberryIO.Native; using Unosquare.Swan; using System; using System.Collections.Generic; using System.Threading.Tasks; namespace Unosquare.RaspberryIO.Gpio { /// /// Provides access to using the SPI buses on the GPIO. /// SPI is a bus that works like a ring shift register /// The number of bytes pushed is equal to the number of bytes received. /// public sealed class SpiChannel { /// /// The minimum frequency of an SPI Channel /// public const Int32 MinFrequency = 500000; /// /// The maximum frequency of an SPI channel /// public const Int32 MaxFrequency = 32000000; /// /// The default frequency of SPI channels /// This is set to 8 Mhz wich is typical in modern hardware. /// public const Int32 DefaultFrequency = 8000000; private static readonly Object SyncRoot = new Object(); private static readonly Dictionary Buses = new Dictionary(); private readonly Object _syncLock = new Object(); /// /// Initializes a new instance of the class. /// /// The channel. /// The frequency. private SpiChannel(SpiChannelNumber channel, Int32 frequency) { lock(SyncRoot) { this.Frequency = frequency.Clamp(MinFrequency, MaxFrequency); this.Channel = (Int32)channel; this.FileDescriptor = WiringPi.WiringPiSPISetup((Int32)channel, this.Frequency); if(this.FileDescriptor < 0) { HardwareException.Throw(nameof(SpiChannel), channel.ToString()); } } } /// /// Gets the standard initialization file descriptor. /// anything negative means error. /// /// /// The file descriptor. /// public Int32 FileDescriptor { get; } /// /// Gets the channel. /// public Int32 Channel { get; } /// /// Gets the frequency. /// public Int32 Frequency { get; } /// /// Sends data and simultaneously receives the data in the return buffer /// /// The buffer. /// The read bytes from the ring-style bus public Byte[] SendReceive(Byte[] buffer) { if(buffer == null || buffer.Length == 0) { return null; } lock(this._syncLock) { Byte[] spiBuffer = new Byte[buffer.Length]; Array.Copy(buffer, spiBuffer, buffer.Length); Int32 result = WiringPi.WiringPiSPIDataRW(this.Channel, spiBuffer, spiBuffer.Length); if(result < 0) { HardwareException.Throw(nameof(SpiChannel), nameof(SendReceive)); } return spiBuffer; } } /// /// Sends data and simultaneously receives the data in the return buffer /// /// The buffer. /// /// The read bytes from the ring-style bus /// public Task SendReceiveAsync(Byte[] buffer) => Task.Run(() => this.SendReceive(buffer)); /// /// Writes the specified buffer the the underlying FileDescriptor. /// Do not use this method if you expect data back. /// This method is efficient if used in a fire-and-forget scenario /// like sending data over to those long RGB LED strips /// /// The buffer. public void Write(Byte[] buffer) { lock(this._syncLock) { Int32 result = Standard.Write(this.FileDescriptor, buffer, buffer.Length); if(result < 0) { HardwareException.Throw(nameof(SpiChannel), nameof(Write)); } } } /// /// Writes the specified buffer the the underlying FileDescriptor. /// Do not use this method if you expect data back. /// This method is efficient if used in a fire-and-forget scenario /// like sending data over to those long RGB LED strips /// /// The buffer. /// The awaitable task public Task WriteAsync(Byte[] buffer) => Task.Run(() => this.Write(buffer)); /// /// Retrieves the spi bus. If the bus channel is not registered it sets it up automatically. /// If it had been previously registered, then the bus is simply returned. /// /// The channel. /// The frequency. /// The usable SPI channel internal static SpiChannel Retrieve(SpiChannelNumber channel, Int32 frequency) { lock(SyncRoot) { if(Buses.ContainsKey(channel)) { return Buses[channel]; } SpiChannel newBus = new SpiChannel(channel, frequency); Buses[channel] = newBus; return newBus; } } } }