namespace Unosquare.WiringPi { using Native; using RaspberryIO.Abstractions; using RaspberryIO.Abstractions.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 : ISpiChannel { /// /// The minimum frequency of a SPI Channel. /// public const int MinFrequency = 500000; /// /// The maximum frequency of a SPI channel. /// public const int MaxFrequency = 32000000; 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()); } } } /// public int FileDescriptor { get; } /// public int Channel { get; } /// public int Frequency { get; } /// 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)); /// public void Write(byte[] buffer) { lock (_syncLock) { var result = SysCall.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 ISpiChannel 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; } } } }