Init RaspberryIO

This commit is contained in:
2019-02-17 14:08:57 +01:00
commit e7c3f7af91
193 changed files with 36743 additions and 0 deletions
+579
View File
@@ -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
}
}
+648
View File
@@ -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
}
}
+93
View File
@@ -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);
}
}
}
}
+195
View File
@@ -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);
}
}
}
}
+72
View File
@@ -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
}
}
+154
View File
@@ -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;
}
}
}
}