2019-12-06 21:09:52 +01:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
using Swan;
|
|
|
|
|
|
|
|
|
|
using Unosquare.RaspberryIO.Abstractions;
|
|
|
|
|
using Unosquare.RaspberryIO.Abstractions.Native;
|
|
|
|
|
|
|
|
|
|
namespace Unosquare.WiringPi {
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 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.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public sealed class SpiChannel : ISpiChannel {
|
2019-12-04 18:57:18 +01:00
|
|
|
|
/// <summary>
|
2019-12-06 21:09:52 +01:00
|
|
|
|
/// The minimum frequency of a SPI Channel.
|
2019-12-04 18:57:18 +01:00
|
|
|
|
/// </summary>
|
2019-12-06 21:09:52 +01:00
|
|
|
|
public const Int32 MinFrequency = 500000;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The maximum frequency of a SPI channel.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public const Int32 MaxFrequency = 32000000;
|
|
|
|
|
|
|
|
|
|
private static readonly Object SyncRoot = new Object();
|
|
|
|
|
private static readonly Dictionary<SpiChannelNumber, SpiChannel> Buses = new Dictionary<SpiChannelNumber, SpiChannel>();
|
|
|
|
|
private readonly Object _syncLock = new Object();
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Initializes a new instance of the <see cref="SpiChannel"/> class.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="channel">The channel.</param>
|
|
|
|
|
/// <param name="frequency">The frequency.</param>
|
|
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public Int32 FileDescriptor {
|
|
|
|
|
get;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public Int32 Channel {
|
|
|
|
|
get;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public Int32 Frequency {
|
|
|
|
|
get;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sends data and simultaneously receives the data in the return buffer.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="buffer">The buffer.</param>
|
|
|
|
|
/// <returns>
|
|
|
|
|
/// The read bytes from the ring-style bus.
|
|
|
|
|
/// </returns>
|
|
|
|
|
public Task<Byte[]> SendReceiveAsync(Byte[] buffer) => Task.Run(() => this.SendReceive(buffer));
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 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.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="buffer">The buffer.</param>
|
|
|
|
|
/// <returns>The awaitable task.</returns>
|
|
|
|
|
public Task WriteAsync(Byte[] buffer) => Task.Run(() => { this.Write(buffer); });
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 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.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="channel">The channel.</param>
|
|
|
|
|
/// <param name="frequency">The frequency.</param>
|
|
|
|
|
/// <returns>The usable SPI channel.</returns>
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-12-04 18:57:18 +01:00
|
|
|
|
}
|