2019-12-03 18:43:54 +01:00
using Unosquare.RaspberryIO.Native ;
using Unosquare.Swan ;
using Unosquare.Swan.Abstractions ;
using System ;
using System.Collections ;
using System.Collections.Generic ;
using System.Collections.ObjectModel ;
using System.Linq ;
using System.Threading.Tasks ;
namespace Unosquare.RaspberryIO.Gpio {
/// <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 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 ( this . Pins ! = null ) {
return ;
}
if ( IsInitialized = = false ) {
Boolean initResult = this . Initialize ( ControllerMode . DirectWithWiringPiPins ) ;
if ( initResult = = false ) {
throw new Exception ( "Unable to initialize the GPIO controller." ) ;
}
}
#region Pin Registration ( 32 WiringPi Pins )
this . RegisterPin ( GpioPin . Pin00 . Value ) ;
this . RegisterPin ( GpioPin . Pin01 . Value ) ;
this . RegisterPin ( GpioPin . Pin02 . Value ) ;
this . RegisterPin ( GpioPin . Pin03 . Value ) ;
this . RegisterPin ( GpioPin . Pin04 . Value ) ;
this . RegisterPin ( GpioPin . Pin05 . Value ) ;
this . RegisterPin ( GpioPin . Pin06 . Value ) ;
this . RegisterPin ( GpioPin . Pin07 . Value ) ;
this . RegisterPin ( GpioPin . Pin08 . Value ) ;
this . RegisterPin ( GpioPin . Pin09 . Value ) ;
this . RegisterPin ( GpioPin . Pin10 . Value ) ;
this . RegisterPin ( GpioPin . Pin11 . Value ) ;
this . RegisterPin ( GpioPin . Pin12 . Value ) ;
this . RegisterPin ( GpioPin . Pin13 . Value ) ;
this . RegisterPin ( GpioPin . Pin14 . Value ) ;
this . RegisterPin ( GpioPin . Pin15 . Value ) ;
this . RegisterPin ( GpioPin . Pin16 . Value ) ;
this . RegisterPin ( GpioPin . Pin17 . Value ) ;
this . RegisterPin ( GpioPin . Pin18 . Value ) ;
this . RegisterPin ( GpioPin . Pin19 . Value ) ;
this . RegisterPin ( GpioPin . Pin20 . Value ) ;
this . RegisterPin ( GpioPin . Pin21 . Value ) ;
this . RegisterPin ( GpioPin . Pin22 . Value ) ;
this . RegisterPin ( GpioPin . Pin23 . Value ) ;
this . RegisterPin ( GpioPin . Pin24 . Value ) ;
this . RegisterPin ( GpioPin . Pin25 . Value ) ;
this . RegisterPin ( GpioPin . Pin26 . Value ) ;
this . RegisterPin ( GpioPin . Pin27 . Value ) ;
this . RegisterPin ( GpioPin . Pin28 . Value ) ;
this . RegisterPin ( GpioPin . Pin29 . Value ) ;
this . RegisterPin ( GpioPin . Pin30 . Value ) ;
this . RegisterPin ( GpioPin . Pin31 . Value ) ;
#endregion
this . Pins = new ReadOnlyCollection < GpioPin > ( this . _pinsByWiringPiPinNumber . Values . ToArray ( ) ) ;
Dictionary < Int32 , GpioPin > headerP1 = new Dictionary < Int32 , GpioPin > ( this . Pins . Count ) ;
Dictionary < Int32 , GpioPin > headerP5 = new Dictionary < Int32 , GpioPin > ( this . Pins . Count ) ;
foreach ( GpioPin pin in this . Pins ) {
Dictionary < Int32 , GpioPin > target = pin . Header = = GpioHeader . P1 ? headerP1 : headerP5 ;
target [ pin . HeaderPinNumber ] = pin ;
}
this . HeaderP1 = new ReadOnlyDictionary < Int32 , GpioPin > ( headerP1 ) ;
this . HeaderP5 = new ReadOnlyDictionary < Int32 , 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 Boolean IsInitialized {
get {
lock ( SyncRoot ) {
return Mode ! = ControllerMode . NotInitialized ;
}
}
}
/// <summary>
/// Gets the number of registered pins in the controller.
/// </summary>
public Int32 Count = > this . Pins . Count ;
#endregion
#region Pin Addressing
/// <summary>
/// Gets the PWM base frequency (in Hz).
/// </summary>
public Int32 PwmBaseFrequency = > 19200000 ;
/// <summary>
/// Gets a red-only collection of all registered pins.
/// </summary>
public ReadOnlyCollection < GpioPin > Pins {
get ;
}
/// <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 < Int32 , GpioPin > HeaderP1 {
get ;
}
/// <summary>
/// Provides all the pins on Header P5 of the Pi as a lookup by physical header pin number.
/// This header is the secondary header and it is rarely used.
/// </summary>
public ReadOnlyDictionary < Int32 , GpioPin > HeaderP5 {
get ;
}
#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 ] = > this . _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 ] = > this . HeaderP1 [ ( Int32 ) 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 ] = > this . HeaderP5 [ ( Int32 ) 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 [ Int32 wiringPiPinNumber ] {
get {
if ( Enum . IsDefined ( typeof ( WiringPiPin ) , wiringPiPinNumber ) = = false ) {
throw new IndexOutOfRangeException ( $"Pin {wiringPiPinNumber} is not registered in the GPIO controller." ) ;
}
return this . _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 ( Int32 group , Int32 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 ( Int32 group , Int32 value ) = > Task . Run ( ( ) = > this . 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 ( ( ) = > this . 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 ( ( ) = > this . 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 ( ) = > this . Pins . 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 ( ) = > this . Pins . 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 ( Int32 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 Int32 WiringPiToBcmPinNumber ( Int32 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 Int32 HaderToBcmPinNumber ( Int32 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 ( this . _pinsByWiringPiPinNumber . ContainsKey ( pin . WiringPiPinNumber ) = = false ) {
this . _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 Boolean 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 ) ;
Int32 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
}
2019-02-17 14:08:57 +01:00
}