2019-12-03 18:43:54 +01:00
|
|
|
|
using Unosquare.RaspberryIO.Native;
|
|
|
|
|
using Unosquare.Swan;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
namespace Unosquare.RaspberryIO.Gpio {
|
|
|
|
|
/// <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 Int32 m_PwmRegister;
|
|
|
|
|
private PwmMode m_PwmMode = PwmMode.Balanced;
|
|
|
|
|
private UInt32 m_PwmRange = 1024;
|
|
|
|
|
private Int32 m_PwmClockDivisor = 1;
|
|
|
|
|
private Int32 m_SoftPwmValue = -1;
|
|
|
|
|
private Int32 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, Int32 headerPinNumber) {
|
|
|
|
|
this.PinNumber = (Int32)wiringPiPinNumber;
|
|
|
|
|
this.WiringPiPinNumber = wiringPiPinNumber;
|
|
|
|
|
this.BcmPinNumber = GpioController.WiringPiToBcmPinNumber((Int32)wiringPiPinNumber);
|
|
|
|
|
this.HeaderPinNumber = headerPinNumber;
|
|
|
|
|
this.Header = (this.PinNumber >= 17 && this.PinNumber <= 20) ? GpioHeader.P5 : GpioHeader.P1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Pin Properties
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the Wiring Pi pin number as an integer.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Int32 PinNumber {
|
|
|
|
|
get;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the WiringPi Pin number
|
|
|
|
|
/// </summary>
|
|
|
|
|
public WiringPiPin WiringPiPinNumber {
|
|
|
|
|
get;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the BCM chip (hardware) pin number.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Int32 BcmPinNumber {
|
|
|
|
|
get;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or the physical header (physical board) pin number.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Int32 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 => this.m_PinMode;
|
|
|
|
|
|
|
|
|
|
set {
|
|
|
|
|
lock(this._syncLock) {
|
|
|
|
|
GpioPinDriveMode mode = value;
|
|
|
|
|
if(mode == GpioPinDriveMode.GpioClock && this.Capabilities.Contains(PinCapability.GPCLK) == false ||
|
|
|
|
|
mode == GpioPinDriveMode.PwmOutput && this.Capabilities.Contains(PinCapability.PWM) == false ||
|
|
|
|
|
mode == GpioPinDriveMode.Input && this.Capabilities.Contains(PinCapability.GP) == false ||
|
|
|
|
|
mode == GpioPinDriveMode.Output && this.Capabilities.Contains(PinCapability.GP) == false) {
|
|
|
|
|
throw new NotSupportedException(
|
|
|
|
|
$"Pin {this.WiringPiPinNumber} '{this.Name}' does not support mode '{mode}'. Pin capabilities are limited to: {String.Join(", ", this.Capabilities)}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WiringPi.PinMode(this.PinNumber, (Int32)mode);
|
|
|
|
|
this.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 => this.PinMode == GpioPinDriveMode.Input ? this.m_ResistorPullMode : GpioPinResistorPullMode.Off;
|
|
|
|
|
|
|
|
|
|
set {
|
|
|
|
|
lock(this._syncLock) {
|
|
|
|
|
if(this.PinMode != GpioPinDriveMode.Input) {
|
|
|
|
|
this.m_ResistorPullMode = GpioPinResistorPullMode.Off;
|
|
|
|
|
throw new InvalidOperationException(
|
|
|
|
|
$"Unable to set the {nameof(this.InputPullMode)} for pin {this.PinNumber} because operating mode is {this.PinMode}."
|
|
|
|
|
+ $" Setting the {nameof(this.InputPullMode)} is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Input}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WiringPi.PullUpDnControl(this.PinNumber, (Int32)value);
|
|
|
|
|
this.m_ResistorPullMode = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the PWM register. Values should be between 0 and 1024
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <value>
|
|
|
|
|
/// The PWM register.
|
|
|
|
|
/// </value>
|
|
|
|
|
public Int32 PwmRegister {
|
|
|
|
|
get => this.m_PwmRegister;
|
|
|
|
|
|
|
|
|
|
set {
|
|
|
|
|
lock(this._syncLock) {
|
|
|
|
|
if(this.PinMode != GpioPinDriveMode.PwmOutput) {
|
|
|
|
|
this.m_PwmRegister = 0;
|
|
|
|
|
|
|
|
|
|
throw new InvalidOperationException(
|
|
|
|
|
$"Unable to write PWM register for pin {this.PinNumber} because operating mode is {this.PinMode}."
|
|
|
|
|
+ $" Writing the PWM register is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.PwmOutput}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Int32 val = value.Clamp(0, 1024);
|
|
|
|
|
|
|
|
|
|
WiringPi.PwmWrite(this.PinNumber, val);
|
|
|
|
|
this.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 => this.PinMode == GpioPinDriveMode.PwmOutput ? this.m_PwmMode : PwmMode.Balanced;
|
|
|
|
|
|
|
|
|
|
set {
|
|
|
|
|
lock(this._syncLock) {
|
|
|
|
|
if(this.PinMode != GpioPinDriveMode.PwmOutput) {
|
|
|
|
|
this.m_PwmMode = PwmMode.Balanced;
|
|
|
|
|
|
|
|
|
|
throw new InvalidOperationException(
|
|
|
|
|
$"Unable to set PWM mode for pin {this.PinNumber} because operating mode is {this.PinMode}."
|
|
|
|
|
+ $" Setting the PWM mode is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.PwmOutput}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WiringPi.PwmSetMode((Int32)value);
|
|
|
|
|
this.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 UInt32 PwmRange {
|
|
|
|
|
get => this.PinMode == GpioPinDriveMode.PwmOutput ? this.m_PwmRange : 0;
|
|
|
|
|
|
|
|
|
|
set {
|
|
|
|
|
lock(this._syncLock) {
|
|
|
|
|
if(this.PinMode != GpioPinDriveMode.PwmOutput) {
|
|
|
|
|
this.m_PwmRange = 1024;
|
|
|
|
|
|
|
|
|
|
throw new InvalidOperationException(
|
|
|
|
|
$"Unable to set PWM range for pin {this.PinNumber} because operating mode is {this.PinMode}."
|
|
|
|
|
+ $" Setting the PWM range is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.PwmOutput}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WiringPi.PwmSetRange(value);
|
|
|
|
|
this.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 Int32 PwmClockDivisor {
|
|
|
|
|
get => this.PinMode == GpioPinDriveMode.PwmOutput ? this.m_PwmClockDivisor : 0;
|
|
|
|
|
|
|
|
|
|
set {
|
|
|
|
|
lock(this._syncLock) {
|
|
|
|
|
if(this.PinMode != GpioPinDriveMode.PwmOutput) {
|
|
|
|
|
this.m_PwmClockDivisor = 1;
|
|
|
|
|
|
|
|
|
|
throw new InvalidOperationException(
|
|
|
|
|
$"Unable to set PWM range for pin {this.PinNumber} because operating mode is {this.PinMode}."
|
|
|
|
|
+ $" Setting the PWM range is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.PwmOutput}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WiringPi.PwmSetClock(value);
|
|
|
|
|
this.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 Boolean IsInSoftToneMode => this.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 Int32 SoftToneFrequency {
|
|
|
|
|
get => this.m_SoftToneFrequency;
|
|
|
|
|
|
|
|
|
|
set {
|
|
|
|
|
lock(this._syncLock) {
|
|
|
|
|
if(this.IsInSoftToneMode == false) {
|
|
|
|
|
Int32 setupResult = WiringPi.SoftToneCreate(this.PinNumber);
|
|
|
|
|
if(setupResult != 0) {
|
|
|
|
|
throw new InvalidOperationException(
|
|
|
|
|
$"Unable to initialize soft tone on pin {this.PinNumber}. Error Code: {setupResult}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WiringPi.SoftToneWrite(this.PinNumber, value);
|
|
|
|
|
this.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 Boolean IsInSoftPwmMode => this.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 Int32 SoftPwmValue {
|
|
|
|
|
get => this.m_SoftPwmValue;
|
|
|
|
|
|
|
|
|
|
set {
|
|
|
|
|
lock(this._syncLock) {
|
|
|
|
|
if(this.IsInSoftPwmMode && value >= 0) {
|
|
|
|
|
WiringPi.SoftPwmWrite(this.PinNumber, value);
|
|
|
|
|
this.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 Int32 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(Int32 value, Int32 range) {
|
|
|
|
|
lock(this._syncLock) {
|
|
|
|
|
if(this.Capabilities.Contains(PinCapability.GP) == false) {
|
|
|
|
|
throw new NotSupportedException($"Pin {this.PinNumber} does not support software PWM");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(this.IsInSoftPwmMode) {
|
|
|
|
|
throw new InvalidOperationException($"{nameof(StartSoftPwm)} has already been called.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Int32 startResult = WiringPi.SoftPwmCreate(this.PinNumber, value, range);
|
|
|
|
|
|
|
|
|
|
if(startResult == 0) {
|
|
|
|
|
this.m_SoftPwmValue = value;
|
|
|
|
|
this.SoftPwmRange = range;
|
|
|
|
|
} else {
|
|
|
|
|
throw new InvalidOperationException(
|
|
|
|
|
$"Could not start software based PWM on pin {this.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(this._syncLock) {
|
|
|
|
|
if(this.PinMode != GpioPinDriveMode.Output) {
|
|
|
|
|
throw new InvalidOperationException(
|
|
|
|
|
$"Unable to write to pin {this.PinNumber} because operating mode is {this.PinMode}."
|
|
|
|
|
+ $" Writes are only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Output}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WiringPi.DigitalWrite(this.PinNumber, (Int32)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(() => this.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(Boolean value)
|
|
|
|
|
=> this.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(Boolean value) => Task.Run(() => this.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(Int32 value) => this.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(Int32 value) => Task.Run(() => this.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(Int32 value) {
|
|
|
|
|
lock(this._syncLock) {
|
|
|
|
|
if(this.PinMode != GpioPinDriveMode.Output) {
|
|
|
|
|
throw new InvalidOperationException(
|
|
|
|
|
$"Unable to write to pin {this.PinNumber} because operating mode is {this.PinMode}."
|
|
|
|
|
+ $" Writes are only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Output}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WiringPi.AnalogWrite(this.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(Int32 value) => Task.Run(() => this.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 Boolean WaitForValue(GpioPinValue status, Int32 timeOutMillisecond) {
|
|
|
|
|
if(this.PinMode != GpioPinDriveMode.Input) {
|
|
|
|
|
throw new InvalidOperationException(
|
|
|
|
|
$"Unable to read from pin {this.PinNumber} because operating mode is {this.PinMode}."
|
|
|
|
|
+ $" Reads are only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Input}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HighResolutionTimer hrt = new HighResolutionTimer();
|
|
|
|
|
hrt.Start();
|
|
|
|
|
do {
|
|
|
|
|
if(this.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 Boolean Read() {
|
|
|
|
|
lock(this._syncLock) {
|
|
|
|
|
if(this.PinMode != GpioPinDriveMode.Input && this.PinMode != GpioPinDriveMode.Output) {
|
|
|
|
|
throw new InvalidOperationException(
|
|
|
|
|
$"Unable to read from pin {this.PinNumber} because operating mode is {this.PinMode}."
|
|
|
|
|
+ $" Reads are only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Input} or {GpioPinDriveMode.Output}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return WiringPi.DigitalRead(this.PinNumber) != 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Reads the digital value on the pin as a boolean value.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>The state of the pin</returns>
|
|
|
|
|
public Task<Boolean> ReadAsync() => Task.Run(() => this.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()
|
|
|
|
|
=> this.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(() => this.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 Int32 ReadLevel() {
|
|
|
|
|
lock(this._syncLock) {
|
|
|
|
|
if(this.PinMode != GpioPinDriveMode.Input) {
|
|
|
|
|
throw new InvalidOperationException(
|
|
|
|
|
$"Unable to read from pin {this.PinNumber} because operating mode is {this.PinMode}."
|
|
|
|
|
+ $" Reads are only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Input}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return WiringPi.AnalogRead(this.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<Int32> ReadLevelAsync() => Task.Run(() => this.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(this.InterruptCallback != null) {
|
|
|
|
|
throw new InvalidOperationException("An interrupt callback was already registered.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(this.PinMode != GpioPinDriveMode.Input) {
|
|
|
|
|
throw new InvalidOperationException(
|
|
|
|
|
$"Unable to {nameof(RegisterInterruptCallback)} for pin {this.PinNumber} because operating mode is {this.PinMode}."
|
|
|
|
|
+ $" Calling {nameof(RegisterInterruptCallback)} is only allowed if {nameof(this.PinMode)} is set to {GpioPinDriveMode.Input}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lock(this._syncLock) {
|
|
|
|
|
Int32 registerResult = WiringPi.WiringPiISR(this.PinNumber, (Int32)edgeDetection, callback);
|
|
|
|
|
if(registerResult == 0) {
|
|
|
|
|
this.InterruptEdgeDetection = edgeDetection;
|
|
|
|
|
this.InterruptCallback = callback;
|
|
|
|
|
} else {
|
|
|
|
|
HardwareException.Throw(nameof(GpioPin), nameof(RegisterInterruptCallback));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
2019-02-17 14:08:57 +01:00
|
|
|
|
}
|