namespace Unosquare.RaspberryIO.Gpio { using Native; using Swan; using System; using System.Collections.Generic; using System.Threading.Tasks; /// /// 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 int MinFrequency = 500000; /// /// The maximum frequency of an SPI channel /// public const int MaxFrequency = 32000000; /// /// The default frequency of SPI channels /// This is set to 8 Mhz wich is typical in modern hardware. /// public const int 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, int frequency) { lock (SyncRoot) { Frequency = frequency.Clamp(MinFrequency, MaxFrequency); Channel = (int)channel; FileDescriptor = WiringPi.WiringPiSPISetup((int)channel, Frequency); if (FileDescriptor < 0) { HardwareException.Throw(nameof(SpiChannel), channel.ToString()); } } } /// /// Gets the standard initialization file descriptor. /// anything negative means error. /// /// /// The file descriptor. /// public int FileDescriptor { get; } /// /// Gets the channel. /// public int Channel { get; } /// /// Gets the frequency. /// public int 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 (_syncLock) { var spiBuffer = new byte[buffer.Length]; Array.Copy(buffer, spiBuffer, buffer.Length); var result = WiringPi.WiringPiSPIDataRW(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(() => 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 (_syncLock) { var result = Standard.Write(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(() => { 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, int frequency) { lock (SyncRoot) { if (Buses.ContainsKey(channel)) return Buses[channel]; var newBus = new SpiChannel(channel, frequency); Buses[channel] = newBus; return newBus; } } } }