未验证 提交 fe22a805 编写于 作者: Z Zhang Yuexin 提交者: GitHub

Add a generic GPIO driver for Allwinner SoCs and Orange Pi Zero/Lite/Lite2 pin map (#1130)

* add cs files

* add csproj

* resolve some feedbacks

* MSBuild fail

* derive from UnixDriver

* reference source files

* reference csproj

* resolve some issues

* using GpioController

* _interruptController is assigned a value in the constructor

* _interruptController nullable type

* nullable check

* SunxiDriver instead of inheriting SysFsDriver; add samples

* add readme; update samples

* add pin mode check; add static to _pinNumberConverter

* call initialize in constructor

* update README.md

* fix init problem; add some validates

* add Orange Pi Lite driver

* clean up; fix cpux-port initial problem; remove internal exceptions

* remove tiny delay

* private opi zero _pinNumberConverter

* add xml comment

* update README.md

* fix error SA1208

* fix copyright headers

* cleanup

* fix error SA1028

* fix CPUX port offset

* count the number of pins; update README.md

* fix typo

* Fix Interop duplication
Co-authored-by: NJose Perez Rodriguez <joperezr@microsoft.com>
上级 03e3a24c
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
namespace Iot.Device.Gpio.Drivers
{
/// <summary>
/// A GPIO driver for the Orange Pi Lite 2.
/// </summary>
/// <remarks>
/// SoC: Allwinner H6 (sun50iw6p1)
/// </remarks>
public class OrangePiLite2Driver : Sun50iw6p1Driver
{
private static readonly int[] _pinNumberConverter = new int[]
{
-1, -1, -1, MapPinNumber('H', 6), -1, MapPinNumber('H', 5), -1, MapPinNumber('H', 4), MapPinNumber('D', 21), -1,
MapPinNumber('D', 22), MapPinNumber('D', 24), MapPinNumber('C', 9), MapPinNumber('D', 23), -1, MapPinNumber('D', 26),
MapPinNumber('C', 8), -1, MapPinNumber('C', 7), MapPinNumber('C', 2), -1, MapPinNumber('C', 3), MapPinNumber('D', 25),
MapPinNumber('C', 0), MapPinNumber('C', 5), -1, MapPinNumber('H', 3)
};
/// <inheritdoc/>
protected override int PinCount => 17;
/// <inheritdoc/>
protected override int ConvertPinNumberToLogicalNumberingScheme(int pinNumber)
{
int num = _pinNumberConverter[pinNumber];
return num != -1 ? num : throw new ArgumentException($"Board (header) pin {pinNumber} is not a GPIO pin on the {GetType().Name} device.", nameof(pinNumber));
}
}
}
\ No newline at end of file
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
namespace Iot.Device.Gpio.Drivers
{
/// <summary>
/// A GPIO driver for the Orange Pi Lite.
/// </summary>
/// <remarks>
/// SoC: Allwinner H3 (sun8iw7p1)
/// </remarks>
public class OrangePiLiteDriver : Sun8iw7p1Driver
{
private static readonly int[] _pinNumberConverter = new int[]
{
-1, -1, -1, MapPinNumber('A', 12), -1, MapPinNumber('A', 11), -1, MapPinNumber('A', 6), MapPinNumber('A', 13), -1,
MapPinNumber('A', 14), MapPinNumber('A', 1), MapPinNumber('D', 14), MapPinNumber('A', 0), -1, MapPinNumber('A', 3),
MapPinNumber('C', 4), -1, MapPinNumber('C', 7), MapPinNumber('C', 0), -1, MapPinNumber('C', 1), MapPinNumber('A', 2),
MapPinNumber('C', 2), MapPinNumber('C', 3), -1, MapPinNumber('A', 21), MapPinNumber('A', 19), MapPinNumber('A', 18),
MapPinNumber('A', 7), -1, MapPinNumber('A', 8), MapPinNumber('G', 8), MapPinNumber('A', 9), -1, MapPinNumber('A', 10),
MapPinNumber('G', 9), MapPinNumber('A', 20), MapPinNumber('G', 6), -1, MapPinNumber('G', 7)
};
/// <inheritdoc/>
protected override int PinCount => 28;
/// <inheritdoc/>
protected override int ConvertPinNumberToLogicalNumberingScheme(int pinNumber)
{
int num = _pinNumberConverter[pinNumber];
return num != -1 ? num : throw new ArgumentException($"Board (header) pin {pinNumber} is not a GPIO pin on the {GetType().Name} device.", nameof(pinNumber));
}
}
}
\ No newline at end of file
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
namespace Iot.Device.Gpio.Drivers
{
/// <summary>
/// A GPIO driver for the Orange Pi Zero.
/// </summary>
/// <remarks>
/// SoC: Allwinner H2+ (sun8iw7p1)
/// </remarks>
public class OrangePiZeroDriver : Sun8iw7p1Driver
{
private static readonly int[] _pinNumberConverter = new int[]
{
-1, -1, -1, MapPinNumber('A', 12), -1, MapPinNumber('A', 11), -1, MapPinNumber('A', 6), MapPinNumber('G', 6), -1,
MapPinNumber('G', 7), MapPinNumber('A', 1), MapPinNumber('A', 7), MapPinNumber('A', 0), -1, MapPinNumber('A', 3),
MapPinNumber('A', 19), -1, MapPinNumber('A', 18), MapPinNumber('A', 15), -1, MapPinNumber('A', 16), MapPinNumber('A', 2),
MapPinNumber('A', 14), MapPinNumber('A', 13), -1, MapPinNumber('A', 10)
};
/// <inheritdoc/>
protected override int PinCount => 17;
/// <inheritdoc/>
protected override int ConvertPinNumberToLogicalNumberingScheme(int pinNumber)
{
int num = _pinNumberConverter[pinNumber];
return num != -1 ? num : throw new ArgumentException($"Board (header) pin {pinNumber} is not a GPIO pin on the {GetType().Name} device.", nameof(pinNumber));
}
}
}
\ No newline at end of file
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace Iot.Device.Gpio.Drivers
{
/// <summary>
/// A GPIO driver for Allwinner H5.
/// </summary>
public class Sun50iw2p1Driver : SunxiDriver
{
/// <inheritdoc/>
protected override int CpuxPortBaseAddress => 0x01C20800;
/// <inheritdoc/>
protected override int CpusPortBaseAddress => 0x01F02C00;
}
}
\ No newline at end of file
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace Iot.Device.Gpio.Drivers
{
/// <summary>
/// A GPIO driver for Allwinner H6.
/// </summary>
public class Sun50iw6p1Driver : SunxiDriver
{
/// <inheritdoc/>
protected override int CpuxPortBaseAddress => 0x0300B000;
/// <inheritdoc/>
protected override int CpusPortBaseAddress => 0x07022000;
}
}
\ No newline at end of file
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace Iot.Device.Gpio.Drivers
{
/// <summary>
/// A GPIO driver for Allwinner H2+/H3.
/// </summary>
public class Sun8iw7p1Driver : SunxiDriver
{
/// <inheritdoc/>
protected override int CpuxPortBaseAddress => 0x01C20800;
/// <inheritdoc/>
protected override int CpusPortBaseAddress => 0x01F02C00;
}
}
\ No newline at end of file
// 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.Device.Gpio;
using System.Device.Gpio.Drivers;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace Iot.Device.Gpio.Drivers
{
/// <summary>
/// A generic GPIO driver for Allwinner SoCs.
/// </summary>
/// <remarks>
/// This is a generic GPIO driver for Allwinner SoCs.
/// It can even drive the internal pins that are not drawn out.
/// Before you operate, you must be clear about what you are doing.
/// </remarks>
public unsafe class SunxiDriver : SysFsDriver
{
private const string GpioMemoryFilePath = "/dev/mem";
private IDictionary<int, PinState> _pinModes = new Dictionary<int, PinState>();
// final_address = mapped_address + (target_address & map_mask) https://stackoverflow.com/a/37922968
private static readonly int _mapMask = Environment.SystemPageSize - 1;
private static readonly object s_initializationLock = new object();
private IntPtr _cpuxPointer = IntPtr.Zero;
private IntPtr _cpusPointer = IntPtr.Zero;
/// <summary>
/// CPUX-PORT base address.
/// </summary>
protected virtual int CpuxPortBaseAddress { get; }
/// <summary>
/// CPUS-PORT base address.
/// </summary>
protected virtual int CpusPortBaseAddress { get; }
/// <inheritdoc/>
protected override int PinCount => throw new PlatformNotSupportedException("This driver is generic so it can not enumerate how many pins are available.");
/// <inheritdoc/>
protected override int ConvertPinNumberToLogicalNumberingScheme(int pinNumber) => throw new PlatformNotSupportedException("This driver is generic so it can not perform conversions between pin numbering schemes.");
/// <summary>
/// Initializes a new instance of the <see cref="SunxiDriver"/> class.
/// </summary>
protected SunxiDriver()
{
Initialize();
}
/// <summary>
/// Initializes a new instance of the <see cref="SunxiDriver"/>.
/// </summary>
/// <param name="cpuxPortBaseAddress">CPUX-PORT base address (This can be find in the corresponding SoC datasheet).</param>
/// <param name="cpusPortBaseAddress">CPUS-PORT base address (This can be find in the corresponding SoC datasheet).</param>
public SunxiDriver(int cpuxPortBaseAddress, int cpusPortBaseAddress)
{
CpuxPortBaseAddress = cpuxPortBaseAddress;
CpusPortBaseAddress = cpusPortBaseAddress;
Initialize();
}
/// <inheritdoc/>
protected override void OpenPin(int pinNumber)
{
SetPinMode(pinNumber, PinMode.Input);
}
/// <inheritdoc/>
protected override void ClosePin(int pinNumber)
{
if (_pinModes.ContainsKey(pinNumber))
{
if (_pinModes[pinNumber].InUseByInterruptDriver)
{
base.ClosePin(pinNumber);
}
switch (_pinModes[pinNumber].CurrentPinMode)
{
case PinMode.InputPullDown:
case PinMode.InputPullUp:
SetPinMode(pinNumber, PinMode.Input);
break;
case PinMode.Output:
Write(pinNumber, PinValue.Low);
SetPinMode(pinNumber, PinMode.Input);
break;
default:
break;
}
_pinModes.Remove(pinNumber);
}
}
/// <inheritdoc/>
protected override void SetPinMode(int pinNumber, PinMode mode)
{
// Get port controller, port number and shift
(int PortController, int Port) unmapped = UnmapPinNumber(pinNumber);
int cfgNum = unmapped.Port / 8;
int cfgShift = unmapped.Port % 8;
int pulNum = unmapped.Port / 16;
int pulShift = unmapped.Port % 16;
// Pn_CFG is used to set the direction; Pn_PUL is used to set the pull up/dowm mode
uint* cfgPointer, pulPointer;
int cfgOffset, pulOffset;
// PortController from A to K
if (unmapped.PortController <= 10)
{
// Pn_CFG initial offset is 0x00
cfgOffset = (CpuxPortBaseAddress + unmapped.PortController * 0x24 + cfgNum * 0x04) & _mapMask;
// Pn_PUL initial offset is 0x1C
pulOffset = (CpuxPortBaseAddress + unmapped.PortController * 0x24 + 0x1C + pulNum * 0x04) & _mapMask;
cfgPointer = (uint*)(_cpuxPointer + cfgOffset);
pulPointer = (uint*)(_cpuxPointer + pulOffset);
}
else
{
cfgOffset = (CpusPortBaseAddress + (unmapped.PortController - 11) * 0x24 + cfgNum * 0x04) & _mapMask;
pulOffset = (CpusPortBaseAddress + (unmapped.PortController - 11) * 0x24 + 0x1C + pulNum * 0x04) & _mapMask;
cfgPointer = (uint*)(_cpusPointer + cfgOffset);
pulPointer = (uint*)(_cpusPointer + pulOffset);
}
uint cfgValue = *cfgPointer;
uint pulValue = *pulPointer;
// Clear register
// Input is 0b000; Output is 0b001
cfgValue &= ~(0b1111U << (cfgShift * 4));
// Pull-up is 0b01; Pull-down is 0b10; Default is 0b00
pulValue &= ~(0b11U << (pulShift * 2));
switch (mode)
{
case PinMode.Output:
cfgValue |= 0b_001U << (cfgShift * 4);
break;
case PinMode.Input:
// After clearing the register, the value is the input mode.
break;
case PinMode.InputPullDown:
pulValue |= 0b10U << (pulShift * 2);
break;
case PinMode.InputPullUp:
pulValue |= 0b01U << (pulShift * 2);
break;
default:
throw new ArgumentException("Unsupported pin mode.");
}
*cfgPointer = cfgValue;
*pulPointer = pulValue;
if (_pinModes.ContainsKey(pinNumber))
{
_pinModes[pinNumber].CurrentPinMode = mode;
}
else
{
_pinModes.Add(pinNumber, new PinState(mode));
}
}
/// <inheritdoc/>
protected override void Write(int pinNumber, PinValue value)
{
(int PortController, int Port) unmapped = UnmapPinNumber(pinNumber);
uint* dataPointer;
int dataOffset;
if (unmapped.PortController <= 10)
{
// Pn_DAT offset is 0x10
dataOffset = (CpuxPortBaseAddress + unmapped.PortController * 0x24 + 0x10) & _mapMask;
dataPointer = (uint*)(_cpuxPointer + dataOffset);
}
else
{
dataOffset = (CpusPortBaseAddress + (unmapped.PortController - 11) * 0x24 + 0x10) & _mapMask;
dataPointer = (uint*)(_cpusPointer + dataOffset);
}
uint dataValue = *dataPointer;
if (value == PinValue.High)
{
dataValue |= 0b1U << unmapped.Port;
}
else
{
dataValue &= ~(0b1U << unmapped.Port);
}
*dataPointer = dataValue;
}
/// <inheritdoc/>
protected unsafe override PinValue Read(int pinNumber)
{
(int PortController, int Port) unmapped = UnmapPinNumber(pinNumber);
uint* dataPointer;
int dataOffset;
if (unmapped.PortController <= 10)
{
// Pn_DAT offset is 0x10
dataOffset = (CpuxPortBaseAddress + unmapped.PortController * 0x24 + 0x10) & _mapMask;
dataPointer = (uint*)(_cpuxPointer + dataOffset);
}
else
{
dataOffset = (CpusPortBaseAddress + (unmapped.PortController - 11) * 0x24 + 0x10) & _mapMask;
dataPointer = (uint*)(_cpusPointer + dataOffset);
}
uint dataValue = *dataPointer;
return Convert.ToBoolean((dataValue >> unmapped.Port) & 0b1) ? PinValue.High : PinValue.Low;
}
/// <inheritdoc/>
protected override void AddCallbackForPinValueChangedEvent(int pinNumber, PinEventTypes eventTypes, PinChangeEventHandler callback)
{
_pinModes[pinNumber].InUseByInterruptDriver = true;
base.OpenPin(pinNumber);
base.AddCallbackForPinValueChangedEvent(pinNumber, eventTypes, callback);
}
/// <inheritdoc/>
protected override void RemoveCallbackForPinValueChangedEvent(int pinNumber, PinChangeEventHandler callback)
{
_pinModes[pinNumber].InUseByInterruptDriver = false;
base.OpenPin(pinNumber);
base.RemoveCallbackForPinValueChangedEvent(pinNumber, callback);
}
/// <inheritdoc/>
protected override WaitForEventResult WaitForEvent(int pinNumber, PinEventTypes eventTypes, CancellationToken cancellationToken)
{
_pinModes[pinNumber].InUseByInterruptDriver = true;
base.OpenPin(pinNumber);
return base.WaitForEvent(pinNumber, eventTypes, cancellationToken);
}
/// <inheritdoc/>
protected override ValueTask<WaitForEventResult> WaitForEventAsync(int pinNumber, PinEventTypes eventTypes, CancellationToken cancellationToken)
{
_pinModes[pinNumber].InUseByInterruptDriver = true;
base.OpenPin(pinNumber);
return base.WaitForEventAsync(pinNumber, eventTypes, cancellationToken);
}
/// <inheritdoc/>
protected override bool IsPinModeSupported(int pinNumber, PinMode mode)
{
return mode switch
{
PinMode.Input or PinMode.InputPullDown or PinMode.InputPullUp or PinMode.Output => true,
_ => false,
};
}
/// <inheritdoc/>
protected override PinMode GetPinMode(int pinNumber)
{
return _pinModes[pinNumber].CurrentPinMode;
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
if (_cpuxPointer != IntPtr.Zero)
{
Interop.munmap(_cpuxPointer, 0);
_cpuxPointer = IntPtr.Zero;
}
if (_cpusPointer != IntPtr.Zero)
{
Interop.munmap(_cpusPointer, 0);
_cpusPointer = IntPtr.Zero;
}
}
private void Initialize()
{
if (_cpuxPointer != IntPtr.Zero)
{
return;
}
lock (s_initializationLock)
{
if (_cpuxPointer != IntPtr.Zero)
{
return;
}
int fileDescriptor = Interop.open(GpioMemoryFilePath, FileOpenFlags.O_RDWR | FileOpenFlags.O_SYNC);
if (fileDescriptor == -1)
{
throw new IOException($"Error {Marshal.GetLastWin32Error()} initializing the Gpio driver (File open error).");
}
IntPtr cpuxMap = Interop.mmap(IntPtr.Zero, Environment.SystemPageSize, MemoryMappedProtections.PROT_READ | MemoryMappedProtections.PROT_WRITE, MemoryMappedFlags.MAP_SHARED, fileDescriptor, CpuxPortBaseAddress & ~_mapMask);
IntPtr cpusMap = Interop.mmap(IntPtr.Zero, Environment.SystemPageSize, MemoryMappedProtections.PROT_READ | MemoryMappedProtections.PROT_WRITE, MemoryMappedFlags.MAP_SHARED, fileDescriptor, CpusPortBaseAddress & ~_mapMask);
if (cpuxMap.ToInt64() == -1)
{
Interop.munmap(cpuxMap, 0);
throw new IOException($"Error {Marshal.GetLastWin32Error()} initializing the Gpio driver (CPUx initialize error).");
}
if (cpusMap.ToInt64() == -1)
{
Interop.munmap(cpusMap, 0);
throw new IOException($"Error {Marshal.GetLastWin32Error()} initializing the Gpio driver (CPUs initialize error).");
}
_cpuxPointer = cpuxMap;
_cpusPointer = cpusMap;
Interop.close(fileDescriptor);
}
}
/// <summary>
/// Map pin number with port controller name to pin number in the driver's logical numbering scheme.
/// </summary>
/// <param name="portController">Port controller name, like 'A', 'C'.</param>
/// <param name="port">Number of pins.</param>
/// <returns>Pin number in the driver's logical numbering scheme.</returns>
public static int MapPinNumber(char portController, int port)
{
int alphabetPosition = (portController >= 'A' && portController <= 'Z') ? portController - 'A' : throw new Exception();
return alphabetPosition * 32 + port;
}
/// <summary>
/// Unmap pin number in the driver's logical numbering scheme to pin number with port name.
/// </summary>
/// <param name="pinNumber">Pin number in the driver's logical numbering scheme.</param>
/// <returns>Pin number with port name.</returns>
protected static (int PortController, int Port) UnmapPinNumber(int pinNumber)
{
int port = pinNumber % 32;
int portController = (pinNumber - port) / 32;
return (portController, port);
}
private class PinState
{
public PinState(PinMode currentMode)
{
CurrentPinMode = currentMode;
InUseByInterruptDriver = false;
}
public PinMode CurrentPinMode { get; set; }
public bool InUseByInterruptDriver { get; set; }
}
}
}
\ No newline at end of file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net5;netcoreapp2.1</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="$(MainLibraryPath)System.Device.Gpio.csproj" />
<Compile Include="../Interop/Unix/Interop.Libraries.cs" />
<Compile Include="../Interop/Unix/Libc/Interop.libc.cs" />
</ItemGroup>
<ItemGroup>
<Compile Remove="samples\**" />
<EmbeddedResource Remove="samples\**" />
<None Remove="samples\**" />
</ItemGroup>
</Project>
\ No newline at end of file
# Sunxi GPIO Driver for .NET
**sunxi** represents the family of ARM SoCs from Allwinner Technology. This project contains a **full function(PULL-UP, PULL-DOWN)** generic GPIO driver `SunxiDriver` for Allwinner SoCs and some special GPIO drivers like `OrangePiZeroDriver`, `OrangePiLiteDriver`, `OrangePiLite2Driver`.
## Getting started
### Special GPIO driver: `OrangePiZeroDriver`
```C#
// For Orange Pi Zero
using GpioController gpio = new GpioController(PinNumberingScheme.Board, new OrangePiZeroDriver());
// Open the GPIO pin.
gpio.OpenPin(7);
// Set the pin mode.
gpio.SetPinMode(7, PinMode.InputPullUp);
// Read current value of the pin.
PinValue value = gpio.Read(7);
```
### Generic GPIO driver: `SunxiDriver`
```C#
// Beacuse this is a generic driver, the pin scheme can only be Logical.
// The base addresses can be found in the corresponding SoC datasheet.
using GpioController gpio = new GpioController(PinNumberingScheme.Logical, new SunxiDriver(cpuxPortBaseAddress: 0x01C20800, cpusPortBaseAddress: 0x01F02C00));
// Convert pin number to logical scheme.
int pinNumber = SunxiDriver.MapPinNumber(portController: 'A', port: 10);
gpio.OpenPin(pinNumber);
gpio.SetPinMode(pinNumber, PinMode.Output);
// Write a value to the pin.
gpio.Write(pinNumber, PinValue.High);
```
## Adding new drivers
### For SoCs
1. Inheriting `SunxiDriver` Class.
```C#
// For Allwinner H2+/H3
public class Sun8iw7p1Driver : SunxiDriver { }
```
2. Overriding the GPIO base addresses.
```C#
protected override int CpuxPortBaseAddress => 0x01C20800;
protected override int CpusPortBaseAddress => 0x01F02C00;
```
### For Boards
1. Inherit the corresponding SoC class.
```C#
public class OrangePiZeroDriver : Sun8iw7p1Driver { }
```
2. Overriding the mapping method for converting a board pin number to the driver's logical numbering scheme.
```C#
// Mapping from board pins to logic pins.
private static readonly int[] _pinNumberConverter = new int[27]
{
-1, -1, -1, MapPinNumber('A', 12), -1, MapPinNumber('A', 11), -1, MapPinNumber('A', 6), MapPinNumber('G', 6), -1,
MapPinNumber('G', 7), MapPinNumber('A', 1), MapPinNumber('A', 7), MapPinNumber('A', 0), -1, MapPinNumber('A', 3),
MapPinNumber('A', 19), -1, MapPinNumber('A', 18), MapPinNumber('A', 15), -1, MapPinNumber('A', 16), MapPinNumber('A', 2),
MapPinNumber('A', 14), MapPinNumber('A', 13), -1, MapPinNumber('A', 10)
};
protected override int PinCount => 17;
protected internal override int ConvertPinNumberToLogicalNumberingScheme(int pinNumber)
{
int num = _pinNumberConverter[pinNumber];
return num != -1 ? num :
throw new ArgumentException($"Board (header) pin {pinNumber} is not a GPIO pin on the {GetType().Name} device.", nameof(pinNumber));
}
```
## References
The wiki of the linux-sunxi community: https://linux-sunxi.org/Main_Page
\ No newline at end of file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\BoardLed\BoardLed.csproj" />
<ProjectReference Include="..\Iot.Device.Gpio.csproj" />
</ItemGroup>
</Project>
// 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.Device.Gpio;
using Iot.Device.BoardLed;
using Iot.Device.Gpio.Drivers;
namespace Sunxi.Gpio.Samples
{
internal class Program
{
private static void Main(string[] args)
{
// Set debounce delay to 5ms
int debounceDelay = 50000;
int pin = 7;
Console.WriteLine($"Let's blink an on-board LED!");
using GpioController controller = new GpioController(PinNumberingScheme.Board, new OrangePiZeroDriver());
using BoardLed led = new BoardLed("orangepi:red:status");
controller.OpenPin(pin, PinMode.InputPullUp);
led.Trigger = "none";
Console.WriteLine($"GPIO pin enabled for use: {pin}.");
Console.WriteLine("Press any key to exit.");
while (!Console.KeyAvailable)
{
if (Debounce())
{
// Button is pressed
led.Brightness = 1;
}
else
{
// Button is unpressed
led.Brightness = 0;
}
}
bool Debounce()
{
long debounceTick = DateTime.Now.Ticks;
PinValue buttonState = controller.Read(pin);
do
{
PinValue currentState = controller.Read(pin);
if (currentState != buttonState)
{
debounceTick = DateTime.Now.Ticks;
buttonState = currentState;
}
}
while (DateTime.Now.Ticks - debounceTick < debounceDelay);
if (buttonState == PinValue.Low)
{
return true;
}
else
{
return false;
}
}
}
}
}
\ No newline at end of file
# Sunxi GPIO Driver‘s Sample
### Hardware required
* Orange Pi Zero
* Switch
* Male/Female Jumper Wires
## Circuit
![](opi_circuit.png)
* Switch 1 - Board Pin7 (GPIO 6)
* Switch 2 - GND
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册