Init RaspberryIO
This commit is contained in:
@@ -0,0 +1,579 @@
|
||||
namespace Unosquare.RaspberryIO.Gpio
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the different drive modes of a GPIO pin
|
||||
/// </summary>
|
||||
public enum GpioPinDriveMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Input drive mode (perform reads)
|
||||
/// </summary>
|
||||
Input = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Output drive mode (perform writes)
|
||||
/// </summary>
|
||||
Output = 1,
|
||||
|
||||
/// <summary>
|
||||
/// PWM output mode (only certain pins support this -- 2 of them at the moment)
|
||||
/// </summary>
|
||||
PwmOutput = 2,
|
||||
|
||||
/// <summary>
|
||||
/// GPIO Clock output mode (only a pin supports this at this time)
|
||||
/// </summary>
|
||||
GpioClock = 3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The GPIO pin resistor mode. This is used on input pins so that their
|
||||
/// lines are not floating
|
||||
/// </summary>
|
||||
public enum GpioPinResistorPullMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Pull resistor not active. Line floating
|
||||
/// </summary>
|
||||
Off = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Pull resistor sets a default value of 0 on no-connects
|
||||
/// </summary>
|
||||
PullDown = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Pull resistor sets a default value of 1 on no-connects
|
||||
/// </summary>
|
||||
PullUp = 2,
|
||||
}
|
||||
|
||||
/// <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 the different edge detection modes for pin interrupts
|
||||
/// </summary>
|
||||
public enum EdgeDetection
|
||||
{
|
||||
/// <summary>
|
||||
/// Assumes edge detection was already setup externally
|
||||
/// </summary>
|
||||
ExternalSetup = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Falling Edge
|
||||
/// </summary>
|
||||
FallingEdge = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Rising edge
|
||||
/// </summary>
|
||||
RisingEdge = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Both, rising and falling edges
|
||||
/// </summary>
|
||||
RisingAndFallingEdges = 3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the GPIO Pin values 0 for low, 1 for High
|
||||
/// </summary>
|
||||
public enum GpioPinValue
|
||||
{
|
||||
/// <summary>
|
||||
/// Digital high
|
||||
/// </summary>
|
||||
High = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Digital low
|
||||
/// </summary>
|
||||
Low = 0
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the Header connectors available
|
||||
/// </summary>
|
||||
public enum GpioHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// Not defined
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// The P1 connector (main connector)
|
||||
/// </summary>
|
||||
P1,
|
||||
|
||||
/// <summary>
|
||||
/// The P5 connector (auxiliary, not commonly used)
|
||||
/// </summary>
|
||||
P5,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines all the available Wiring Pi Pin Numbers
|
||||
/// </summary>
|
||||
public enum WiringPiPin
|
||||
{
|
||||
/// <summary>
|
||||
/// The unknown
|
||||
/// </summary>
|
||||
Unknown = -1,
|
||||
|
||||
/// <summary>
|
||||
/// The pin00
|
||||
/// </summary>
|
||||
Pin00 = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The pin01
|
||||
/// </summary>
|
||||
Pin01 = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The pin02
|
||||
/// </summary>
|
||||
Pin02 = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The pin03
|
||||
/// </summary>
|
||||
Pin03 = 3,
|
||||
|
||||
/// <summary>
|
||||
/// The pin04
|
||||
/// </summary>
|
||||
Pin04 = 4,
|
||||
|
||||
/// <summary>
|
||||
/// The pin05
|
||||
/// </summary>
|
||||
Pin05 = 5,
|
||||
|
||||
/// <summary>
|
||||
/// The pin06
|
||||
/// </summary>
|
||||
Pin06 = 6,
|
||||
|
||||
/// <summary>
|
||||
/// The pin07
|
||||
/// </summary>
|
||||
Pin07 = 7,
|
||||
|
||||
/// <summary>
|
||||
/// The pin08
|
||||
/// </summary>
|
||||
Pin08 = 8,
|
||||
|
||||
/// <summary>
|
||||
/// The pin09
|
||||
/// </summary>
|
||||
Pin09 = 9,
|
||||
|
||||
/// <summary>
|
||||
/// The pin10
|
||||
/// </summary>
|
||||
Pin10 = 10,
|
||||
|
||||
/// <summary>
|
||||
/// The pin11
|
||||
/// </summary>
|
||||
Pin11 = 11,
|
||||
|
||||
/// <summary>
|
||||
/// The pin12
|
||||
/// </summary>
|
||||
Pin12 = 12,
|
||||
|
||||
/// <summary>
|
||||
/// The pin13
|
||||
/// </summary>
|
||||
Pin13 = 13,
|
||||
|
||||
/// <summary>
|
||||
/// The pin14
|
||||
/// </summary>
|
||||
Pin14 = 14,
|
||||
|
||||
/// <summary>
|
||||
/// The pin15
|
||||
/// </summary>
|
||||
Pin15 = 15,
|
||||
|
||||
/// <summary>
|
||||
/// The pin16
|
||||
/// </summary>
|
||||
Pin16 = 16,
|
||||
|
||||
/// <summary>
|
||||
/// The pin17
|
||||
/// </summary>
|
||||
Pin17 = 17,
|
||||
|
||||
/// <summary>
|
||||
/// The pin18
|
||||
/// </summary>
|
||||
Pin18 = 18,
|
||||
|
||||
/// <summary>
|
||||
/// The pin19
|
||||
/// </summary>
|
||||
Pin19 = 19,
|
||||
|
||||
/// <summary>
|
||||
/// The pin20
|
||||
/// </summary>
|
||||
Pin20 = 20,
|
||||
|
||||
/// <summary>
|
||||
/// The pin21
|
||||
/// </summary>
|
||||
Pin21 = 21,
|
||||
|
||||
/// <summary>
|
||||
/// The pin22
|
||||
/// </summary>
|
||||
Pin22 = 22,
|
||||
|
||||
/// <summary>
|
||||
/// The pin23
|
||||
/// </summary>
|
||||
Pin23 = 23,
|
||||
|
||||
/// <summary>
|
||||
/// The pin24
|
||||
/// </summary>
|
||||
Pin24 = 24,
|
||||
|
||||
/// <summary>
|
||||
/// The pin25
|
||||
/// </summary>
|
||||
Pin25 = 25,
|
||||
|
||||
/// <summary>
|
||||
/// The pin26
|
||||
/// </summary>
|
||||
Pin26 = 26,
|
||||
|
||||
/// <summary>
|
||||
/// The pin27
|
||||
/// </summary>
|
||||
Pin27 = 27,
|
||||
|
||||
/// <summary>
|
||||
/// The pin28
|
||||
/// </summary>
|
||||
Pin28 = 28,
|
||||
|
||||
/// <summary>
|
||||
/// The pin29
|
||||
/// </summary>
|
||||
Pin29 = 29,
|
||||
|
||||
/// <summary>
|
||||
/// The pin30
|
||||
/// </summary>
|
||||
Pin30 = 30,
|
||||
|
||||
/// <summary>
|
||||
/// The pin31
|
||||
/// </summary>
|
||||
Pin31 = 31,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the different pins on the P1 Header
|
||||
/// as commonly referenced by Raspberry Pi Documentation.
|
||||
/// Enumeration values correspond to the physical pin number.
|
||||
/// </summary>
|
||||
public enum P1
|
||||
{
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 02
|
||||
/// </summary>
|
||||
Gpio02 = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 03
|
||||
/// </summary>
|
||||
Gpio03 = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 04
|
||||
/// </summary>
|
||||
Gpio04 = 7,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 17
|
||||
/// </summary>
|
||||
Gpio17 = 11,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 27
|
||||
/// </summary>
|
||||
Gpio27 = 13,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 22
|
||||
/// </summary>
|
||||
Gpio22 = 15,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 10
|
||||
/// </summary>
|
||||
Gpio10 = 19,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 09
|
||||
/// </summary>
|
||||
Gpio09 = 21,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 11
|
||||
/// </summary>
|
||||
Gpio11 = 23,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 05
|
||||
/// </summary>
|
||||
Gpio05 = 29,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 06
|
||||
/// </summary>
|
||||
Gpio06 = 31,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 13
|
||||
/// </summary>
|
||||
Gpio13 = 33,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 19
|
||||
/// </summary>
|
||||
Gpio19 = 35,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 26
|
||||
/// </summary>
|
||||
Gpio26 = 37,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 14
|
||||
/// </summary>
|
||||
Gpio14 = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 15
|
||||
/// </summary>
|
||||
Gpio15 = 10,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 18
|
||||
/// </summary>
|
||||
Gpio18 = 12,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 23
|
||||
/// </summary>
|
||||
Gpio23 = 16,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 24
|
||||
/// </summary>
|
||||
Gpio24 = 18,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 25
|
||||
/// </summary>
|
||||
Gpio25 = 22,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 08
|
||||
/// </summary>
|
||||
Gpio08 = 24,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 07
|
||||
/// </summary>
|
||||
Gpio07 = 26,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 12
|
||||
/// </summary>
|
||||
Gpio12 = 32,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 16
|
||||
/// </summary>
|
||||
Gpio16 = 36,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 20
|
||||
/// </summary>
|
||||
Gpio20 = 38,
|
||||
|
||||
/// <summary>
|
||||
/// Header P1, GPIO Pin 21
|
||||
/// </summary>
|
||||
Gpio21 = 40
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the different pins on the P5 Header
|
||||
/// as commonly referenced by Raspberry Pi documentation.
|
||||
/// Enumeration values correspond to the physical pin number.
|
||||
/// </summary>
|
||||
public enum P5
|
||||
{
|
||||
/// <summary>
|
||||
/// Header P5, GPIO Pin 28
|
||||
/// </summary>
|
||||
Gpio28 = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Header P5, GPIO Pin 29
|
||||
/// </summary>
|
||||
Gpio29 = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Header P5, GPIO Pin 30
|
||||
/// </summary>
|
||||
Gpio30 = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Header P5, GPIO Pin 31
|
||||
/// </summary>
|
||||
Gpio31 = 6
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the different pin capabilities
|
||||
/// </summary>
|
||||
public enum PinCapability
|
||||
{
|
||||
/// <summary>
|
||||
/// General Purpose capability: Digital and Analog Read/Write
|
||||
/// </summary>
|
||||
GP,
|
||||
|
||||
/// <summary>
|
||||
/// General Purpose Clock (not PWM)
|
||||
/// </summary>
|
||||
GPCLK,
|
||||
|
||||
/// <summary>
|
||||
/// i2c data channel
|
||||
/// </summary>
|
||||
I2CSDA,
|
||||
|
||||
/// <summary>
|
||||
/// i2c clock channel
|
||||
/// </summary>
|
||||
I2CSCL,
|
||||
|
||||
/// <summary>
|
||||
/// SPI Master Out, Slave In channel
|
||||
/// </summary>
|
||||
SPIMOSI,
|
||||
|
||||
/// <summary>
|
||||
/// SPI Master In, Slave Out channel
|
||||
/// </summary>
|
||||
SPIMISO,
|
||||
|
||||
/// <summary>
|
||||
/// SPI Clock channel
|
||||
/// </summary>
|
||||
SPICLK,
|
||||
|
||||
/// <summary>
|
||||
/// SPI Chip Select Channel
|
||||
/// </summary>
|
||||
SPICS,
|
||||
|
||||
/// <summary>
|
||||
/// UART Request to Send Channel
|
||||
/// </summary>
|
||||
UARTRTS,
|
||||
|
||||
/// <summary>
|
||||
/// UART Transmit Channel
|
||||
/// </summary>
|
||||
UARTTXD,
|
||||
|
||||
/// <summary>
|
||||
/// UART Receive Channel
|
||||
/// </summary>
|
||||
UARTRXD,
|
||||
|
||||
/// <summary>
|
||||
/// Hardware Pule Width Modulation
|
||||
/// </summary>
|
||||
PWM
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the SPI channel numbers
|
||||
/// </summary>
|
||||
internal enum SpiChannelNumber
|
||||
{
|
||||
/// <summary>
|
||||
/// The channel 0
|
||||
/// </summary>
|
||||
Channel0 = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The channel 1
|
||||
/// </summary>
|
||||
Channel1 = 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,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,594 @@
|
||||
namespace Unosquare.RaspberryIO.Gpio
|
||||
{
|
||||
using Native;
|
||||
using Swan;
|
||||
using Swan.Abstractions;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a singleton of the Raspberry Pi GPIO controller
|
||||
/// as an IReadOnlyCollection of GpioPins
|
||||
/// Low level operations are accomplished by using the Wiring Pi library.
|
||||
/// Use the Instance property to access the singleton's instance
|
||||
/// </summary>
|
||||
public sealed class GpioController : SingletonBase<GpioController>, IReadOnlyCollection<GpioPin>
|
||||
{
|
||||
#region Private Declarations
|
||||
|
||||
private const string WiringPiCodesEnvironmentVariable = "WIRINGPI_CODES";
|
||||
private static readonly object SyncRoot = new object();
|
||||
private readonly ReadOnlyCollection<GpioPin> _pinCollection;
|
||||
private readonly ReadOnlyDictionary<int, GpioPin> _headerP1Pins;
|
||||
private readonly ReadOnlyDictionary<int, GpioPin> _headerP5Pins;
|
||||
private readonly Dictionary<WiringPiPin, GpioPin> _pinsByWiringPiPinNumber = new Dictionary<WiringPiPin, GpioPin>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors and Initialization
|
||||
|
||||
/// <summary>
|
||||
/// Prevents a default instance of the <see cref="GpioController" /> class from being created.
|
||||
/// It in turn initializes the controller and registers the pin -- in that order.
|
||||
/// </summary>
|
||||
/// <exception cref="Exception">Unable to initialize the GPIO controller.</exception>
|
||||
private GpioController()
|
||||
{
|
||||
if (_pinCollection != null)
|
||||
return;
|
||||
|
||||
if (IsInitialized == false)
|
||||
{
|
||||
var initResult = Initialize(ControllerMode.DirectWithWiringPiPins);
|
||||
if (initResult == false)
|
||||
throw new Exception("Unable to initialize the GPIO controller.");
|
||||
}
|
||||
|
||||
#region Pin Registration (32 WiringPi Pins)
|
||||
|
||||
RegisterPin(GpioPin.Pin00.Value);
|
||||
RegisterPin(GpioPin.Pin01.Value);
|
||||
RegisterPin(GpioPin.Pin02.Value);
|
||||
RegisterPin(GpioPin.Pin03.Value);
|
||||
RegisterPin(GpioPin.Pin04.Value);
|
||||
RegisterPin(GpioPin.Pin05.Value);
|
||||
RegisterPin(GpioPin.Pin06.Value);
|
||||
RegisterPin(GpioPin.Pin07.Value);
|
||||
RegisterPin(GpioPin.Pin08.Value);
|
||||
RegisterPin(GpioPin.Pin09.Value);
|
||||
RegisterPin(GpioPin.Pin10.Value);
|
||||
RegisterPin(GpioPin.Pin11.Value);
|
||||
RegisterPin(GpioPin.Pin12.Value);
|
||||
RegisterPin(GpioPin.Pin13.Value);
|
||||
RegisterPin(GpioPin.Pin14.Value);
|
||||
RegisterPin(GpioPin.Pin15.Value);
|
||||
RegisterPin(GpioPin.Pin16.Value);
|
||||
RegisterPin(GpioPin.Pin17.Value);
|
||||
RegisterPin(GpioPin.Pin18.Value);
|
||||
RegisterPin(GpioPin.Pin19.Value);
|
||||
RegisterPin(GpioPin.Pin20.Value);
|
||||
RegisterPin(GpioPin.Pin21.Value);
|
||||
RegisterPin(GpioPin.Pin22.Value);
|
||||
RegisterPin(GpioPin.Pin23.Value);
|
||||
RegisterPin(GpioPin.Pin24.Value);
|
||||
RegisterPin(GpioPin.Pin25.Value);
|
||||
RegisterPin(GpioPin.Pin26.Value);
|
||||
RegisterPin(GpioPin.Pin27.Value);
|
||||
RegisterPin(GpioPin.Pin28.Value);
|
||||
RegisterPin(GpioPin.Pin29.Value);
|
||||
RegisterPin(GpioPin.Pin30.Value);
|
||||
RegisterPin(GpioPin.Pin31.Value);
|
||||
|
||||
#endregion
|
||||
|
||||
_pinCollection = new ReadOnlyCollection<GpioPin>(_pinsByWiringPiPinNumber.Values.ToArray());
|
||||
var headerP1 = new Dictionary<int, GpioPin>(_pinCollection.Count);
|
||||
var headerP5 = new Dictionary<int, GpioPin>(_pinCollection.Count);
|
||||
foreach (var pin in _pinCollection)
|
||||
{
|
||||
var target = pin.Header == GpioHeader.P1 ? headerP1 : headerP5;
|
||||
target[pin.HeaderPinNumber] = pin;
|
||||
}
|
||||
|
||||
_headerP1Pins = new ReadOnlyDictionary<int, GpioPin>(headerP1);
|
||||
_headerP5Pins = 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 number of registered pins in the controller.
|
||||
/// </summary>
|
||||
public int Count => _pinCollection.Count;
|
||||
|
||||
#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 registered pins.
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<GpioPin> Pins => _pinCollection;
|
||||
|
||||
/// <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 => _headerP1Pins;
|
||||
|
||||
/// <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 => _headerP5Pins;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Individual Pin Properties
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 00.
|
||||
/// </summary>
|
||||
public GpioPin Pin00 => GpioPin.Pin00.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 01.
|
||||
/// </summary>
|
||||
public GpioPin Pin01 => GpioPin.Pin01.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 02.
|
||||
/// </summary>
|
||||
public GpioPin Pin02 => GpioPin.Pin02.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 03.
|
||||
/// </summary>
|
||||
public GpioPin Pin03 => GpioPin.Pin03.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 04.
|
||||
/// </summary>
|
||||
public GpioPin Pin04 => GpioPin.Pin04.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 05.
|
||||
/// </summary>
|
||||
public GpioPin Pin05 => GpioPin.Pin05.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 06.
|
||||
/// </summary>
|
||||
public GpioPin Pin06 => GpioPin.Pin06.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 07.
|
||||
/// </summary>
|
||||
public GpioPin Pin07 => GpioPin.Pin07.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 08.
|
||||
/// </summary>
|
||||
public GpioPin Pin08 => GpioPin.Pin08.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 09.
|
||||
/// </summary>
|
||||
public GpioPin Pin09 => GpioPin.Pin09.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 10.
|
||||
/// </summary>
|
||||
public GpioPin Pin10 => GpioPin.Pin10.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 11.
|
||||
/// </summary>
|
||||
public GpioPin Pin11 => GpioPin.Pin11.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 12.
|
||||
/// </summary>
|
||||
public GpioPin Pin12 => GpioPin.Pin12.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 13.
|
||||
/// </summary>
|
||||
public GpioPin Pin13 => GpioPin.Pin13.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 14.
|
||||
/// </summary>
|
||||
public GpioPin Pin14 => GpioPin.Pin14.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 15.
|
||||
/// </summary>
|
||||
public GpioPin Pin15 => GpioPin.Pin15.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 16.
|
||||
/// </summary>
|
||||
public GpioPin Pin16 => GpioPin.Pin16.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 17.
|
||||
/// </summary>
|
||||
public GpioPin Pin17 => GpioPin.Pin17.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 18.
|
||||
/// </summary>
|
||||
public GpioPin Pin18 => GpioPin.Pin18.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 19.
|
||||
/// </summary>
|
||||
public GpioPin Pin19 => GpioPin.Pin19.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 20.
|
||||
/// </summary>
|
||||
public GpioPin Pin20 => GpioPin.Pin20.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 21.
|
||||
/// </summary>
|
||||
public GpioPin Pin21 => GpioPin.Pin21.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 22.
|
||||
/// </summary>
|
||||
public GpioPin Pin22 => GpioPin.Pin22.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 23.
|
||||
/// </summary>
|
||||
public GpioPin Pin23 => GpioPin.Pin23.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 24.
|
||||
/// </summary>
|
||||
public GpioPin Pin24 => GpioPin.Pin24.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 25.
|
||||
/// </summary>
|
||||
public GpioPin Pin25 => GpioPin.Pin25.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 26.
|
||||
/// </summary>
|
||||
public GpioPin Pin26 => GpioPin.Pin26.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 27.
|
||||
/// </summary>
|
||||
public GpioPin Pin27 => GpioPin.Pin27.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 28.
|
||||
/// </summary>
|
||||
public GpioPin Pin28 => GpioPin.Pin28.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 29.
|
||||
/// </summary>
|
||||
public GpioPin Pin29 => GpioPin.Pin29.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 30.
|
||||
/// </summary>
|
||||
public GpioPin Pin30 => GpioPin.Pin30.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Provides direct access to Pin known to Wiring Pi (not the pin header number) as Pin 31.
|
||||
/// </summary>
|
||||
public GpioPin Pin31 => GpioPin.Pin31.Value;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Indexers
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the initialization mode.
|
||||
/// </summary>
|
||||
private static ControllerMode Mode { get; set; } = ControllerMode.NotInitialized;
|
||||
|
||||
/// <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] => _pinsByWiringPiPinNumber[pinNumber];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="GpioPin"/> with the specified 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[P1 pinNumber] => HeaderP1[(int)pinNumber];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="GpioPin"/> with the specified 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[P5 pinNumber] => HeaderP5[(int)pinNumber];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="GpioPin"/> with the specified Wiring Pi pin number.
|
||||
/// Use the HeaderP1 and HeaderP5 lookups if you would like to retrieve pins by physical pin number.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The <see cref="GpioPin"/>.
|
||||
/// </value>
|
||||
/// <param name="wiringPiPinNumber">The pin number as defined by Wiring Pi. This is not the header pin number as pin number in headers are obvoisly repeating.</param>
|
||||
/// <returns>A reference to the GPIO pin</returns>
|
||||
/// <exception cref="IndexOutOfRangeException">When the pin index is not found</exception>
|
||||
public GpioPin this[int wiringPiPinNumber]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Enum.IsDefined(typeof(WiringPiPin), wiringPiPinNumber) == false)
|
||||
throw new IndexOutOfRangeException($"Pin {wiringPiPinNumber} is not registered in the GPIO controller.");
|
||||
|
||||
return _pinsByWiringPiPinNumber[(WiringPiPin)wiringPiPinNumber];
|
||||
}
|
||||
}
|
||||
|
||||
#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 firts 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 unsopported
|
||||
/// </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 firts 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 unsopported
|
||||
/// </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() => _pinCollection.GetEnumerator();
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates through the collection.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
|
||||
/// </returns>
|
||||
IEnumerator IEnumerable.GetEnumerator() => _pinCollection.GetEnumerator();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper and Init Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the GPIO pin by BCM pin number.
|
||||
/// </summary>
|
||||
/// <param name="bcmPinNumber">The BCM pin number.</param>
|
||||
/// <returns>The GPIO pin</returns>
|
||||
public GpioPin GetGpioPinByBcmPinNumber(int bcmPinNumber) => this.First(pin => pin.BcmPinNumber == bcmPinNumber);
|
||||
|
||||
/// <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>
|
||||
/// Short-hand method of registering pins
|
||||
/// </summary>
|
||||
/// <param name="pin">The pin.</param>
|
||||
private void RegisterPin(GpioPin pin)
|
||||
{
|
||||
if (_pinsByWiringPiPinNumber.ContainsKey(pin.WiringPiPinNumber) == false)
|
||||
_pinsByWiringPiPinNumber[pin.WiringPiPinNumber] = pin;
|
||||
else
|
||||
throw new InvalidOperationException($"Pin {pin.WiringPiPinNumber} has been registered");
|
||||
}
|
||||
|
||||
/// <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 (Runtime.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", EnvironmentVariableTarget.Process);
|
||||
int setpuResult;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case ControllerMode.DirectWithWiringPiPins:
|
||||
{
|
||||
setpuResult = WiringPi.WiringPiSetup();
|
||||
break;
|
||||
}
|
||||
|
||||
case ControllerMode.DirectWithBcmPins:
|
||||
{
|
||||
setpuResult = WiringPi.WiringPiSetupGpio();
|
||||
break;
|
||||
}
|
||||
|
||||
case ControllerMode.DirectWithHeaderPins:
|
||||
{
|
||||
setpuResult = WiringPi.WiringPiSetupPhys();
|
||||
break;
|
||||
}
|
||||
|
||||
case ControllerMode.FileStreamWithHardwarePins:
|
||||
{
|
||||
setpuResult = WiringPi.WiringPiSetupSys();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new ArgumentException($"'{mode}' is not a valid initialization mode.");
|
||||
}
|
||||
}
|
||||
|
||||
Mode = setpuResult == 0 ? mode : ControllerMode.NotInitialized;
|
||||
return IsInitialized;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
namespace Unosquare.RaspberryIO.Gpio
|
||||
{
|
||||
using System;
|
||||
|
||||
public partial class GpioPin
|
||||
{
|
||||
#region Static Pin Definitions
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin08 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin08, 3)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP, PinCapability.I2CSDA },
|
||||
Name = "BCM 2 (SDA)"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin09 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin09, 5)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP, PinCapability.I2CSCL },
|
||||
Name = "BCM 3 (SCL)"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin07 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin07, 7)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP, PinCapability.GPCLK },
|
||||
Name = "BCM 4 (GPCLK0)"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin00 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin00, 11)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP, PinCapability.UARTRTS },
|
||||
Name = "BCM 17"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin02 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin02, 13)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP },
|
||||
Name = "BCM 27"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin03 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin03, 15)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP },
|
||||
Name = "BCM 22"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin12 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin12, 19)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP, PinCapability.SPIMOSI },
|
||||
Name = "BCM 10 (MOSI)"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin13 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin13, 21)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP, PinCapability.SPIMISO },
|
||||
Name = "BCM 9 (MISO)"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin14 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin14, 23)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP, PinCapability.SPICLK },
|
||||
Name = "BCM 11 (SCLCK)"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin30 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin30, 27)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.I2CSDA },
|
||||
Name = "BCM 0 (ID_SD)"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin31 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin31, 28)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.I2CSCL },
|
||||
Name = "BCM 1 (ID_SC)"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin11 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin11, 26)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP, PinCapability.SPICS },
|
||||
Name = "BCM 7 (CE1)"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin10 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin10, 24)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP, PinCapability.SPICS },
|
||||
Name = "BCM 8 (CE0)"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin06 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin06, 22)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP },
|
||||
Name = "BCM 25"
|
||||
});
|
||||
internal static readonly Lazy<GpioPin> Pin05 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin05, 18)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP },
|
||||
Name = "BCM 24"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin04 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin04, 16)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP },
|
||||
Name = "BCM 23"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin01 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin01, 12)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP, PinCapability.PWM },
|
||||
Name = "BCM 18 (PWM0)"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin16 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin16, 10)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.UARTRXD },
|
||||
Name = "BCM 15 (RXD)"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin15 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin15, 8)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.UARTTXD },
|
||||
Name = "BCM 14 (TXD)"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin21 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin21, 29)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP },
|
||||
Name = "BCM 5"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin22 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin22, 31)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP },
|
||||
Name = "BCM 6"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin23 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin23, 33)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP, PinCapability.PWM },
|
||||
Name = "BCM 13 (PWM1)"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin24 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin24, 35)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP, PinCapability.SPIMISO },
|
||||
Name = "BCM 19 (MISO)"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin25 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin25, 37)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP },
|
||||
Name = "BCM 26"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin29 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin29, 40)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP, PinCapability.SPICLK },
|
||||
Name = "BCM 21 (SCLK)"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin28 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin28, 38)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP, PinCapability.SPIMOSI },
|
||||
Name = "BCM 20 (MOSI)"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin27 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin27, 36)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP },
|
||||
Name = "BCM 16"
|
||||
});
|
||||
internal static readonly Lazy<GpioPin> Pin26 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin26, 32)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP },
|
||||
Name = "BCM 12 (PWM0)"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin17 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin17, 3)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP, PinCapability.I2CSDA },
|
||||
Name = "BCM 28 (SDA)"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin18 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin18, 4)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP, PinCapability.I2CSCL },
|
||||
Name = "BCM 29 (SCL)"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin19 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin19, 5)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP },
|
||||
Name = "BCM 30"
|
||||
});
|
||||
|
||||
internal static readonly Lazy<GpioPin> Pin20 = new Lazy<GpioPin>(() => new GpioPin(WiringPiPin.Pin20, 6)
|
||||
{
|
||||
Capabilities = new[] { PinCapability.GP },
|
||||
Name = "BCM 31"
|
||||
});
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,648 @@
|
||||
namespace Unosquare.RaspberryIO.Gpio
|
||||
{
|
||||
using Native;
|
||||
using Swan;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <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
|
||||
{
|
||||
#region Property Backing
|
||||
|
||||
private readonly object _syncLock = new object();
|
||||
private GpioPinDriveMode m_PinMode;
|
||||
private GpioPinResistorPullMode m_ResistorPullMode;
|
||||
private int m_PwmRegister;
|
||||
private PwmMode m_PwmMode = PwmMode.Balanced;
|
||||
private uint m_PwmRange = 1024;
|
||||
private int m_PwmClockDivisor = 1;
|
||||
private int m_SoftPwmValue = -1;
|
||||
private int m_SoftToneFrequency = -1;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GpioPin"/> class.
|
||||
/// </summary>
|
||||
/// <param name="wiringPiPinNumber">The wiring pi pin number.</param>
|
||||
/// <param name="headerPinNumber">The header pin number.</param>
|
||||
private GpioPin(WiringPiPin wiringPiPinNumber, int headerPinNumber)
|
||||
{
|
||||
PinNumber = (int)wiringPiPinNumber;
|
||||
WiringPiPinNumber = wiringPiPinNumber;
|
||||
BcmPinNumber = GpioController.WiringPiToBcmPinNumber((int)wiringPiPinNumber);
|
||||
HeaderPinNumber = headerPinNumber;
|
||||
Header = (PinNumber >= 17 && PinNumber <= 20) ? GpioHeader.P5 : GpioHeader.P1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Pin Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Wiring Pi pin number as an integer.
|
||||
/// </summary>
|
||||
public int PinNumber { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the WiringPi Pin number
|
||||
/// </summary>
|
||||
public WiringPiPin WiringPiPinNumber { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the BCM chip (hardware) pin number.
|
||||
/// </summary>
|
||||
public int BcmPinNumber { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or the physical header (physical board) pin number.
|
||||
/// </summary>
|
||||
public int HeaderPinNumber { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pin's header (physical board) location.
|
||||
/// </summary>
|
||||
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; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Hardware-Specific Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pin operating mode.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The pin mode.
|
||||
/// </value>
|
||||
/// <exception cref="NotSupportedException">Thrown when a pin does not support the given operation mode.</exception>
|
||||
public GpioPinDriveMode PinMode
|
||||
{
|
||||
get => m_PinMode;
|
||||
|
||||
set
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
var mode = value;
|
||||
if ((mode == GpioPinDriveMode.GpioClock && Capabilities.Contains(PinCapability.GPCLK) == false) ||
|
||||
(mode == GpioPinDriveMode.PwmOutput && Capabilities.Contains(PinCapability.PWM) == false) ||
|
||||
(mode == GpioPinDriveMode.Input && Capabilities.Contains(PinCapability.GP) == false) ||
|
||||
(mode == GpioPinDriveMode.Output && Capabilities.Contains(PinCapability.GP) == false))
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
$"Pin {WiringPiPinNumber} '{Name}' does not support mode '{mode}'. Pin capabilities are limited to: {string.Join(", ", Capabilities)}");
|
||||
}
|
||||
|
||||
WiringPi.PinMode(PinNumber, (int)mode);
|
||||
m_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; } = EdgeDetection.ExternalSetup;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Hardware PWM Members
|
||||
|
||||
/// <summary>
|
||||
/// This sets or gets the pull-up or pull-down resistor mode on the pin, which should be set as an input.
|
||||
/// Unlike the Arduino, the BCM2835 has both pull-up an 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.
|
||||
/// </summary>
|
||||
public GpioPinResistorPullMode InputPullMode
|
||||
{
|
||||
get => PinMode == GpioPinDriveMode.Input ? m_ResistorPullMode : GpioPinResistorPullMode.Off;
|
||||
|
||||
set
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (PinMode != GpioPinDriveMode.Input)
|
||||
{
|
||||
m_ResistorPullMode = GpioPinResistorPullMode.Off;
|
||||
throw new InvalidOperationException(
|
||||
$"Unable to set the {nameof(InputPullMode)} for pin {PinNumber} because operating mode is {PinMode}."
|
||||
+ $" Setting the {nameof(InputPullMode)} is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input}");
|
||||
}
|
||||
|
||||
WiringPi.PullUpDnControl(PinNumber, (int)value);
|
||||
m_ResistorPullMode = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the PWM register. Values should be between 0 and 1024
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The PWM register.
|
||||
/// </value>
|
||||
public int PwmRegister
|
||||
{
|
||||
get => m_PwmRegister;
|
||||
|
||||
set
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (PinMode != GpioPinDriveMode.PwmOutput)
|
||||
{
|
||||
m_PwmRegister = 0;
|
||||
|
||||
throw new InvalidOperationException(
|
||||
$"Unable to write PWM register for pin {PinNumber} because operating mode is {PinMode}."
|
||||
+ $" Writing the PWM register is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.PwmOutput}");
|
||||
}
|
||||
|
||||
var val = value.Clamp(0, 1024);
|
||||
|
||||
WiringPi.PwmWrite(PinNumber, val);
|
||||
m_PwmRegister = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 ? m_PwmMode : PwmMode.Balanced;
|
||||
|
||||
set
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (PinMode != GpioPinDriveMode.PwmOutput)
|
||||
{
|
||||
m_PwmMode = PwmMode.Balanced;
|
||||
|
||||
throw new InvalidOperationException(
|
||||
$"Unable to set PWM mode for pin {PinNumber} because operating mode is {PinMode}."
|
||||
+ $" Setting the PWM mode is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.PwmOutput}");
|
||||
}
|
||||
|
||||
WiringPi.PwmSetMode((int)value);
|
||||
m_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 ? m_PwmRange : 0;
|
||||
|
||||
set
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (PinMode != GpioPinDriveMode.PwmOutput)
|
||||
{
|
||||
m_PwmRange = 1024;
|
||||
|
||||
throw new InvalidOperationException(
|
||||
$"Unable to set PWM range for pin {PinNumber} because operating mode is {PinMode}."
|
||||
+ $" Setting the PWM range is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.PwmOutput}");
|
||||
}
|
||||
|
||||
WiringPi.PwmSetRange(value);
|
||||
m_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 ? m_PwmClockDivisor : 0;
|
||||
|
||||
set
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (PinMode != GpioPinDriveMode.PwmOutput)
|
||||
{
|
||||
m_PwmClockDivisor = 1;
|
||||
|
||||
throw new InvalidOperationException(
|
||||
$"Unable to set PWM range for pin {PinNumber} because operating mode is {PinMode}."
|
||||
+ $" Setting the PWM range is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.PwmOutput}");
|
||||
}
|
||||
|
||||
WiringPi.PwmSetClock(value);
|
||||
m_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 => m_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 => m_SoftToneFrequency;
|
||||
|
||||
set
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (IsInSoftToneMode == false)
|
||||
{
|
||||
var setupResult = WiringPi.SoftToneCreate(PinNumber);
|
||||
if (setupResult != 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Unable to initialize soft tone on pin {PinNumber}. Error Code: {setupResult}");
|
||||
}
|
||||
}
|
||||
|
||||
WiringPi.SoftToneWrite(PinNumber, value);
|
||||
m_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 => m_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 => m_SoftPwmValue;
|
||||
|
||||
set
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (IsInSoftPwmMode && value >= 0)
|
||||
{
|
||||
WiringPi.SoftPwmWrite(PinNumber, value);
|
||||
m_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 (Capabilities.Contains(PinCapability.GP) == false)
|
||||
throw new NotSupportedException($"Pin {PinNumber} does not support software PWM");
|
||||
|
||||
if (IsInSoftPwmMode)
|
||||
throw new InvalidOperationException($"{nameof(StartSoftPwm)} has already been called.");
|
||||
|
||||
var startResult = WiringPi.SoftPwmCreate(PinNumber, value, range);
|
||||
|
||||
if (startResult == 0)
|
||||
{
|
||||
m_SoftPwmValue = value;
|
||||
SoftPwmRange = range;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Could not start software based PWM on pin {PinNumber}. Error code: {startResult}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Output Mode (Write) Members
|
||||
|
||||
/// <summary>
|
||||
/// Writes the specified pin value.
|
||||
/// This method performs a digital write
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
public void Write(GpioPinValue value)
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (PinMode != GpioPinDriveMode.Output)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Unable to write to pin {PinNumber} because operating mode is {PinMode}."
|
||||
+ $" Writes are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Output}");
|
||||
}
|
||||
|
||||
WiringPi.DigitalWrite(PinNumber, (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 {PinNumber} because operating mode is {PinMode}."
|
||||
+ $" Writes are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Output}");
|
||||
}
|
||||
|
||||
WiringPi.AnalogWrite(PinNumber, 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 {PinNumber} 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;
|
||||
|
||||
Pi.Timing.SleepMicroseconds(101); // 101 uses nanosleep as opposed to a loop.
|
||||
}
|
||||
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 {PinNumber} because operating mode is {PinMode}."
|
||||
+ $" Reads are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input} or {GpioPinDriveMode.Output}");
|
||||
}
|
||||
|
||||
return WiringPi.DigitalRead(PinNumber) != 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 {PinNumber} because operating mode is {PinMode}."
|
||||
+ $" Reads are only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input}");
|
||||
}
|
||||
|
||||
return WiringPi.AnalogRead(PinNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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
|
||||
|
||||
/// <summary>
|
||||
/// Registers the interrupt callback on the pin. Pin mode has to be set to Input.
|
||||
/// </summary>
|
||||
/// <param name="edgeDetection">The edge detection.</param>
|
||||
/// <param name="callback">The callback.</param>
|
||||
/// <exception cref="ArgumentException">callback</exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// An interrupt callback was already registered.
|
||||
/// or
|
||||
/// RegisterInterruptCallback
|
||||
/// </exception>
|
||||
public void RegisterInterruptCallback(EdgeDetection edgeDetection, InterruptServiceRoutineCallback callback)
|
||||
{
|
||||
if (callback == null)
|
||||
throw new ArgumentException($"{nameof(callback)} cannot be null");
|
||||
|
||||
if (InterruptCallback != null)
|
||||
throw new InvalidOperationException("An interrupt callback was already registered.");
|
||||
|
||||
if (PinMode != GpioPinDriveMode.Input)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Unable to {nameof(RegisterInterruptCallback)} for pin {PinNumber} because operating mode is {PinMode}."
|
||||
+ $" Calling {nameof(RegisterInterruptCallback)} is only allowed if {nameof(PinMode)} is set to {GpioPinDriveMode.Input}");
|
||||
}
|
||||
|
||||
lock (_syncLock)
|
||||
{
|
||||
var registerResult = WiringPi.WiringPiISR(PinNumber, (int)edgeDetection, callback);
|
||||
if (registerResult == 0)
|
||||
{
|
||||
InterruptEdgeDetection = edgeDetection;
|
||||
InterruptCallback = callback;
|
||||
}
|
||||
else
|
||||
{
|
||||
HardwareException.Throw(nameof(GpioPin), nameof(RegisterInterruptCallback));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
namespace Unosquare.RaspberryIO.Gpio
|
||||
{
|
||||
using Native;
|
||||
using Swan.Abstractions;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
/// <summary>
|
||||
/// A simple wrapper for the I2c bus on the Raspberry Pi
|
||||
/// </summary>
|
||||
public class I2CBus : SingletonBase<I2CBus>
|
||||
{
|
||||
// TODO: It would be nice to integrate i2c device detection.
|
||||
private static readonly object SyncRoot = new object();
|
||||
private readonly Dictionary<int, I2CDevice> _devices = new Dictionary<int, I2CDevice>();
|
||||
|
||||
/// <summary>
|
||||
/// Prevents a default instance of the <see cref="I2CBus"/> class from being created.
|
||||
/// </summary>
|
||||
private I2CBus()
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the registered devices as a read only collection.
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<I2CDevice> Devices => new ReadOnlyCollection<I2CDevice>(_devices.Values.ToArray());
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="I2CDevice"/> with the specified device identifier.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The <see cref="I2CDevice"/>.
|
||||
/// </value>
|
||||
/// <param name="deviceId">The device identifier.</param>
|
||||
/// <returns>A reference to an I2C device</returns>
|
||||
public I2CDevice this[int deviceId] => GetDeviceById(deviceId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device by identifier.
|
||||
/// </summary>
|
||||
/// <param name="deviceId">The device identifier.</param>
|
||||
/// <returns>The device reference</returns>
|
||||
public I2CDevice GetDeviceById(int deviceId)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return _devices[deviceId];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a device to the bus by its Id. If the device is already registered it simply returns the existing device.
|
||||
/// </summary>
|
||||
/// <param name="deviceId">The device identifier.</param>
|
||||
/// <returns>The device reference</returns>
|
||||
/// <exception cref="KeyNotFoundException">When the device file descriptor is not found</exception>
|
||||
public I2CDevice 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
namespace Unosquare.RaspberryIO.Gpio
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Native;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a device on the I2C Bus
|
||||
/// </summary>
|
||||
public class I2CDevice
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device identifier.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The device identifier.
|
||||
/// </value>
|
||||
public int DeviceId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the standard POSIX file descriptor.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The file descriptor.
|
||||
/// </value>
|
||||
public int FileDescriptor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Reads a byte from the specified file descriptor
|
||||
/// </summary>
|
||||
/// <returns>The byte from device</returns>
|
||||
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)
|
||||
{
|
||||
return 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
namespace Unosquare.RaspberryIO.Gpio
|
||||
{
|
||||
using Swan.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// The SPI Bus containing the 2 SPI channels
|
||||
/// </summary>
|
||||
public class SpiBus : SingletonBase<SpiBus>
|
||||
{
|
||||
/// <summary>
|
||||
/// Prevents a default instance of the <see cref="SpiBus"/> class from being created.
|
||||
/// </summary>
|
||||
private SpiBus()
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
#region SPI Access
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the channel 0 frequency in Hz.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The channel0 frequency.
|
||||
/// </value>
|
||||
public int Channel0Frequency { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the SPI bus on channel 1.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The channel0.
|
||||
/// </value>
|
||||
public SpiChannel Channel0
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Channel0Frequency == 0)
|
||||
Channel0Frequency = SpiChannel.DefaultFrequency;
|
||||
|
||||
return SpiChannel.Retrieve(SpiChannelNumber.Channel0, Channel0Frequency);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the channel 1 frequency in Hz
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The channel1 frequency.
|
||||
/// </value>
|
||||
public int Channel1Frequency { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the SPI bus on channel 1.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The channel1.
|
||||
/// </value>
|
||||
public SpiChannel Channel1
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Channel1Frequency == 0)
|
||||
Channel1Frequency = SpiChannel.DefaultFrequency;
|
||||
|
||||
return SpiChannel.Retrieve(SpiChannelNumber.Channel1, Channel1Frequency);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
namespace Unosquare.RaspberryIO.Gpio
|
||||
{
|
||||
using 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
|
||||
{
|
||||
/// <summary>
|
||||
/// The minimum frequency of an SPI Channel
|
||||
/// </summary>
|
||||
public const int MinFrequency = 500000;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum frequency of an SPI channel
|
||||
/// </summary>
|
||||
public const int MaxFrequency = 32000000;
|
||||
|
||||
/// <summary>
|
||||
/// The default frequency of SPI channels
|
||||
/// This is set to 8 Mhz wich is typical in modern hardware.
|
||||
/// </summary>
|
||||
public const int DefaultFrequency = 8000000;
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the standard initialization file descriptor.
|
||||
/// anything negative means error.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The file descriptor.
|
||||
/// </value>
|
||||
public int FileDescriptor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the channel.
|
||||
/// </summary>
|
||||
public int Channel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the frequency.
|
||||
/// </summary>
|
||||
public int Frequency { get; }
|
||||
|
||||
/// <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 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));
|
||||
|
||||
/// <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>
|
||||
public void Write(byte[] buffer)
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
var result = Standard.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 SpiChannel Retrieve(SpiChannelNumber channel, int frequency)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
if (Buses.ContainsKey(channel))
|
||||
return Buses[channel];
|
||||
|
||||
var newBus = new SpiChannel(channel, frequency);
|
||||
Buses[channel] = newBus;
|
||||
return newBus;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user