This commit is contained in:
2019-12-04 18:57:18 +01:00
parent 4692422c9a
commit 6263791dff
225 changed files with 33065 additions and 2 deletions
+30
View File
@@ -0,0 +1,30 @@
namespace Unosquare.WiringPi
{
using RaspberryIO.Abstractions;
using Swan.DependencyInjection;
/// <summary>
/// Represents the Bootstrap class to extract resources.
/// </summary>
/// <seealso cref="Unosquare.RaspberryIO.Abstractions.IBootstrap" />
public class BootstrapWiringPi : IBootstrap
{
private static readonly object SyncLock = new object();
/// <inheritdoc />
public void Bootstrap()
{
lock (SyncLock)
{
Resources.EmbeddedResources.ExtractAll();
DependencyContainer.Current.Register<IGpioController>(new GpioController());
DependencyContainer.Current.Register<ISpiBus>(new SpiBus());
DependencyContainer.Current.Register<II2CBus>(new I2CBus());
DependencyContainer.Current.Register<ISystemInfo>(new SystemInfo());
DependencyContainer.Current.Register<ITiming>(new Timing());
DependencyContainer.Current.Register<IThreading>(new Threading());
}
}
}
}
+289
View File
@@ -0,0 +1,289 @@
namespace Unosquare.WiringPi
{
using System;
/// <summary>
/// Defines all the available Wiring Pi Pin Numbers.
/// </summary>
public enum WiringPiPin
{
/// <summary>
/// Unknown WiringPi pin.
/// </summary>
Unknown = -1,
/// <summary>
/// WiringPi pin 0.
/// </summary>
Pin00 = 0,
/// <summary>
/// WiringPi pin 1.
/// </summary>
Pin01 = 1,
/// <summary>
/// WiringPi pin 2.
/// </summary>
Pin02 = 2,
/// <summary>
/// WiringPi pin 3.
/// </summary>
Pin03 = 3,
/// <summary>
/// WiringPi pin 4.
/// </summary>
Pin04 = 4,
/// <summary>
/// WiringPi pin 5.
/// </summary>
Pin05 = 5,
/// <summary>
/// WiringPi pin 6.
/// </summary>
Pin06 = 6,
/// <summary>
/// WiringPi pin 7.
/// </summary>
Pin07 = 7,
/// <summary>
/// WiringPi pin 8.
/// </summary>
Pin08 = 8,
/// <summary>
/// WiringPi pin 9.
/// </summary>
Pin09 = 9,
/// <summary>
/// WiringPi pin 10.
/// </summary>
Pin10 = 10,
/// <summary>
/// WiringPi pin 11.
/// </summary>
Pin11 = 11,
/// <summary>
/// WiringPi pin 12.
/// </summary>
Pin12 = 12,
/// <summary>
/// WiringPi pin 13.
/// </summary>
Pin13 = 13,
/// <summary>
/// WiringPi pin 14.
/// </summary>
Pin14 = 14,
/// <summary>
/// WiringPi pin 15.
/// </summary>
Pin15 = 15,
/// <summary>
/// WiringPi pin 16.
/// </summary>
Pin16 = 16,
/// <summary>
/// WiringPi pin 17.
/// </summary>
Pin17 = 17,
/// <summary>
/// WiringPi pin 18.
/// </summary>
Pin18 = 18,
/// <summary>
/// WiringPi pin 19.
/// </summary>
Pin19 = 19,
/// <summary>
/// WiringPi pin 20.
/// </summary>
Pin20 = 20,
/// <summary>
/// WiringPi pin 21.
/// </summary>
Pin21 = 21,
/// <summary>
/// WiringPi pin 22.
/// </summary>
Pin22 = 22,
/// <summary>
/// WiringPi pin 23.
/// </summary>
Pin23 = 23,
/// <summary>
/// WiringPi pin 24.
/// </summary>
Pin24 = 24,
/// <summary>
/// WiringPi pin 25.
/// </summary>
Pin25 = 25,
/// <summary>
/// WiringPi pin 26.
/// </summary>
Pin26 = 26,
/// <summary>
/// WiringPi pin 27.
/// </summary>
Pin27 = 27,
/// <summary>
/// WiringPi pin 28.
/// </summary>
Pin28 = 28,
/// <summary>
/// WiringPi pin 29.
/// </summary>
Pin29 = 29,
/// <summary>
/// WiringPi pin 30.
/// </summary>
Pin30 = 30,
/// <summary>
/// WiringPi pin 31.
/// </summary>
Pin31 = 31,
}
/// <summary>
/// Defines the different pin capabilities.
/// </summary>
[Flags]
public enum PinCapability
{
/// <summary>
/// General Purpose capability: Digital and Analog Read/Write
/// </summary>
GP = 0x01,
/// <summary>
/// General Purpose Clock (not PWM)
/// </summary>
GPCLK = 0x02,
/// <summary>
/// i2c data channel
/// </summary>
I2CSDA = 0x04,
/// <summary>
/// i2c clock channel
/// </summary>
I2CSCL = 0x08,
/// <summary>
/// SPI Master Out, Slave In channel
/// </summary>
SPIMOSI = 0x10,
/// <summary>
/// SPI Master In, Slave Out channel
/// </summary>
SPIMISO = 0x20,
/// <summary>
/// SPI Clock channel
/// </summary>
SPICLK = 0x40,
/// <summary>
/// SPI Chip Select Channel
/// </summary>
SPICS = 0x80,
/// <summary>
/// UART Request to Send Channel
/// </summary>
UARTRTS = 0x100,
/// <summary>
/// UART Transmit Channel
/// </summary>
UARTTXD = 0x200,
/// <summary>
/// UART Receive Channel
/// </summary>
UARTRXD = 0x400,
/// <summary>
/// Hardware Pule Width Modulation
/// </summary>
PWM = 0x800,
}
/// <summary>
/// The PWM mode.
/// </summary>
public enum PwmMode
{
/// <summary>
/// PWM pulses are sent using mark-sign patterns (old school)
/// </summary>
MarkSign = 0,
/// <summary>
/// PWM pulses are sent as a balanced signal (default, newer mode)
/// </summary>
Balanced = 1,
}
/// <summary>
/// Defines GPIO controller initialization modes.
/// </summary>
internal enum ControllerMode
{
/// <summary>
/// The not initialized
/// </summary>
NotInitialized,
/// <summary>
/// The direct with wiring pi pins
/// </summary>
DirectWithWiringPiPins,
/// <summary>
/// The direct with BCM pins
/// </summary>
DirectWithBcmPins,
/// <summary>
/// The direct with header pins
/// </summary>
DirectWithHeaderPins,
/// <summary>
/// The file stream with hardware pins
/// </summary>
FileStreamWithHardwarePins,
}
}
+582
View File
@@ -0,0 +1,582 @@
namespace Unosquare.WiringPi
{
using Native;
using RaspberryIO.Abstractions;
using Swan;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
/// <summary>
/// Represents the Raspberry Pi GPIO controller
/// as an IReadOnlyCollection of GpioPins.
///
/// Low level operations are accomplished by using the Wiring Pi library.
/// </summary>
public sealed class GpioController : IGpioController
{
#region Private Declarations
private const string WiringPiCodesEnvironmentVariable = "WIRINGPI_CODES";
private static readonly object SyncRoot = new object();
private readonly List<GpioPin> _pins;
#endregion
#region Constructors and Initialization
/// <summary>
/// Initializes static members of the <see cref="GpioController"/> class.
/// </summary>
static GpioController()
{
var wiringPiEdgeDetection = new Dictionary<EdgeDetection, int>
{
{EdgeDetection.FallingEdge, 21},
{EdgeDetection.RisingEdge, 1},
{EdgeDetection.FallingAndRisingEdge, 3},
};
WiringPiEdgeDetectionMapping = new ReadOnlyDictionary<EdgeDetection, int>(wiringPiEdgeDetection);
}
/// <summary>
/// Initializes a new instance of the <see cref="GpioController"/> class.
/// </summary>
/// <exception cref="System.Exception">Unable to initialize the GPIO controller.</exception>
internal GpioController()
{
if (_pins != null)
return;
if (IsInitialized == false)
{
var initResult = Initialize(ControllerMode.DirectWithBcmPins);
if (initResult == false)
throw new Exception("Unable to initialize the GPIO controller.");
}
_pins = new List<GpioPin>
{
GpioPin.Pin00.Value,
GpioPin.Pin01.Value,
GpioPin.Pin02.Value,
GpioPin.Pin03.Value,
GpioPin.Pin04.Value,
GpioPin.Pin05.Value,
GpioPin.Pin06.Value,
GpioPin.Pin07.Value,
GpioPin.Pin08.Value,
GpioPin.Pin09.Value,
GpioPin.Pin10.Value,
GpioPin.Pin11.Value,
GpioPin.Pin12.Value,
GpioPin.Pin13.Value,
GpioPin.Pin14.Value,
GpioPin.Pin15.Value,
GpioPin.Pin16.Value,
GpioPin.Pin17.Value,
GpioPin.Pin18.Value,
GpioPin.Pin19.Value,
GpioPin.Pin20.Value,
GpioPin.Pin21.Value,
GpioPin.Pin22.Value,
GpioPin.Pin23.Value,
GpioPin.Pin24.Value,
GpioPin.Pin25.Value,
GpioPin.Pin26.Value,
GpioPin.Pin27.Value,
GpioPin.Pin28.Value,
GpioPin.Pin29.Value,
GpioPin.Pin30.Value,
GpioPin.Pin31.Value,
};
var headerP1 = new Dictionary<int, GpioPin>(_pins.Count);
var headerP5 = new Dictionary<int, GpioPin>(_pins.Count);
foreach (var pin in _pins)
{
if (pin.PhysicalPinNumber < 0)
continue;
var header = pin.Header == GpioHeader.P1 ? headerP1 : headerP5;
header[pin.PhysicalPinNumber] = pin;
}
HeaderP1 = new ReadOnlyDictionary<int, GpioPin>(headerP1);
HeaderP5 = new ReadOnlyDictionary<int, GpioPin>(headerP5);
}
/// <summary>
/// Determines if the underlying GPIO controller has been initialized properly.
/// </summary>
/// <value>
/// <c>true</c> if the controller is properly initialized; otherwise, <c>false</c>.
/// </value>
public static bool IsInitialized
{
get
{
lock (SyncRoot)
{
return Mode != ControllerMode.NotInitialized;
}
}
}
/// <summary>
/// Gets the wiring pi edge detection mapping.
/// </summary>
internal static ReadOnlyDictionary<EdgeDetection, int> WiringPiEdgeDetectionMapping { get; }
/// <inheritdoc />
/// <summary>
/// Gets the number of registered pins in the controller.
/// </summary>
public int Count => Pins.Count;
/// <summary>
/// Gets or sets the initialization mode.
/// </summary>
private static ControllerMode Mode { get; set; } = ControllerMode.NotInitialized;
#endregion
#region Pin Addressing
/// <summary>
/// Gets the PWM base frequency (in Hz).
/// </summary>
public int PwmBaseFrequency => 19200000;
/// <summary>
/// Gets a red-only collection of all pins.
/// </summary>
public ReadOnlyCollection<GpioPin> Pins => new ReadOnlyCollection<GpioPin>(_pins);
/// <summary>
/// Provides all the pins on Header P1 of the Pi as a lookup by physical header pin number.
/// This header is the main header and it is the one commonly used.
/// </summary>
public ReadOnlyDictionary<int, GpioPin> HeaderP1 { get; }
/// <summary>
/// Provides all the pins on Header P5 of the Pi as a lookup by physical header pin number.
/// This header is the secondary header and it is rarely used.
/// </summary>
public ReadOnlyDictionary<int, GpioPin> HeaderP5 { get; }
#endregion
#region Individual Pin Properties
/// <summary>
/// Provides direct access to Pin known as BCM0.
/// </summary>
public GpioPin Pin00 => GpioPin.Pin00.Value;
/// <summary>
/// Provides direct access to Pin known as BCM1.
/// </summary>
public GpioPin Pin01 => GpioPin.Pin01.Value;
/// <summary>
/// Provides direct access to Pin known as BCM2.
/// </summary>
public GpioPin Pin02 => GpioPin.Pin02.Value;
/// <summary>
/// Provides direct access to Pin known as BCM3.
/// </summary>
public GpioPin Pin03 => GpioPin.Pin03.Value;
/// <summary>
/// Provides direct access to Pin known as BCM4.
/// </summary>
public GpioPin Pin04 => GpioPin.Pin04.Value;
/// <summary>
/// Provides direct access to Pin known as BCM5.
/// </summary>
public GpioPin Pin05 => GpioPin.Pin05.Value;
/// <summary>
/// Provides direct access to Pin known as BCM6.
/// </summary>
public GpioPin Pin06 => GpioPin.Pin06.Value;
/// <summary>
/// Provides direct access to Pin known as BCM7.
/// </summary>
public GpioPin Pin07 => GpioPin.Pin07.Value;
/// <summary>
/// Provides direct access to Pin known as BCM8.
/// </summary>
public GpioPin Pin08 => GpioPin.Pin08.Value;
/// <summary>
/// Provides direct access to Pin known as BCM9.
/// </summary>
public GpioPin Pin09 => GpioPin.Pin09.Value;
/// <summary>
/// Provides direct access to Pin known as BCM10.
/// </summary>
public GpioPin Pin10 => GpioPin.Pin10.Value;
/// <summary>
/// Provides direct access to Pin known as BCM11.
/// </summary>
public GpioPin Pin11 => GpioPin.Pin11.Value;
/// <summary>
/// Provides direct access to Pin known as BCM12.
/// </summary>
public GpioPin Pin12 => GpioPin.Pin12.Value;
/// <summary>
/// Provides direct access to Pin known as BCM13.
/// </summary>
public GpioPin Pin13 => GpioPin.Pin13.Value;
/// <summary>
/// Provides direct access to Pin known as BCM14.
/// </summary>
public GpioPin Pin14 => GpioPin.Pin14.Value;
/// <summary>
/// Provides direct access to Pin known as BCM15.
/// </summary>
public GpioPin Pin15 => GpioPin.Pin15.Value;
/// <summary>
/// Provides direct access to Pin known as BCM16.
/// </summary>
public GpioPin Pin16 => GpioPin.Pin16.Value;
/// <summary>
/// Provides direct access to Pin known as BCM17.
/// </summary>
public GpioPin Pin17 => GpioPin.Pin17.Value;
/// <summary>
/// Provides direct access to Pin known as BCM18.
/// </summary>
public GpioPin Pin18 => GpioPin.Pin18.Value;
/// <summary>
/// Provides direct access to Pin known as BCM19.
/// </summary>
public GpioPin Pin19 => GpioPin.Pin19.Value;
/// <summary>
/// Provides direct access to Pin known as BCM20.
/// </summary>
public GpioPin Pin20 => GpioPin.Pin20.Value;
/// <summary>
/// Provides direct access to Pin known as BCM21.
/// </summary>
public GpioPin Pin21 => GpioPin.Pin21.Value;
/// <summary>
/// Provides direct access to Pin known as BCM22.
/// </summary>
public GpioPin Pin22 => GpioPin.Pin22.Value;
/// <summary>
/// Provides direct access to Pin known as BCM23.
/// </summary>
public GpioPin Pin23 => GpioPin.Pin23.Value;
/// <summary>
/// Provides direct access to Pin known as BCM24.
/// </summary>
public GpioPin Pin24 => GpioPin.Pin24.Value;
/// <summary>
/// Provides direct access to Pin known as BCM25.
/// </summary>
public GpioPin Pin25 => GpioPin.Pin25.Value;
/// <summary>
/// Provides direct access to Pin known as BCM26.
/// </summary>
public GpioPin Pin26 => GpioPin.Pin26.Value;
/// <summary>
/// Provides direct access to Pin known as BCM27.
/// </summary>
public GpioPin Pin27 => GpioPin.Pin27.Value;
/// <summary>
/// Provides direct access to Pin known as BCM28 (available on Header P5).
/// </summary>
public GpioPin Pin28 => GpioPin.Pin28.Value;
/// <summary>
/// Provides direct access to Pin known as BCM29 (available on Header P5).
/// </summary>
public GpioPin Pin29 => GpioPin.Pin29.Value;
/// <summary>
/// Provides direct access to Pin known as BCM30 (available on Header P5).
/// </summary>
public GpioPin Pin30 => GpioPin.Pin30.Value;
/// <summary>
/// Provides direct access to Pin known as BCM31 (available on Header P5).
/// </summary>
public GpioPin Pin31 => GpioPin.Pin31.Value;
#endregion
#region Indexers
/// <inheritdoc />
public IGpioPin this[BcmPin bcmPin] => Pins[(int)bcmPin];
/// <inheritdoc />
public IGpioPin this[int bcmPinNumber]
{
get
{
if (!Enum.IsDefined(typeof(BcmPin), bcmPinNumber))
throw new IndexOutOfRangeException($"Pin {bcmPinNumber} is not registered in the GPIO controller.");
return Pins[bcmPinNumber];
}
}
/// <inheritdoc />
public IGpioPin this[P1 pinNumber] => HeaderP1[(int)pinNumber];
/// <inheritdoc />
public IGpioPin this[P5 pinNumber] => HeaderP5[(int)pinNumber];
/// <summary>
/// Gets the <see cref="GpioPin"/> with the specified Wiring Pi pin number.
/// </summary>
/// <value>
/// The <see cref="GpioPin"/>.
/// </value>
/// <param name="pinNumber">The pin number.</param>
/// <returns>A reference to the GPIO pin.</returns>
public GpioPin this[WiringPiPin pinNumber]
{
get
{
if (pinNumber == WiringPiPin.Unknown)
throw new InvalidOperationException("You can not get an unknown WiringPi pin.");
return Pins.First(p => p.WiringPiPinNumber == pinNumber);
}
}
#endregion
#region Pin Group Methods (Read, Write, Pad Drive)
/// <summary>
/// This sets the “strength” of the pad drivers for a particular group of pins.
/// There are 3 groups of pins and the drive strength is from 0 to 7.
/// Do not use this unless you know what you are doing.
/// </summary>
/// <param name="group">The group.</param>
/// <param name="value">The value.</param>
public void SetPadDrive(int group, int value)
{
lock (SyncRoot)
{
WiringPi.SetPadDrive(group, value);
}
}
/// <summary>
/// This sets the “strength” of the pad drivers for a particular group of pins.
/// There are 3 groups of pins and the drive strength is from 0 to 7.
/// Do not use this unless you know what you are doing.
/// </summary>
/// <param name="group">The group.</param>
/// <param name="value">The value.</param>
/// <returns>The awaitable task.</returns>
public Task SetPadDriveAsync(int group, int value) =>
Task.Run(() => SetPadDrive(group, value));
/// <summary>
/// This writes the 8-bit byte supplied to the first 8 GPIO pins.
/// It’s the fastest way to set all 8 bits at once to a particular value,
/// although it still takes two write operations to the Pi’s GPIO hardware.
/// </summary>
/// <param name="value">The value.</param>
/// <exception cref="InvalidOperationException">PinMode.</exception>
public void WriteByte(byte value)
{
lock (SyncRoot)
{
if (this.Skip(0).Take(8).Any(p => p.PinMode != GpioPinDriveMode.Output))
{
throw new InvalidOperationException(
$"All first 8 pins (0 to 7) need their {nameof(GpioPin.PinMode)} to be set to {GpioPinDriveMode.Output}");
}
WiringPi.DigitalWriteByte(value);
}
}
/// <summary>
/// This writes the 8-bit byte supplied to the first 8 GPIO pins.
/// It’s the fastest way to set all 8 bits at once to a particular value,
/// although it still takes two write operations to the Pi’s GPIO hardware.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>The awaitable task.</returns>
public Task WriteByteAsync(byte value) =>
Task.Run(() => WriteByte(value));
/// <summary>
/// This reads the 8-bit byte supplied to the first 8 GPIO pins.
/// It’s the fastest way to get all 8 bits at once to a particular value.
/// Please note this function is undocumented and unsupported.
/// </summary>
/// <returns>A byte from the GPIO.</returns>
/// <exception cref="InvalidOperationException">PinMode.</exception>
public byte ReadByte()
{
lock (SyncRoot)
{
if (this.Skip(0).Take(8).Any(p =>
p.PinMode != GpioPinDriveMode.Input && p.PinMode != GpioPinDriveMode.Output))
{
throw new InvalidOperationException(
$"All first 8 pins (0 to 7) need their {nameof(GpioPin.PinMode)} to be set to {GpioPinDriveMode.Input} or {GpioPinDriveMode.Output}");
}
return (byte)WiringPi.DigitalReadByte();
}
}
/// <summary>
/// This reads the 8-bit byte supplied to the first 8 GPIO pins.
/// It’s the fastest way to get all 8 bits at once to a particular value.
/// Please note this function is undocumented and unsupported.
/// </summary>
/// <returns>A byte from the GPIO.</returns>
public Task<byte> ReadByteAsync() =>
Task.Run(ReadByte);
#endregion
#region IReadOnlyCollection Implementation
/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>
/// A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection.
/// </returns>
public IEnumerator<GpioPin> GetEnumerator() => Pins.GetEnumerator();
/// <inheritdoc />
IEnumerator<IGpioPin> IEnumerable<IGpioPin>.GetEnumerator() => Pins.GetEnumerator();
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator() => Pins.GetEnumerator();
#endregion
#region Helper and Init Methods
/// <summary>
/// Converts the Wirings Pi pin number to the BCM pin number.
/// </summary>
/// <param name="wiringPiPinNumber">The wiring pi pin number.</param>
/// <returns>The converted pin.</returns>
internal static int WiringPiToBcmPinNumber(int wiringPiPinNumber)
{
lock (SyncRoot)
{
return WiringPi.WpiPinToGpio(wiringPiPinNumber);
}
}
/// <summary>
/// Converts the Physical (Header) pin number to BCM pin number.
/// </summary>
/// <param name="headerPinNumber">The header pin number.</param>
/// <returns>The converted pin.</returns>
internal static int HaderToBcmPinNumber(int headerPinNumber)
{
lock (SyncRoot)
{
return WiringPi.PhysPinToGpio(headerPinNumber);
}
}
/// <summary>
/// Initializes the controller given the initialization mode and pin numbering scheme.
/// </summary>
/// <param name="mode">The mode.</param>
/// <returns>True when successful.</returns>
/// <exception cref="PlatformNotSupportedException">
/// This library does not support the platform.
/// </exception>
/// <exception cref="InvalidOperationException">Library was already Initialized.</exception>
/// <exception cref="ArgumentException">The init mode is invalid.</exception>
private bool Initialize(ControllerMode mode)
{
if (SwanRuntime.OS != Swan.OperatingSystem.Unix)
throw new PlatformNotSupportedException("This library does not support the platform");
lock (SyncRoot)
{
if (IsInitialized)
throw new InvalidOperationException($"Cannot call {nameof(Initialize)} more than once.");
Environment.SetEnvironmentVariable(WiringPiCodesEnvironmentVariable, "1");
int setupResult;
switch (mode)
{
case ControllerMode.DirectWithWiringPiPins:
{
setupResult = WiringPi.WiringPiSetup();
break;
}
case ControllerMode.DirectWithBcmPins:
{
setupResult = WiringPi.WiringPiSetupGpio();
break;
}
case ControllerMode.DirectWithHeaderPins:
{
setupResult = WiringPi.WiringPiSetupPhys();
break;
}
case ControllerMode.FileStreamWithHardwarePins:
{
setupResult = WiringPi.WiringPiSetupSys();
break;
}
default:
{
throw new ArgumentException($"'{mode}' is not a valid initialization mode.");
}
}
Mode = setupResult == 0 ? mode : ControllerMode.NotInitialized;
return IsInitialized;
}
}
#endregion
}
}
+200
View File
@@ -0,0 +1,200 @@
namespace Unosquare.WiringPi
{
using RaspberryIO.Abstractions;
using System;
public partial class GpioPin
{
internal static readonly Lazy<GpioPin> Pin00 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio00)
{
Capabilities = PinCapability.GP | PinCapability.I2CSDA,
Name = $"BCM 0 {(SystemInfo.GetBoardRevision() == BoardRevision.Rev1 ? "(SDA)" : "(ID_SD)")}",
});
internal static readonly Lazy<GpioPin> Pin01 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio01)
{
Capabilities = PinCapability.GP | PinCapability.I2CSCL,
Name = $"BCM 1 {(SystemInfo.GetBoardRevision() == BoardRevision.Rev1 ? "(SCL)" : "(ID_SC)")}",
});
internal static readonly Lazy<GpioPin> Pin02 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio02)
{
Capabilities = PinCapability.GP | PinCapability.I2CSDA,
Name = "BCM 2 (SDA)",
});
internal static readonly Lazy<GpioPin> Pin03 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio03)
{
Capabilities = PinCapability.GP | PinCapability.I2CSCL,
Name = "BCM 3 (SCL)",
});
internal static readonly Lazy<GpioPin> Pin04 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio04)
{
Capabilities = PinCapability.GP | PinCapability.GPCLK,
Name = "BCM 4 (GPCLK0)",
});
internal static readonly Lazy<GpioPin> Pin05 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio05)
{
Capabilities = PinCapability.GP,
Name = "BCM 5",
});
internal static readonly Lazy<GpioPin> Pin06 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio06)
{
Capabilities = PinCapability.GP,
Name = "BCM 6",
});
internal static readonly Lazy<GpioPin> Pin07 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio07)
{
Capabilities = PinCapability.GP | PinCapability.SPICS,
Name = "BCM 7 (CE1)",
});
internal static readonly Lazy<GpioPin> Pin08 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio08)
{
Capabilities = PinCapability.GP | PinCapability.SPICS,
Name = "BCM 8 (CE0)",
});
internal static readonly Lazy<GpioPin> Pin09 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio09)
{
Capabilities = PinCapability.GP | PinCapability.SPIMISO,
Name = "BCM 9 (MISO)",
});
internal static readonly Lazy<GpioPin> Pin10 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio10)
{
Capabilities = PinCapability.GP | PinCapability.SPIMOSI,
Name = "BCM 10 (MOSI)",
});
internal static readonly Lazy<GpioPin> Pin11 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio11)
{
Capabilities = PinCapability.GP | PinCapability.SPICLK,
Name = "BCM 11 (SCLCK)",
});
internal static readonly Lazy<GpioPin> Pin12 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio12)
{
Capabilities = PinCapability.GP | PinCapability.PWM,
Name = "BCM 12 (PWM0)",
});
internal static readonly Lazy<GpioPin> Pin13 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio13)
{
Capabilities = PinCapability.GP | PinCapability.PWM,
Name = "BCM 13 (PWM1)",
});
internal static readonly Lazy<GpioPin> Pin14 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio14)
{
Capabilities = PinCapability.UARTTXD,
Name = "BCM 14 (TXD)",
});
internal static readonly Lazy<GpioPin> Pin15 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio15)
{
Capabilities = PinCapability.UARTRXD,
Name = "BCM 15 (RXD)",
});
internal static readonly Lazy<GpioPin> Pin16 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio16)
{
Capabilities = PinCapability.GP,
Name = "BCM 16",
});
internal static readonly Lazy<GpioPin> Pin17 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio17)
{
Capabilities = PinCapability.GP | PinCapability.UARTRTS,
Name = "BCM 17",
});
internal static readonly Lazy<GpioPin> Pin18 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio18)
{
Capabilities = PinCapability.GP | PinCapability.PWM,
Name = "BCM 18 (PWM0)",
});
internal static readonly Lazy<GpioPin> Pin19 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio19)
{
Capabilities = PinCapability.GP | PinCapability.PWM | PinCapability.SPIMISO,
Name = "BCM 19 (MISO)",
});
internal static readonly Lazy<GpioPin> Pin20 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio20)
{
Capabilities = PinCapability.GP | PinCapability.SPIMOSI,
Name = "BCM 20 (MOSI)",
});
internal static readonly Lazy<GpioPin> Pin21 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio21)
{
Capabilities = PinCapability.GP | PinCapability.SPICLK,
Name = $"BCM 21{(SystemInfo.GetBoardRevision() == BoardRevision.Rev1 ? string.Empty : " (SCLK)")}",
});
internal static readonly Lazy<GpioPin> Pin22 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio22)
{
Capabilities = PinCapability.GP,
Name = "BCM 22",
});
internal static readonly Lazy<GpioPin> Pin23 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio23)
{
Capabilities = PinCapability.GP,
Name = "BCM 23",
});
internal static readonly Lazy<GpioPin> Pin24 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio24)
{
Capabilities = PinCapability.GP,
Name = "BCM 24",
});
internal static readonly Lazy<GpioPin> Pin25 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio25)
{
Capabilities = PinCapability.GP,
Name = "BCM 25",
});
internal static readonly Lazy<GpioPin> Pin26 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio26)
{
Capabilities = PinCapability.GP,
Name = "BCM 26",
});
internal static readonly Lazy<GpioPin> Pin27 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio27)
{
Capabilities = PinCapability.GP,
Name = "BCM 27",
});
internal static readonly Lazy<GpioPin> Pin28 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio28)
{
Capabilities = PinCapability.GP | PinCapability.I2CSDA,
Name = "BCM 28 (SDA)",
});
internal static readonly Lazy<GpioPin> Pin29 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio29)
{
Capabilities = PinCapability.GP | PinCapability.I2CSCL,
Name = "BCM 29 (SCL)",
});
internal static readonly Lazy<GpioPin> Pin30 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio30)
{
Capabilities = PinCapability.GP,
Name = "BCM 30",
});
internal static readonly Lazy<GpioPin> Pin31 = new Lazy<GpioPin>(() => new GpioPin(BcmPin.Gpio31)
{
Capabilities = PinCapability.GP,
Name = "BCM 31",
});
}
}
+654
View File
@@ -0,0 +1,654 @@
namespace Unosquare.WiringPi
{
using System;
using System.Threading.Tasks;
using Native;
using RaspberryIO.Abstractions;
using RaspberryIO.Abstractions.Native;
using Swan.Diagnostics;
using Definitions = RaspberryIO.Abstractions.Definitions;
/// <summary>
/// Represents a GPIO Pin, its location and its capabilities.
/// Full pin reference available here:
/// http://pinout.xyz/pinout/pin31_gpio6 and http://wiringpi.com/pins/.
/// </summary>
public sealed partial class GpioPin : IGpioPin
{
#region Property Backing
private static readonly int[] GpioToWiringPi;
private static readonly int[] GpioToWiringPiR1 =
{
8, 9, -1, -1, 7, -1, -1, 11, 10, 13, 12, 14, -1, -1, 15, 16, -1, 0, 1, -1, -1, 2, 3, 4, 5, 6, -1, -1, -1, -1, -1, -1,
};
private static readonly int[] GpioToWiringPiR2 =
{
30, 31, 8, 9, 7, 21, 22, 11, 10, 13, 12, 14, 26, 23, 15, 16, 27, 0, 1, 24, 28, 29, 3, 4, 5, 6, 25, 2, 17, 18, 19, 20,
};
private readonly object _syncLock = new object();
private GpioPinDriveMode _pinMode;
private GpioPinResistorPullMode _resistorPullMode;
private int _pwmRegister;
private PwmMode _pwmMode = PwmMode.Balanced;
private uint _pwmRange = 1024;
private int _pwmClockDivisor = 1;
private int _softPwmValue = -1;
private int _softToneFrequency = -1;
#endregion
#region Constructor
static GpioPin()
{
GpioToWiringPi = SystemInfo.GetBoardRevision() ==
BoardRevision.Rev1 ? GpioToWiringPiR1 : GpioToWiringPiR2;
}
/// <summary>
/// Initializes a new instance of the <see cref="GpioPin"/> class.
/// </summary>
/// <param name="bcmPinNumber">The BCM pin number.</param>
private GpioPin(BcmPin bcmPinNumber)
{
BcmPin = bcmPinNumber;
BcmPinNumber = (int)bcmPinNumber;
WiringPiPinNumber = BcmToWiringPiPinNumber(bcmPinNumber);
PhysicalPinNumber = Definitions.BcmToPhysicalPinNumber(SystemInfo.GetBoardRevision(), bcmPinNumber);
Header = (BcmPinNumber >= 28 && BcmPinNumber <= 31) ? GpioHeader.P5 : GpioHeader.P1;
}
#endregion
#region Pin Properties
/// <inheritdoc />
public BcmPin BcmPin { get; }
/// <inheritdoc />
public int BcmPinNumber { get; }
/// <inheritdoc />
public int PhysicalPinNumber { get; }
/// <summary>
/// Gets the WiringPi Pin number.
/// </summary>
public WiringPiPin WiringPiPinNumber { get; }
/// <inheritdoc />
public GpioHeader Header { get; }
/// <summary>
/// Gets the friendly name of the pin.
/// </summary>
public string Name { get; private set; }
/// <summary>
/// Gets the hardware mode capabilities of this pin.
/// </summary>
public PinCapability Capabilities { get; private set; }
/// <inheritdoc />
public bool Value
{
get => Read();
set => Write(value);
}
#endregion
#region Hardware-Specific Properties
/// <inheritdoc />
/// <exception cref="T:System.NotSupportedException">Thrown when a pin does not support the given operation mode.</exception>
public GpioPinDriveMode PinMode
{
get => _pinMode;
set
{
lock (_syncLock)
{
var mode = value;
if ((mode == GpioPinDriveMode.GpioClock && !HasCapability(PinCapability.GPCLK)) ||
(mode == GpioPinDriveMode.PwmOutput && !HasCapability(PinCapability.PWM)) ||
(mode == GpioPinDriveMode.Input && !HasCapability(PinCapability.GP)) ||
(mode == GpioPinDriveMode.Output && !HasCapability(PinCapability.GP)))
{
throw new NotSupportedException(
$"Pin {BcmPinNumber} '{Name}' does not support mode '{mode}'. Pin capabilities are limited to: {Capabilities}");
}
WiringPi.PinMode(BcmPinNumber, (int)mode);
_pinMode = mode;
}
}
}
/// <summary>
/// Gets the interrupt callback. Returns null if no interrupt
/// has been registered.
/// </summary>
public InterruptServiceRoutineCallback InterruptCallback { get; private set; }
/// <summary>
/// Gets the interrupt edge detection mode.
/// </summary>
public EdgeDetection InterruptEdgeDetection { get; private set; }
/// <summary>
/// Determines whether the specified capability has capability.
/// </summary>
/// <param name="capability">The capability.</param>
/// <returns>
/// <c>true</c> if the specified capability has capability; otherwise, <c>false</c>.
/// </returns>
public bool HasCapability(PinCapability capability) =>
(Capabilities & capability) == capability;
#endregion
#region Hardware PWM Members
/// <inheritdoc />
public GpioPinResistorPullMode InputPullMode
{
get => PinMode == GpioPinDriveMode.Input ? _resistorPullMode : GpioPinResistorPullMode.Off;
set
{
lock (_syncLock)
{
if (PinMode != GpioPinDriveMode.Input)
{
_resistorPullMode = GpioPinResistorPullMode.Off;
throw new InvalidOperationException(
$"Unable to set the {nameof(InputPullMode)} for pin {BcmPinNumber} because operating mode is {PinMode}."
+ $" Setting the {nameof(InputPullMode)} is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input}");
}
WiringPi.PullUpDnControl(BcmPinNumber, (int)value);
_resistorPullMode = value;
}
}
}
/// <summary>
/// Gets or sets the PWM register.
/// </summary>
/// <value>
/// The PWM register.
/// </value>
public int PwmRegister
{
get => _pwmRegister;
set
{
lock (_syncLock)
{
if (!HasCapability(PinCapability.PWM))
{
_pwmRegister = 0;
throw new NotSupportedException(
$"Pin {BcmPinNumber} '{Name}' does not support mode '{GpioPinDriveMode.PwmOutput}'. Pin capabilities are limited to: {Capabilities}");
}
WiringPi.PwmWrite(BcmPinNumber, value);
_pwmRegister = value;
}
}
}
/// <summary>
/// The PWM generator can run in 2 modes – “balanced” and “mark:space”. The mark:space mode is traditional,
/// however the default mode in the Pi is “balanced”.
/// </summary>
/// <value>
/// The PWM mode.
/// </value>
/// <exception cref="InvalidOperationException">When pin mode is not set a Pwn output.</exception>
public PwmMode PwmMode
{
get => PinMode == GpioPinDriveMode.PwmOutput ? _pwmMode : PwmMode.Balanced;
set
{
lock (_syncLock)
{
if (!HasCapability(PinCapability.PWM))
{
_pwmMode = PwmMode.Balanced;
throw new NotSupportedException(
$"Pin {BcmPinNumber} '{Name}' does not support mode '{GpioPinDriveMode.PwmOutput}'. Pin capabilities are limited to: {Capabilities}");
}
WiringPi.PwmSetMode((int)value);
_pwmMode = value;
}
}
}
/// <summary>
/// This sets the range register in the PWM generator. The default is 1024.
/// </summary>
/// <value>
/// The PWM range.
/// </value>
/// <exception cref="InvalidOperationException">When pin mode is not set to PWM output.</exception>
public uint PwmRange
{
get => PinMode == GpioPinDriveMode.PwmOutput ? _pwmRange : 0;
set
{
lock (_syncLock)
{
if (!HasCapability(PinCapability.PWM))
{
_pwmRange = 1024;
throw new NotSupportedException(
$"Pin {BcmPinNumber} '{Name}' does not support mode '{GpioPinDriveMode.PwmOutput}'. Pin capabilities are limited to: {Capabilities}");
}
WiringPi.PwmSetRange(value);
_pwmRange = value;
}
}
}
/// <summary>
/// Gets or sets the PWM clock divisor.
/// </summary>
/// <value>
/// The PWM clock divisor.
/// </value>
/// <exception cref="InvalidOperationException">When pin mode is not set to PWM output.</exception>
public int PwmClockDivisor
{
get => PinMode == GpioPinDriveMode.PwmOutput ? _pwmClockDivisor : 0;
set
{
lock (_syncLock)
{
if (!HasCapability(PinCapability.PWM))
{
_pwmClockDivisor = 1;
throw new NotSupportedException(
$"Pin {BcmPinNumber} '{Name}' does not support mode '{GpioPinDriveMode.PwmOutput}'. Pin capabilities are limited to: {Capabilities}");
}
WiringPi.PwmSetClock(value);
_pwmClockDivisor = value;
}
}
}
#endregion
#region Software Tone Members
/// <summary>
/// Gets a value indicating whether this instance is in software based tone generator mode.
/// </summary>
/// <value>
/// <c>true</c> if this instance is in soft tone mode; otherwise, <c>false</c>.
/// </value>
public bool IsInSoftToneMode => _softToneFrequency >= 0;
/// <summary>
/// Gets or sets the soft tone frequency. 0 to 5000 Hz is typical.
/// </summary>
/// <value>
/// The soft tone frequency.
/// </value>
/// <exception cref="InvalidOperationException">When soft tones cannot be initialized on the pin.</exception>
public int SoftToneFrequency
{
get => _softToneFrequency;
set
{
lock (_syncLock)
{
if (IsInSoftToneMode == false)
{
var setupResult = WiringPi.SoftToneCreate(BcmPinNumber);
if (setupResult != 0)
{
throw new InvalidOperationException(
$"Unable to initialize soft tone on pin {BcmPinNumber}. Error Code: {setupResult}");
}
}
WiringPi.SoftToneWrite(BcmPinNumber, value);
_softToneFrequency = value;
}
}
}
#endregion
#region Software PWM Members
/// <summary>
/// Gets a value indicating whether this pin is in software based PWM mode.
/// </summary>
/// <value>
/// <c>true</c> if this instance is in soft PWM mode; otherwise, <c>false</c>.
/// </value>
public bool IsInSoftPwmMode => _softPwmValue >= 0;
/// <summary>
/// Gets or sets the software PWM value on the pin.
/// </summary>
/// <value>
/// The soft PWM value.
/// </value>
/// <exception cref="InvalidOperationException">StartSoftPwm.</exception>
public int SoftPwmValue
{
get => _softPwmValue;
set
{
lock (_syncLock)
{
if (IsInSoftPwmMode && value >= 0)
{
WiringPi.SoftPwmWrite(BcmPinNumber, value);
_softPwmValue = value;
}
else
{
throw new InvalidOperationException($"Software PWM requires a call to {nameof(StartSoftPwm)}.");
}
}
}
}
/// <summary>
/// Gets the software PWM range used upon starting the PWM.
/// </summary>
public int SoftPwmRange { get; private set; } = -1;
/// <summary>
/// Starts the software based PWM on this pin.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="range">The range.</param>
/// <exception cref="NotSupportedException">When the pin does not suppoert PWM.</exception>
/// <exception cref="InvalidOperationException">StartSoftPwm
/// or.</exception>
public void StartSoftPwm(int value, int range)
{
lock (_syncLock)
{
if (!HasCapability(PinCapability.GP))
throw new NotSupportedException($"Pin {BcmPinNumber} does not support software PWM");
if (IsInSoftPwmMode)
throw new InvalidOperationException($"{nameof(StartSoftPwm)} has already been called.");
var startResult = WiringPi.SoftPwmCreate(BcmPinNumber, value, range);
if (startResult == 0)
{
_softPwmValue = value;
SoftPwmRange = range;
}
else
{
throw new InvalidOperationException(
$"Could not start software based PWM on pin {BcmPinNumber}. Error code: {startResult}");
}
}
}
#endregion
#region Output Mode (Write) Members
/// <inheritdoc />
public void Write(GpioPinValue value)
{
lock (_syncLock)
{
if (PinMode != GpioPinDriveMode.Output)
{
throw new InvalidOperationException(
$"Unable to write to pin {BcmPinNumber} because operating mode is {PinMode}."
+ $" Writes are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Output}");
}
WiringPi.DigitalWrite(BcmPinNumber, (int)value);
}
}
/// <summary>
/// Writes the value asynchronously.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>The awaitable task.</returns>
public Task WriteAsync(GpioPinValue value) => Task.Run(() => { Write(value); });
/// <summary>
/// Writes the specified bit value.
/// This method performs a digital write.
/// </summary>
/// <param name="value">if set to <c>true</c> [value].</param>
public void Write(bool value)
=> Write(value ? GpioPinValue.High : GpioPinValue.Low);
/// <summary>
/// Writes the specified bit value.
/// This method performs a digital write.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>
/// The awaitable task.
/// </returns>
public Task WriteAsync(bool value) => Task.Run(() => { Write(value); });
/// <summary>
/// Writes the specified value. 0 for low, any other value for high
/// This method performs a digital write.
/// </summary>
/// <param name="value">The value.</param>
public void Write(int value) => Write(value != 0 ? GpioPinValue.High : GpioPinValue.Low);
/// <summary>
/// Writes the specified value. 0 for low, any other value for high
/// This method performs a digital write.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>The awaitable task.</returns>
public Task WriteAsync(int value) => Task.Run(() => { Write(value); });
/// <summary>
/// Writes the specified value as an analog level.
/// You will need to register additional analog modules to enable this function for devices such as the Gertboard.
/// </summary>
/// <param name="value">The value.</param>
public void WriteLevel(int value)
{
lock (_syncLock)
{
if (PinMode != GpioPinDriveMode.Output)
{
throw new InvalidOperationException(
$"Unable to write to pin {BcmPinNumber} because operating mode is {PinMode}."
+ $" Writes are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Output}");
}
WiringPi.AnalogWrite(BcmPinNumber, value);
}
}
/// <summary>
/// Writes the specified value as an analog level.
/// You will need to register additional analog modules to enable this function for devices such as the Gertboard.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>The awaitable task.</returns>
public Task WriteLevelAsync(int value) => Task.Run(() => { WriteLevel(value); });
#endregion
#region Input Mode (Read) Members
/// <summary>
/// Wait for specific pin status.
/// </summary>
/// <param name="status">status to check.</param>
/// <param name="timeOutMillisecond">timeout to reach status.</param>
/// <returns>true/false.</returns>
public bool WaitForValue(GpioPinValue status, int timeOutMillisecond)
{
if (PinMode != GpioPinDriveMode.Input)
{
throw new InvalidOperationException(
$"Unable to read from pin {BcmPinNumber} because operating mode is {PinMode}."
+ $" Reads are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input}");
}
var hrt = new HighResolutionTimer();
hrt.Start();
do
{
if (ReadValue() == status)
return true;
}
while (hrt.ElapsedMilliseconds <= timeOutMillisecond);
return false;
}
/// <summary>
/// Reads the digital value on the pin as a boolean value.
/// </summary>
/// <returns>The state of the pin.</returns>
public bool Read()
{
lock (_syncLock)
{
if (PinMode != GpioPinDriveMode.Input && PinMode != GpioPinDriveMode.Output)
{
throw new InvalidOperationException(
$"Unable to read from pin {BcmPinNumber} because operating mode is {PinMode}."
+ $" Reads are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input} or {GpioPinDriveMode.Output}");
}
return WiringPi.DigitalRead(BcmPinNumber) != 0;
}
}
/// <summary>
/// Reads the digital value on the pin as a boolean value.
/// </summary>
/// <returns>The state of the pin.</returns>
public Task<bool> ReadAsync() => Task.Run(Read);
/// <summary>
/// Reads the digital value on the pin as a High or Low value.
/// </summary>
/// <returns>The state of the pin.</returns>
public GpioPinValue ReadValue()
=> Read() ? GpioPinValue.High : GpioPinValue.Low;
/// <summary>
/// Reads the digital value on the pin as a High or Low value.
/// </summary>
/// <returns>The state of the pin.</returns>
public Task<GpioPinValue> ReadValueAsync() => Task.Run(ReadValue);
/// <summary>
/// Reads the analog value on the pin.
/// This returns the value read on the supplied analog input pin. You will need to register
/// additional analog modules to enable this function for devices such as the Gertboard,
/// quick2Wire analog board, etc.
/// </summary>
/// <returns>The analog level.</returns>
/// <exception cref="InvalidOperationException">When the pin mode is not configured as an input.</exception>
public int ReadLevel()
{
lock (_syncLock)
{
if (PinMode != GpioPinDriveMode.Input)
{
throw new InvalidOperationException(
$"Unable to read from pin {BcmPinNumber} because operating mode is {PinMode}."
+ $" Reads are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input}");
}
return WiringPi.AnalogRead(BcmPinNumber);
}
}
/// <summary>
/// Reads the analog value on the pin.
/// This returns the value read on the supplied analog input pin. You will need to register
/// additional analog modules to enable this function for devices such as the Gertboard,
/// quick2Wire analog board, etc.
/// </summary>
/// <returns>The analog level.</returns>
public Task<int> ReadLevelAsync() => Task.Run(ReadLevel);
#endregion
#region Interrupts
/// <inheritdoc />
/// <exception cref="ArgumentNullException">callback.</exception>
public void RegisterInterruptCallback(EdgeDetection edgeDetection, Action callback)
{
if (callback == null)
throw new ArgumentNullException(nameof(callback));
if (PinMode != GpioPinDriveMode.Input)
{
throw new InvalidOperationException(
$"Unable to {nameof(RegisterInterruptCallback)} for pin {BcmPinNumber} because operating mode is {PinMode}."
+ $" Calling {nameof(RegisterInterruptCallback)} is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input}");
}
lock (_syncLock)
{
var isrCallback = new InterruptServiceRoutineCallback(callback);
var registerResult = WiringPi.WiringPiISR(BcmPinNumber, GetWiringPiEdgeDetection(edgeDetection), isrCallback);
if (registerResult == 0)
{
InterruptEdgeDetection = edgeDetection;
InterruptCallback = isrCallback;
}
else
{
HardwareException.Throw(nameof(GpioPin), nameof(RegisterInterruptCallback));
}
}
}
/// <inheritdoc />
public void RegisterInterruptCallback(EdgeDetection edgeDetection, Action<int, int, uint> callback) =>
throw new NotSupportedException("WiringPi does only support a simple interrupt callback that has no parameters.");
internal static WiringPiPin BcmToWiringPiPinNumber(BcmPin pin) =>
(WiringPiPin)GpioToWiringPi[(int)pin];
private static int GetWiringPiEdgeDetection(EdgeDetection edgeDetection) =>
GpioController.WiringPiEdgeDetectionMapping[edgeDetection];
#endregion
}
}
+72
View File
@@ -0,0 +1,72 @@
namespace Unosquare.WiringPi
{
using Native;
using RaspberryIO.Abstractions;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
/// <inheritdoc />
/// <summary>
/// A simple wrapper for the I2c bus on the Raspberry Pi.
/// </summary>
public class I2CBus : II2CBus
{
// TODO: It would be nice to integrate i2c device detection.
private static readonly object SyncRoot = new object();
private readonly Dictionary<int, II2CDevice> _devices = new Dictionary<int, II2CDevice>();
/// <inheritdoc />
public ReadOnlyCollection<II2CDevice> Devices
{
get
{
lock (SyncRoot)
return new ReadOnlyCollection<II2CDevice>(_devices.Values.ToArray());
}
}
/// <inheritdoc />
public II2CDevice this[int deviceId] => GetDeviceById(deviceId);
/// <inheritdoc />
public II2CDevice GetDeviceById(int deviceId)
{
lock (SyncRoot)
return _devices[deviceId];
}
/// <inheritdoc />
/// <exception cref="KeyNotFoundException">When the device file descriptor is not found.</exception>
public II2CDevice AddDevice(int deviceId)
{
lock (SyncRoot)
{
if (_devices.ContainsKey(deviceId))
return _devices[deviceId];
var fileDescriptor = SetupFileDescriptor(deviceId);
if (fileDescriptor < 0)
throw new KeyNotFoundException($"Device with id {deviceId} could not be registered with the I2C bus. Error Code: {fileDescriptor}.");
var device = new I2CDevice(deviceId, fileDescriptor);
_devices[deviceId] = device;
return device;
}
}
/// <summary>
/// This initializes the I2C system with your given device identifier.
/// The ID is the I2C number of the device and you can use the i2cdetect program to find this out.
/// wiringPiI2CSetup() will work out which revision Raspberry Pi you have and open the appropriate device in /dev.
/// The return value is the standard Linux filehandle, or -1 if any error – in which case, you can consult errno as usual.
/// </summary>
/// <param name="deviceId">The device identifier.</param>
/// <returns>The Linux file handle.</returns>
private static int SetupFileDescriptor(int deviceId)
{
lock (SyncRoot)
return WiringPi.WiringPiI2CSetup(deviceId);
}
}
}
+181
View File
@@ -0,0 +1,181 @@
namespace Unosquare.WiringPi
{
using Native;
using RaspberryIO.Abstractions;
using RaspberryIO.Abstractions.Native;
using System;
using System.Threading.Tasks;
/// <summary>
/// Represents a device on the I2C Bus.
/// </summary>
public class I2CDevice : II2CDevice
{
private readonly object _syncLock = new object();
/// <summary>
/// Initializes a new instance of the <see cref="I2CDevice"/> class.
/// </summary>
/// <param name="deviceId">The device identifier.</param>
/// <param name="fileDescriptor">The file descriptor.</param>
internal I2CDevice(int deviceId, int fileDescriptor)
{
DeviceId = deviceId;
FileDescriptor = fileDescriptor;
}
/// <inheritdoc />
public int DeviceId { get; }
/// <inheritdoc />
public int FileDescriptor { get; }
/// <inheritdoc />
public byte Read()
{
lock (_syncLock)
{
var result = WiringPi.WiringPiI2CRead(FileDescriptor);
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(Read));
return (byte)result;
}
}
/// <summary>
/// Reads a byte from the specified file descriptor.
/// </summary>
/// <returns>The byte from device.</returns>
public Task<byte> ReadAsync() => Task.Run(Read);
/// <summary>
/// Reads a buffer of the specified length, one byte at a time.
/// </summary>
/// <param name="length">The length.</param>
/// <returns>The byte array from device.</returns>
public byte[] Read(int length)
{
lock (_syncLock)
{
var buffer = new byte[length];
for (var i = 0; i < length; i++)
{
var result = WiringPi.WiringPiI2CRead(FileDescriptor);
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(Read));
buffer[i] = (byte)result;
}
return buffer;
}
}
/// <summary>
/// Reads a buffer of the specified length, one byte at a time.
/// </summary>
/// <param name="length">The length.</param>
/// <returns>The byte array from device.</returns>
public Task<byte[]> ReadAsync(int length) => Task.Run(() => Read(length));
/// <summary>
/// Writes a byte of data the specified file descriptor.
/// </summary>
/// <param name="data">The data.</param>
public void Write(byte data)
{
lock (_syncLock)
{
var result = WiringPi.WiringPiI2CWrite(FileDescriptor, data);
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(Write));
}
}
/// <summary>
/// Writes a byte of data the specified file descriptor.
/// </summary>
/// <param name="data">The data.</param>
/// <returns>The awaitable task.</returns>
public Task WriteAsync(byte data) => Task.Run(() => { Write(data); });
/// <summary>
/// Writes a set of bytes to the specified file descriptor.
/// </summary>
/// <param name="data">The data.</param>
public void Write(byte[] data)
{
lock (_syncLock)
{
foreach (var b in data)
{
var result = WiringPi.WiringPiI2CWrite(FileDescriptor, b);
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(Write));
}
}
}
/// <summary>
/// Writes a set of bytes to the specified file descriptor.
/// </summary>
/// <param name="data">The data.</param>
/// <returns>The awaitable task.</returns>
public Task WriteAsync(byte[] data) => Task.Run(() => { Write(data); });
/// <summary>
/// These write an 8 or 16-bit data value into the device register indicated.
/// </summary>
/// <param name="address">The register.</param>
/// <param name="data">The data.</param>
public void WriteAddressByte(int address, byte data)
{
lock (_syncLock)
{
var result = WiringPi.WiringPiI2CWriteReg8(FileDescriptor, address, data);
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(WriteAddressByte));
}
}
/// <summary>
/// These write an 8 or 16-bit data value into the device register indicated.
/// </summary>
/// <param name="address">The register.</param>
/// <param name="data">The data.</param>
public void WriteAddressWord(int address, ushort data)
{
lock (_syncLock)
{
var result = WiringPi.WiringPiI2CWriteReg16(FileDescriptor, address, data);
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(WriteAddressWord));
}
}
/// <summary>
/// These read an 8 or 16-bit value from the device register indicated.
/// </summary>
/// <param name="address">The register.</param>
/// <returns>The address byte from device.</returns>
public byte ReadAddressByte(int address)
{
lock (_syncLock)
{
var result = WiringPi.WiringPiI2CReadReg8(FileDescriptor, address);
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(ReadAddressByte));
return (byte)result;
}
}
/// <summary>
/// These read an 8 or 16-bit value from the device register indicated.
/// </summary>
/// <param name="address">The register.</param>
/// <returns>The address word from device.</returns>
public ushort ReadAddressWord(int address)
{
lock (_syncLock)
{
var result = WiringPi.WiringPiI2CReadReg16(FileDescriptor, address);
if (result < 0) HardwareException.Throw(nameof(I2CDevice), nameof(ReadAddressWord));
return Convert.ToUInt16(result);
}
}
}
}
+12
View File
@@ -0,0 +1,12 @@
namespace Unosquare.WiringPi.Native
{
/// <summary>
/// A delegate defining a callback for an Interrupt Service Routine.
/// </summary>
public delegate void InterruptServiceRoutineCallback();
/// <summary>
/// Defines the body of a thread worker.
/// </summary>
public delegate void ThreadWorker();
}
+19
View File
@@ -0,0 +1,19 @@
namespace Unosquare.WiringPi.Native
{
using System;
using System.Runtime.InteropServices;
internal static class SysCall
{
internal const string LibCLibrary = "libc";
[DllImport(LibCLibrary, EntryPoint = "chmod", SetLastError = true)]
public static extern int Chmod(string filename, uint mode);
[DllImport(LibCLibrary, EntryPoint = "strtol", SetLastError = true)]
public static extern int StringToInteger(string numberString, IntPtr endPointer, int numberBase);
[DllImport(LibCLibrary, EntryPoint = "write", SetLastError = true)]
public static extern int Write(int fd, byte[] buffer, int count);
}
}
+74
View File
@@ -0,0 +1,74 @@
namespace Unosquare.WiringPi.Native
{
using System.Runtime.InteropServices;
public partial class WiringPi
{
/// <summary>
/// Simple device read. Some devices present data when you read them without having to do any register transactions.
/// </summary>
/// <param name="fd">The fd.</param>
/// <returns>The result.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CRead", SetLastError = true)]
public static extern int WiringPiI2CRead(int fd);
/// <summary>
/// These read an 8-bit value from the device register indicated.
/// </summary>
/// <param name="fd">The fd.</param>
/// <param name="reg">The reg.</param>
/// <returns>The result.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CReadReg8", SetLastError = true)]
public static extern int WiringPiI2CReadReg8(int fd, int reg);
/// <summary>
/// These read a 16-bit value from the device register indicated.
/// </summary>
/// <param name="fd">The fd.</param>
/// <param name="reg">The reg.</param>
/// <returns>The result.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CReadReg16", SetLastError = true)]
public static extern int WiringPiI2CReadReg16(int fd, int reg);
/// <summary>
/// Simple device write. Some devices accept data this way without needing to access any internal registers.
/// </summary>
/// <param name="fd">The fd.</param>
/// <param name="data">The data.</param>
/// <returns>The result.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWrite", SetLastError = true)]
public static extern int WiringPiI2CWrite(int fd, int data);
/// <summary>
/// These write an 8-bit data value into the device register indicated.
/// </summary>
/// <param name="fd">The fd.</param>
/// <param name="reg">The reg.</param>
/// <param name="data">The data.</param>
/// <returns>The result.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWriteReg8", SetLastError = true)]
public static extern int WiringPiI2CWriteReg8(int fd, int reg, int data);
/// <summary>
/// These write a 16-bit data value into the device register indicated.
/// </summary>
/// <param name="fd">The fd.</param>
/// <param name="reg">The reg.</param>
/// <param name="data">The data.</param>
/// <returns>The result.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CWriteReg16", SetLastError = true)]
public static extern int WiringPiI2CWriteReg16(int fd, int reg, int data);
/// <summary>
/// This initializes the I2C system with your given device identifier.
/// The ID is the I2C number of the device and you can use the i2cdetect program to find this out. wiringPiI2CSetup()
/// will work out which revision Raspberry Pi you have and open the appropriate device in /dev.
/// The return value is the standard Linux filehandle, or -1 if any error – in which case, you can consult errno as usual.
/// E.g. the popular MCP23017 GPIO expander is usually device Id 0x20, so this is the number you would pass into wiringPiI2CSetup().
/// </summary>
/// <param name="devId">The dev identifier.</param>
/// <returns>The result.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiI2CSetup", SetLastError = true)]
public static extern int WiringPiI2CSetup(int devId);
}
}
@@ -0,0 +1,69 @@
namespace Unosquare.WiringPi.Native
{
using System.Runtime.InteropServices;
public partial class WiringPi
{
/// <summary>
/// This opens and initialises the serial device and sets the baud rate. It sets the port into “raw” mode (character at a time and no translations),
/// and sets the read timeout to 10 seconds. The return value is the file descriptor or -1 for any error, in which case errno will be set as appropriate.
/// The wiringSerial library is intended to provide simplified control – suitable for most applications, however if you need advanced control
/// – e.g. parity control, modem control lines (via a USB adapter, there are none on the Pi’s on-board UART!) and so on,
/// then you need to do some of this the old fashioned way.
/// </summary>
/// <param name="device">The device.</param>
/// <param name="baud">The baud.</param>
/// <returns>The result.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "serialOpen", SetLastError = true)]
public static extern int SerialOpen(string device, int baud);
/// <summary>
/// Closes the device identified by the file descriptor given.
/// </summary>
/// <param name="fd">The fd.</param>
/// <returns>The result.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "serialClose", SetLastError = true)]
public static extern int SerialClose(int fd);
/// <summary>
/// Sends the single byte to the serial device identified by the given file descriptor.
/// </summary>
/// <param name="fd">The fd.</param>
/// <param name="c">The c.</param>
[DllImport(WiringPiLibrary, EntryPoint = "serialPutchar", SetLastError = true)]
public static extern void SerialPutchar(int fd, byte c);
/// <summary>
/// Sends the nul-terminated string to the serial device identified by the given file descriptor.
/// </summary>
/// <param name="fd">The fd.</param>
/// <param name="s">The s.</param>
[DllImport(WiringPiLibrary, EntryPoint = "serialPuts", SetLastError = true)]
public static extern void SerialPuts(int fd, string s);
/// <summary>
/// Returns the number of characters available for reading, or -1 for any error condition,
/// in which case errno will be set appropriately.
/// </summary>
/// <param name="fd">The fd.</param>
/// <returns>The result.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "serialDataAvail", SetLastError = true)]
public static extern int SerialDataAvail(int fd);
/// <summary>
/// Returns the next character available on the serial device.
/// This call will block for up to 10 seconds if no data is available (when it will return -1).
/// </summary>
/// <param name="fd">The fd.</param>
/// <returns>The result.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "serialGetchar", SetLastError = true)]
public static extern int SerialGetchar(int fd);
/// <summary>
/// This discards all data received, or waiting to be send down the given device.
/// </summary>
/// <param name="fd">The fd.</param>
[DllImport(WiringPiLibrary, EntryPoint = "serialFlush", SetLastError = true)]
public static extern void SerialFlush(int fd);
}
}
@@ -0,0 +1,36 @@
namespace Unosquare.WiringPi.Native
{
using System.Runtime.InteropServices;
public partial class WiringPi
{
#region WiringPi - Shift Library
/// <summary>
/// This shifts an 8-bit data value in with the data appearing on the dPin and the clock being sent out on the cPin.
/// Order is either LSBFIRST or MSBFIRST. The data is sampled after the cPin goes high.
/// (So cPin high, sample data, cPin low, repeat for 8 bits) The 8-bit value is returned by the function.
/// </summary>
/// <param name="dPin">The d pin.</param>
/// <param name="cPin">The c pin.</param>
/// <param name="order">The order.</param>
/// <returns>The result.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "shiftIn", SetLastError = true)]
public static extern byte ShiftIn(byte dPin, byte cPin, byte order);
/// <summary>
/// The shifts an 8-bit data value val out with the data being sent out on dPin and the clock being sent out on the cPin.
/// order is as above. Data is clocked out on the rising or falling edge – ie. dPin is set, then cPin is taken high then low
/// – repeated for the 8 bits.
/// </summary>
/// <param name="dPin">The d pin.</param>
/// <param name="cPin">The c pin.</param>
/// <param name="order">The order.</param>
/// <param name="val">The value.</param>
[DllImport(WiringPiLibrary, EntryPoint = "shiftOut", SetLastError = true)]
public static extern void ShiftOut(byte dPin, byte cPin, byte order, byte val);
#endregion
}
}
@@ -0,0 +1,64 @@
namespace Unosquare.WiringPi.Native
{
using System.Runtime.InteropServices;
public partial class WiringPi
{
#region WiringPi - Soft PWM (https://github.com/WiringPi/WiringPi/blob/master/wiringPi/softPwm.h)
/// <summary>
/// This creates a software controlled PWM pin. You can use any GPIO pin and the pin numbering will be that of the wiringPiSetup()
/// function you used. Use 100 for the pwmRange, then the value can be anything from 0 (off) to 100 (fully on) for the given pin.
/// The return value is 0 for success. Anything else and you should check the global errno variable to see what went wrong.
/// </summary>
/// <param name="pin">The pin.</param>
/// <param name="initialValue">The initial value.</param>
/// <param name="pwmRange">The PWM range.</param>
/// <returns>The result.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "softPwmCreate", SetLastError = true)]
public static extern int SoftPwmCreate(int pin, int initialValue, int pwmRange);
/// <summary>
/// This updates the PWM value on the given pin. The value is checked to be in-range and pins that haven’t previously
/// been initialized via softPwmCreate will be silently ignored.
/// </summary>
/// <param name="pin">The pin.</param>
/// <param name="value">The value.</param>
[DllImport(WiringPiLibrary, EntryPoint = "softPwmWrite", SetLastError = true)]
public static extern void SoftPwmWrite(int pin, int value);
/// <summary>
/// This function is undocumented.
/// </summary>
/// <param name="pin">The pin.</param>
[DllImport(WiringPiLibrary, EntryPoint = "softPwmStop", SetLastError = true)]
public static extern void SoftPwmStop(int pin);
/// <summary>
/// This creates a software controlled tone pin. You can use any GPIO pin and the pin numbering will be that of the wiringPiSetup() function you used.
/// The return value is 0 for success. Anything else and you should check the global errno variable to see what went wrong.
/// </summary>
/// <param name="pin">The pin.</param>
/// <returns>The result.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "softToneCreate", SetLastError = true)]
public static extern int SoftToneCreate(int pin);
/// <summary>
/// This function is undocumented.
/// </summary>
/// <param name="pin">The pin.</param>
[DllImport(WiringPiLibrary, EntryPoint = "softToneStop", SetLastError = true)]
public static extern void SoftToneStop(int pin);
/// <summary>
/// This updates the tone frequency value on the given pin. The tone will be played until you set the frequency to 0.
/// </summary>
/// <param name="pin">The pin.</param>
/// <param name="freq">The freq.</param>
[DllImport(WiringPiLibrary, EntryPoint = "softToneWrite", SetLastError = true)]
public static extern void SoftToneWrite(int pin, int freq);
#endregion
}
}
+53
View File
@@ -0,0 +1,53 @@
namespace Unosquare.WiringPi.Native
{
using System.Runtime.InteropServices;
public partial class WiringPi
{
#region WiringPi - SPI Library Calls
/// <summary>
/// This function is undocumented.
/// </summary>
/// <param name="channel">The channel.</param>
/// <returns>Unknown.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPIGetFd", SetLastError = true)]
public static extern int WiringPiSPIGetFd(int channel);
/// <summary>
/// This performs a simultaneous write/read transaction over the selected SPI bus. Data that was in your buffer is overwritten by data returned from the SPI bus.
/// That’s all there is in the helper library. It is possible to do simple read and writes over the SPI bus using the standard read() and write() system calls though –
/// write() may be better to use for sending data to chains of shift registers, or those LED strings where you send RGB triplets of data.
/// Devices such as A/D and D/A converters usually need to perform a concurrent write/read transaction to work.
/// </summary>
/// <param name="channel">The channel.</param>
/// <param name="data">The data.</param>
/// <param name="len">The length.</param>
/// <returns>The result.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPIDataRW", SetLastError = true)]
public static extern int WiringPiSPIDataRW(int channel, byte[] data, int len);
/// <summary>
/// This function is undocumented.
/// </summary>
/// <param name="channel">The channel.</param>
/// <param name="speed">The speed.</param>
/// <param name="mode">The mode.</param>
/// <returns>Unkown.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPISetupMode", SetLastError = true)]
public static extern int WiringPiSPISetupMode(int channel, int speed, int mode);
/// <summary>
/// This is the way to initialize a channel (The Pi has 2 channels; 0 and 1). The speed parameter is an integer
/// in the range 500,000 through 32,000,000 and represents the SPI clock speed in Hz.
/// The returned value is the Linux file-descriptor for the device, or -1 on error. If an error has happened, you may use the standard errno global variable to see why.
/// </summary>
/// <param name="channel">The channel.</param>
/// <param name="speed">The speed.</param>
/// <returns>The Linux file descriptor for the device or -1 for error.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSPISetup", SetLastError = true)]
public static extern int WiringPiSPISetup(int channel, int speed);
#endregion
}
}
+376
View File
@@ -0,0 +1,376 @@
namespace Unosquare.WiringPi.Native
{
using System.Runtime.InteropServices;
/// <summary>
/// Provides native C WiringPi Library function call wrappers
/// All credit for the native library goes to the author of http://wiringpi.com/
/// The wrappers were written based on https://github.com/WiringPi/WiringPi/blob/master/wiringPi/wiringPi.h.
/// </summary>
public partial class WiringPi
{
internal const string WiringPiLibrary = "libwiringPi.so.2.50";
#region WiringPi - Core Functions (https://github.com/WiringPi/WiringPi/blob/master/wiringPi/wiringPi.h)
/// <summary>
/// This initialises wiringPi and assumes that the calling program is going to be using the wiringPi pin numbering scheme.
/// This is a simplified numbering scheme which provides a mapping from virtual pin numbers 0 through 16 to the real underlying Broadcom GPIO pin numbers.
/// See the pins page for a table which maps the wiringPi pin number to the Broadcom GPIO pin number to the physical location on the edge connector.
/// This function needs to be called with root privileges.
/// </summary>
/// <returns>The result code.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetup", SetLastError = true)]
public static extern int WiringPiSetup();
/// <summary>
/// This initialises wiringPi but uses the /sys/class/gpio interface rather than accessing the hardware directly.
/// This can be called as a non-root user provided the GPIO pins have been exported before-hand using the gpio program.
/// Pin numbering in this mode is the native Broadcom GPIO numbers – the same as wiringPiSetupGpio() above,
/// so be aware of the differences between Rev 1 and Rev 2 boards.
///
/// Note: In this mode you can only use the pins which have been exported via the /sys/class/gpio interface before you run your program.
/// You can do this in a separate shell-script, or by using the system() function from inside your program to call the gpio program.
/// Also note that some functions have no effect when using this mode as they’re not currently possible to action unless called with root privileges.
/// (although you can use system() to call gpio to set/change modes if needed).
/// </summary>
/// <returns>The result code.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupSys", SetLastError = true)]
public static extern int WiringPiSetupSys();
/// <summary>
/// This is identical to wiringPiSetup, however it allows the calling programs to use the Broadcom GPIO
/// pin numbers directly with no re-mapping.
/// As above, this function needs to be called with root privileges, and note that some pins are different
/// from revision 1 to revision 2 boards.
/// </summary>
/// <returns>The result code.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupGpio", SetLastError = true)]
public static extern int WiringPiSetupGpio();
/// <summary>
/// Identical to wiringPiSetup, however it allows the calling programs to use the physical pin numbers on the P1 connector only.
/// This function needs to be called with root privileges.
/// </summary>
/// <returns>The result code.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiSetupPhys", SetLastError = true)]
public static extern int WiringPiSetupPhys();
/// <summary>
/// This function is undocumented.
/// </summary>
/// <param name="pin">The pin.</param>
/// <param name="mode">The mode.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pinModeAlt", SetLastError = true)]
public static extern void PinModeAlt(int pin, int mode);
/// <summary>
/// This sets the mode of a pin to either INPUT, OUTPUT, PWM_OUTPUT or GPIO_CLOCK.
/// Note that only wiringPi pin 1 (BCM_GPIO 18) supports PWM output and only wiringPi pin 7 (BCM_GPIO 4)
/// supports CLOCK output modes.
///
/// This function has no effect when in Sys mode. If you need to change the pin mode, then you can
/// do it with the gpio program in a script before you start your program.
/// </summary>
/// <param name="pin">The pin.</param>
/// <param name="mode">The mode.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pinMode", SetLastError = true)]
public static extern void PinMode(int pin, int mode);
/// <summary>
/// This sets the pull-up or pull-down resistor mode on the given pin, which should be set as an input.
/// Unlike the Arduino, the BCM2835 has both pull-up and down internal resistors. The parameter pud should be; PUD_OFF,
/// (no pull up/down), PUD_DOWN (pull to ground) or PUD_UP (pull to 3.3v) The internal pull up/down resistors
/// have a value of approximately 50KΩ on the Raspberry Pi.
///
/// This function has no effect on the Raspberry Pi’s GPIO pins when in Sys mode.
/// If you need to activate a pull-up/pull-down, then you can do it with the gpio program in a script before you start your program.
/// </summary>
/// <param name="pin">The pin.</param>
/// <param name="pud">The pud.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pullUpDnControl", SetLastError = true)]
public static extern void PullUpDnControl(int pin, int pud);
/// <summary>
/// This function returns the value read at the given pin. It will be HIGH or LOW (1 or 0) depending on the logic level at the pin.
/// </summary>
/// <param name="pin">The pin.</param>
/// <returns>The result code.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "digitalRead", SetLastError = true)]
public static extern int DigitalRead(int pin);
/// <summary>
/// Writes the value HIGH or LOW (1 or 0) to the given pin which must have been previously set as an output.
/// WiringPi treats any non-zero number as HIGH, however 0 is the only representation of LOW.
/// </summary>
/// <param name="pin">The pin.</param>
/// <param name="value">The value.</param>
[DllImport(WiringPiLibrary, EntryPoint = "digitalWrite", SetLastError = true)]
public static extern void DigitalWrite(int pin, int value);
/// <summary>
/// Writes the value to the PWM register for the given pin. The Raspberry Pi has one
/// on-board PWM pin, pin 1 (BMC_GPIO 18, Phys 12) and the range is 0-1024.
/// Other PWM devices may have other PWM ranges.
/// This function is not able to control the Pi’s on-board PWM when in Sys mode.
/// </summary>
/// <param name="pin">The pin.</param>
/// <param name="value">The value.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pwmWrite", SetLastError = true)]
public static extern void PwmWrite(int pin, int value);
/// <summary>
/// This returns the value read on the supplied analog input pin. You will need to
/// register additional analog modules to enable this function for devices such as the Gertboard, quick2Wire analog board, etc.
/// </summary>
/// <param name="pin">The pin.</param>
/// <returns>The result code.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "analogRead", SetLastError = true)]
public static extern int AnalogRead(int pin);
/// <summary>
/// This writes the given value to the supplied analog pin. You will need to register additional
/// analog modules to enable this function for devices such as the Gertboard.
/// </summary>
/// <param name="pin">The pin.</param>
/// <param name="value">The value.</param>
[DllImport(WiringPiLibrary, EntryPoint = "analogWrite", SetLastError = true)]
public static extern void AnalogWrite(int pin, int value);
/// <summary>
/// This returns the board revision of the Raspberry Pi. It will be either 1 or 2. Some of the BCM_GPIO pins changed number and
/// function when moving from board revision 1 to 2, so if you are using BCM_GPIO pin numbers, then you need to be aware of the differences.
/// </summary>
/// <returns>The result code.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "piBoardRev", SetLastError = true)]
public static extern int PiBoardRev();
/// <summary>
/// This function is undocumented.
/// </summary>
/// <param name="model">The model.</param>
/// <param name="mem">The memory.</param>
/// <param name="maker">The maker.</param>
/// <param name="overVolted">The over volted.</param>
/// <returns>The result code.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "piBoardId", SetLastError = true)]
public static extern int PiBoardId(ref int model, ref int mem, ref int maker, ref int overVolted);
/// <summary>
/// This returns the BCM_GPIO pin number of the supplied wiringPi pin. It takes the board revision into account.
/// </summary>
/// <param name="wPiPin">The w pi pin.</param>
/// <returns>The result code.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wpiPinToGpio", SetLastError = true)]
public static extern int WpiPinToGpio(int wPiPin);
/// <summary>
/// This returns the BCM_GPIO pin number of the supplied physical pin on the P1 connector.
/// </summary>
/// <param name="physPin">The physical pin.</param>
/// <returns>The result code.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "physPinToGpio", SetLastError = true)]
public static extern int PhysPinToGpio(int physPin);
/// <summary>
/// This sets the “strength” of the pad drivers for a particular group of pins.
/// There are 3 groups of pins and the drive strength is from 0 to 7. Do not use this unless you know what you are doing.
/// </summary>
/// <param name="group">The group.</param>
/// <param name="value">The value.</param>
/// <returns>The result code.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "setPadDrive", SetLastError = true)]
public static extern int SetPadDrive(int group, int value);
/// <summary>
/// Undocumented function.
/// </summary>
/// <param name="pin">The pin.</param>
/// <returns>The result code.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "getAlt", SetLastError = true)]
public static extern int GetAlt(int pin);
/// <summary>
/// Undocumented function.
/// </summary>
/// <param name="pin">The pin.</param>
/// <param name="freq">The freq.</param>
/// <returns>The result code.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "pwmToneWrite", SetLastError = true)]
public static extern int PwmToneWrite(int pin, int freq);
/// <summary>
/// This writes the 8-bit byte supplied to the first 8 GPIO pins.
/// It’s the fastest way to set all 8 bits at once to a particular value, although it still takes two write operations to the Pi’s GPIO hardware.
/// </summary>
/// <param name="value">The value.</param>
[DllImport(WiringPiLibrary, EntryPoint = "digitalWriteByte", SetLastError = true)]
public static extern void DigitalWriteByte(int value);
/// <summary>
/// This writes the 8-bit byte supplied to the first 8 GPIO pins.
/// It’s the fastest way to set all 8 bits at once to a particular value, although it still takes two write operations to the Pi’s GPIO hardware.
/// </summary>
/// <param name="value">The value.</param>
[DllImport(WiringPiLibrary, EntryPoint = "digitalWriteByte2", SetLastError = true)]
public static extern void DigitalWriteByte2(int value);
/// <summary>
/// Undocumented function
/// This reads the 8-bit byte supplied to the first 8 GPIO pins.
/// It’s the fastest way to get all 8 bits at once to a particular value.
/// </summary>
/// <returns>The result code.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "digitalReadByte", SetLastError = true)]
public static extern uint DigitalReadByte();
/// <summary>
/// Undocumented function
/// This reads the 8-bit byte supplied to the first 8 GPIO pins.
/// It’s the fastest way to get all 8 bits at once to a particular value.
/// </summary>
/// <returns>The result code.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "digitalReadByte2", SetLastError = true)]
public static extern uint DigitalReadByte2();
/// <summary>
/// The PWM generator can run in 2 modes – “balanced” and “mark:space”. The mark:space mode is traditional,
/// however the default mode in the Pi is “balanced”. You can switch modes by supplying the parameter: PWM_MODE_BAL or PWM_MODE_MS.
/// </summary>
/// <param name="mode">The mode.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pwmSetMode", SetLastError = true)]
public static extern void PwmSetMode(int mode);
/// <summary>
/// This sets the range register in the PWM generator. The default is 1024.
/// </summary>
/// <param name="range">The range.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pwmSetRange", SetLastError = true)]
public static extern void PwmSetRange(uint range);
/// <summary>
/// This sets the divisor for the PWM clock.
/// Note: The PWM control functions can not be used when in Sys mode.
/// To understand more about the PWM system, you’ll need to read the Broadcom ARM peripherals manual.
/// </summary>
/// <param name="divisor">The divisor.</param>
[DllImport(WiringPiLibrary, EntryPoint = "pwmSetClock", SetLastError = true)]
public static extern void PwmSetClock(int divisor);
/// <summary>
/// Undocumented function.
/// </summary>
/// <param name="pin">The pin.</param>
/// <param name="freq">The freq.</param>
[DllImport(WiringPiLibrary, EntryPoint = "gpioClockSet", SetLastError = true)]
public static extern void GpioClockSet(int pin, int freq);
/// <summary>
/// This function registers a function to received interrupts on the specified pin.
/// The edgeType parameter is either INT_EDGE_FALLING, INT_EDGE_RISING, INT_EDGE_BOTH or INT_EDGE_SETUP.
/// If it is INT_EDGE_SETUP then no initialisation of the pin will happen – it’s assumed that you have already setup the pin elsewhere
/// (e.g. with the gpio program), but if you specify one of the other types, then the pin will be exported and initialised as specified.
/// This is accomplished via a suitable call to the gpio utility program, so it need to be available.
/// The pin number is supplied in the current mode – native wiringPi, BCM_GPIO, physical or Sys modes.
/// This function will work in any mode, and does not need root privileges to work.
/// The function will be called when the interrupt triggers. When it is triggered, it’s cleared in the dispatcher before calling your function,
/// so if a subsequent interrupt fires before you finish your handler, then it won’t be missed. (However it can only track one more interrupt,
/// if more than one interrupt fires while one is being handled then they will be ignored)
/// This function is run at a high priority (if the program is run using sudo, or as root) and executes concurrently with the main program.
/// It has full access to all the global variables, open file handles and so on.
/// </summary>
/// <param name="pin">The pin.</param>
/// <param name="mode">The mode.</param>
/// <param name="method">The method.</param>
/// <returns>The result code.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "wiringPiISR", SetLastError = true)]
public static extern int WiringPiISR(int pin, int mode, InterruptServiceRoutineCallback method);
/// <summary>
/// This function creates a thread which is another function in your program previously declared using the PI_THREAD declaration.
/// This function is then run concurrently with your main program. An example may be to have this function wait for an interrupt while
/// your program carries on doing other tasks. The thread can indicate an event, or action by using global variables to
/// communicate back to the main program, or other threads.
/// </summary>
/// <param name="method">The method.</param>
/// <returns>The result code.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "piThreadCreate", SetLastError = true)]
public static extern int PiThreadCreate(ThreadWorker method);
/// <summary>
/// These allow you to synchronise variable updates from your main program to any threads running in your program. keyNum is a number from 0 to 3 and represents a key.
/// When another process tries to lock the same key, it will be stalled until the first process has unlocked the same key.
/// You may need to use these functions to ensure that you get valid data when exchanging data between your main program and a thread
/// – otherwise it’s possible that the thread could wake-up halfway during your data copy and change the data –
/// so the data you end up copying is incomplete, or invalid. See the wfi.c program in the examples directory for an example.
/// </summary>
/// <param name="key">The key.</param>
[DllImport(WiringPiLibrary, EntryPoint = "piLock", SetLastError = true)]
public static extern void PiLock(int key);
/// <summary>
/// These allow you to synchronise variable updates from your main program to any threads running in your program. keyNum is a number from 0 to 3 and represents a key.
/// When another process tries to lock the same key, it will be stalled until the first process has unlocked the same key.
/// You may need to use these functions to ensure that you get valid data when exchanging data between your main program and a thread
/// – otherwise it’s possible that the thread could wake-up halfway during your data copy and change the data –
/// so the data you end up copying is incomplete, or invalid. See the wfi.c program in the examples directory for an example.
/// </summary>
/// <param name="key">The key.</param>
[DllImport(WiringPiLibrary, EntryPoint = "piUnlock", SetLastError = true)]
public static extern void PiUnlock(int key);
/// <summary>
/// This attempts to shift your program (or thread in a multi-threaded program) to a higher priority
/// and enables a real-time scheduling. The priority parameter should be from 0 (the default) to 99 (the maximum).
/// This won’t make your program go any faster, but it will give it a bigger slice of time when other programs are running.
/// The priority parameter works relative to others – so you can make one program priority 1 and another priority 2
/// and it will have the same effect as setting one to 10 and the other to 90 (as long as no other
/// programs are running with elevated priorities)
/// The return value is 0 for success and -1 for error. If an error is returned, the program should then consult the errno global variable, as per the usual conventions.
/// Note: Only programs running as root can change their priority. If called from a non-root program then nothing happens.
/// </summary>
/// <param name="priority">The priority.</param>
/// <returns>The result code.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "piHiPri", SetLastError = true)]
public static extern int PiHiPri(int priority);
/// <summary>
/// This causes program execution to pause for at least howLong milliseconds.
/// Due to the multi-tasking nature of Linux it could be longer.
/// Note that the maximum delay is an unsigned 32-bit integer or approximately 49 days.
/// </summary>
/// <param name="howLong">The how long.</param>
[DllImport(WiringPiLibrary, EntryPoint = "delay", SetLastError = true)]
public static extern void Delay(uint howLong);
/// <summary>
/// This causes program execution to pause for at least howLong microseconds.
/// Due to the multi-tasking nature of Linux it could be longer.
/// Note that the maximum delay is an unsigned 32-bit integer microseconds or approximately 71 minutes.
/// Delays under 100 microseconds are timed using a hard-coded loop continually polling the system time,
/// Delays over 100 microseconds are done using the system nanosleep() function – You may need to consider the implications
/// of very short delays on the overall performance of the system, especially if using threads.
/// </summary>
/// <param name="howLong">The how long.</param>
[DllImport(WiringPiLibrary, EntryPoint = "delayMicroseconds", SetLastError = true)]
public static extern void DelayMicroseconds(uint howLong);
/// <summary>
/// This returns a number representing the number of milliseconds since your program called one of the wiringPiSetup functions.
/// It returns an unsigned 32-bit number which wraps after 49 days.
/// </summary>
/// <returns>The result code.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "millis", SetLastError = true)]
public static extern uint Millis();
/// <summary>
/// This returns a number representing the number of microseconds since your program called one of
/// the wiringPiSetup functions. It returns an unsigned 32-bit number which wraps after approximately 71 minutes.
/// </summary>
/// <returns>The result code.</returns>
[DllImport(WiringPiLibrary, EntryPoint = "micros", SetLastError = true)]
public static extern uint Micros();
#endregion
}
}
@@ -0,0 +1,65 @@
namespace Unosquare.WiringPi.Resources
{
using Native;
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Reflection;
/// <summary>
/// Provides access to embedded assembly files.
/// </summary>
internal static class EmbeddedResources
{
/// <summary>
/// Initializes static members of the <see cref="EmbeddedResources"/> class.
/// </summary>
static EmbeddedResources()
{
ResourceNames =
new ReadOnlyCollection<string>(typeof(EmbeddedResources).Assembly.GetManifestResourceNames());
}
/// <summary>
/// Gets the resource names.
/// </summary>
/// <value>
/// The resource names.
/// </value>
public static ReadOnlyCollection<string> ResourceNames { get; }
/// <summary>
/// Extracts all the file resources to the specified base path.
/// </summary>
public static void ExtractAll()
{
var basePath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
var executablePermissions = SysCall.StringToInteger("0777", IntPtr.Zero, 8);
foreach (var resourceName in ResourceNames)
{
var filename = resourceName.Substring($"{typeof(EmbeddedResources).Namespace}.".Length);
var targetPath = Path.Combine(basePath, filename);
if (File.Exists(targetPath)) return;
using (var stream = typeof(EmbeddedResources).Assembly
.GetManifestResourceStream(resourceName))
{
using (var outputStream = File.OpenWrite(targetPath))
{
stream?.CopyTo(outputStream);
}
try
{
SysCall.Chmod(targetPath, (uint)executablePermissions);
}
catch
{
/* Ignore */
}
}
}
}
}
}
Binary file not shown.
Binary file not shown.
+43
View File
@@ -0,0 +1,43 @@
namespace Unosquare.WiringPi
{
using RaspberryIO.Abstractions;
/// <summary>
/// The SPI Bus containing the 2 SPI channels.
/// </summary>
public class SpiBus : ISpiBus
{
/// <inheritdoc />
public int Channel0Frequency { get; set; }
/// <inheritdoc />
public int Channel1Frequency { get; set; }
/// <inheritdoc />
public int DefaultFrequency => 8000000;
/// <inheritdoc />
public ISpiChannel Channel0
{
get
{
if (Channel0Frequency == 0)
Channel0Frequency = DefaultFrequency;
return SpiChannel.Retrieve(SpiChannelNumber.Channel0, Channel0Frequency);
}
}
/// <inheritdoc />
public ISpiChannel Channel1
{
get
{
if (Channel1Frequency == 0)
Channel1Frequency = DefaultFrequency;
return SpiChannel.Retrieve(SpiChannelNumber.Channel1, Channel1Frequency);
}
}
}
}
+130
View File
@@ -0,0 +1,130 @@
namespace Unosquare.WiringPi
{
using Native;
using RaspberryIO.Abstractions;
using RaspberryIO.Abstractions.Native;
using Swan;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
/// <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
{
/// <summary>
/// The minimum frequency of a SPI Channel.
/// </summary>
public const int MinFrequency = 500000;
/// <summary>
/// The maximum frequency of a SPI channel.
/// </summary>
public const int 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, 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());
}
}
}
/// <inheritdoc />
public int FileDescriptor { get; }
/// <inheritdoc />
public int Channel { get; }
/// <inheritdoc />
public int Frequency { get; }
/// <inheritdoc />
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;
}
}
/// <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(() => SendReceive(buffer));
/// <inheritdoc />
public void Write(byte[] buffer)
{
lock (_syncLock)
{
var result = SysCall.Write(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(() => { 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, int frequency)
{
lock (SyncRoot)
{
if (Buses.ContainsKey(channel))
return Buses[channel];
var newBus = new SpiChannel(channel, frequency);
Buses[channel] = newBus;
return newBus;
}
}
}
}
+45
View File
@@ -0,0 +1,45 @@
namespace Unosquare.WiringPi
{
using Native;
using RaspberryIO.Abstractions;
using System;
/// <summary>
/// Represents the WiringPi system info.
/// </summary>
/// <seealso cref="ISystemInfo" />
public class SystemInfo : ISystemInfo
{
private static readonly object Lock = new object();
private static bool _revGetted;
private static BoardRevision _boardRevision = BoardRevision.Rev2;
/// <inheritdoc />
public BoardRevision BoardRevision => GetBoardRevision();
/// <inheritdoc />
public Version LibraryVersion
{
get
{
var libParts = WiringPi.WiringPiLibrary.Split('.');
var major = int.Parse(libParts[libParts.Length - 2]);
var minor = int.Parse(libParts[libParts.Length - 1]);
return new Version(major, minor);
}
}
internal static BoardRevision GetBoardRevision()
{
lock (Lock)
{
if (_revGetted) return _boardRevision;
var val = WiringPi.PiBoardRev();
_boardRevision = val == 1 ? BoardRevision.Rev1 : BoardRevision.Rev2;
_revGetted = true;
}
return _boardRevision;
}
}
}
+72
View File
@@ -0,0 +1,72 @@
namespace Unosquare.WiringPi
{
using Native;
using RaspberryIO.Abstractions;
using RaspberryIO.Abstractions.Native;
using Swan;
using System;
/// <summary>
/// Use this class to access threading methods using interop.
/// </summary>
/// <seealso cref="IThreading" />
public class Threading : IThreading
{
/// <summary>
/// This attempts to shift your program (or thread in a multi-threaded program) to a higher priority and
/// enables a real-time scheduling. The priority parameter should be from 0 (the default) to 99 (the maximum).
/// This won’t make your program go any faster, but it will give it a bigger slice of time when other programs
/// are running. The priority parameter works relative to others – so you can make one program priority 1 and
/// another priority 2 and it will have the same effect as setting one to 10 and the other to 90
/// (as long as no other programs are running with elevated priorities).
/// </summary>
/// <param name="priority">The priority.</param>
public void SetThreadPriority(int priority)
{
priority = priority.Clamp(0, 99);
var result = WiringPi.PiHiPri(priority);
if (result < 0) HardwareException.Throw(nameof(Timing), nameof(SetThreadPriority));
}
/// <summary>
/// These allow you to synchronize variable updates from your main program to any threads running in your program.
/// keyNum is a number from 0 to 3 and represents a “key”. When another process tries to lock the same key,
/// it will be stalled until the first process has unlocked the same key.
/// </summary>
/// <param name="key">The key.</param>
public void Lock(ThreadLockKey key) => WiringPi.PiLock((int)key);
/// <summary>
/// These allow you to synchronize variable updates from your main program to any threads running in your program.
/// keyNum is a number from 0 to 3 and represents a “key”. When another process tries to lock the same key,
/// it will be stalled until the first process has unlocked the same key.
/// </summary>
/// <param name="key">The key.</param>
public void Unlock(ThreadLockKey key) => WiringPi.PiUnlock((int)key);
/// <inheritdoc />
/// <summary>
/// This is really nothing more than a simplified interface to the Posix threads mechanism that Linux supports.
/// See the manual pages on Posix threads (man pthread) if you need more control over them.
/// </summary>
/// <exception cref="ArgumentNullException">worker.</exception>
public void StartThread(Action worker)
{
if (worker == null)
throw new ArgumentNullException(nameof(worker));
var result = WiringPi.PiThreadCreate(new ThreadWorker(worker));
if (result != 0)
HardwareException.Throw(nameof(Timing), nameof(StartThread));
}
/// <inheritdoc />
public UIntPtr StartThreadEx(Action<UIntPtr> worker, UIntPtr userData) =>
throw new NotSupportedException("WiringPi does only support a simple thread callback that has no parameters.");
/// <inheritdoc />
public void StopThreadEx(UIntPtr handle) =>
throw new NotSupportedException("WiringPi does not support stopping threads.");
}
}
+36
View File
@@ -0,0 +1,36 @@
namespace Unosquare.WiringPi
{
using Native;
using RaspberryIO.Abstractions;
/// <summary>
/// Provides access to timing and threading properties and methods.
/// </summary>
public class Timing : ITiming
{
/// <inheritdoc />
/// <summary>
/// This returns a number representing the number of milliseconds since your program
/// initialized the GPIO controller.
/// It returns an unsigned 32-bit number which wraps after 49 days.
/// </summary>
public uint Milliseconds => WiringPi.Millis();
/// <inheritdoc />
/// <summary>
/// This returns a number representing the number of microseconds since your
/// program initialized the GPIO controller
/// It returns an unsigned 32-bit number which wraps after approximately 71 minutes.
/// </summary>
public uint Microseconds => WiringPi.Micros();
/// <inheritdoc cref="ITiming.SleepMilliseconds(uint)" />
public static void Sleep(uint millis) => WiringPi.Delay(millis);
/// <inheritdoc />
public void SleepMilliseconds(uint millis) => Sleep(millis);
/// <inheritdoc />
public void SleepMicroseconds(uint micros) => WiringPi.DelayMicroseconds(micros);
}
}
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>This library uses WiringPi to enables developers to use the various Raspberry Pi's hardware modules including the Camera to capture images and video, the GPIO pins, and both, the SPI and I2C buses.</Description>
<Copyright>Unosquare (c) 2016-2019</Copyright>
<TargetFramework>netcoreapp3.0</TargetFramework>
<AssemblyName>Unosquare.WiringPi</AssemblyName>
<PackageId>Unosquare.WiringPi</PackageId>
<Version>0.4.2</Version>
<Authors>Unosquare</Authors>
<PackageIconUrl>https://github.com/unosquare/wiringpi-dotnet/raw/master/logos/raspberryio-logo-32.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/unosquare/wiringpi-dotnet</PackageProjectUrl>
<PackageLicenseUrl>https://raw.githubusercontent.com/unosquare/wiringpi-dotnet/master/LICENSE</PackageLicenseUrl>
<PackageTags>Raspberry Pi GPIO Camera SPI I2C Embedded IoT Mono C# .NET wiringPi</PackageTags>
<LangVersion>7.3</LangVersion>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\gpio" />
<EmbeddedResource Include="Resources\libwiringPi.so.2.50" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Swan\Swan.csproj" />
<ProjectReference Include="..\Unosquare.RaspberryIO.Abstractions\Unosquare.RaspberryIO.Abstractions.csproj" />
</ItemGroup>
</Project>