using System; using System.Collections.Generic; using System.Threading.Tasks; using Swan; using Unosquare.RaspberryIO.Abstractions; using Unosquare.RaspberryIO.Abstractions.Native; namespace Unosquare.WiringPi { /// /// 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 Int32 MinFrequency = 500000; /// /// The maximum frequency of a SPI channel. /// public const Int32 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, Int32 frequency) { lock(SyncRoot) { this.Frequency = frequency.Clamp(MinFrequency, MaxFrequency); this.Channel = (Int32)channel; this.FileDescriptor = Native.WiringPi.WiringPiSPISetup((Int32)channel, this.Frequency); if(this.FileDescriptor < 0) { HardwareException.Throw(nameof(SpiChannel), channel.ToString()); } } } /// public Int32 FileDescriptor { get; } /// public Int32 Channel { get; } /// public Int32 Frequency { get; } /// 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 = Native.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)); /// public void Write(Byte[] buffer) { lock(this._syncLock) { Int32 result = Native.SysCall.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 ISpiChannel 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; } } } }