Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Board approach, iteration 2 #1128

Merged
merged 37 commits into from
May 20, 2021
Merged

Board approach, iteration 2 #1128

merged 37 commits into from
May 20, 2021

Conversation

pgrawehr
Copy link
Contributor

@pgrawehr pgrawehr commented Jul 3, 2020

Adds a Board base class to simplify handling common features of a piece of hardware.

Also adds support for proper pin mode handling on the Raspberry Pi. I.e. it is now possible to use pins interchangeably as PWM and GPIO, or switch between software and hardware SPI on the same set of pins.

@colombod
Copy link
Member

This set of abstraction and changes are key to simplify and consolidate other boards design like the piTop 4 module. hope this is merged soon. Very excellent work.

/// <returns>An I2cDevice connection</returns>
protected abstract I2cDevice CreateSimpleI2cDevice(I2cConnectionSettings connectionSettings, int[] pinAssignment);

public I2cDevice CreateI2cDevice(I2cConnectionSettings connectionSettings, int[] pinAssignment, PinNumberingScheme pinNumberingScheme)
Copy link
Member

Choose a reason for hiding this comment

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

this interface will always create a device manager even if the same connection settings are used. it would be more interesting if there was the option here to track the connections setting used so far, that would make the board something that can be inspected like the pitop boards and plates in this repo .

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So you would want to make sure that each device (here: every instance that uses the same address) can be created only once? Could indeed further reduce usage errors, since most devices won't like it if they're being used i.e. from multiple bindings at the same time.

Copy link
Member

Choose a reason for hiding this comment

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

Yes you are spot on. In my simple implementation reusing a connection settings yield to the already built device. the interesting part is that the device managers can then auto remove themselves if disposed. Do you think this could imply the need for some sort of reference counting? there are some useful disposables classes in rx.Net that could be very useful like refcount disposables, serial disposables and disposable factories.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll consider this for later, I think. Adding a test that no two I2cDevice instances with the same ConnectionSettings can be done without changing the API later.

@Ellerbach Ellerbach added the area-System.Device.Gpio Contains types for using general-purpose I/O (GPIO) pins label Oct 14, 2020
/// </summary>
/// <param name="pinNumber">Pin number</param>
/// <returns>(Alternate) Pin mode. 0 = Alt0, 1= Alt1... -1 Gpio Input, -2 Gpio Output</returns>
protected internal int GetAlternatePinMode(int pinNumber)
Copy link
Member

@krwq krwq Oct 30, 2020

Choose a reason for hiding this comment

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

should this be an enum (flags)? I assume there might be more than one mode, right?

Copy link
Member

@krwq krwq Oct 30, 2020

Choose a reason for hiding this comment

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

Ah, this is about current pin mode, not all possible options... (still.. comment about enum applies)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would really like to use an enum here. Unfortunately, the set of alternate pin modes is hardware dependent. So if I go with the names that are most common here, they would be Raspberry Pi-specific and be confusing to use on some other hardware (the Arduino binding for instance uses a similar semantics, but with different values)

Maybe return a class instead? (That would be handled much like a handle for most of what the client does)?

Copy link
Member

Choose a reason for hiding this comment

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

if you have some decent design with struct (class is too heavy to be used instead of enum) then fine, otherwise I'm fine with leaving as is

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Used a class with static members now. This syntactically looks identical to using an enum, but is extensible.

/// <param name="pinNumber">The pin number, in the boards default numbering scheme</param>
/// <param name="usage">Intended usage of the pin</param>
/// <param name="owner">Class that owns the pin (use "this")</param>
public virtual void ReservePin(int pinNumber, PinUsage usage, object owner)
Copy link
Member

Choose a reason for hiding this comment

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

I'd prefer this was not part of the initial version of abstraction, this needs more thought. Perhaps this is needed long term and this is how the API would look like but I'd prefer we did it separately as this PR is already large enough for this rather niche concept

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have to check whether it can be removed easily. I think the method is internally used already.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is one of the main features of the new approach, actually. It manages the pin usages and keeps track of who uses which pin for what purpose. This is public to also allow reserving pins directly from a client (i.e. for UART, which is not currently managed here)

/// </summary>
/// <param name="pinNumber">The logical pin number to use.</param>
/// <param name="usage">The intended usage</param>
protected virtual void ActivatePinMode(int pinNumber, PinUsage usage)
Copy link
Member

Choose a reason for hiding this comment

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

if anything this should be throwing by default but I'd rather keep this concept out of the initial abstraction

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This also belongs to the main feature: It allows (see the RaspberryPiBoard specific implementation) to set the alternate pin mode for a pin. This internally calls SetAlternatePinMode on the Raspi

@pgrawehr pgrawehr changed the base branch from feature-boardAbstraction to master November 1, 2020 12:34
return board;
}

throw new PlatformNotSupportedException("Could not find a matching board driver for this hardware");
Copy link
Member

Choose a reason for hiding this comment

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

Is there a reason why we don't try as well with CustomBoard?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because the CustomBoard requires custom drivers. It's "default" implementation would be equivalent to the GenericBoard.

/// <summary>
/// A generic board class. Uses generic implementations for GPIO, I2C etc
/// </summary>
public class GenericBoard : Board
Copy link
Member

Choose a reason for hiding this comment

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

Is there a specific reason why this is public? I would rather have this (and the RaspberryPiBoard and CustomBoard) internal, and just have the Create method on the base class to create the right one for you. We could also even have an overload of Create that takes in an Enum so that we allow callers to specify which board they want to create. In the past on dotnet/iot we have found that using factory pattern is better (like in SPI and I2C) for future refactorings since it was way easier to adapt and develop that when compared to GPIO (which doesn't have the factory pattern)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the one you would use if you can't use or don't want to use the RaspberryPiBoard. It uses all the default implementations.

@pgrawehr
Copy link
Contributor Author

pgrawehr commented May 6, 2021

@joperezr I think I have addressed the open issues. Should be ready to go now.

Copy link
Member

@joperezr joperezr left a comment

Choose a reason for hiding this comment

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

Thank you so much for driving this through @pgrawehr I know how hard it has been and it is a long journey, but I couldn't be more happy to merge this in 😄 Thanks for the great contribution!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Device.Gpio Contains types for using general-purpose I/O (GPIO) pins
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants