-
Notifications
You must be signed in to change notification settings - Fork 616
Description
Core Objective
As a device binding producer, I would like the ability to instantiate the binding with a GPIO SPI implementation instead of redundant code to bit-bang logic within each binding. This would be similar to how a UnixSpiDevice or Windows10SpiDevice is currently used to create a binding.
NOTE: There is a "very early" prototype located here.
Use the Mcp23xxx binding and a UnixSpiDevice for example:
// With UnixSpiDevice.
var settings = new SpiConnectionSettings(0, 0)
{
ClockFrequency = 1000000,
Mode = SpiMode.Mode0
};
var spiDevice = new UnixSpiDevice(settings);
var mcp23xxx = new Mcp23xxx(spiDevice);Having a GpioSpiDevice offering, there could be something like the following:
NOTE: chipSelectActiveLow is active low in most cases, but some chips and hardware designs use active high.
public GpioSpiDevice(int sclk, int miso, int mosi, SpiConnectionSettings connectionSettings, bool chipSelectActiveLow = true)
Therefore, you could pass in the GpioSpiDevice like below:
NOTE: Notice different modes can also be used and low-level should accommodate (assuming the device supports that mode 😄). Frequency (clock delay) is not currently coded in prototype.
// With GpioSpiDevice.
var settings = new SpiConnectionSettings(0, 25)
{
ClockFrequency = 1000000,
Mode = SpiMode.Mode3
};
var spiDevice = new GpioSpiDevice(18, 23, 24, settings);
var mcp23xxx = new Mcp23xxx(spiDevice);GpioSpiDevice would be located with the other current SpiDevice drivers.
System
Device
Spi
Drivers
GpioSpiDevice.cs // Or GpioSpiDriver depending on naming scheme
UnixSpiDevice.Linux.cs
...
This might help with having abstract classes using the different types of interface devices. For example...
- GpioSpiDevice, UnixSpiDevice and Windows10SpiDevice could be passed to an Mcp23Sxx device where 'S' represents SPI devices.
- GpioI2cDevice (a future proposal), UnixI2cDevice and Windows10I2cDevice could be passed to an Mcp230xx device where '0' represents I2C devices.
A few thoughts to point out
-
busIddoesn't really mean anything in this scenario. It could be ignored by creating an overloaded constructornew SpiConnectionSettings(chipSelectLine). -
chipSelectActiveLowseems to be more for settings so it could be added to SpiConnectionSettings and defaulted to TRUE instead of passing intoGpioSpiDeviceconstructor. So the following could be used:
new SpiConnectionSettings(chipSelectLine, chipSelectActiveLow). This might confuse peeps when using for native interface and configured elsewhere. I'm on the fence with this. -
There is a proposal for a GPIO Toggle helper where it would help support logic in other areas, as well.
-
It would be nice to have some bit helper methods to perform MSB/LSB as this varies with components.
-
It would be nice to have a helper for PinValue.Low = false or PinValue.High = true. There is a proposal related to this here: [Proposal] Add ReadBool and WriteBool to GpioController #121
-
One other thing to point out, is how much code can be reduced like in the Mcp3008 binding. The current code to read via GPIO SPI shown below:
private int ReadGpio(int channel, InputConfiguration inputConfiguration)
{
while (true)
{
int result = 0;
byte command = GetConfigurationBits(channel, inputConfiguration);
_controller.Write(_cs, PinValue.High);
_controller.Write(_clk, PinValue.Low);
_controller.Write(_cs, PinValue.Low);
for (int cnt = 0; cnt < 5; cnt++)
{
if ((command & 0b1000_0000) > 0)
{
_controller.Write(_mosi, PinValue.High);
}
else
{
_controller.Write(_mosi, PinValue.Low);
}
command <<= 1;
_controller.Write(_clk, PinValue.High);
_controller.Write(_clk, PinValue.Low);
}
for (int cnt = 0; cnt < 12; cnt++)
{
_controller.Write(_clk, PinValue.High);
_controller.Write(_clk, PinValue.Low);
result <<= 1;
if (_controller.Read(_miso) == PinValue.High)
{
result |= 0b0000_0001;
}
}
_controller.Write(_cs, PinValue.High);
result >>= 1;
return result;
}
}Basically, can use the same code as the ReadSpi method (replacing with the GpioSpiDevice, of course 😄):
private int ReadSpi(int channel, InputConfiguration inputConfiguration)
{
byte configurationBits = GetConfigurationBits(channel, inputConfiguration);
byte[] writeBuffer = new byte[] { configurationBits, 0, 0 };
byte[] readBuffer = new byte[3];
_spiDevice.TransferFullDuplex(writeBuffer, readBuffer);
int result = (readBuffer[0] & 0b0000_0001) << 9;
result |= (readBuffer[1] & 0b1111_1111) << 1;
result |= (readBuffer[2] & 0b1000_0000) >> 7;
result = result & 0b0011_1111_1111;
return result;
}As mentioned, this is just a prototype, but works. I'm still testing bits/modes and such.
Thoughts?