未验证 提交 ddbfc0d9 编写于 作者: L Laurent Ellerbach 提交者: GitHub

Adding GpioPin (#1895)

* Adding GpioPin

* adjusting based on PR feedback

* Adjusting to have natively drivers.

* make methods virtual

* adding Toggle to System.Device drivers

* Board and Mcp23xxx adjustments

* adding FT4222, FT232H and Pcx857x

* adding Arduino and Seasaw

* adjsting file encoding after conflict merge

* Adding missing Toggle implementation in Arduino native

* Adjusting test for new pattern with GpioPin

* adjusting PR feedback

* typo in test

* fixing nit

* fixing test

* fixing test
上级 895d5e55
......@@ -30,13 +30,14 @@ public class GpioControllerSoftwareTests : IDisposable
}
[Fact]
public void OpenTwiceThrows()
public void OpenTwiceGpioPinAndClosedTwiceThrows()
{
var ctrl = new GpioController(PinNumberingScheme.Logical, _mockedGpioDriver.Object);
_mockedGpioDriver.Setup(x => x.OpenPinEx(1));
_mockedGpioDriver.Setup(x => x.ClosePinEx(1));
ctrl.OpenPin(1);
Assert.Throws<InvalidOperationException>(() => ctrl.OpenPin(1));
GpioPin gpioPin1 = ctrl.OpenPin(1);
GpioPin gpiopin2 = ctrl.OpenPin(1);
Assert.Equal(gpioPin1, gpiopin2);
ctrl.ClosePin(1);
Assert.Throws<InvalidOperationException>(() => ctrl.ClosePin(1));
}
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Text;
using Moq;
using Xunit;
namespace System.Device.Gpio.Tests
{
public class GpioPinTests : IDisposable
{
private const int PinNumber = 2;
private Mock<MockableGpioDriver> _mockedGpioDriver;
public GpioPinTests()
{
_mockedGpioDriver = new Mock<MockableGpioDriver>(MockBehavior.Default);
_mockedGpioDriver.CallBase = true;
}
public void Dispose()
{
_mockedGpioDriver.VerifyAll();
}
[Fact]
public void TestOpenPin()
{
// Arrange
_mockedGpioDriver.Setup(x => x.OpenPinEx(PinNumber));
_mockedGpioDriver.Setup(x => x.IsPinModeSupportedEx(PinNumber, It.IsAny<PinMode>())).Returns(true);
_mockedGpioDriver.Setup(x => x.SetPinModeEx(PinNumber, It.IsAny<PinMode>()));
_mockedGpioDriver.Setup(x => x.GetPinModeEx(PinNumber)).Returns(PinMode.Input);
var ctrl = new GpioController(PinNumberingScheme.Logical, _mockedGpioDriver.Object);
// Act
GpioPin pin = ctrl.OpenPin(PinNumber, PinMode.Input);
// Assert
Assert.Equal(pin.PinNumber, PinNumber);
Assert.Equal(PinMode.Input, pin.GetPinMode());
}
[Fact]
public void TestClosePin()
{
// Arrange
_mockedGpioDriver.Setup(x => x.OpenPinEx(PinNumber));
_mockedGpioDriver.Setup(x => x.IsPinModeSupportedEx(PinNumber, It.IsAny<PinMode>())).Returns(true);
_mockedGpioDriver.Setup(x => x.SetPinModeEx(PinNumber, It.IsAny<PinMode>()));
var ctrl = new GpioController(PinNumberingScheme.Logical, _mockedGpioDriver.Object);
// Act
GpioPin pin = ctrl.OpenPin(PinNumber, PinMode.Input);
ctrl.ClosePin(PinNumber);
// Assert
// This should work even if the pin is closed in the controller as the driver has no idea
// Of the controller behavior.
var ret = pin.Read();
}
[Fact]
public void TestToggleReadWrite()
{
// Arrange
PinValue pinValue = PinValue.High;
_mockedGpioDriver.Setup(x => x.OpenPinEx(PinNumber));
_mockedGpioDriver.Setup(x => x.IsPinModeSupportedEx(PinNumber, It.IsAny<PinMode>())).Returns(true);
_mockedGpioDriver.Setup(x => x.SetPinModeEx(PinNumber, It.IsAny<PinMode>()));
_mockedGpioDriver.Setup(x => x.ReadEx(PinNumber)).Returns(pinValue);
var ctrl = new GpioController(PinNumberingScheme.Logical, _mockedGpioDriver.Object);
// Act
GpioPin pin = ctrl.OpenPin(PinNumber, PinMode.Input);
pin.Write(pinValue);
// Assert
Assert.Equal(pinValue, pin.Read());
pin.Toggle();
// Make sure we setup the drive properly
pinValue = !pinValue;
_mockedGpioDriver.Setup(x => x.ReadEx(PinNumber)).Returns(pinValue);
Assert.Equal(pinValue, pin.Read());
}
}
}
......@@ -6,6 +6,7 @@ using System.Threading;
using System.Runtime.InteropServices;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Net.NetworkInformation;
namespace System.Device.Gpio.Drivers;
......@@ -21,6 +22,7 @@ public class LibGpiodDriver : UnixDriver
private readonly ConcurrentDictionary<int, SafeLineHandle> _pinNumberToSafeLineHandle;
private readonly ConcurrentDictionary<int, LibGpiodDriverEventHandler> _pinNumberToEventHandler;
private readonly int _pinCount;
private readonly ConcurrentDictionary<int, PinValue> _pinValue;
private SafeChipHandle _chip;
/// <inheritdoc />
......@@ -76,6 +78,7 @@ public class LibGpiodDriver : UnixDriver
_pinCount = Interop.libgpiod.gpiod_chip_num_lines(_chip);
_pinNumberToEventHandler = new ConcurrentDictionary<int, LibGpiodDriverEventHandler>();
_pinNumberToSafeLineHandle = new ConcurrentDictionary<int, SafeLineHandle>();
_pinValue = new ConcurrentDictionary<int, PinValue>();
}
catch (DllNotFoundException)
{
......@@ -134,6 +137,7 @@ public class LibGpiodDriver : UnixDriver
pinHandle?.Dispose();
// We know this works
_pinNumberToSafeLineHandle.TryRemove(pinNumber, out _);
_pinValue.TryRemove(pinNumber, out _);
}
}
}
......@@ -211,6 +215,9 @@ public class LibGpiodDriver : UnixDriver
}
_pinNumberToSafeLineHandle.TryAdd(pinNumber, pinHandle);
// This is setting up a default value without reading the driver as it's the default behavior.
// If the Setmode with an initial value is used, this is going to be corrected automatically
_pinValue.TryAdd(pinNumber, PinValue.Low);
}
}
......@@ -225,12 +232,16 @@ public class LibGpiodDriver : UnixDriver
throw ExceptionHelper.GetIOException(ExceptionResource.ReadPinError, Marshal.GetLastWin32Error(), pinNumber);
}
_pinValue[pinNumber] = result;
return result;
}
throw ExceptionHelper.GetInvalidOperationException(ExceptionResource.PinNotOpenedError, pin: pinNumber);
}
/// <inheritdoc/>
protected internal override void Toggle(int pinNumber) => Write(pinNumber, !_pinValue[pinNumber]);
/// <inheritdoc/>
protected internal override void RemoveCallbackForPinValueChangedEvent(int pinNumber, PinChangeEventHandler callback)
{
......@@ -307,6 +318,7 @@ public class LibGpiodDriver : UnixDriver
pinNumber);
}
_pinValue[pinNumber] = initialValue;
pinHandle.PinMode = mode;
return;
}
......@@ -372,6 +384,7 @@ public class LibGpiodDriver : UnixDriver
}
Interop.libgpiod.gpiod_line_set_value(pinHandle, (value == PinValue.High) ? 1 : 0);
_pinValue[pinNumber] = value;
}
/// <inheritdoc/>
......@@ -399,6 +412,7 @@ public class LibGpiodDriver : UnixDriver
}
_pinNumberToSafeLineHandle.Clear();
_pinValue.Clear();
}
_chip?.Dispose();
......
......@@ -198,6 +198,9 @@ public class RaspberryPi3Driver : GpioDriver
/// <inheritdoc/>
protected internal override PinValue Read(int pinNumber) => InternalDriver.Read(pinNumber);
/// <inheritdoc/>
protected internal override void Toggle(int pinNumber) => InternalDriver.Toggle(pinNumber);
/// <inheritdoc/>
protected internal override void RemoveCallbackForPinValueChangedEvent(int pinNumber, PinChangeEventHandler callback) => InternalDriver.RemoveCallbackForPinValueChangedEvent(pinNumber, callback);
......
......@@ -175,6 +175,13 @@ internal unsafe class RaspberryPi3LinuxDriver : GpioDriver
return Convert.ToBoolean((register >> (pinNumber % 32)) & 1) ? PinValue.High : PinValue.Low;
}
/// <inheritdoc/>
protected internal override void Toggle(int pinNumber)
{
ValidatePinNumber(pinNumber);
_interruptDriver!.Toggle(pinNumber);
}
/// <summary>
/// Removes a handler for a pin value changed event.
/// </summary>
......
......@@ -28,6 +28,7 @@ public class SysFsDriver : UnixDriver
private readonly List<int> _exportedPins = new List<int>();
private readonly Dictionary<int, UnixDriverDevicePin> _devicePins = new Dictionary<int, UnixDriverDevicePin>();
private readonly Dictionary<int, PinValue> _pinValues = new Dictionary<int, PinValue>();
private TimeSpan _statusUpdateSleepTime = TimeSpan.FromMilliseconds(1);
private int _pollFileDescriptor = -1;
private Thread? _eventDetectionThread;
......@@ -118,6 +119,8 @@ public class SysFsDriver : UnixDriver
SysFsHelpers.EnsureReadWriteAccessToPath(pinPath);
_exportedPins.Add(pinNumber);
// Default value is low, otherwise it's the set pin mode with default value that will override this
_pinValues.Add(pinNumber, PinValue.Low);
}
catch (UnauthorizedAccessException e)
{
......@@ -145,6 +148,7 @@ public class SysFsDriver : UnixDriver
{
_devicePins[pinNumber].Dispose();
_devicePins.Remove(pinNumber);
_pinValues.Remove(pinNumber);
}
// If this controller wasn't the one that opened the pin, then Remove will return false, so we don't need to close it.
......@@ -248,9 +252,13 @@ public class SysFsDriver : UnixDriver
throw new InvalidOperationException("There was an attempt to read from a pin that is not open.");
}
_pinValues[pinNumber] = result;
return result;
}
/// <inheritdoc/>
protected internal override void Toggle(int pinNumber) => Write(pinNumber, !_pinValues[pinNumber]);
private PinValue ConvertSysFsValueToPinValue(string value)
{
return value.Trim() switch
......@@ -275,6 +283,7 @@ public class SysFsDriver : UnixDriver
{
string sysFsValue = ConvertPinValueToSysFs(value);
File.WriteAllText(valuePath, sysFsValue);
_pinValues[pinNumber] = value;
}
catch (UnauthorizedAccessException e)
{
......@@ -588,6 +597,7 @@ public class SysFsDriver : UnixDriver
}
_devicePins.Clear();
_pinValues.Clear();
if (_pollFileDescriptor != -1)
{
Interop.close(_pollFileDescriptor);
......
......@@ -14,6 +14,7 @@ public class Windows10Driver : GpioDriver
{
private static readonly WinGpio.GpioController s_winGpioController = WinGpio.GpioController.GetDefault();
private readonly Dictionary<int, Windows10DriverPin> _openPins = new Dictionary<int, Windows10DriverPin>();
private readonly Dictionary<int, PinValue> _pinValues = new Dictionary<int, PinValue>();
/// <summary>
/// Initializes a new instance of the <see cref="Windows10Driver"/> class.
......@@ -68,6 +69,7 @@ public class Windows10Driver : GpioDriver
{
pin.ClosePin();
_openPins.Remove(pinNumber);
_pinValues.Remove(pinNumber);
}
}
......@@ -113,6 +115,8 @@ public class Windows10Driver : GpioDriver
WinGpio.GpioPin windowsPin = s_winGpioController.OpenPin(pinNumber, WinGpio.GpioSharingMode.Exclusive);
_openPins[pinNumber] = new Windows10DriverPin(this, windowsPin);
// Default value, override by the set pin mode with another default value
_pinValues[pinNumber] = PinValue.Low;
}
/// <summary>
......@@ -120,7 +124,15 @@ public class Windows10Driver : GpioDriver
/// </summary>
/// <param name="pinNumber">The pin number in the driver's logical numbering scheme.</param>
/// <returns>The value of the pin.</returns>
protected internal override PinValue Read(int pinNumber) => LookupOpenPin(pinNumber).Read();
protected internal override PinValue Read(int pinNumber)
{
var value = LookupOpenPin(pinNumber).Read();
_pinValues[pinNumber] = value;
return value;
}
/// <inheritdoc/>
protected internal override void Toggle(int pinNumber) => Write(pinNumber, !_pinValues[pinNumber]);
/// <summary>
/// Removes a handler for a pin value changed event.
......@@ -152,7 +164,11 @@ public class Windows10Driver : GpioDriver
/// </summary>
/// <param name="pinNumber">The pin number in the driver's logical numbering scheme.</param>
/// <param name="value">The value to be written to the pin.</param>
protected internal override void Write(int pinNumber, PinValue value) => LookupOpenPin(pinNumber).Write(value);
protected internal override void Write(int pinNumber, PinValue value)
{
LookupOpenPin(pinNumber).Write(value);
_pinValues[pinNumber] = value;
}
/// <summary>
/// Lookups an open pin in the driver.
......
......@@ -66,6 +66,12 @@ namespace System.Device.Gpio.Drivers
throw new PlatformNotSupportedException();
}
/// <inheritdoc />
protected internal override void Toggle(int pinNumber)
{
throw new PlatformNotSupportedException();
}
/// <inheritdoc />
protected internal override void RemoveCallbackForPinValueChangedEvent(int pinNumber, PinChangeEventHandler callback)
{
......
......@@ -30,6 +30,7 @@ public class GpioController : IDisposable
/// If a pin element exists, that pin is open. Uses current controller's numbering scheme
/// </summary>
private readonly ConcurrentDictionary<int, PinValue?> _openPins;
private readonly ConcurrentDictionary<int, GpioPin> _gpioPins;
private GpioDriver _driver;
/// <summary>
......@@ -50,6 +51,7 @@ public class GpioController : IDisposable
_driver = driver;
NumberingScheme = numberingScheme;
_openPins = new ConcurrentDictionary<int, PinValue?>();
_gpioPins = new ConcurrentDictionary<int, GpioPin>();
}
/// <summary>
......@@ -87,15 +89,17 @@ public class GpioController : IDisposable
/// The driver attempts to open the pin without changing its mode or value.
/// </summary>
/// <param name="pinNumber">The pin number in the controller's numbering scheme.</param>
public void OpenPin(int pinNumber)
public GpioPin OpenPin(int pinNumber)
{
if (IsPinOpen(pinNumber))
{
throw new InvalidOperationException($"Pin {pinNumber} is already open.");
return _gpioPins[pinNumber];
}
OpenPinCore(pinNumber);
_openPins.TryAdd(pinNumber, null);
_gpioPins[pinNumber] = new GpioPin(pinNumber, _driver);
return _gpioPins[pinNumber];
}
/// <summary>
......@@ -113,10 +117,11 @@ public class GpioController : IDisposable
/// </summary>
/// <param name="pinNumber">The pin number in the controller's numbering scheme.</param>
/// <param name="mode">The mode to be set.</param>
public void OpenPin(int pinNumber, PinMode mode)
public GpioPin OpenPin(int pinNumber, PinMode mode)
{
OpenPin(pinNumber);
var pin = OpenPin(pinNumber);
SetPinMode(pinNumber, mode);
return pin;
}
/// <summary>
......@@ -126,13 +131,14 @@ public class GpioController : IDisposable
/// <param name="mode">The mode to be set.</param>
/// <param name="initialValue">The initial value to be set if the mode is output. The driver will attempt to set the mode without causing glitches to the other value.
/// (if <paramref name="initialValue"/> is <see cref="PinValue.High"/>, the pin should not glitch to low during open)</param>
public void OpenPin(int pinNumber, PinMode mode, PinValue initialValue)
public GpioPin OpenPin(int pinNumber, PinMode mode, PinValue initialValue)
{
OpenPin(pinNumber);
var pin = OpenPin(pinNumber);
// Set the desired initial value
_openPins[pinNumber] = initialValue;
SetPinMode(pinNumber, mode);
return pin;
}
/// <summary>
......@@ -159,6 +165,7 @@ public class GpioController : IDisposable
{
int logicalPinNumber = GetLogicalPinNumber(pinNumber);
_driver.ClosePin(logicalPinNumber);
_gpioPins.TryRemove(pinNumber, out _);
}
/// <summary>
......@@ -243,6 +250,20 @@ public class GpioController : IDisposable
return _driver.Read(logicalPinNumber);
}
/// <summary>
/// Toggle the current value of a pin.
/// </summary>
/// <param name="pinNumber">The pin number in the controller's numbering scheme.</param>
public virtual void Toggle(int pinNumber)
{
if (!IsPinOpen(pinNumber))
{
throw new InvalidOperationException($"Can not read from pin {pinNumber} because it is not open.");
}
_driver.Toggle(pinNumber);
}
/// <summary>
/// Writes a value to a pin.
/// </summary>
......@@ -375,6 +396,7 @@ public class GpioController : IDisposable
}
_openPins.Clear();
_gpioPins.Clear();
_driver?.Dispose();
_driver = null!;
}
......
......@@ -82,6 +82,12 @@ public abstract class GpioDriver : IDisposable
/// <returns>The value of the pin.</returns>
protected internal abstract PinValue Read(int pinNumber);
/// <summary>
/// Toggle the current value of a pin.
/// </summary>
/// <param name="pinNumber">The pin number in the driver's logical numbering scheme.</param>
protected internal virtual void Toggle(int pinNumber) => Write(pinNumber, !Read(pinNumber));
/// <summary>
/// Writes a value to a pin.
/// </summary>
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace System.Device.Gpio
{
/// <summary>
/// Represents a general-purpose I/O (GPIO) pin.
/// </summary>
public class GpioPin
{
private readonly int _pinNumber;
private readonly GpioDriver _driver;
internal GpioPin(int pinNumber, GpioDriver driver)
{
_driver = driver;
_pinNumber = pinNumber;
}
/// <summary>
/// Gets the pin number of the general-purpose I/O (GPIO) pin.
/// </summary>
/// <value>
/// The pin number of the GPIO pin.
/// </value>
public virtual int PinNumber => _pinNumber;
/// <summary>
/// Gets the current pin mode for the general-purpose I/O (GPIO) pin. The pin mode specifies whether the pin is configured as an input or an output, and determines how values are driven onto the pin.
/// </summary>
/// <returns>An enumeration value that indicates the current pin mode for the GPIO pin.
/// The pin mode specifies whether the pin is configured as an input or an output, and determines how values are driven onto the pin.</returns>
public virtual PinMode GetPinMode() => _driver.GetPinMode(_pinNumber);
/// <summary>
/// Gets whether the general-purpose I/O (GPIO) pin supports the specified pin mode.
/// </summary>
/// <param name="pinMode">The pin mode that you want to check for support.</param>
/// <returns>
/// <see langword="true"/> if the GPIO pin supports the pin mode that pinMode specifies; otherwise false.
/// If you specify a pin mode for which this method returns <see langword="false"/> when you call <see cref="SetPinMode"/>, <see cref="SetPinMode"/> generates an exception.
/// </returns>
public virtual bool IsPinModeSupported(PinMode pinMode) => _driver.IsPinModeSupported(_pinNumber, pinMode);
/// <summary>
/// Sets the pin mode of the general-purpose I/O (GPIO) pin.
/// The pin mode specifies whether the pin is configured as an input or an output, and determines how values are driven onto the pin.
/// </summary>
/// <param name="value">An enumeration value that specifies pin mode to use for the GPIO pin.
/// The pin mode specifies whether the pin is configured as an input or an output, and determines how values are driven onto the pin.</param>
/// <exception cref="ArgumentException">The GPIO pin does not support the specified pin mode.</exception>
public virtual void SetPinMode(PinMode value) => _driver.SetPinMode(_pinNumber, value);
/// <summary>
/// Reads the current value of the general-purpose I/O (GPIO) pin.
/// </summary>
/// <returns>The current value of the GPIO pin. If the pin is configured as an output, this value is the last value written to the pin.</returns>
public virtual PinValue Read() => _driver.Read(_pinNumber);
/// <summary>
/// Drives the specified value onto the general purpose I/O (GPIO) pin according to the current pin mode for the pin
/// if the pin is configured as an output, or updates the latched output value for the pin if the pin is configured as an input.
/// </summary>
/// <param name="value">The enumeration value to write to the GPIO pin.
/// <para>If the GPIO pin is configured as an output, the method drives the specified value onto the pin according to the current pin mode for the pin.</para>
/// <para>If the GPIO pin is configured as an input, the method updates the latched output value for the pin. The latched output value is driven onto the pin when the configuration for the pin changes to output.</para>
/// </param>
/// <exception cref="InvalidOperationException">This exception will be thrown on an attempt to write to a pin that hasn't been opened or is not configured as output.</exception>
public virtual void Write(PinValue value) => _driver.Write(_pinNumber, value);
/// <summary>
/// Occurs when the value of the general-purpose I/O (GPIO) pin changes, either because of an external stimulus when the pin is configured as an input, or when a value is written to the pin when the pin in configured as an output.
/// </summary>
public virtual event PinChangeEventHandler ValueChanged
{
add
{
_driver.AddCallbackForPinValueChangedEvent(_pinNumber, PinEventTypes.Falling | PinEventTypes.Rising, value);
}
remove
{
_driver.RemoveCallbackForPinValueChangedEvent(_pinNumber, value);
}
}
/// <summary>
/// Toggles the output of the general purpose I/O (GPIO) pin if the pin is configured as an output.
/// </summary>
public virtual void Toggle() => _driver.Toggle(_pinNumber);
}
}
......@@ -18,6 +18,7 @@ namespace Iot.Device.Arduino
private readonly IReadOnlyCollection<SupportedPinConfiguration> _supportedPinConfigurations;
private readonly Dictionary<int, CallbackContainer> _callbackContainers;
private readonly ConcurrentDictionary<int, PinMode> _pinModes;
private readonly ConcurrentDictionary<int, PinValue> _pinValues;
private readonly object _callbackContainersLock;
private readonly AutoResetEvent _waitForEventResetEvent;
private readonly ILogger _logger;
......@@ -31,6 +32,7 @@ namespace Iot.Device.Arduino
_waitForEventResetEvent = new AutoResetEvent(false);
_callbackContainersLock = new object();
_pinModes = new ConcurrentDictionary<int, PinMode>();
_pinValues = new ConcurrentDictionary<int, PinValue>();
_outputPinValues = new ConcurrentDictionary<int, PinValue?>();
_logger = this.GetCurrentClassLogger();
......@@ -54,11 +56,14 @@ namespace Iot.Device.Arduino
{
throw new ArgumentOutOfRangeException(nameof(pinNumber), $"Pin {pinNumber} is not valid");
}
_pinValues.TryAdd(pinNumber, PinValue.Low);
}
protected override void ClosePin(int pinNumber)
{
_pinModes.TryRemove(pinNumber, out _);
_pinValues.TryRemove(pinNumber, out _);
}
protected override void SetPinMode(int pinNumber, PinMode mode)
......@@ -152,9 +157,12 @@ namespace Iot.Device.Arduino
protected override PinValue Read(int pinNumber)
{
return _device.ReadDigitalPin(pinNumber);
_pinValues[pinNumber] = _device.ReadDigitalPin(pinNumber);
return _pinValues[pinNumber];
}
protected override void Toggle(int pinNumber) => Write(pinNumber, !_pinValues[pinNumber]);
protected override void Write(int pinNumber, PinValue value)
{
if (_outputPinValues.TryGetValue(pinNumber, out PinValue? existingValue) && existingValue != null)
......@@ -168,6 +176,7 @@ namespace Iot.Device.Arduino
_device.WriteDigitalPin(pinNumber, value);
_outputPinValues.AddOrUpdate(pinNumber, x => value, (y, z) => value);
_pinValues[pinNumber] = value;
}
protected override WaitForEventResult WaitForEvent(int pinNumber, PinEventTypes eventTypes, CancellationToken cancellationToken)
......@@ -283,6 +292,7 @@ namespace Iot.Device.Arduino
_callbackContainers.Clear();
}
_pinValues.Clear();
_outputPinValues.Clear();
_device.DigitalPortValueUpdated -= FirmataOnDigitalPortValueUpdated;
}
......
......@@ -65,6 +65,12 @@ namespace Iot.Device.Board
throw new NotSupportedException("No such pin");
}
/// <inheritdoc />
protected override void Toggle(int pinNumber)
{
throw new NotSupportedException("No such pin");
}
/// <inheritdoc />
protected override void Write(int pinNumber, PinValue value)
{
......
......@@ -30,6 +30,7 @@ namespace Iot.Device.Board
}
private const int SupportedPinCount = 256;
private readonly Dictionary<int, PinValue> _pinValues = new Dictionary<int, PinValue>();
private KeyState[] _state;
......@@ -49,6 +50,10 @@ namespace Iot.Device.Board
_pollThread = null;
_terminateThread = true;
foreach (var key in Enum.GetValues(typeof(LedKey)))
{
_pinValues.Add((int)key!, GetLedState((LedKey)key).KeyValue);
}
}
/// <inheritdoc />
......@@ -129,6 +134,27 @@ namespace Iot.Device.Board
}
private void SetLedState(LedKey key, PinValue state)
{
(int virtualKey, int currentKeyState) = GetLedState(key);
_pinValues[(int)key] = state;
if ((state == PinValue.High && currentKeyState == 0) ||
(state == PinValue.Low && currentKeyState != 0))
{
// Simulate a key press
Interop.keybd_event((byte)virtualKey,
0x45,
Interop.KEYEVENTF_EXTENDEDKEY,
IntPtr.Zero);
// Simulate a key release
Interop.keybd_event((byte)virtualKey,
0x45,
Interop.KEYEVENTF_EXTENDEDKEY | Interop.KEYEVENTF_KEYUP,
IntPtr.Zero);
}
}
private (int VirtualKey, int KeyValue) GetLedState(LedKey key)
{
int virtualKey = 0;
if (key == LedKey.NumLock)
......@@ -149,22 +175,7 @@ namespace Iot.Device.Board
}
// Bit 1 indicates whether the LED is currently on or off (or, whether Scroll lock, num lock, caps lock is on)
int currentKeyState = Interop.GetKeyState(virtualKey) & 1;
if ((state == PinValue.High && currentKeyState == 0) ||
(state == PinValue.Low && currentKeyState != 0))
{
// Simulate a key press
Interop.keybd_event((byte)virtualKey,
0x45,
Interop.KEYEVENTF_EXTENDEDKEY | 0,
IntPtr.Zero);
// Simulate a key release
Interop.keybd_event((byte)virtualKey,
0x45,
Interop.KEYEVENTF_EXTENDEDKEY | Interop.KEYEVENTF_KEYUP,
IntPtr.Zero);
}
return (virtualKey, Interop.GetKeyState(virtualKey) & 1);
}
/// <inheritdoc />
......@@ -181,6 +192,9 @@ namespace Iot.Device.Board
}
}
/// <inheritdoc />
protected override void Toggle(int pinNumber) => Write(pinNumber, !_pinValues[pinNumber]);
/// <inheritdoc />
protected override void Write(int pinNumber, PinValue value)
{
......@@ -213,7 +227,8 @@ namespace Iot.Device.Board
{
return new WaitForEventResult()
{
EventTypes = PinEventTypes.Rising, TimedOut = false
EventTypes = PinEventTypes.Rising,
TimedOut = false
};
}
else if (eventTypes == PinEventTypes.Falling && newState == PinValue.Low)
......
......@@ -57,12 +57,13 @@ namespace Iot.Device.Board.Tests
}
[Fact]
public void OpenPinAlreadyAssignedThrows()
public void GetSameGpioPinWhenOpen()
{
var board = CreateBoard();
var ctrl = board.CreateGpioController();
ctrl.OpenPin(1);
Assert.Throws<InvalidOperationException>(() => ctrl.OpenPin(1));
var firstGpioPin = ctrl.OpenPin(1);
var secondGpioPin = ctrl.OpenPin(1);
Assert.Equal(firstGpioPin, secondGpioPin);
}
[Fact]
......
......@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Device.Gpio;
using System.Linq;
using System.Threading;
......@@ -13,6 +14,8 @@ namespace Iot.Device.Ft232H
/// </summary>
public class Ft232HGpio : GpioDriver
{
private readonly Dictionary<int, PinValue> _pinValues = new Dictionary<int, PinValue>();
/// <summary>
/// Store the FTDI Device Information
/// </summary>
......@@ -64,12 +67,14 @@ namespace Iot.Device.Ft232H
}
DeviceInformation.PinOpen[pinNumber] = true;
_pinValues.TryAdd(pinNumber, PinValue.Low);
}
/// <inheritdoc/>
protected override void ClosePin(int pinNumber)
{
DeviceInformation.PinOpen[pinNumber] = false;
_pinValues.Remove(pinNumber);
}
/// <inheritdoc/>
......@@ -130,15 +135,20 @@ namespace Iot.Device.Ft232H
if (pinNumber < 8)
{
var val = DeviceInformation.GetGpioValuesLow();
return (((val >> pinNumber) & 0x01) == 0x01) ? PinValue.High : PinValue.Low;
_pinValues[pinNumber] = (((val >> pinNumber) & 0x01) == 0x01) ? PinValue.High : PinValue.Low;
}
else
{
var valhigh = DeviceInformation.GetGpioValuesHigh();
return (((valhigh >> (pinNumber - 8)) & 0x01) == 0x01) ? PinValue.High : PinValue.Low;
_pinValues[pinNumber] = (((valhigh >> (pinNumber - 8)) & 0x01) == 0x01) ? PinValue.High : PinValue.Low;
}
return _pinValues[pinNumber];
}
/// <inheritdoc/>
protected override void Toggle(int pinNumber) => Write(pinNumber, !_pinValues[pinNumber]);
/// <inheritdoc/>
protected override void Write(int pinNumber, PinValue value)
{
......@@ -172,6 +182,8 @@ namespace Iot.Device.Ft232H
DeviceInformation.SetGpioValuesHigh();
}
_pinValues[pinNumber] = value;
}
/// <inheritdoc/>
......
......@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Device.Gpio;
using System.IO;
using System.Linq;
......@@ -18,6 +19,7 @@ namespace Iot.Device.Ft4222
{
private const int PinCountConst = 4;
private readonly Dictionary<int, PinValue> _pinValues = new Dictionary<int, PinValue>();
private SafeFtHandle _ftHandle;
private GpioPinMode[] _gpioDirections = new GpioPinMode[PinCountConst];
private GpioTrigger[] _gpioTriggers = new GpioTrigger[PinCountConst];
......@@ -110,6 +112,7 @@ namespace Iot.Device.Ft4222
/// <inheritdoc/>
protected override void ClosePin(int pinNumber)
{
_pinValues.Remove(pinNumber);
}
/// <inheritdoc/>
......@@ -124,6 +127,7 @@ namespace Iot.Device.Ft4222
/// <inheritdoc/>
protected override void OpenPin(int pinNumber)
{
_pinValues.TryAdd(pinNumber, PinValue.Low);
}
/// <inheritdoc/>
......@@ -136,9 +140,13 @@ namespace Iot.Device.Ft4222
throw new IOException($"{nameof(Read)}: failed to write GPIO, status: {status}");
}
return pinVal == GpioPinValue.High ? PinValue.High : PinValue.Low;
_pinValues[pinNumber] = pinVal == GpioPinValue.High ? PinValue.High : PinValue.Low;
return _pinValues[pinNumber];
}
/// <inheritdoc/>
protected override void Toggle(int pinNumber) => Write(pinNumber, !_pinValues[pinNumber]);
/// <inheritdoc/>
protected override void AddCallbackForPinValueChangedEvent(int pinNumber, PinEventTypes eventTypes, PinChangeEventHandler callback)
{
......@@ -265,12 +273,15 @@ namespace Iot.Device.Ft4222
{
throw new IOException($"{nameof(Write)}: failed to write GPIO, status: {status}");
}
_pinValues[pinNumber] = value;
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
_ftHandle.Dispose();
_pinValues.Clear();
base.Dispose(disposing);
}
}
......
......@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Device.Gpio;
using System.Runtime.CompilerServices;
using System.Threading;
......@@ -16,6 +17,7 @@ namespace Iot.Device.Mcp23xxx
private readonly int _reset;
private readonly int _interruptA;
private readonly int _interruptB;
private readonly Dictionary<int, PinValue> _pinValues = new Dictionary<int, PinValue>();
private BankStyle _bankStyle;
private GpioController? _controller;
private bool _shouldDispose;
......@@ -243,6 +245,7 @@ namespace Iot.Device.Mcp23xxx
_controller = null;
}
_pinValues.Clear();
_bus?.Dispose();
_bus = null!;
base.Dispose(disposing);
......@@ -359,9 +362,13 @@ namespace Iot.Device.Mcp23xxx
new PinValuePair(pinNumber, default)
};
Read(pinValuePairs);
return pinValuePairs[0].PinValue;
_pinValues[pinNumber] = pinValuePairs[0].PinValue;
return _pinValues[pinNumber];
}
/// <inheritdoc/>
protected override void Toggle(int pinNumber) => Write(pinNumber, !_pinValues[pinNumber]);
/// <summary>
/// Reads the value of a set of pins
/// </summary>
......@@ -394,6 +401,7 @@ namespace Iot.Device.Mcp23xxx
{
int pin = pinValuePairs[i].PinNumber;
pinValuePairs[i] = new PinValuePair(pin, result & (1 << pin));
_pinValues[pin] = pinValuePairs[i].PinValue;
}
}
......@@ -410,6 +418,7 @@ namespace Iot.Device.Mcp23xxx
new PinValuePair(pinNumber, value)
};
Write(pinValuePairs);
_pinValues[pinNumber] = value;
}
/// <summary>
......@@ -452,6 +461,10 @@ namespace Iot.Device.Mcp23xxx
}
_gpioCache = newValue;
foreach (PinValuePair pinValuePair in pinValuePairs)
{
_pinValues[pinValuePair.PinNumber] = pinValuePair.PinValue;
}
}
private ushort SetBits(ushort current, ushort bits, ushort mask)
......@@ -519,12 +532,14 @@ namespace Iot.Device.Mcp23xxx
protected override void OpenPin(int pinNumber)
{
// No-op
_pinValues.TryAdd(pinNumber, PinValue.Low);
}
/// <inheritdoc/>
protected override void ClosePin(int pinNumber)
{
// No-op
_pinValues.Remove(pinNumber);
}
/// <inheritdoc/>
......
......@@ -214,6 +214,8 @@ namespace Iot.Device.Mcp23xxx.Tests
return PinValue.Low;
}
protected override void Toggle(int pinNumber) => Write(pinNumber, !Read(pinNumber));
public void Read(Span<PinValuePair> pinValuePairs)
{
for (int i = 0; i < pinValuePairs.Length; i++)
......
......@@ -3,6 +3,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Device.Gpio;
using System.Device.I2c;
using System.Runtime.CompilerServices;
......@@ -22,13 +23,14 @@ namespace Iot.Device.Pcx857x
protected I2cDevice Device { get; }
private readonly GpioController? _controller;
private readonly int _interrupt;
private readonly Dictionary<int, PinValue> _pinValues = new Dictionary<int, PinValue>();
private bool _shouldDispose;
// Pin mode bits- 0 for input, 1 for output to match PinMode
private ushort _pinModes;
// Pin value bits, 1 for high
private ushort _pinValues;
private ushort _pinValueBits;
/// <summary>
/// Remote I/O expander for I2C-bus with interrupt.
......@@ -106,6 +108,7 @@ namespace Iot.Device.Pcx857x
protected override void ClosePin(int pinNumber)
{
// No-op
_pinValues.Remove(pinNumber);
}
/// <inheritdoc/>
......@@ -117,6 +120,7 @@ namespace Iot.Device.Pcx857x
}
Device.Dispose();
_pinValues.Clear();
base.Dispose(disposing);
}
......@@ -124,6 +128,7 @@ namespace Iot.Device.Pcx857x
protected override void OpenPin(int pinNumber)
{
// No-op
_pinValues.TryAdd(pinNumber, PinValue.Low);
}
/// <inheritdoc/>
......@@ -134,9 +139,13 @@ namespace Iot.Device.Pcx857x
new PinValuePair(pinNumber, default)
};
Read(values);
return values[0].PinValue;
_pinValues[pinNumber] = values[0].PinValue;
return _pinValues[pinNumber];
}
/// <inheritdoc/>
protected override void Toggle(int pinNumber) => Write(pinNumber, !_pinValues[pinNumber]);
/// <summary>
/// Reads multiple pins from the device
/// </summary>
......@@ -161,6 +170,7 @@ namespace Iot.Device.Pcx857x
{
int pin = pinValues[i].PinNumber;
pinValues[i] = new PinValuePair(pin, (data >> pin) & 1);
_pinValues[pin] = pinValues[i].PinValue;
}
}
......@@ -198,21 +208,21 @@ namespace Iot.Device.Pcx857x
throw new ArgumentOutOfRangeException(nameof(mode), "Only Input and Output modes are supported.");
}
WritePins(_pinValues);
WritePins(_pinValueBits);
}
private void WritePins(ushort value)
{
// We need to set all input pins to high
_pinValues = (ushort)(value | ~_pinModes);
_pinValueBits = (ushort)(value | ~_pinModes);
if (PinCount == 8)
{
WriteByte((byte)_pinValues);
WriteByte((byte)_pinValueBits);
}
else
{
InternalWriteUInt16(_pinValues);
InternalWriteUInt16(_pinValueBits);
}
}
......@@ -224,6 +234,7 @@ namespace Iot.Device.Pcx857x
new PinValuePair(pinNumber, value)
};
Write(values);
_pinValues[pinNumber] = value;
}
/// <summary>
......@@ -250,7 +261,11 @@ namespace Iot.Device.Pcx857x
return current;
}
WritePins(SetBits(_pinValues, (ushort)values, (ushort)pins));
WritePins(SetBits(_pinValueBits, (ushort)values, (ushort)pins));
foreach (PinValuePair pinValuePair in pinValues)
{
_pinValues[pinValuePair.PinNumber] = pinValuePair.PinValue;
}
}
/// <inheritdoc/>
......
......@@ -161,6 +161,8 @@ namespace Iot.Device.Pcx857x.Tests
return PinValue.Low;
}
protected override void Toggle(int pinNumber) => Write(pinNumber, !Read(pinNumber));
public void Read(Span<PinValuePair> pinValues)
{
for (int i = 0; i < pinValues.Length; i++)
......
......@@ -15,6 +15,7 @@ namespace Iot.Device.Seesaw
public class SeesawGpioDriver : GpioDriver
{
private readonly Dictionary<int, PinMode> _openPins;
private readonly Dictionary<int, PinValue> _pinValues;
private Seesaw _seesawDevice;
......@@ -41,6 +42,7 @@ namespace Iot.Device.Seesaw
}
_openPins = new Dictionary<int, PinMode>();
_pinValues = new Dictionary<int, PinValue>();
}
/// <summary>
......@@ -67,6 +69,7 @@ namespace Iot.Device.Seesaw
}
_openPins.Remove(pinNumber);
_pinValues.Remove(pinNumber);
}
/// <summary>
......@@ -145,6 +148,7 @@ namespace Iot.Device.Seesaw
}
_openPins.Add(pinNumber, mode);
_pinValues.Add(pinNumber, PinValue.Low);
SetPinMode(pinNumber, mode);
}
......@@ -165,9 +169,13 @@ namespace Iot.Device.Seesaw
throw new InvalidOperationException("Can not read a value from a pin that is not open.");
}
return _seesawDevice.ReadGpioDigital((byte)pinNumber) ? PinValue.High : PinValue.Low;
_pinValues[pinNumber] = _seesawDevice.ReadGpioDigital((byte)pinNumber) ? PinValue.High : PinValue.Low;
return _pinValues[pinNumber];
}
/// <inheritdoc/>
protected override void Toggle(int pinNumber) => Write(pinNumber, !_pinValues[pinNumber]);
/// <summary>
/// Read the given pins with the given pin numbers.
/// </summary>
......@@ -219,6 +227,7 @@ namespace Iot.Device.Seesaw
}
_seesawDevice.WriteGpioDigital((byte)pinNumber, (value == PinValue.High));
_pinValues[pinNumber] = value;
}
/// <summary>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册