Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions src/devices/Board/RaspberryPiBoard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using System.IO;
using System.Linq;
using System.Text;
using Iot.Device.Gpio;
using UnitsNet;

namespace Iot.Device.Board
Expand Down Expand Up @@ -925,5 +926,68 @@ public override ComponentInformation QueryComponentInformation()
ret.Properties["PinCount"] = PinCount.ToString(CultureInfo.InvariantCulture);
return ret;
}

/// <summary>
/// Creates a derived GPIO controller that uses physical pin mapping for the Raspberry Pi.
/// </summary>
public VirtualGpioController CreatePhysicalPinMapping()
{
Initialize();

return CreatePhysicalPinMapping(_managedGpioController!);
}

/// <summary>
/// Converts a board pin number to the driver's logical numbering scheme.
/// </summary>
/// <param name="pinNumber">The physical pin number to convert.</param>
/// <returns>The pin number in the driver's logical numbering scheme.</returns>
private static int ConvertPinNumberFromPhysicalToLogical(int pinNumber)
{
return pinNumber switch
{
3 => 2,
5 => 3,
7 => 4,
8 => 14,
10 => 15,
11 => 17,
12 => 18,
13 => 27,
15 => 22,
16 => 23,
18 => 24,
19 => 10,
21 => 9,
22 => 25,
23 => 11,
24 => 8,
26 => 7,
27 => 0,
28 => 1,
29 => 5,
31 => 6,
32 => 12,
33 => 13,
35 => 19,
36 => 16,
37 => 26,
38 => 20,
40 => 21,
_ => -1
};
}

/// <summary>
/// Creates a derived GPIO controller of the provided one which uses physical board numbering.
/// Use this if you manually created the controller.
/// </summary>
/// <param name="defaultController">The controller to use. Must be for a Raspberry Pi.</param>
/// <returns>A controller that uses physical board numbering</returns>
/// <remarks>If the provided <paramref name="defaultController"/> is not for a Raspberry Pi, the behavior might be undefined.</remarks>
public static VirtualGpioController CreatePhysicalPinMapping(GpioController defaultController)
{
return new VirtualGpioControllerWithDefault(defaultController, ConvertPinNumberFromPhysicalToLogical);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,12 @@ protected override void Dispose(bool disposing)
_pins.Clear();
}

/// <inheritdoc />
/// <summary>
/// This operation is not supported, as the virtual controller doesn't know what to do here.
/// Use <see cref="VirtualGpioControllerWithDefault"/> for an alternative approach if you need to dynamically assign pins.
/// </summary>
/// <param name="pinNumber">The number of the pin</param>
/// <exception cref="InvalidOperationException">Always. This operation is not supported</exception>
protected override void OpenPinCore(int pinNumber)
{
// Not clear what this should do
Expand Down
62 changes: 62 additions & 0 deletions src/devices/Board/VirtualGpioControllerWithDefault.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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.Linq;
using System.Text;
using System.Threading.Tasks;

#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member

namespace Iot.Device.Gpio
{
/// <summary>
/// A virtual GPIO controller that serves primarily for a pin mapping on an existing controller.
/// Unlike <see cref="VirtualGpioController"/> it has a default controller rather than individual pins
/// and thus also supports <see cref="GpioController.OpenPin(int)"/>.
/// </summary>
public class VirtualGpioControllerWithDefault : VirtualGpioController
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider something along the lines of Mapping/Mapped(Virtual)GpioController

{
private readonly GpioController _defaultController;
private readonly Func<int, int> _fromVirtualToRealMapping;

/// <summary>
/// Create a controller that by default uses the given controller to open a pin, using the given mapping.
/// Note that you can still use <see cref="VirtualGpioController.Add(int, GpioPin)"/> to manually add pins from another controller.
/// </summary>
/// <param name="defaultController">The default controller</param>
/// <param name="fromVirtualToRealMapping">A mapping function from virtual to logical. This should return -1 for unknown pins.</param>
public VirtualGpioControllerWithDefault(GpioController defaultController, Func<int, int> fromVirtualToRealMapping)
{
_defaultController = defaultController ?? throw new ArgumentNullException(nameof(defaultController));
_fromVirtualToRealMapping = fromVirtualToRealMapping ?? throw new ArgumentNullException(nameof(fromVirtualToRealMapping));
}

/// <summary>
/// Opens a pin from the default controller
/// </summary>
/// <param name="pinNumber">The pin number</param>
protected override void OpenPinCore(int pinNumber)
{
int realPinNumber = MapToRealPin(pinNumber);
if (realPinNumber == -1)
{
throw new InvalidOperationException($"Virtual Pin Number {pinNumber} is unknown");
}

var pin = _defaultController.OpenPin(realPinNumber);
if (!Add(pinNumber, pin))
{
_defaultController.ClosePin(realPinNumber);
throw new InvalidOperationException($"Virtual Pin Number {pinNumber} is in use already");
}
}

public int MapToRealPin(int virtualPin)
{
return _fromVirtualToRealMapping(virtualPin);
}
}
}