diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Driver/DisplayScreen.cs b/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Driver/DisplayScreen.cs
index 6cdbf5ea26..7890a4b511 100644
--- a/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Driver/DisplayScreen.cs
+++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Driver/DisplayScreen.cs
@@ -193,12 +193,12 @@ private void DrawLoopThreaded()
{
Resolver.App.InvokeOnMainThread((_) =>
{
- if (!_updateInProgress && (IsInvalid || Controls.Any(c => c.IsInvalid)))
+ lock (Controls.SyncRoot)
{
- _graphics.Clear(BackgroundColor);
-
- lock (Controls.SyncRoot)
+ if (!_updateInProgress && (IsInvalid || Controls.Any(c => c.IsInvalid)))
{
+ _graphics.Clear(BackgroundColor);
+
foreach (var control in Controls)
{
if (control != null)
@@ -207,13 +207,11 @@ private void DrawLoopThreaded()
RefreshTree(control);
}
}
+ _graphics.Show();
+ IsInvalid = false;
}
- _graphics.Show();
- IsInvalid = false;
}
- }
-
- );
+ });
Thread.Sleep(50);
}
diff --git a/Source/Meadow.Foundation.Peripherals/Displays.AsciiConsole/Driver/AsciiConsoleDisplay.CharacterBuffer.cs b/Source/Meadow.Foundation.Peripherals/Displays.AsciiConsole/Driver/AsciiConsoleDisplay.CharacterBuffer.cs
new file mode 100644
index 0000000000..60959be317
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/Displays.AsciiConsole/Driver/AsciiConsoleDisplay.CharacterBuffer.cs
@@ -0,0 +1,89 @@
+using Meadow.Foundation.Graphics;
+using Meadow.Foundation.Graphics.Buffers;
+using System;
+
+namespace Meadow.Foundation.Displays
+{
+ public partial class AsciiConsoleDisplay
+ {
+ internal class CharacterBuffer : IPixelBuffer
+ {
+ public int Width { get; }
+ public int Height { get; }
+ public ColorMode ColorMode => ColorMode.Format8bppGray;
+ public int BitDepth => 6;
+
+ public int ByteCount => throw new NotImplementedException();
+
+ public byte[] Buffer => throw new NotImplementedException();
+
+ private char[,] _buffer;
+
+ public CharacterBuffer(int width, int height)
+ {
+ Width = width;
+ Height = height;
+
+ _buffer = new char[width, height];
+ }
+
+ public void Clear()
+ {
+ Fill(Color.Black);
+ }
+
+ public void Fill(Color color)
+ {
+ for (var y = 0; y < Height; y++)
+ {
+ for (var x = 0; x < Width; x++)
+ {
+ SetPixel(x, y, color);
+ }
+ }
+ }
+
+ public void Fill(int originX, int originY, int width, int height, Color color)
+ {
+ for (var y = originY; y < height + originY; y++)
+ {
+ for (var x = originX; x < width + originX; x++)
+ {
+ SetPixel(x, y, color);
+ }
+ }
+ }
+
+ internal char GetPixelCharacter(int x, int y)
+ {
+ return _buffer[x, y];
+ }
+
+ public Color GetPixel(int x, int y)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void InvertPixel(int x, int y)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void SetPixel(int x, int y, Color color)
+ {
+ _buffer[x, y] = ColorToCharacter(color);
+ }
+
+ public void WriteBuffer(int originX, int originY, IPixelBuffer buffer)
+ {
+ throw new NotImplementedException();
+ }
+
+ private char ColorToCharacter(Color color)
+ {
+ var index = (int)((color.Color8bppGray / 255d) * (_colors.Length - 1));
+ return _colors[index];
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/Displays.AsciiConsole/Driver/AsciiConsoleDisplay.cs b/Source/Meadow.Foundation.Peripherals/Displays.AsciiConsole/Driver/AsciiConsoleDisplay.cs
new file mode 100644
index 0000000000..cccda70867
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/Displays.AsciiConsole/Driver/AsciiConsoleDisplay.cs
@@ -0,0 +1,90 @@
+using Meadow.Foundation.Graphics;
+using Meadow.Foundation.Graphics.Buffers;
+using System;
+
+namespace Meadow.Foundation.Displays;
+
+public partial class AsciiConsoleDisplay : IGraphicsDisplay
+{
+ private readonly CharacterBuffer _buffer;
+ private const string _colors = " `.-':_,^=;><+!rc*/z?sLTv)J7(|Fi{C}fI31tlu[neoZ5Yxjya]2ESwqkP6h9d4VpOGbUAKXHm8RD#$Bg0MNWQ%&@"; // 92 "colors"
+
+ public ColorMode ColorMode => PixelBuffer.ColorMode;
+
+ public ColorMode SupportedColorModes => ColorMode.Format8bppGray;
+
+ public int Width => PixelBuffer.Width;
+ public int Height => PixelBuffer.Height;
+ public IPixelBuffer PixelBuffer => _buffer;
+
+ public AsciiConsoleDisplay(int width, int height)
+ {
+ Console.Clear();
+ Console.SetCursorPosition(0, 0);
+
+ _buffer = new CharacterBuffer(width, height);
+ PixelBuffer.Clear();
+ }
+
+ public void Clear(bool updateDisplay = false)
+ {
+ PixelBuffer.Clear();
+ }
+
+ public void DrawPixel(int x, int y, Color color)
+ {
+ PixelBuffer.SetPixel(x, y, color);
+ }
+
+ public void DrawPixel(int x, int y, bool enabled)
+ {
+ PixelBuffer.SetPixel(x, y, enabled ? Color.White : Color.Black);
+ }
+
+ public void Fill(Color fillColor, bool updateDisplay = false)
+ {
+ PixelBuffer.Fill(fillColor);
+ if (updateDisplay)
+ {
+ Show();
+ }
+ }
+
+ public void Fill(int x, int y, int width, int height, Color fillColor)
+ {
+ PixelBuffer.Fill(x, y, width, height, fillColor);
+ }
+
+ public void InvertPixel(int x, int y)
+ {
+ PixelBuffer.InvertPixel(x, y);
+ }
+
+ public void Show()
+ {
+ for (var y = 0; y < Height; y++)
+ {
+ for (var x = 0; x < Width; x++)
+ {
+ Console.SetCursorPosition(x, y);
+ Console.Write(_buffer.GetPixelCharacter(x, y));
+ }
+ }
+ }
+
+ public void Show(int left, int top, int right, int bottom)
+ {
+ for (; left < right; left++)
+ {
+ for (; top < bottom; top++)
+ {
+ Console.SetCursorPosition(left, top);
+ Console.Write(_buffer.GetPixelCharacter(left, top));
+ }
+ }
+ }
+
+ public void WriteBuffer(int x, int y, IPixelBuffer displayBuffer)
+ {
+ }
+}
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/Displays.AsciiConsole/Driver/Meadow.Displays.AsciiConsole.csproj b/Source/Meadow.Foundation.Peripherals/Displays.AsciiConsole/Driver/Meadow.Displays.AsciiConsole.csproj
new file mode 100644
index 0000000000..9c7220b4fa
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/Displays.AsciiConsole/Driver/Meadow.Displays.AsciiConsole.csproj
@@ -0,0 +1,27 @@
+
+
+ Readme.md
+ enable
+ 10.0
+ Apache-2.0
+ true
+ icon.png
+ Wilderness Labs, Inc
+ netstandard2.1
+ Library
+ AsciiConsole
+ Wilderness Labs, Inc
+ http://developer.wildernesslabs.co/Meadow/Meadow.Foundation/
+ Meadow.Foundation.Displays.AsciiConsole
+ https://github.com/WildernessLabs/Meadow.Foundation
+ Meadow,Meadow.Foundation,Displays,Ascii,Console
+ 1.6.0.1
+ true
+ Ascii console display
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/Displays.AsciiConsole/Driver/Readme.md b/Source/Meadow.Foundation.Peripherals/Displays.AsciiConsole/Driver/Readme.md
new file mode 100644
index 0000000000..6946ceb6fd
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/Displays.AsciiConsole/Driver/Readme.md
@@ -0,0 +1,12 @@
+# Meadow.Foundation.Displays.AsciiConsoleDisplay
+
+## How to Contribute
+
+- **Found a bug?** [Report an issue](https://github.com/WildernessLabs/Meadow_Issues/issues)
+- Have a **feature idea or driver request?** [Open a new feature request](https://github.com/WildernessLabs/Meadow_Issues/issues)
+- Want to **contribute code?** Fork the [Meadow.Foundation](https://github.com/WildernessLabs/Meadow.Foundation) repository and submit a pull request against the `develop` branch
+
+
+## Need Help?
+
+If you have questions or need assistance, please join the Wilderness Labs [community on Slack](http://slackinvite.wildernesslabs.co/).
diff --git a/Source/Meadow.Foundation.Peripherals/Displays.AsciiConsole/Samples/AsciiConsoleDisplay_Sample/AsciiConsoleDisplay_Sample.csproj b/Source/Meadow.Foundation.Peripherals/Displays.AsciiConsole/Samples/AsciiConsoleDisplay_Sample/AsciiConsoleDisplay_Sample.csproj
new file mode 100644
index 0000000000..45276c4063
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/Displays.AsciiConsole/Samples/AsciiConsoleDisplay_Sample/AsciiConsoleDisplay_Sample.csproj
@@ -0,0 +1,16 @@
+
+
+
+ Exe
+ net7.0
+ enable
+ enable
+ App
+
+
+
+
+
+
+
+
diff --git a/Source/Meadow.Foundation.Peripherals/Displays.AsciiConsole/Samples/AsciiConsoleDisplay_Sample/Program.cs b/Source/Meadow.Foundation.Peripherals/Displays.AsciiConsole/Samples/AsciiConsoleDisplay_Sample/Program.cs
new file mode 100644
index 0000000000..b989abfae4
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/Displays.AsciiConsole/Samples/AsciiConsoleDisplay_Sample/Program.cs
@@ -0,0 +1,103 @@
+using Meadow;
+using Meadow.Foundation.Displays;
+using Meadow.Foundation.Graphics;
+using Meadow.Foundation.Graphics.MicroLayout;
+
+namespace AsciiConsoleDisplay_Sample;
+
+internal class Program
+{
+ private static void Main(string[] args)
+ {
+ DrawShapes();
+ }
+
+ private static async Task MovingBox()
+ {
+ var colors = typeof(Color)
+ .GetFields(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public)
+ .Where(f => f.FieldType == typeof(Color))
+ .Select(c => (Color)c.GetValue(null)!)
+ .ToArray();
+
+ var colorIndex = 0;
+ var display = new AsciiConsoleDisplay(20, 15);
+ var screen = new DisplayScreen(display);
+ var box = new Box(0, 0, 4, 3)
+ {
+ ForeColor = colors[colorIndex]
+ };
+ screen.Controls.Add(box);
+
+ var x = 0;
+ var y = 0;
+ var xdir = 1;
+ var ydir = 1;
+
+ while (true)
+ {
+ screen.BeginUpdate();
+ box.Top = y;
+ box.Left = x;
+ box.ForeColor = colors[colorIndex];
+ screen.EndUpdate();
+
+ await Task.Delay(500);
+
+ if ((x >= display.Width - 4) || (x < 0))
+ {
+ xdir *= -1;
+ }
+ if ((y >= display.Height - 4) || (y < 0))
+ {
+ ydir *= -1;
+ }
+ x += (1 * xdir);
+ y += (1 * ydir);
+
+ colorIndex++;
+ if (colorIndex >= colors.Length) colorIndex = 0;
+ }
+ }
+
+ private static void DrawShapes()
+ {
+ var display = new AsciiConsoleDisplay(80, 60);
+
+ var graphics = new MicroGraphics(display)
+ {
+ IgnoreOutOfBoundsPixels = true,
+ };
+
+ graphics.Clear();
+
+ graphics.DrawTriangle(5, 5, 30, 30, 5, 30, Color.Red, false);
+ graphics.DrawRectangle(10, 12, 40, 20, Color.Yellow, false);
+ graphics.DrawCircle(20, 20, 20, Color.White, false);
+
+ graphics.Show();
+ }
+
+ private static async Task CycleColors()
+ {
+ var display = new AsciiConsoleDisplay(40, 30);
+
+ var colors = typeof(Color).GetFields(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public).Where(c => c.FieldType == typeof(Color));
+ foreach (var color in colors)
+ {
+ for (var x = 0; x < 16; x++)
+ {
+ for (var y = 0; y < 16; y++)
+ {
+ var c = (Color)color.GetValue(null)!;
+
+ display.DrawPixel(x, y, c);
+ }
+ }
+
+ display.Show();
+
+ await Task.Delay(100);
+ }
+ }
+}
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Data Sheets/an495-cp2112-interface-specification.pdf b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Data Sheets/an495-cp2112-interface-specification.pdf
new file mode 100644
index 0000000000..207374f298
Binary files /dev/null and b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Data Sheets/an495-cp2112-interface-specification.pdf differ
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Data Sheets/an496-hid-usb-to-smbus-api-specification.pdf b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Data Sheets/an496-hid-usb-to-smbus-api-specification.pdf
new file mode 100644
index 0000000000..d214c8d3ed
Binary files /dev/null and b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Data Sheets/an496-hid-usb-to-smbus-api-specification.pdf differ
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Cp2112.PinDefinitions.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Cp2112.PinDefinitions.cs
new file mode 100644
index 0000000000..7e010a2e2d
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Cp2112.PinDefinitions.cs
@@ -0,0 +1,139 @@
+using Meadow.Hardware;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Meadow.Foundation.ICs.IOExpanders;
+
+public partial class Cp2112
+{
+ ///
+ /// Provides definitions for the pins of the Cp2112 device.
+ ///
+ public class PinDefinitions : IPinDefinitions
+ {
+ ///
+ public IEnumerator GetEnumerator() => AllPins.GetEnumerator();
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ ///
+ /// Collection of pins
+ ///
+ public IList AllPins { get; } = new List();
+
+ ///
+ public IPinController? Controller { get; set; }
+
+ ///
+ /// Create a new PinDefinitions object
+ ///
+ internal PinDefinitions(Cp2112 controller)
+ {
+ Controller = controller;
+ InitAllPins();
+ }
+
+ ///
+ /// Gets the pin representing IO0 on the Cp2112 device.
+ ///
+ public IPin IO0 => new Pin(
+ Controller,
+ "IO0",
+ (byte)(1 << 0),
+ new List {
+ new DigitalChannelInfo("IO0", interruptCapable: false, pullUpCapable: false, pullDownCapable: false)
+ });
+
+ ///
+ /// Gets the pin representing IO1 on the Cp2112 device.
+ ///
+ public IPin IO1 => new Pin(
+ Controller,
+ "IO1",
+ (byte)(1 << 1),
+ new List {
+ new DigitalChannelInfo("IO1", interruptCapable: false, pullUpCapable: false, pullDownCapable: false)
+ });
+
+ ///
+ /// Gets the pin representing IO2 on the Cp2112 device.
+ ///
+ public IPin IO2 => new Pin(
+ Controller,
+ "IO2",
+ (byte)(1 << 2),
+ new List {
+ new DigitalChannelInfo("IO2", interruptCapable: false, pullUpCapable: false, pullDownCapable: false)
+ });
+
+ ///
+ /// Gets the pin representing IO3 on the Cp2112 device.
+ ///
+ public IPin IO3 => new Pin(
+ Controller,
+ "IO3",
+ (byte)(1 << 3),
+ new List {
+ new DigitalChannelInfo("IO3", interruptCapable: false, pullUpCapable: false, pullDownCapable: false)
+ });
+
+ ///
+ /// Gets the pin representing IO4 on the Cp2112 device.
+ ///
+ public IPin IO4 => new Pin(
+ Controller,
+ "IO4",
+ (byte)(1 << 4),
+ new List {
+ new DigitalChannelInfo("IO4", interruptCapable: false, pullUpCapable: false, pullDownCapable: false)
+ });
+
+ ///
+ /// Gets the pin representing IO5 on the Cp2112 device.
+ ///
+ public IPin IO5 => new Pin(
+ Controller,
+ "IO5",
+ (byte)(1 << 5),
+ new List {
+ new DigitalChannelInfo("IO5", interruptCapable: false, pullUpCapable: false, pullDownCapable: false)
+ });
+
+ ///
+ /// Gets the pin representing IO6 on the Cp2112 device.
+ ///
+ public IPin IO6 => new Pin(
+ Controller,
+ "IO6",
+ (byte)(1 << 6),
+ new List {
+ new DigitalChannelInfo("IO6", interruptCapable: false, pullUpCapable: false, pullDownCapable: false)
+ });
+
+ ///
+ /// Gets the pin representing IO7 on the Cp2112 device.
+ ///
+ public IPin IO7 => new Pin(
+ Controller,
+ "IO7",
+ (byte)(1 << 7),
+ new List {
+ new DigitalChannelInfo("IO7", interruptCapable: false, pullUpCapable: false, pullDownCapable: false)
+ });
+
+ ///
+ /// Initialized all pins of the CP2112
+ ///
+ protected void InitAllPins()
+ {
+ // add all our pins to the collection
+ AllPins.Add(IO0);
+ AllPins.Add(IO1);
+ AllPins.Add(IO2);
+ AllPins.Add(IO3);
+ AllPins.Add(IO4);
+ AllPins.Add(IO5);
+ AllPins.Add(IO6);
+ AllPins.Add(IO7);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Cp2112.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Cp2112.cs
new file mode 100644
index 0000000000..41fa0cdd42
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Cp2112.cs
@@ -0,0 +1,174 @@
+using Meadow.Hardware;
+using System;
+using System.Linq;
+using static Meadow.Foundation.ICs.IOExpanders.Native;
+
+namespace Meadow.Foundation.ICs.IOExpanders;
+
+///
+/// Represents a CP2112 USB IO Expander
+///
+public partial class Cp2112 :
+ IDisposable,
+ IDigitalInputOutputController,
+ IDigitalOutputController,
+ II2cController
+{
+ private bool _isDisposed;
+
+ private IntPtr _handle;
+ private int _deviceNumber;
+ private ushort _vid;
+ private ushort _pid;
+ private byte _direction;
+ private byte _mode;
+ private byte _function;
+ private byte _clockDivisor;
+
+ internal Cp2112(int deviceNumber, ushort vid, ushort pid)
+ {
+ _deviceNumber = deviceNumber;
+ _vid = vid;
+ _pid = pid;
+
+ Pins = new PinDefinitions(this);
+ }
+
+ ///
+ /// The pins
+ ///
+ public PinDefinitions Pins { get; }
+
+ private bool IsOpen()
+ {
+ if (_handle == IntPtr.Zero) return false; ;
+
+ int isOpen = 0;
+ CheckStatus(Functions.HidSmbus_IsOpened(_handle, ref isOpen));
+
+ return isOpen != 0;
+ }
+
+ private void Open()
+ {
+ if (IsOpen()) return;
+
+ CheckStatus(Functions.HidSmbus_Open(ref _handle, _deviceNumber, _vid, _pid));
+ CheckStatus(Functions.HidSmbus_GetGpioConfig(_handle, ref _direction, ref _mode, ref _function, ref _clockDivisor));
+ }
+
+ private void Close()
+ {
+ if (!IsOpen()) return;
+
+ CheckStatus(Native.Functions.HidSmbus_Close(_handle));
+ _handle = IntPtr.Zero;
+ }
+
+ ///
+ public II2cBus CreateI2cBus(int busNumber = 0)
+ {
+ return CreateI2cBus(busNumber, I2cBusSpeed.Standard);
+ }
+
+ ///
+ public II2cBus CreateI2cBus(IPin[] pins, I2cBusSpeed busSpeed)
+ {
+ return CreateI2cBus(0, busSpeed);
+ }
+
+ ///
+ public II2cBus CreateI2cBus(IPin clock, IPin data, I2cBusSpeed busSpeed)
+ {
+ return CreateI2cBus(0, busSpeed);
+ }
+
+ ///
+ public II2cBus CreateI2cBus(int busNumber, I2cBusSpeed busSpeed)
+ {
+ // TODO: only allow this once
+ // TODO: convert frequency
+ // TODO: lock out access to GPIO0,1,5 and 7
+
+ //HID_SMBUS_STATUS HidSmbus_SetSmbusConfig(HID_SMBUS_DEVICE device,DWORD bitRate, BYTE address, BOOL autoReadRespond, WORD writeTimeout,WORD readTimeout, BOOL sclLowTimeout, WORD transferRetries)
+ // Functions.HidSmbus_SetSmbusConfig(_handle, 100000, 0x02, 0, 100, 100, 0, 2);
+ //HID_SMBUS_STATUS HidSmbus_SetGpioConfig(HID_SMBUS_DEVICE device,BYTE direction, BYTE mode, BYTE special, BYTE clkDiv)
+ // Functions.HidSmbus_SetGpioConfig(_handle, 0x20, 0x20, 0x13, 0xFF); //GPIO5 output/push-pull/GPIO0,1,7 special function/clkDiv=48MHz/(2x255)
+ //HID_SMBUS_STATUS HidSmbus_WriteLatch(HID_SMBUS_DEVICE device,BYTE latchValue, BYTE latchMask)
+ // Functions.HidSmbus_WriteLatch(_handle, 0, 0x20); //"Low" active for GPIO5
+
+ Open();
+
+ return new Cp2112I2cBus(this, busSpeed);
+ }
+
+ internal void I2CWrite(byte peripheralAddress, Span writeBuffer)
+ {
+ CheckStatus(Functions.HidSmbus_WriteRequest(_handle, peripheralAddress, writeBuffer.ToArray(), (byte)writeBuffer.Length));
+ }
+
+ internal void SetState(byte pinMask)
+ {
+ CheckStatus(Functions.HidSmbus_WriteLatch(_handle, pinMask, pinMask));
+ }
+
+ internal void ClearState(byte pinMask)
+ {
+ CheckStatus(Functions.HidSmbus_WriteLatch(_handle, (byte)~pinMask, pinMask));
+ }
+
+ ///
+ public IDigitalInputPort CreateDigitalInputPort(IPin pin, ResistorMode resistorMode)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ public IDigitalOutputPort CreateDigitalOutputPort(IPin pin, bool initialState = false, OutputType initialOutputType = OutputType.PushPull)
+ {
+ // TODO: check if pin is in use already
+ Open();
+
+ var d = _direction;
+ d |= (byte)pin.Key;
+
+ var mode = _mode;
+ if (initialOutputType == OutputType.PushPull)
+ {
+ mode |= (byte)pin.Key;
+ }
+
+ CheckStatus(Functions.HidSmbus_SetGpioConfig(_handle, d, mode, _function, _clockDivisor));
+ _direction = d;
+ _mode = mode;
+ // _stateMask |= (byte)pin.Key;
+
+ return new Cp2112DigitalOutputPort(pin, (pin.SupportedChannels.First(c => c is DigitalChannelInfo) as IDigitalChannelInfo)!, initialState, initialOutputType, this);
+ }
+
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+ Close();
+
+ _isDisposed = true;
+ }
+ }
+
+ ///
+ /// Finalizes native resources for the CP2112
+ ///
+ ~Cp2112()
+ {
+ Dispose(false);
+ }
+
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+}
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Cp2112Collection.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Cp2112Collection.cs
new file mode 100644
index 0000000000..f7f3c00ae1
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Cp2112Collection.cs
@@ -0,0 +1,79 @@
+using System.Collections;
+using System.Collections.Generic;
+
+#nullable enable
+
+namespace Meadow.Foundation.ICs.IOExpanders;
+
+///
+/// Represents a collection of Cp2112 devices and provides functionality for device enumeration.
+///
+public class Cp2112Collection : IEnumerable
+{
+ private static Cp2112Collection? _instance;
+
+ private List _list = new List();
+
+ ///
+ /// Gets the number of Cp2112 devices connected to the host machine.
+ ///
+ public int Count => _list.Count;
+
+ ///
+ /// Gets the Cp2112 device at the specified index in the collection.
+ ///
+ /// The index of the Cp2112 device to retrieve.
+ public Cp2112 this[int index] => _list[index];
+
+ private Cp2112Collection()
+ {
+ }
+
+ ///
+ /// Refreshes the collection by detecting and updating Cp2112 devices.
+ ///
+ public void Refresh()
+ {
+ _list.Clear();
+
+ uint deviceCount = 0;
+
+ var vid = Native.UsbParameters.SG_VID;
+ var pid = Native.UsbParameters.CP2112_PID;
+
+ Native.CheckStatus(Native.Functions.HidSmbus_GetNumDevices(ref deviceCount, vid, pid));
+
+ for (var i = 0; i < deviceCount; i++)
+ {
+ _list.Add(new Cp2112(i, vid, pid));
+ }
+
+ }
+
+ ///
+ public IEnumerator GetEnumerator()
+ {
+ return _list.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ ///
+ /// Gets the singleton instance of Cp2112Collection, initializing it if necessary.
+ ///
+ public static Cp2112Collection Devices
+ {
+ get
+ {
+ if (_instance == null)
+ {
+ _instance = new Cp2112Collection();
+ _instance.Refresh();
+ }
+ return _instance;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Cp2112DigitalInputPort.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Cp2112DigitalInputPort.cs
new file mode 100644
index 0000000000..d1c072d8f2
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Cp2112DigitalInputPort.cs
@@ -0,0 +1,47 @@
+using Meadow.Hardware;
+using System;
+
+namespace Meadow.Foundation.ICs.IOExpanders
+{
+ ///
+ /// Represents a digital input port implementation for the CP2112 bus.
+ ///
+ public sealed class Cp2112DigitalInputPort : DigitalInputPortBase
+ {
+ private readonly Cp2112 _device;
+
+ ///
+ /// Instantiates a .
+ ///
+ /// The pin connected to the input port.
+ /// The digital channel info associated with the pin.
+ /// The CP2112 device instance.
+ internal Cp2112DigitalInputPort(IPin pin, IDigitalChannelInfo info, Cp2112 device)
+ : base(pin, info)
+ {
+ _device = device;
+ }
+
+ ///
+ /// Gets the current state of the input port.
+ ///
+ /// The current state of the input port.
+ public override bool State
+ {
+ get
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ ///
+ /// Gets or sets the resistor mode of the input port.
+ ///
+ /// The CP2112 does not support internal resistors.
+ public override ResistorMode Resistor
+ {
+ get => ResistorMode.Disabled;
+ set => throw new NotSupportedException("The CP2112 does not support internal resistors");
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Cp2112DigitalOutputPort.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Cp2112DigitalOutputPort.cs
new file mode 100644
index 0000000000..e853208589
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Cp2112DigitalOutputPort.cs
@@ -0,0 +1,54 @@
+using Meadow.Hardware;
+using System;
+
+namespace Meadow.Foundation.ICs.IOExpanders
+{
+ ///
+ /// Digital output port for CP2112 devices.
+ ///
+ public sealed class Cp2112DigitalOutputPort : DigitalOutputPortBase
+ {
+ private readonly Cp2112 _device;
+ private readonly bool _state;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The pin to use.
+ /// The digital channel info.
+ /// The initial state of the output port.
+ /// The initial output type.
+ /// The CP2112 device.
+ internal Cp2112DigitalOutputPort(IPin pin, IDigitalChannelInfo info, bool initialState, OutputType initialOutputType, Cp2112 device)
+ : base(pin, info, initialState, initialOutputType)
+ {
+ _device = device;
+ State = initialState;
+ }
+
+ ///
+ /// Gets or sets the state of the digital output port.
+ ///
+ ///
+ /// The state of the digital output port.
+ ///
+ public override bool State
+ {
+ get => _state;
+ set
+ {
+ if (value)
+ {
+ Console.WriteLine("ON");
+ _device.SetState((byte)this.Pin.Key);
+
+ }
+ else
+ {
+ Console.WriteLine("OFF");
+ _device.ClearState((byte)this.Pin.Key);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Cp2112I2cBus.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Cp2112I2cBus.cs
new file mode 100644
index 0000000000..192158611b
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Cp2112I2cBus.cs
@@ -0,0 +1,64 @@
+using Meadow.Hardware;
+using System;
+
+namespace Meadow.Foundation.ICs.IOExpanders;
+
+///
+/// Represents an I2C bus implementation using the Cp2112 device.
+///
+public sealed class Cp2112I2cBus : II2cBus, IDisposable
+{
+ private bool _isDisposed;
+ private Cp2112 _device;
+
+ internal Cp2112I2cBus(Cp2112 device, I2cBusSpeed busSpeed)
+ {
+ BusSpeed = busSpeed;
+ _device = device;
+ }
+
+ ///
+ public I2cBusSpeed BusSpeed { get; set; }
+
+ private void Dispose(bool _)
+ {
+ if (!_isDisposed)
+ {
+ _isDisposed = true;
+ }
+ }
+
+ ///
+ /// Finalizer for the Cp2112I2cBus class, used to release unmanaged resources.
+ ///
+ ~Cp2112I2cBus()
+ {
+ Dispose(false);
+ }
+
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ public void Exchange(byte peripheralAddress, Span writeBuffer, Span readBuffer)
+ {
+ Write(peripheralAddress, writeBuffer);
+ Read(peripheralAddress, readBuffer);
+ }
+
+ ///
+ public void Read(byte peripheralAddress, Span readBuffer)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ public void Write(byte peripheralAddress, Span writeBuffer)
+ {
+ _device.I2CWrite(peripheralAddress, writeBuffer);
+ }
+}
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/ICs.IOExpanders.Cp2112.csproj b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/ICs.IOExpanders.Cp2112.csproj
new file mode 100644
index 0000000000..002e3a838a
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/ICs.IOExpanders.Cp2112.csproj
@@ -0,0 +1,28 @@
+
+
+ Readme.md
+ enable
+ 10.0
+ Apache-2.0
+ true
+ icon.png
+ Wilderness Labs, Inc
+ netstandard2.1
+ Library
+ Cp2112
+ Wilderness Labs, Inc
+ http://developer.wildernesslabs.co/Meadow/Meadow.Foundation/
+ Meadow.Foundation.ICs.IOExpanders.Cp2112
+ https://github.com/WildernessLabs/Meadow.Foundation
+ Meadow.Foundation,IOExpanders,expander,IO,CP2112
+ 0.1.0
+ true
+ false
+ CP2112 USB IOExpander for GPIO, I2C, SPI on Windows
+
+
+
+
+
+
+
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Native.Functions.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Native.Functions.cs
new file mode 100644
index 0000000000..fa6cf15a93
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Native.Functions.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Meadow.Foundation.ICs.IOExpanders
+{
+ internal static partial class Native
+ {
+
+ public class Functions
+ {
+ private const string HIDtoSMB = "SLABHIDtoSMBus";
+
+ [DllImport(HIDtoSMB)]
+ public static extern HID_SMBUS_STATUS HidSmbus_GetNumDevices(ref uint numDevices, ushort vid, ushort pid);
+
+ [DllImport(HIDtoSMB)]
+ public static extern HID_SMBUS_STATUS HidSmbus_Open(ref IntPtr device, int deviceNum, ushort vid, ushort pid);
+
+ [DllImport(HIDtoSMB)]
+ public static extern HID_SMBUS_STATUS HidSmbus_Close(IntPtr device);
+
+ [DllImport(HIDtoSMB)]
+ public static extern HID_SMBUS_STATUS HidSmbus_IsOpened(IntPtr device, ref int opened);
+
+ [DllImport(HIDtoSMB)]
+ public static extern HID_SMBUS_STATUS HidSmbus_GetGpioConfig(IntPtr device, ref byte direction, ref byte mode, ref byte function, ref byte clockDivisor);
+
+ [DllImport(HIDtoSMB)]
+ public static extern HID_SMBUS_STATUS HidSmbus_SetGpioConfig(IntPtr device, byte direction, byte mode, byte function, byte clockDivisor);
+
+ [DllImport(HIDtoSMB)]
+ public static extern HID_SMBUS_STATUS HidSmbus_ReadLatch(IntPtr device, out byte value);
+
+ [DllImport(HIDtoSMB)]
+ public static extern HID_SMBUS_STATUS HidSmbus_WriteLatch(IntPtr device, byte value, byte mask);
+
+ [DllImport(HIDtoSMB)]
+ public static extern HID_SMBUS_STATUS HidSmbus_ReadRequest(IntPtr device, byte slaveAddress, byte numBytesToRead);
+
+ [DllImport(HIDtoSMB)]
+ public static extern HID_SMBUS_STATUS HidSmbus_GetReadResponse(IntPtr device, byte statusS0, byte[] buffer, byte bufferSize, out byte bytesRead);
+
+ [DllImport(HIDtoSMB)]
+ public static extern HID_SMBUS_STATUS HidSmbus_AddressReadRequest(IntPtr device, byte slaveAddress, short numBytesToRead, byte targetAddressSize, byte[] targetAddress);
+
+ [DllImport(HIDtoSMB)]
+ public static extern HID_SMBUS_STATUS HidSmbus_GetSmbusConfig(IntPtr device, out uint bitRate, out byte address, out int autoReadRespond, out short writeTimeout, out short readTimeout, out int sclLowtimeout, out short transferRetries);
+
+ [DllImport(HIDtoSMB)]
+ public static extern HID_SMBUS_STATUS HidSmbus_SetSmbusConfig(IntPtr device, uint bitRate, byte address, int autoReadRespond, short writeTimeout, short readTimeout, int sclLowTimeout, short transferRetries);
+
+ // HID_SMBUS_STATUS HidSmbus_WriteRequest (HID_SMBUS_DEVICE device, BYTE slaveAddress, BYTE* buffer, BYTE numBytesToWrite)
+ [DllImport(HIDtoSMB)]
+ public static extern HID_SMBUS_STATUS HidSmbus_WriteRequest(IntPtr device, byte slaveAddress, byte[] buffer, byte numBytes);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Native.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Native.cs
new file mode 100644
index 0000000000..3c2700661e
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Native.cs
@@ -0,0 +1,60 @@
+using System;
+
+namespace Meadow.Foundation.ICs.IOExpanders
+{
+ internal static partial class Native
+ {
+ public static bool CheckStatus(Native.HID_SMBUS_STATUS status)
+ {
+ if (status == Native.HID_SMBUS_STATUS.HID_SMBUS_SUCCESS)
+ {
+ return true;
+ }
+
+ throw new Exception($"Native error: {status}");
+ }
+
+ public enum HID_SMBUS_STATUS
+ {
+ HID_SMBUS_SUCCESS = 0x00,
+ HID_SMBUS_DEVICE_NOT_FOUND = 0x01,
+ HID_SMBUS_INVALID_HANDLE = 0x02,
+ HID_SMBUS_INVALID_DEVICE_OBJECT = 0x03,
+ HID_SMBUS_INVALID_PARAMETER = 0x04,
+ HID_SMBUS_INVALID_REQUEST_LENGTH = 0x05,
+ HID_SMBUS_READ_ERROR = 0x10,
+ HID_SMBUS_WRITE_ERROR = 0x11,
+ HID_SMBUS_READ_TIMED_OUT = 0x12,
+ HID_SMBUS_WRITE_TIMED_OUT = 0x13,
+ HID_SMBUS_DEVICE_IO_FAILED = 0x14,
+ HID_SMBUS_DEVICE_ACCESS_ERROR = 0x15,
+ HID_SMBUS_DEVICE_NOT_SUPPORTED = 0x16,
+ HID_SMBUS_UNKNOWN_ERROR = 0xFF,
+ }
+
+ public static class UsbParameters
+ {
+ public const ushort SG_VID = 0x10C4;
+ public const ushort CP2112_PID = 0xEA90;
+ }
+
+ public const byte HID_SMBUS_S0_IDLE = 0x00;
+ public const byte HID_SMBUS_S0_BUSY = 0x01;
+ public const byte HID_SMBUS_S0_COMPLETE = 0x02;
+ public const byte HID_SMBUS_S0_ERROR = 0x03;
+
+ // HID_SMBUS_TRANSFER_S0 = HID_SMBUS_S0_BUSY
+ public const byte HID_SMBUS_S1_BUSY_ADDRESS_ACKED = 0x00;
+ public const byte HID_SMBUS_S1_BUSY_ADDRESS_NACKED = 0x01;
+ public const byte HID_SMBUS_S1_BUSY_READING = 0x02;
+ public const byte HID_SMBUS_S1_BUSY_WRITING = 0x03;
+
+ // HID_SMBUS_TRANSFER_S0 = HID_SMBUS_S0_ERROR
+ public const byte HID_SMBUS_S1_ERROR_TIMEOUT_NACK = 0x00;
+ public const byte HID_SMBUS_S1_ERROR_TIMEOUT_BUS_NOT_FREE = 0x01;
+ public const byte HID_SMBUS_S1_ERROR_ARB_LOST = 0x02;
+ public const byte HID_SMBUS_S1_ERROR_READ_INCOMPLETE = 0x03;
+ public const byte HID_SMBUS_S1_ERROR_WRITE_INCOMPLETE = 0x04;
+ public const byte HID_SMBUS_S1_ERROR_SUCCESS_AFTER_RETRY = 0x05;
+ }
+}
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Readme.md b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Readme.md
new file mode 100644
index 0000000000..213db860a5
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Driver/Readme.md
@@ -0,0 +1,12 @@
+# Meadow.Foundation.IOEXpanders.Cp2112
+
+## How to Contribute
+
+- **Found a bug?** [Report an issue](https://github.com/WildernessLabs/Meadow_Issues/issues)
+- Have a **feature idea or driver request?** [Open a new feature request](https://github.com/WildernessLabs/Meadow_Issues/issues)
+- Want to **contribute code?** Fork the [Meadow.Foundation](https://github.com/WildernessLabs/Meadow.Foundation) repository and submit a pull request against the `develop` branch
+
+
+## Need Help?
+
+If you have questions or need assistance, please join the Wilderness Labs [community on Slack](http://slackinvite.wildernesslabs.co/).
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Native/Windows/x64/SLABHIDDevice.dll b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Native/Windows/x64/SLABHIDDevice.dll
new file mode 100644
index 0000000000..eea9fc92fd
Binary files /dev/null and b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Native/Windows/x64/SLABHIDDevice.dll differ
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Native/Windows/x64/SLABHIDtoSMBus.dll b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Native/Windows/x64/SLABHIDtoSMBus.dll
new file mode 100644
index 0000000000..0548bc2e32
Binary files /dev/null and b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Native/Windows/x64/SLABHIDtoSMBus.dll differ
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Native/Windows/x86/SLABHIDDevice.dll b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Native/Windows/x86/SLABHIDDevice.dll
new file mode 100644
index 0000000000..03094b91a3
Binary files /dev/null and b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Native/Windows/x86/SLABHIDDevice.dll differ
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Native/Windows/x86/SLABHIDtoSMBus.dll b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Native/Windows/x86/SLABHIDtoSMBus.dll
new file mode 100644
index 0000000000..9282e47b6c
Binary files /dev/null and b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Native/Windows/x86/SLABHIDtoSMBus.dll differ
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Samples/Cp2112_Windows_Sample/Cp2112_Windows_Sample.csproj b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Samples/Cp2112_Windows_Sample/Cp2112_Windows_Sample.csproj
new file mode 100644
index 0000000000..ac8e2c5048
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Samples/Cp2112_Windows_Sample/Cp2112_Windows_Sample.csproj
@@ -0,0 +1,23 @@
+
+
+
+ Exe
+ net7.0
+ enable
+ enable
+
+
+
+
+ Always
+
+
+ Always
+
+
+
+
+
+
+
+
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Samples/Cp2112_Windows_Sample/Program.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Samples/Cp2112_Windows_Sample/Program.cs
new file mode 100644
index 0000000000..a44900a707
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Cp2112/Samples/Cp2112_Windows_Sample/Program.cs
@@ -0,0 +1,28 @@
+// See https://aka.ms/new-console-template for more information
+using Meadow.Foundation.ICs.IOExpanders;
+using Meadow.Hardware;
+
+Console.WriteLine("HELLO FROM THE WILDERNESS CP2112 DRIVER!");
+
+if (Cp2112Collection.Devices.Count == 0)
+{
+ Console.WriteLine("No CP2112 devices detected!");
+ Console.ReadKey();
+ return;
+}
+
+var cp = Cp2112Collection.Devices[0];
+
+var output = cp.Pins.IO6.CreateDigitalOutputPort();
+
+while (true)
+{
+ output.State = true;
+ Thread.Sleep(1000);
+ output.State = false;
+ Thread.Sleep(1000);
+}
+
+Console.ReadKey();
+
+
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Data Sheets/AN_108_Command_Processor_for_MPSSE_and_MCU_Host_Bus_Emulation_Modes.pdf b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Data Sheets/AN_108_Command_Processor_for_MPSSE_and_MCU_Host_Bus_Emulation_Modes.pdf
new file mode 100644
index 0000000000..ef93742266
Binary files /dev/null and b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Data Sheets/AN_108_Command_Processor_for_MPSSE_and_MCU_Host_Bus_Emulation_Modes.pdf differ
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Data Sheets/D2XX_Programmers_Guide.pdf b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Data Sheets/D2XX_Programmers_Guide.pdf
new file mode 100644
index 0000000000..69920646fb
Binary files /dev/null and b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Data Sheets/D2XX_Programmers_Guide.pdf differ
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Data Sheets/ftd2xx.h b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Data Sheets/ftd2xx.h
new file mode 100644
index 0000000000..895f2267f5
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Data Sheets/ftd2xx.h
@@ -0,0 +1,1446 @@
+/*++
+
+Copyright © 2001-2011 Future Technology Devices International Limited
+
+THIS SOFTWARE IS PROVIDED BY FUTURE TECHNOLOGY DEVICES INTERNATIONAL LIMITED "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+FUTURE TECHNOLOGY DEVICES INTERNATIONAL LIMITED BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+OF SUBSTITUTE GOODS OR SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+FTDI DRIVERS MAY BE USED ONLY IN CONJUNCTION WITH PRODUCTS BASED ON FTDI PARTS.
+
+FTDI DRIVERS MAY BE DISTRIBUTED IN ANY FORM AS LONG AS LICENSE INFORMATION IS NOT MODIFIED.
+
+IF A CUSTOM VENDOR ID AND/OR PRODUCT ID OR DESCRIPTION STRING ARE USED, IT IS THE
+RESPONSIBILITY OF THE PRODUCT MANUFACTURER TO MAINTAIN ANY CHANGES AND SUBSEQUENT WHQL
+RE-CERTIFICATION AS A RESULT OF MAKING THESE CHANGES.
+
+
+Module Name:
+
+ftd2xx.h
+
+Abstract:
+
+Native USB device driver for FTDI FT232x, FT245x, FT2232x and FT4232x devices
+FTD2XX library definitions
+
+Environment:
+
+kernel & user mode
+
+
+--*/
+
+
+#ifndef FTD2XX_H
+#define FTD2XX_H
+
+#ifdef _WIN32
+// Compiling on Windows
+#include
+
+// The following ifdef block is the standard way of creating macros
+// which make exporting from a DLL simpler. All files within this DLL
+// are compiled with the FTD2XX_EXPORTS symbol defined on the command line.
+// This symbol should not be defined on any project that uses this DLL.
+// This way any other project whose source files include this file see
+// FTD2XX_API functions as being imported from a DLL, whereas this DLL
+// sees symbols defined with this macro as being exported.
+
+#ifdef FTD2XX_EXPORTS
+#define FTD2XX_API __declspec(dllexport)
+#elif defined(FTD2XX_STATIC)
+// Avoid decorations when linking statically to D2XX.
+#define FTD2XX_API
+// Static D2XX depends on these Windows libs:
+#pragma comment(lib, "setupapi.lib")
+#pragma comment(lib, "advapi32.lib")
+#pragma comment(lib, "user32.lib")
+#else
+#define FTD2XX_API __declspec(dllimport)
+#endif
+
+#else // _WIN32
+// Compiling on non-Windows platform.
+#include "WinTypes.h"
+// No decorations needed.
+#define FTD2XX_API
+
+#endif // _WIN32
+
+typedef PVOID FT_HANDLE;
+typedef ULONG FT_STATUS;
+
+//
+// Device status
+//
+enum {
+ FT_OK,
+ FT_INVALID_HANDLE,
+ FT_DEVICE_NOT_FOUND,
+ FT_DEVICE_NOT_OPENED,
+ FT_IO_ERROR,
+ FT_INSUFFICIENT_RESOURCES,
+ FT_INVALID_PARAMETER,
+ FT_INVALID_BAUD_RATE,
+
+ FT_DEVICE_NOT_OPENED_FOR_ERASE,
+ FT_DEVICE_NOT_OPENED_FOR_WRITE,
+ FT_FAILED_TO_WRITE_DEVICE,
+ FT_EEPROM_READ_FAILED,
+ FT_EEPROM_WRITE_FAILED,
+ FT_EEPROM_ERASE_FAILED,
+ FT_EEPROM_NOT_PRESENT,
+ FT_EEPROM_NOT_PROGRAMMED,
+ FT_INVALID_ARGS,
+ FT_NOT_SUPPORTED,
+ FT_OTHER_ERROR,
+ FT_DEVICE_LIST_NOT_READY,
+};
+
+
+#define FT_SUCCESS(status) ((status) == FT_OK)
+
+//
+// FT_OpenEx Flags
+//
+
+#define FT_OPEN_BY_SERIAL_NUMBER 1
+#define FT_OPEN_BY_DESCRIPTION 2
+#define FT_OPEN_BY_LOCATION 4
+
+#define FT_OPEN_MASK (FT_OPEN_BY_SERIAL_NUMBER | \
+ FT_OPEN_BY_DESCRIPTION | \
+ FT_OPEN_BY_LOCATION)
+
+//
+// FT_ListDevices Flags (used in conjunction with FT_OpenEx Flags
+//
+
+#define FT_LIST_NUMBER_ONLY 0x80000000
+#define FT_LIST_BY_INDEX 0x40000000
+#define FT_LIST_ALL 0x20000000
+
+#define FT_LIST_MASK (FT_LIST_NUMBER_ONLY|FT_LIST_BY_INDEX|FT_LIST_ALL)
+
+//
+// Baud Rates
+//
+
+#define FT_BAUD_300 300
+#define FT_BAUD_600 600
+#define FT_BAUD_1200 1200
+#define FT_BAUD_2400 2400
+#define FT_BAUD_4800 4800
+#define FT_BAUD_9600 9600
+#define FT_BAUD_14400 14400
+#define FT_BAUD_19200 19200
+#define FT_BAUD_38400 38400
+#define FT_BAUD_57600 57600
+#define FT_BAUD_115200 115200
+#define FT_BAUD_230400 230400
+#define FT_BAUD_460800 460800
+#define FT_BAUD_921600 921600
+
+//
+// Word Lengths
+//
+
+#define FT_BITS_8 (UCHAR) 8
+#define FT_BITS_7 (UCHAR) 7
+
+//
+// Stop Bits
+//
+
+#define FT_STOP_BITS_1 (UCHAR) 0
+#define FT_STOP_BITS_2 (UCHAR) 2
+
+//
+// Parity
+//
+
+#define FT_PARITY_NONE (UCHAR) 0
+#define FT_PARITY_ODD (UCHAR) 1
+#define FT_PARITY_EVEN (UCHAR) 2
+#define FT_PARITY_MARK (UCHAR) 3
+#define FT_PARITY_SPACE (UCHAR) 4
+
+//
+// Flow Control
+//
+
+#define FT_FLOW_NONE 0x0000
+#define FT_FLOW_RTS_CTS 0x0100
+#define FT_FLOW_DTR_DSR 0x0200
+#define FT_FLOW_XON_XOFF 0x0400
+
+//
+// Purge rx and tx buffers
+//
+#define FT_PURGE_RX 1
+#define FT_PURGE_TX 2
+
+//
+// Events
+//
+
+typedef void (*PFT_EVENT_HANDLER)(DWORD,DWORD);
+
+#define FT_EVENT_RXCHAR 1
+#define FT_EVENT_MODEM_STATUS 2
+#define FT_EVENT_LINE_STATUS 4
+
+//
+// Timeouts
+//
+
+#define FT_DEFAULT_RX_TIMEOUT 300
+#define FT_DEFAULT_TX_TIMEOUT 300
+
+//
+// Device types
+//
+
+typedef ULONG FT_DEVICE;
+
+enum {
+ FT_DEVICE_BM,
+ FT_DEVICE_AM,
+ FT_DEVICE_100AX,
+ FT_DEVICE_UNKNOWN,
+ FT_DEVICE_2232C,
+ FT_DEVICE_232R,
+ FT_DEVICE_2232H,
+ FT_DEVICE_4232H,
+ FT_DEVICE_232H,
+ FT_DEVICE_X_SERIES,
+ FT_DEVICE_4222H_0,
+ FT_DEVICE_4222H_1_2,
+ FT_DEVICE_4222H_3,
+ FT_DEVICE_4222_PROG,
+ FT_DEVICE_900,
+ FT_DEVICE_930,
+ FT_DEVICE_UMFTPD3A,
+};
+
+//
+// Bit Modes
+//
+
+#define FT_BITMODE_RESET 0x00
+#define FT_BITMODE_ASYNC_BITBANG 0x01
+#define FT_BITMODE_MPSSE 0x02
+#define FT_BITMODE_SYNC_BITBANG 0x04
+#define FT_BITMODE_MCU_HOST 0x08
+#define FT_BITMODE_FAST_SERIAL 0x10
+#define FT_BITMODE_CBUS_BITBANG 0x20
+#define FT_BITMODE_SYNC_FIFO 0x40
+
+//
+// FT232R CBUS Options EEPROM values
+//
+
+#define FT_232R_CBUS_TXDEN 0x00 // Tx Data Enable
+#define FT_232R_CBUS_PWRON 0x01 // Power On
+#define FT_232R_CBUS_RXLED 0x02 // Rx LED
+#define FT_232R_CBUS_TXLED 0x03 // Tx LED
+#define FT_232R_CBUS_TXRXLED 0x04 // Tx and Rx LED
+#define FT_232R_CBUS_SLEEP 0x05 // Sleep
+#define FT_232R_CBUS_CLK48 0x06 // 48MHz clock
+#define FT_232R_CBUS_CLK24 0x07 // 24MHz clock
+#define FT_232R_CBUS_CLK12 0x08 // 12MHz clock
+#define FT_232R_CBUS_CLK6 0x09 // 6MHz clock
+#define FT_232R_CBUS_IOMODE 0x0A // IO Mode for CBUS bit-bang
+#define FT_232R_CBUS_BITBANG_WR 0x0B // Bit-bang write strobe
+#define FT_232R_CBUS_BITBANG_RD 0x0C // Bit-bang read strobe
+
+//
+// FT232H CBUS Options EEPROM values
+//
+
+#define FT_232H_CBUS_TRISTATE 0x00 // Tristate
+#define FT_232H_CBUS_TXLED 0x01 // Tx LED
+#define FT_232H_CBUS_RXLED 0x02 // Rx LED
+#define FT_232H_CBUS_TXRXLED 0x03 // Tx and Rx LED
+#define FT_232H_CBUS_PWREN 0x04 // Power Enable
+#define FT_232H_CBUS_SLEEP 0x05 // Sleep
+#define FT_232H_CBUS_DRIVE_0 0x06 // Drive pin to logic 0
+#define FT_232H_CBUS_DRIVE_1 0x07 // Drive pin to logic 1
+#define FT_232H_CBUS_IOMODE 0x08 // IO Mode for CBUS bit-bang
+#define FT_232H_CBUS_TXDEN 0x09 // Tx Data Enable
+#define FT_232H_CBUS_CLK30 0x0A // 30MHz clock
+#define FT_232H_CBUS_CLK15 0x0B // 15MHz clock
+#define FT_232H_CBUS_CLK7_5 0x0C // 7.5MHz clock
+
+//
+// FT X Series CBUS Options EEPROM values
+//
+
+#define FT_X_SERIES_CBUS_TRISTATE 0x00 // Tristate
+#define FT_X_SERIES_CBUS_TXLED 0x01 // Tx LED
+#define FT_X_SERIES_CBUS_RXLED 0x02 // Rx LED
+#define FT_X_SERIES_CBUS_TXRXLED 0x03 // Tx and Rx LED
+#define FT_X_SERIES_CBUS_PWREN 0x04 // Power Enable
+#define FT_X_SERIES_CBUS_SLEEP 0x05 // Sleep
+#define FT_X_SERIES_CBUS_DRIVE_0 0x06 // Drive pin to logic 0
+#define FT_X_SERIES_CBUS_DRIVE_1 0x07 // Drive pin to logic 1
+#define FT_X_SERIES_CBUS_IOMODE 0x08 // IO Mode for CBUS bit-bang
+#define FT_X_SERIES_CBUS_TXDEN 0x09 // Tx Data Enable
+#define FT_X_SERIES_CBUS_CLK24 0x0A // 24MHz clock
+#define FT_X_SERIES_CBUS_CLK12 0x0B // 12MHz clock
+#define FT_X_SERIES_CBUS_CLK6 0x0C // 6MHz clock
+#define FT_X_SERIES_CBUS_BCD_CHARGER 0x0D // Battery charger detected
+#define FT_X_SERIES_CBUS_BCD_CHARGER_N 0x0E // Battery charger detected inverted
+#define FT_X_SERIES_CBUS_I2C_TXE 0x0F // I2C Tx empty
+#define FT_X_SERIES_CBUS_I2C_RXF 0x10 // I2C Rx full
+#define FT_X_SERIES_CBUS_VBUS_SENSE 0x11 // Detect VBUS
+#define FT_X_SERIES_CBUS_BITBANG_WR 0x12 // Bit-bang write strobe
+#define FT_X_SERIES_CBUS_BITBANG_RD 0x13 // Bit-bang read strobe
+#define FT_X_SERIES_CBUS_TIMESTAMP 0x14 // Toggle output when a USB SOF token is received
+#define FT_X_SERIES_CBUS_KEEP_AWAKE 0x15 //
+
+
+// Driver types
+#define FT_DRIVER_TYPE_D2XX 0
+#define FT_DRIVER_TYPE_VCP 1
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifdef FTD2XX_STATIC
+ FTD2XX_API
+ FT_STATUS WINAPI FT_Initialise(
+ void
+ );
+
+ FTD2XX_API
+ void WINAPI FT_Finalise(
+ void
+ );
+#endif // FTD2XX_STATIC
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_Open(
+ int deviceNumber,
+ FT_HANDLE *pHandle
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_OpenEx(
+ PVOID pArg1,
+ DWORD Flags,
+ FT_HANDLE *pHandle
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_ListDevices(
+ PVOID pArg1,
+ PVOID pArg2,
+ DWORD Flags
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_Close(
+ FT_HANDLE ftHandle
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_Read(
+ FT_HANDLE ftHandle,
+ LPVOID lpBuffer,
+ DWORD dwBytesToRead,
+ LPDWORD lpBytesReturned
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_Write(
+ FT_HANDLE ftHandle,
+ LPVOID lpBuffer,
+ DWORD dwBytesToWrite,
+ LPDWORD lpBytesWritten
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_IoCtl(
+ FT_HANDLE ftHandle,
+ DWORD dwIoControlCode,
+ LPVOID lpInBuf,
+ DWORD nInBufSize,
+ LPVOID lpOutBuf,
+ DWORD nOutBufSize,
+ LPDWORD lpBytesReturned,
+ LPOVERLAPPED lpOverlapped
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_SetBaudRate(
+ FT_HANDLE ftHandle,
+ ULONG BaudRate
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_SetDivisor(
+ FT_HANDLE ftHandle,
+ USHORT Divisor
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_SetDataCharacteristics(
+ FT_HANDLE ftHandle,
+ UCHAR WordLength,
+ UCHAR StopBits,
+ UCHAR Parity
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_SetFlowControl(
+ FT_HANDLE ftHandle,
+ USHORT FlowControl,
+ UCHAR XonChar,
+ UCHAR XoffChar
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_ResetDevice(
+ FT_HANDLE ftHandle
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_SetDtr(
+ FT_HANDLE ftHandle
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_ClrDtr(
+ FT_HANDLE ftHandle
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_SetRts(
+ FT_HANDLE ftHandle
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_ClrRts(
+ FT_HANDLE ftHandle
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_GetModemStatus(
+ FT_HANDLE ftHandle,
+ ULONG *pModemStatus
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_SetChars(
+ FT_HANDLE ftHandle,
+ UCHAR EventChar,
+ UCHAR EventCharEnabled,
+ UCHAR ErrorChar,
+ UCHAR ErrorCharEnabled
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_Purge(
+ FT_HANDLE ftHandle,
+ ULONG Mask
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_SetTimeouts(
+ FT_HANDLE ftHandle,
+ ULONG ReadTimeout,
+ ULONG WriteTimeout
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_GetQueueStatus(
+ FT_HANDLE ftHandle,
+ DWORD *dwRxBytes
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_SetEventNotification(
+ FT_HANDLE ftHandle,
+ DWORD Mask,
+ PVOID Param
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_GetStatus(
+ FT_HANDLE ftHandle,
+ DWORD *dwRxBytes,
+ DWORD *dwTxBytes,
+ DWORD *dwEventDWord
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_SetBreakOn(
+ FT_HANDLE ftHandle
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_SetBreakOff(
+ FT_HANDLE ftHandle
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_SetWaitMask(
+ FT_HANDLE ftHandle,
+ DWORD Mask
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_WaitOnMask(
+ FT_HANDLE ftHandle,
+ DWORD *Mask
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_GetEventStatus(
+ FT_HANDLE ftHandle,
+ DWORD *dwEventDWord
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_ReadEE(
+ FT_HANDLE ftHandle,
+ DWORD dwWordOffset,
+ LPWORD lpwValue
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_WriteEE(
+ FT_HANDLE ftHandle,
+ DWORD dwWordOffset,
+ WORD wValue
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_EraseEE(
+ FT_HANDLE ftHandle
+ );
+
+ //
+ // structure to hold program data for FT_EE_Program, FT_EE_ProgramEx, FT_EE_Read
+ // and FT_EE_ReadEx functions
+ //
+ typedef struct ft_program_data {
+
+ DWORD Signature1; // Header - must be 0x00000000
+ DWORD Signature2; // Header - must be 0xffffffff
+ DWORD Version; // Header - FT_PROGRAM_DATA version
+ // 0 = original
+ // 1 = FT2232 extensions
+ // 2 = FT232R extensions
+ // 3 = FT2232H extensions
+ // 4 = FT4232H extensions
+ // 5 = FT232H extensions
+
+ WORD VendorId; // 0x0403
+ WORD ProductId; // 0x6001
+ char *Manufacturer; // "FTDI"
+ char *ManufacturerId; // "FT"
+ char *Description; // "USB HS Serial Converter"
+ char *SerialNumber; // "FT000001" if fixed, or NULL
+ WORD MaxPower; // 0 < MaxPower <= 500
+ WORD PnP; // 0 = disabled, 1 = enabled
+ WORD SelfPowered; // 0 = bus powered, 1 = self powered
+ WORD RemoteWakeup; // 0 = not capable, 1 = capable
+ //
+ // Rev4 (FT232B) extensions
+ //
+ UCHAR Rev4; // non-zero if Rev4 chip, zero otherwise
+ UCHAR IsoIn; // non-zero if in endpoint is isochronous
+ UCHAR IsoOut; // non-zero if out endpoint is isochronous
+ UCHAR PullDownEnable; // non-zero if pull down enabled
+ UCHAR SerNumEnable; // non-zero if serial number to be used
+ UCHAR USBVersionEnable; // non-zero if chip uses USBVersion
+ WORD USBVersion; // BCD (0x0200 => USB2)
+ //
+ // Rev 5 (FT2232) extensions
+ //
+ UCHAR Rev5; // non-zero if Rev5 chip, zero otherwise
+ UCHAR IsoInA; // non-zero if in endpoint is isochronous
+ UCHAR IsoInB; // non-zero if in endpoint is isochronous
+ UCHAR IsoOutA; // non-zero if out endpoint is isochronous
+ UCHAR IsoOutB; // non-zero if out endpoint is isochronous
+ UCHAR PullDownEnable5; // non-zero if pull down enabled
+ UCHAR SerNumEnable5; // non-zero if serial number to be used
+ UCHAR USBVersionEnable5; // non-zero if chip uses USBVersion
+ WORD USBVersion5; // BCD (0x0200 => USB2)
+ UCHAR AIsHighCurrent; // non-zero if interface is high current
+ UCHAR BIsHighCurrent; // non-zero if interface is high current
+ UCHAR IFAIsFifo; // non-zero if interface is 245 FIFO
+ UCHAR IFAIsFifoTar; // non-zero if interface is 245 FIFO CPU target
+ UCHAR IFAIsFastSer; // non-zero if interface is Fast serial
+ UCHAR AIsVCP; // non-zero if interface is to use VCP drivers
+ UCHAR IFBIsFifo; // non-zero if interface is 245 FIFO
+ UCHAR IFBIsFifoTar; // non-zero if interface is 245 FIFO CPU target
+ UCHAR IFBIsFastSer; // non-zero if interface is Fast serial
+ UCHAR BIsVCP; // non-zero if interface is to use VCP drivers
+ //
+ // Rev 6 (FT232R) extensions
+ //
+ UCHAR UseExtOsc; // Use External Oscillator
+ UCHAR HighDriveIOs; // High Drive I/Os
+ UCHAR EndpointSize; // Endpoint size
+ UCHAR PullDownEnableR; // non-zero if pull down enabled
+ UCHAR SerNumEnableR; // non-zero if serial number to be used
+ UCHAR InvertTXD; // non-zero if invert TXD
+ UCHAR InvertRXD; // non-zero if invert RXD
+ UCHAR InvertRTS; // non-zero if invert RTS
+ UCHAR InvertCTS; // non-zero if invert CTS
+ UCHAR InvertDTR; // non-zero if invert DTR
+ UCHAR InvertDSR; // non-zero if invert DSR
+ UCHAR InvertDCD; // non-zero if invert DCD
+ UCHAR InvertRI; // non-zero if invert RI
+ UCHAR Cbus0; // Cbus Mux control
+ UCHAR Cbus1; // Cbus Mux control
+ UCHAR Cbus2; // Cbus Mux control
+ UCHAR Cbus3; // Cbus Mux control
+ UCHAR Cbus4; // Cbus Mux control
+ UCHAR RIsD2XX; // non-zero if using D2XX driver
+ //
+ // Rev 7 (FT2232H) Extensions
+ //
+ UCHAR PullDownEnable7; // non-zero if pull down enabled
+ UCHAR SerNumEnable7; // non-zero if serial number to be used
+ UCHAR ALSlowSlew; // non-zero if AL pins have slow slew
+ UCHAR ALSchmittInput; // non-zero if AL pins are Schmitt input
+ UCHAR ALDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
+ UCHAR AHSlowSlew; // non-zero if AH pins have slow slew
+ UCHAR AHSchmittInput; // non-zero if AH pins are Schmitt input
+ UCHAR AHDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
+ UCHAR BLSlowSlew; // non-zero if BL pins have slow slew
+ UCHAR BLSchmittInput; // non-zero if BL pins are Schmitt input
+ UCHAR BLDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
+ UCHAR BHSlowSlew; // non-zero if BH pins have slow slew
+ UCHAR BHSchmittInput; // non-zero if BH pins are Schmitt input
+ UCHAR BHDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
+ UCHAR IFAIsFifo7; // non-zero if interface is 245 FIFO
+ UCHAR IFAIsFifoTar7; // non-zero if interface is 245 FIFO CPU target
+ UCHAR IFAIsFastSer7; // non-zero if interface is Fast serial
+ UCHAR AIsVCP7; // non-zero if interface is to use VCP drivers
+ UCHAR IFBIsFifo7; // non-zero if interface is 245 FIFO
+ UCHAR IFBIsFifoTar7; // non-zero if interface is 245 FIFO CPU target
+ UCHAR IFBIsFastSer7; // non-zero if interface is Fast serial
+ UCHAR BIsVCP7; // non-zero if interface is to use VCP drivers
+ UCHAR PowerSaveEnable; // non-zero if using BCBUS7 to save power for self-powered designs
+ //
+ // Rev 8 (FT4232H) Extensions
+ //
+ UCHAR PullDownEnable8; // non-zero if pull down enabled
+ UCHAR SerNumEnable8; // non-zero if serial number to be used
+ UCHAR ASlowSlew; // non-zero if A pins have slow slew
+ UCHAR ASchmittInput; // non-zero if A pins are Schmitt input
+ UCHAR ADriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
+ UCHAR BSlowSlew; // non-zero if B pins have slow slew
+ UCHAR BSchmittInput; // non-zero if B pins are Schmitt input
+ UCHAR BDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
+ UCHAR CSlowSlew; // non-zero if C pins have slow slew
+ UCHAR CSchmittInput; // non-zero if C pins are Schmitt input
+ UCHAR CDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
+ UCHAR DSlowSlew; // non-zero if D pins have slow slew
+ UCHAR DSchmittInput; // non-zero if D pins are Schmitt input
+ UCHAR DDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
+ UCHAR ARIIsTXDEN; // non-zero if port A uses RI as RS485 TXDEN
+ UCHAR BRIIsTXDEN; // non-zero if port B uses RI as RS485 TXDEN
+ UCHAR CRIIsTXDEN; // non-zero if port C uses RI as RS485 TXDEN
+ UCHAR DRIIsTXDEN; // non-zero if port D uses RI as RS485 TXDEN
+ UCHAR AIsVCP8; // non-zero if interface is to use VCP drivers
+ UCHAR BIsVCP8; // non-zero if interface is to use VCP drivers
+ UCHAR CIsVCP8; // non-zero if interface is to use VCP drivers
+ UCHAR DIsVCP8; // non-zero if interface is to use VCP drivers
+ //
+ // Rev 9 (FT232H) Extensions
+ //
+ UCHAR PullDownEnableH; // non-zero if pull down enabled
+ UCHAR SerNumEnableH; // non-zero if serial number to be used
+ UCHAR ACSlowSlewH; // non-zero if AC pins have slow slew
+ UCHAR ACSchmittInputH; // non-zero if AC pins are Schmitt input
+ UCHAR ACDriveCurrentH; // valid values are 4mA, 8mA, 12mA, 16mA
+ UCHAR ADSlowSlewH; // non-zero if AD pins have slow slew
+ UCHAR ADSchmittInputH; // non-zero if AD pins are Schmitt input
+ UCHAR ADDriveCurrentH; // valid values are 4mA, 8mA, 12mA, 16mA
+ UCHAR Cbus0H; // Cbus Mux control
+ UCHAR Cbus1H; // Cbus Mux control
+ UCHAR Cbus2H; // Cbus Mux control
+ UCHAR Cbus3H; // Cbus Mux control
+ UCHAR Cbus4H; // Cbus Mux control
+ UCHAR Cbus5H; // Cbus Mux control
+ UCHAR Cbus6H; // Cbus Mux control
+ UCHAR Cbus7H; // Cbus Mux control
+ UCHAR Cbus8H; // Cbus Mux control
+ UCHAR Cbus9H; // Cbus Mux control
+ UCHAR IsFifoH; // non-zero if interface is 245 FIFO
+ UCHAR IsFifoTarH; // non-zero if interface is 245 FIFO CPU target
+ UCHAR IsFastSerH; // non-zero if interface is Fast serial
+ UCHAR IsFT1248H; // non-zero if interface is FT1248
+ UCHAR FT1248CpolH; // FT1248 clock polarity - clock idle high (1) or clock idle low (0)
+ UCHAR FT1248LsbH; // FT1248 data is LSB (1) or MSB (0)
+ UCHAR FT1248FlowControlH; // FT1248 flow control enable
+ UCHAR IsVCPH; // non-zero if interface is to use VCP drivers
+ UCHAR PowerSaveEnableH; // non-zero if using ACBUS7 to save power for self-powered designs
+
+ } FT_PROGRAM_DATA, *PFT_PROGRAM_DATA;
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_EE_Program(
+ FT_HANDLE ftHandle,
+ PFT_PROGRAM_DATA pData
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_EE_ProgramEx(
+ FT_HANDLE ftHandle,
+ PFT_PROGRAM_DATA pData,
+ char *Manufacturer,
+ char *ManufacturerId,
+ char *Description,
+ char *SerialNumber
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_EE_Read(
+ FT_HANDLE ftHandle,
+ PFT_PROGRAM_DATA pData
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_EE_ReadEx(
+ FT_HANDLE ftHandle,
+ PFT_PROGRAM_DATA pData,
+ char *Manufacturer,
+ char *ManufacturerId,
+ char *Description,
+ char *SerialNumber
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_EE_UASize(
+ FT_HANDLE ftHandle,
+ LPDWORD lpdwSize
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_EE_UAWrite(
+ FT_HANDLE ftHandle,
+ PUCHAR pucData,
+ DWORD dwDataLen
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_EE_UARead(
+ FT_HANDLE ftHandle,
+ PUCHAR pucData,
+ DWORD dwDataLen,
+ LPDWORD lpdwBytesRead
+ );
+
+
+ typedef struct ft_eeprom_header {
+ FT_DEVICE deviceType; // FTxxxx device type to be programmed
+ // Device descriptor options
+ WORD VendorId; // 0x0403
+ WORD ProductId; // 0x6001
+ UCHAR SerNumEnable; // non-zero if serial number to be used
+ // Config descriptor options
+ WORD MaxPower; // 0 < MaxPower <= 500
+ UCHAR SelfPowered; // 0 = bus powered, 1 = self powered
+ UCHAR RemoteWakeup; // 0 = not capable, 1 = capable
+ // Hardware options
+ UCHAR PullDownEnable; // non-zero if pull down in suspend enabled
+ } FT_EEPROM_HEADER, *PFT_EEPROM_HEADER;
+
+
+ // FT232B EEPROM structure for use with FT_EEPROM_Read and FT_EEPROM_Program
+ typedef struct ft_eeprom_232b {
+ // Common header
+ FT_EEPROM_HEADER common; // common elements for all device EEPROMs
+ } FT_EEPROM_232B, *PFT_EEPROM_232B;
+
+
+ // FT2232 EEPROM structure for use with FT_EEPROM_Read and FT_EEPROM_Program
+ typedef struct ft_eeprom_2232 {
+ // Common header
+ FT_EEPROM_HEADER common; // common elements for all device EEPROMs
+ // Drive options
+ UCHAR AIsHighCurrent; // non-zero if interface is high current
+ UCHAR BIsHighCurrent; // non-zero if interface is high current
+ // Hardware options
+ UCHAR AIsFifo; // non-zero if interface is 245 FIFO
+ UCHAR AIsFifoTar; // non-zero if interface is 245 FIFO CPU target
+ UCHAR AIsFastSer; // non-zero if interface is Fast serial
+ UCHAR BIsFifo; // non-zero if interface is 245 FIFO
+ UCHAR BIsFifoTar; // non-zero if interface is 245 FIFO CPU target
+ UCHAR BIsFastSer; // non-zero if interface is Fast serial
+ // Driver option
+ UCHAR ADriverType; //
+ UCHAR BDriverType; //
+ } FT_EEPROM_2232, *PFT_EEPROM_2232;
+
+
+ // FT232R EEPROM structure for use with FT_EEPROM_Read and FT_EEPROM_Program
+ typedef struct ft_eeprom_232r {
+ // Common header
+ FT_EEPROM_HEADER common; // common elements for all device EEPROMs
+ // Drive options
+ UCHAR IsHighCurrent; // non-zero if interface is high current
+ // Hardware options
+ UCHAR UseExtOsc; // Use External Oscillator
+ UCHAR InvertTXD; // non-zero if invert TXD
+ UCHAR InvertRXD; // non-zero if invert RXD
+ UCHAR InvertRTS; // non-zero if invert RTS
+ UCHAR InvertCTS; // non-zero if invert CTS
+ UCHAR InvertDTR; // non-zero if invert DTR
+ UCHAR InvertDSR; // non-zero if invert DSR
+ UCHAR InvertDCD; // non-zero if invert DCD
+ UCHAR InvertRI; // non-zero if invert RI
+ UCHAR Cbus0; // Cbus Mux control
+ UCHAR Cbus1; // Cbus Mux control
+ UCHAR Cbus2; // Cbus Mux control
+ UCHAR Cbus3; // Cbus Mux control
+ UCHAR Cbus4; // Cbus Mux control
+ // Driver option
+ UCHAR DriverType; //
+ } FT_EEPROM_232R, *PFT_EEPROM_232R;
+
+
+ // FT2232H EEPROM structure for use with FT_EEPROM_Read and FT_EEPROM_Program
+ typedef struct ft_eeprom_2232h {
+ // Common header
+ FT_EEPROM_HEADER common; // common elements for all device EEPROMs
+ // Drive options
+ UCHAR ALSlowSlew; // non-zero if AL pins have slow slew
+ UCHAR ALSchmittInput; // non-zero if AL pins are Schmitt input
+ UCHAR ALDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
+ UCHAR AHSlowSlew; // non-zero if AH pins have slow slew
+ UCHAR AHSchmittInput; // non-zero if AH pins are Schmitt input
+ UCHAR AHDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
+ UCHAR BLSlowSlew; // non-zero if BL pins have slow slew
+ UCHAR BLSchmittInput; // non-zero if BL pins are Schmitt input
+ UCHAR BLDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
+ UCHAR BHSlowSlew; // non-zero if BH pins have slow slew
+ UCHAR BHSchmittInput; // non-zero if BH pins are Schmitt input
+ UCHAR BHDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
+ // Hardware options
+ UCHAR AIsFifo; // non-zero if interface is 245 FIFO
+ UCHAR AIsFifoTar; // non-zero if interface is 245 FIFO CPU target
+ UCHAR AIsFastSer; // non-zero if interface is Fast serial
+ UCHAR BIsFifo; // non-zero if interface is 245 FIFO
+ UCHAR BIsFifoTar; // non-zero if interface is 245 FIFO CPU target
+ UCHAR BIsFastSer; // non-zero if interface is Fast serial
+ UCHAR PowerSaveEnable; // non-zero if using BCBUS7 to save power for self-powered designs
+ // Driver option
+ UCHAR ADriverType; //
+ UCHAR BDriverType; //
+ } FT_EEPROM_2232H, *PFT_EEPROM_2232H;
+
+
+ // FT4232H EEPROM structure for use with FT_EEPROM_Read and FT_EEPROM_Program
+ typedef struct ft_eeprom_4232h {
+ // Common header
+ FT_EEPROM_HEADER common; // common elements for all device EEPROMs
+ // Drive options
+ UCHAR ASlowSlew; // non-zero if A pins have slow slew
+ UCHAR ASchmittInput; // non-zero if A pins are Schmitt input
+ UCHAR ADriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
+ UCHAR BSlowSlew; // non-zero if B pins have slow slew
+ UCHAR BSchmittInput; // non-zero if B pins are Schmitt input
+ UCHAR BDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
+ UCHAR CSlowSlew; // non-zero if C pins have slow slew
+ UCHAR CSchmittInput; // non-zero if C pins are Schmitt input
+ UCHAR CDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
+ UCHAR DSlowSlew; // non-zero if D pins have slow slew
+ UCHAR DSchmittInput; // non-zero if D pins are Schmitt input
+ UCHAR DDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
+ // Hardware options
+ UCHAR ARIIsTXDEN; // non-zero if port A uses RI as RS485 TXDEN
+ UCHAR BRIIsTXDEN; // non-zero if port B uses RI as RS485 TXDEN
+ UCHAR CRIIsTXDEN; // non-zero if port C uses RI as RS485 TXDEN
+ UCHAR DRIIsTXDEN; // non-zero if port D uses RI as RS485 TXDEN
+ // Driver option
+ UCHAR ADriverType; //
+ UCHAR BDriverType; //
+ UCHAR CDriverType; //
+ UCHAR DDriverType; //
+ } FT_EEPROM_4232H, *PFT_EEPROM_4232H;
+
+
+ // FT232H EEPROM structure for use with FT_EEPROM_Read and FT_EEPROM_Program
+ typedef struct ft_eeprom_232h {
+ // Common header
+ FT_EEPROM_HEADER common; // common elements for all device EEPROMs
+ // Drive options
+ UCHAR ACSlowSlew; // non-zero if AC bus pins have slow slew
+ UCHAR ACSchmittInput; // non-zero if AC bus pins are Schmitt input
+ UCHAR ACDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
+ UCHAR ADSlowSlew; // non-zero if AD bus pins have slow slew
+ UCHAR ADSchmittInput; // non-zero if AD bus pins are Schmitt input
+ UCHAR ADDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
+ // CBUS options
+ UCHAR Cbus0; // Cbus Mux control
+ UCHAR Cbus1; // Cbus Mux control
+ UCHAR Cbus2; // Cbus Mux control
+ UCHAR Cbus3; // Cbus Mux control
+ UCHAR Cbus4; // Cbus Mux control
+ UCHAR Cbus5; // Cbus Mux control
+ UCHAR Cbus6; // Cbus Mux control
+ UCHAR Cbus7; // Cbus Mux control
+ UCHAR Cbus8; // Cbus Mux control
+ UCHAR Cbus9; // Cbus Mux control
+ // FT1248 options
+ UCHAR FT1248Cpol; // FT1248 clock polarity - clock idle high (1) or clock idle low (0)
+ UCHAR FT1248Lsb; // FT1248 data is LSB (1) or MSB (0)
+ UCHAR FT1248FlowControl; // FT1248 flow control enable
+ // Hardware options
+ UCHAR IsFifo; // non-zero if interface is 245 FIFO
+ UCHAR IsFifoTar; // non-zero if interface is 245 FIFO CPU target
+ UCHAR IsFastSer; // non-zero if interface is Fast serial
+ UCHAR IsFT1248 ; // non-zero if interface is FT1248
+ UCHAR PowerSaveEnable; //
+ // Driver option
+ UCHAR DriverType; //
+ } FT_EEPROM_232H, *PFT_EEPROM_232H;
+
+
+ // FT X Series EEPROM structure for use with FT_EEPROM_Read and FT_EEPROM_Program
+ typedef struct ft_eeprom_x_series {
+ // Common header
+ FT_EEPROM_HEADER common; // common elements for all device EEPROMs
+ // Drive options
+ UCHAR ACSlowSlew; // non-zero if AC bus pins have slow slew
+ UCHAR ACSchmittInput; // non-zero if AC bus pins are Schmitt input
+ UCHAR ACDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
+ UCHAR ADSlowSlew; // non-zero if AD bus pins have slow slew
+ UCHAR ADSchmittInput; // non-zero if AD bus pins are Schmitt input
+ UCHAR ADDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
+ // CBUS options
+ UCHAR Cbus0; // Cbus Mux control
+ UCHAR Cbus1; // Cbus Mux control
+ UCHAR Cbus2; // Cbus Mux control
+ UCHAR Cbus3; // Cbus Mux control
+ UCHAR Cbus4; // Cbus Mux control
+ UCHAR Cbus5; // Cbus Mux control
+ UCHAR Cbus6; // Cbus Mux control
+ // UART signal options
+ UCHAR InvertTXD; // non-zero if invert TXD
+ UCHAR InvertRXD; // non-zero if invert RXD
+ UCHAR InvertRTS; // non-zero if invert RTS
+ UCHAR InvertCTS; // non-zero if invert CTS
+ UCHAR InvertDTR; // non-zero if invert DTR
+ UCHAR InvertDSR; // non-zero if invert DSR
+ UCHAR InvertDCD; // non-zero if invert DCD
+ UCHAR InvertRI; // non-zero if invert RI
+ // Battery Charge Detect options
+ UCHAR BCDEnable; // Enable Battery Charger Detection
+ UCHAR BCDForceCbusPWREN; // asserts the power enable signal on CBUS when charging port detected
+ UCHAR BCDDisableSleep; // forces the device never to go into sleep mode
+ // I2C options
+ WORD I2CSlaveAddress; // I2C slave device address
+ DWORD I2CDeviceId; // I2C device ID
+ UCHAR I2CDisableSchmitt; // Disable I2C Schmitt trigger
+ // FT1248 options
+ UCHAR FT1248Cpol; // FT1248 clock polarity - clock idle high (1) or clock idle low (0)
+ UCHAR FT1248Lsb; // FT1248 data is LSB (1) or MSB (0)
+ UCHAR FT1248FlowControl; // FT1248 flow control enable
+ // Hardware options
+ UCHAR RS485EchoSuppress; //
+ UCHAR PowerSaveEnable; //
+ // Driver option
+ UCHAR DriverType; //
+ } FT_EEPROM_X_SERIES, *PFT_EEPROM_X_SERIES;
+
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_EEPROM_Read(
+ FT_HANDLE ftHandle,
+ void *eepromData,
+ DWORD eepromDataSize,
+ char *Manufacturer,
+ char *ManufacturerId,
+ char *Description,
+ char *SerialNumber
+ );
+
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_EEPROM_Program(
+ FT_HANDLE ftHandle,
+ void *eepromData,
+ DWORD eepromDataSize,
+ char *Manufacturer,
+ char *ManufacturerId,
+ char *Description,
+ char *SerialNumber
+ );
+
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_SetLatencyTimer(
+ FT_HANDLE ftHandle,
+ UCHAR ucLatency
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_GetLatencyTimer(
+ FT_HANDLE ftHandle,
+ PUCHAR pucLatency
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_SetBitMode(
+ FT_HANDLE ftHandle,
+ UCHAR ucMask,
+ UCHAR ucEnable
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_GetBitMode(
+ FT_HANDLE ftHandle,
+ PUCHAR pucMode
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_SetUSBParameters(
+ FT_HANDLE ftHandle,
+ ULONG ulInTransferSize,
+ ULONG ulOutTransferSize
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_SetDeadmanTimeout(
+ FT_HANDLE ftHandle,
+ ULONG ulDeadmanTimeout
+ );
+
+#ifndef _WIN32
+ // Extra functions for non-Windows platforms to compensate
+ // for lack of .INF file to specify Vendor and Product IDs.
+
+ FTD2XX_API
+ FT_STATUS FT_SetVIDPID(
+ DWORD dwVID,
+ DWORD dwPID
+ );
+
+ FTD2XX_API
+ FT_STATUS FT_GetVIDPID(
+ DWORD * pdwVID,
+ DWORD * pdwPID
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_GetDeviceLocId(
+ FT_HANDLE ftHandle,
+ LPDWORD lpdwLocId
+ );
+#endif // _WIN32
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_GetDeviceInfo(
+ FT_HANDLE ftHandle,
+ FT_DEVICE *lpftDevice,
+ LPDWORD lpdwID,
+ PCHAR SerialNumber,
+ PCHAR Description,
+ LPVOID Dummy
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_StopInTask(
+ FT_HANDLE ftHandle
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_RestartInTask(
+ FT_HANDLE ftHandle
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_SetResetPipeRetryCount(
+ FT_HANDLE ftHandle,
+ DWORD dwCount
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_ResetPort(
+ FT_HANDLE ftHandle
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_CyclePort(
+ FT_HANDLE ftHandle
+ );
+
+
+ //
+ // Win32-type functions
+ //
+
+ FTD2XX_API
+ FT_HANDLE WINAPI FT_W32_CreateFile(
+ LPCTSTR lpszName,
+ DWORD dwAccess,
+ DWORD dwShareMode,
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ DWORD dwCreate,
+ DWORD dwAttrsAndFlags,
+ HANDLE hTemplate
+ );
+
+ FTD2XX_API
+ BOOL WINAPI FT_W32_CloseHandle(
+ FT_HANDLE ftHandle
+ );
+
+ FTD2XX_API
+ BOOL WINAPI FT_W32_ReadFile(
+ FT_HANDLE ftHandle,
+ LPVOID lpBuffer,
+ DWORD nBufferSize,
+ LPDWORD lpBytesReturned,
+ LPOVERLAPPED lpOverlapped
+ );
+
+ FTD2XX_API
+ BOOL WINAPI FT_W32_WriteFile(
+ FT_HANDLE ftHandle,
+ LPVOID lpBuffer,
+ DWORD nBufferSize,
+ LPDWORD lpBytesWritten,
+ LPOVERLAPPED lpOverlapped
+ );
+
+ FTD2XX_API
+ DWORD WINAPI FT_W32_GetLastError(
+ FT_HANDLE ftHandle
+ );
+
+ FTD2XX_API
+ BOOL WINAPI FT_W32_GetOverlappedResult(
+ FT_HANDLE ftHandle,
+ LPOVERLAPPED lpOverlapped,
+ LPDWORD lpdwBytesTransferred,
+ BOOL bWait
+ );
+
+ FTD2XX_API
+ BOOL WINAPI FT_W32_CancelIo(
+ FT_HANDLE ftHandle
+ );
+
+
+ //
+ // Win32 COMM API type functions
+ //
+ typedef struct _FTCOMSTAT {
+ DWORD fCtsHold : 1;
+ DWORD fDsrHold : 1;
+ DWORD fRlsdHold : 1;
+ DWORD fXoffHold : 1;
+ DWORD fXoffSent : 1;
+ DWORD fEof : 1;
+ DWORD fTxim : 1;
+ DWORD fReserved : 25;
+ DWORD cbInQue;
+ DWORD cbOutQue;
+ } FTCOMSTAT, *LPFTCOMSTAT;
+
+ typedef struct _FTDCB {
+ DWORD DCBlength; /* sizeof(FTDCB) */
+ DWORD BaudRate; /* Baudrate at which running */
+ DWORD fBinary: 1; /* Binary Mode (skip EOF check) */
+ DWORD fParity: 1; /* Enable parity checking */
+ DWORD fOutxCtsFlow:1; /* CTS handshaking on output */
+ DWORD fOutxDsrFlow:1; /* DSR handshaking on output */
+ DWORD fDtrControl:2; /* DTR Flow control */
+ DWORD fDsrSensitivity:1; /* DSR Sensitivity */
+ DWORD fTXContinueOnXoff: 1; /* Continue TX when Xoff sent */
+ DWORD fOutX: 1; /* Enable output X-ON/X-OFF */
+ DWORD fInX: 1; /* Enable input X-ON/X-OFF */
+ DWORD fErrorChar: 1; /* Enable Err Replacement */
+ DWORD fNull: 1; /* Enable Null stripping */
+ DWORD fRtsControl:2; /* Rts Flow control */
+ DWORD fAbortOnError:1; /* Abort all reads and writes on Error */
+ DWORD fDummy2:17; /* Reserved */
+ WORD wReserved; /* Not currently used */
+ WORD XonLim; /* Transmit X-ON threshold */
+ WORD XoffLim; /* Transmit X-OFF threshold */
+ BYTE ByteSize; /* Number of bits/byte, 4-8 */
+ BYTE Parity; /* 0-4=None,Odd,Even,Mark,Space */
+ BYTE StopBits; /* FT_STOP_BITS_1 or FT_STOP_BITS_2 */
+ char XonChar; /* Tx and Rx X-ON character */
+ char XoffChar; /* Tx and Rx X-OFF character */
+ char ErrorChar; /* Error replacement char */
+ char EofChar; /* End of Input character */
+ char EvtChar; /* Received Event character */
+ WORD wReserved1; /* Fill for now. */
+ } FTDCB, *LPFTDCB;
+
+ typedef struct _FTTIMEOUTS {
+ DWORD ReadIntervalTimeout; /* Maximum time between read chars. */
+ DWORD ReadTotalTimeoutMultiplier; /* Multiplier of characters. */
+ DWORD ReadTotalTimeoutConstant; /* Constant in milliseconds. */
+ DWORD WriteTotalTimeoutMultiplier; /* Multiplier of characters. */
+ DWORD WriteTotalTimeoutConstant; /* Constant in milliseconds. */
+ } FTTIMEOUTS,*LPFTTIMEOUTS;
+
+
+ FTD2XX_API
+ BOOL WINAPI FT_W32_ClearCommBreak(
+ FT_HANDLE ftHandle
+ );
+
+ FTD2XX_API
+ BOOL WINAPI FT_W32_ClearCommError(
+ FT_HANDLE ftHandle,
+ LPDWORD lpdwErrors,
+ LPFTCOMSTAT lpftComstat
+ );
+
+ FTD2XX_API
+ BOOL WINAPI FT_W32_EscapeCommFunction(
+ FT_HANDLE ftHandle,
+ DWORD dwFunc
+ );
+
+ FTD2XX_API
+ BOOL WINAPI FT_W32_GetCommModemStatus(
+ FT_HANDLE ftHandle,
+ LPDWORD lpdwModemStatus
+ );
+
+ FTD2XX_API
+ BOOL WINAPI FT_W32_GetCommState(
+ FT_HANDLE ftHandle,
+ LPFTDCB lpftDcb
+ );
+
+ FTD2XX_API
+ BOOL WINAPI FT_W32_GetCommTimeouts(
+ FT_HANDLE ftHandle,
+ FTTIMEOUTS *pTimeouts
+ );
+
+ FTD2XX_API
+ BOOL WINAPI FT_W32_PurgeComm(
+ FT_HANDLE ftHandle,
+ DWORD dwMask
+ );
+
+ FTD2XX_API
+ BOOL WINAPI FT_W32_SetCommBreak(
+ FT_HANDLE ftHandle
+ );
+
+ FTD2XX_API
+ BOOL WINAPI FT_W32_SetCommMask(
+ FT_HANDLE ftHandle,
+ ULONG ulEventMask
+ );
+
+ FTD2XX_API
+ BOOL WINAPI FT_W32_GetCommMask(
+ FT_HANDLE ftHandle,
+ LPDWORD lpdwEventMask
+ );
+
+ FTD2XX_API
+ BOOL WINAPI FT_W32_SetCommState(
+ FT_HANDLE ftHandle,
+ LPFTDCB lpftDcb
+ );
+
+ FTD2XX_API
+ BOOL WINAPI FT_W32_SetCommTimeouts(
+ FT_HANDLE ftHandle,
+ FTTIMEOUTS *pTimeouts
+ );
+
+ FTD2XX_API
+ BOOL WINAPI FT_W32_SetupComm(
+ FT_HANDLE ftHandle,
+ DWORD dwReadBufferSize,
+ DWORD dwWriteBufferSize
+ );
+
+ FTD2XX_API
+ BOOL WINAPI FT_W32_WaitCommEvent(
+ FT_HANDLE ftHandle,
+ PULONG pulEvent,
+ LPOVERLAPPED lpOverlapped
+ );
+
+
+ //
+ // Device information
+ //
+
+ typedef struct _ft_device_list_info_node {
+ ULONG Flags;
+ ULONG Type;
+ ULONG ID;
+ DWORD LocId;
+ char SerialNumber[16];
+ char Description[64];
+ FT_HANDLE ftHandle;
+ } FT_DEVICE_LIST_INFO_NODE;
+
+ // Device information flags
+ enum {
+ FT_FLAGS_OPENED = 1,
+ FT_FLAGS_HISPEED = 2
+ };
+
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_CreateDeviceInfoList(
+ LPDWORD lpdwNumDevs
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_GetDeviceInfoList(
+ FT_DEVICE_LIST_INFO_NODE *pDest,
+ LPDWORD lpdwNumDevs
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_GetDeviceInfoDetail(
+ DWORD dwIndex,
+ LPDWORD lpdwFlags,
+ LPDWORD lpdwType,
+ LPDWORD lpdwID,
+ LPDWORD lpdwLocId,
+ LPVOID lpSerialNumber,
+ LPVOID lpDescription,
+ FT_HANDLE *pftHandle
+ );
+
+
+ //
+ // Version information
+ //
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_GetDriverVersion(
+ FT_HANDLE ftHandle,
+ LPDWORD lpdwVersion
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_GetLibraryVersion(
+ LPDWORD lpdwVersion
+ );
+
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_Rescan(
+ void
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_Reload(
+ WORD wVid,
+ WORD wPid
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_GetComPortNumber(
+ FT_HANDLE ftHandle,
+ LPLONG lpdwComPortNumber
+ );
+
+
+ //
+ // FT232H additional EEPROM functions
+ //
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_EE_ReadConfig(
+ FT_HANDLE ftHandle,
+ UCHAR ucAddress,
+ PUCHAR pucValue
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_EE_WriteConfig(
+ FT_HANDLE ftHandle,
+ UCHAR ucAddress,
+ UCHAR ucValue
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_EE_ReadECC(
+ FT_HANDLE ftHandle,
+ UCHAR ucOption,
+ LPWORD lpwValue
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_GetQueueStatusEx(
+ FT_HANDLE ftHandle,
+ DWORD *dwRxBytes
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_ComPortIdle(
+ FT_HANDLE ftHandle
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_ComPortCancelIdle(
+ FT_HANDLE ftHandle
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_VendorCmdGet(
+ FT_HANDLE ftHandle,
+ UCHAR Request,
+ UCHAR *Buf,
+ USHORT Len
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_VendorCmdSet(
+ FT_HANDLE ftHandle,
+ UCHAR Request,
+ UCHAR *Buf,
+ USHORT Len
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_VendorCmdGetEx(
+ FT_HANDLE ftHandle,
+ USHORT wValue,
+ UCHAR *Buf,
+ USHORT Len
+ );
+
+ FTD2XX_API
+ FT_STATUS WINAPI FT_VendorCmdSetEx(
+ FT_HANDLE ftHandle,
+ USHORT wValue,
+ UCHAR *Buf,
+ USHORT Len
+ );
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* FTD2XX_H */
+
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/AssemblyInfo.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/AssemblyInfo.cs
new file mode 100644
index 0000000000..00e534e733
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/AssemblyInfo.cs
@@ -0,0 +1,4 @@
+
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("FT232.Unit.Tests")]
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Exceptions.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Exceptions.cs
new file mode 100644
index 0000000000..b991346e09
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Exceptions.cs
@@ -0,0 +1,33 @@
+using System;
+
+namespace Meadow.Foundation.ICs.IOExpanders;
+
+///
+/// Represents an exception thrown when no FT232 device is found during a connection check.
+///
+public class DeviceNotFoundException : Exception
+{
+ ///
+ /// Initializes a new instance of the class
+ /// with a default error message instructing to check the connection.
+ ///
+ internal DeviceNotFoundException()
+ : base("No FT232 device found. Check your connection")
+ {
+ }
+}
+
+///
+/// Represents an exception thrown when the Ftd2xx driver is not installed for device operation in Ftd2xx mode.
+///
+public class DriverNotInstalledException : Exception
+{
+ ///
+ /// Initializes a new instance of the class
+ /// with a default error message indicating that the Ftd2xx driver must be installed.
+ ///
+ internal DriverNotInstalledException()
+ : base("The Ftd2xx driver must be installed to use the device in Ftd2xx mode.")
+ {
+ }
+}
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232DigitalOutputPort.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232DigitalOutputPort.cs
index 6c20155878..0ef7b5ec56 100644
--- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232DigitalOutputPort.cs
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232DigitalOutputPort.cs
@@ -6,20 +6,20 @@ namespace Meadow.Foundation.ICs.IOExpanders
///
/// Digital output port for FT232 devices.
///
- public sealed class Ft232DigitalOutputPort : DigitalOutputPortBase
+ public sealed class MpsseDigitalOutputPort : DigitalOutputPortBase
{
private readonly IFt232Bus _bus;
private bool _state;
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// The pin to use.
/// The digital channel info.
/// The initial state of the output port.
/// The initial output type.
/// The FT232 bus.
- internal Ft232DigitalOutputPort(IPin pin, IDigitalChannelInfo info, bool initialState, OutputType initialOutputType, IFt232Bus bus)
+ internal MpsseDigitalOutputPort(IPin pin, IDigitalChannelInfo info, bool initialState, OutputType initialOutputType, IFt232Bus bus)
: base(pin, info, initialState, initialOutputType)
{
if (initialOutputType != OutputType.PushPull)
@@ -50,10 +50,10 @@ public override bool State
}
else
{
- s &= (byte)~((byte)Pin.Key);
+ s &= (byte)~(byte)Pin.Key;
}
- var result = Native.Functions.FT_WriteGPIO(_bus.Handle, _bus.GpioDirectionMask, s);
+ var result = Native.Mpsse.FT_WriteGPIO(_bus.Handle, _bus.GpioDirectionMask, s);
Native.CheckStatus(result);
_bus.GpioState = s;
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232I2cBus.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232I2cBus.cs
deleted file mode 100644
index 5cd0a02b6b..0000000000
--- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232I2cBus.cs
+++ /dev/null
@@ -1,165 +0,0 @@
-using Meadow.Hardware;
-using System;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using static Meadow.Foundation.ICs.IOExpanders.Native;
-
-namespace Meadow.Foundation.ICs.IOExpanders
-{
- ///
- /// Represents an I2C bus using the FT232H USB to I2C bridge.
- ///
- public sealed class Ft232I2cBus : IFt232Bus, II2cBus, IDisposable
- {
- private const byte DefaultLatencyTimer = 10;
- private const I2CChannelOptions DefaultChannelOptions = I2CChannelOptions.None;
-
- private bool _isDisposed;
-
- ///
- /// Gets the handle for the FT232H I2C bus.
- ///
- public IntPtr Handle { get; private set; }
-
- ///
- /// Gets or sets the GPIO direction mask for the FT232H I2C bus.
- ///
- public byte GpioDirectionMask { get; set; }
-
- ///
- /// Gets or sets the GPIO state for the FT232H I2C bus.
- ///
- public byte GpioState { get; set; }
-
- internal bool IsOpen { get; private set; } = false;
- internal int ChannelNumber { get; }
- private FT_DEVICE_LIST_INFO_NODE InfoNode { get; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- internal Ft232I2cBus(int channelNumber, FT_DEVICE_LIST_INFO_NODE info)
- {
- ChannelNumber = channelNumber;
- InfoNode = info;
- }
-
- ///
- /// Gets or sets the bus speed for the FT232H I2C bus.
- ///
- public I2cBusSpeed BusSpeed { get; set; }
-
- private void Dispose(bool disposing)
- {
- if (!_isDisposed)
- {
- CloseChannel();
-
- _isDisposed = true;
- }
- }
-
- ///
- /// Finalizes an instance of the class.
- ///
- ~Ft232I2cBus()
- {
- // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
- Dispose(false);
- }
-
- ///
- /// Releases the unmanaged resources used by the object.
- ///
- public void Dispose()
- {
- // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- internal void Open(I2CClockRate clockRate = I2CClockRate.Standard)
- {
- if (CheckStatus(Functions.I2C_OpenChannel(ChannelNumber, out IntPtr handle)))
- {
- Handle = handle;
-
- var config = new I2CChannelConfig
- {
- ClockRate = clockRate,
- LatencyTimer = DefaultLatencyTimer,
- Options = DefaultChannelOptions
- };
-
- CheckStatus(Functions.I2C_InitChannel(Handle, ref config));
-
- IsOpen = true;
- }
- }
-
- private void CloseChannel()
- {
- if (Handle != IntPtr.Zero)
- {
- CheckStatus(Functions.I2C_CloseChannel(Handle));
- Handle = IntPtr.Zero;
- }
- }
-
- ///
- /// Exchanges data with a peripheral on the I2C bus.
- ///
- /// The address of the peripheral device.
- /// The data to write to the peripheral.
- /// The data to read from the peripheral.
- public void Exchange(byte peripheralAddress, Span writeBuffer, Span readBuffer)
- {
- Write(peripheralAddress, writeBuffer);
- Read(peripheralAddress, readBuffer);
- }
-
- ///
- /// Reads data from a peripheral on the I2C bus.
- ///
- /// The address of the peripheral device.
- /// The buffer to store the read data.
- public void Read(byte peripheralAddress, Span readBuffer)
- {
- var status = Functions.I2C_DeviceRead(
- Handle,
- peripheralAddress,
- readBuffer.Length,
- MemoryMarshal.GetReference(readBuffer),
- out int transferred,
- I2CTransferOptions.FAST_TRANSFER | I2CTransferOptions.FAST_TRANSFER_BYTES
- //I2CTransferOptions.START_BIT | I2CTransferOptions.STOP_BIT | I2CTransferOptions.NACK_LAST_BYTE
- //I2CTransferOptions.START_BIT | I2CTransferOptions.STOP_BIT | I2CTransferOptions.FAST_TRANSFER | I2CTransferOptions.NACK_LAST_BYTE
- );
-
- Debug.WriteLine($"transferred: {transferred}");
- CheckStatus(status);
- }
-
- ///
- /// Writes data to a peripheral on the I2C bus.
- ///
- /// The address of the peripheral device.
- /// The data to write to the peripheral.
- public void Write(byte peripheralAddress, Span writeBuffer)
- {
- var status = Functions.I2C_DeviceWrite(
- Handle,
- peripheralAddress,
- writeBuffer.Length,
- MemoryMarshal.GetReference(writeBuffer),
- out int transferred,
- I2CTransferOptions.FAST_TRANSFER | I2CTransferOptions.FAST_TRANSFER_BYTES
- //I2CTransferOptions.START_BIT | I2CTransferOptions.BREAK_ON_NACK
- //I2CTransferOptions.START_BIT | I2CTransferOptions.STOP_BIT | I2CTransferOptions.NACK_LAST_BYTE
- );
-
- Debug.WriteLine($"transferred: {transferred}");
- // CheckStatus(status);
- }
- }
-}
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232SpiBus.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232SpiBus.cs
index a5acd246f8..0dfeab01b4 100644
--- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232SpiBus.cs
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232SpiBus.cs
@@ -5,288 +5,273 @@
using static Meadow.Foundation.ICs.IOExpanders.Ft232h;
using static Meadow.Foundation.ICs.IOExpanders.Native;
-namespace Meadow.Foundation.ICs.IOExpanders
+namespace Meadow.Foundation.ICs.IOExpanders;
+
+///
+/// Represents an SPI bus implementation using the FT232 device.
+///
+public sealed class Ft232SpiBus : IFt232Bus, ISpiBus, IDisposable
{
///
- /// Represents an SPI bus using the FT232H USB to SPI bridge.
+ /// The default SPI clock rate for the FT232H
///
- public sealed class Ft232SpiBus : IFt232Bus, ISpiBus, IDisposable
- {
- ///
- /// The default clock rate for the FT232 SPI bus.
- ///
- public const uint DefaultClockRate = 25000000;
+ public const uint DefaultClockRate = 25000000;
+ private const byte DefaultLatencyTimer = 10; // from the FTDI sample
- private const byte DefaultLatencyTimer = 10; // from the FTDI sample
+ private bool _isDisposed;
- private bool _isDisposed;
+ private SpiClockConfiguration _config = default!;
+ private SpiChannelConfig _channelConfig;
- private SpiClockConfiguration _config;
- private SpiChannelConfig _channelConfig;
+ ///
+ public IntPtr Handle { get; private set; }
+ ///
+ public byte GpioDirectionMask { get; set; }
+ ///
+ public byte GpioState { get; set; }
+ internal bool IsOpen { get; private set; } = false;
+ internal int ChannelNumber { get; }
+ private FT_DEVICE_LIST_INFO_NODE InfoNode { get; }
- ///
- /// Gets the handle for the FT232H SPI bus.
- ///
- public IntPtr Handle { get; private set; }
+ ///
+ public Frequency[] SupportedSpeeds => new Frequency[] { new Frequency(30d, Frequency.UnitType.Megahertz) };
- ///
- /// Gets or sets the GPIO direction mask for the FT232H SPI bus.
- ///
- public byte GpioDirectionMask { get; set; }
+ internal Ft232SpiBus(int channelNumber, FT_DEVICE_LIST_INFO_NODE info)
+ {
+ ChannelNumber = channelNumber;
+ InfoNode = info;
+ }
- ///
- /// Gets or sets the GPIO state for the FT232H SPI bus.
- ///
- public byte GpioState { get; set; }
+ private void Dispose(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+ CloseChannel();
- internal bool IsOpen { get; private set; } = false;
- internal int ChannelNumber { get; }
- private FT_DEVICE_LIST_INFO_NODE InfoNode { get; }
+ _isDisposed = true;
+ }
+ }
- ///
- /// Gets the supported SPI bus speeds for the FT232H SPI bus.
- ///
- public Frequency[] SupportedSpeeds => new Frequency[] { new Frequency(30d, Frequency.UnitType.Megahertz) };
+ ///
+ /// Finalizer for the Ft232SpiBus class, used to release unmanaged resources.
+ ///
+ ~Ft232SpiBus()
+ {
+ // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
+ Dispose(false);
+ }
- internal Ft232SpiBus(int channelNumber, FT_DEVICE_LIST_INFO_NODE info)
+ ///
+ public SpiClockConfiguration Configuration
+ {
+ get { return _config; }
+ set
{
- ChannelNumber = channelNumber;
- InfoNode = info;
+ _channelConfig = CreateChannelConfig(value);
+ _config = value;
+ this.Configuration.Changed += OnConfigurationChanged;
}
+ }
- private void Dispose(bool disposing)
- {
- if (!_isDisposed)
- {
- CloseChannel();
-
- _isDisposed = true;
- }
- }
+ private void OnConfigurationChanged(object sender, EventArgs e)
+ {
+ var changed = false;
- ///
- /// Finalizes an instance of the class.
- ///
- ~Ft232SpiBus()
+ if (Configuration.Speed.Hertz != _channelConfig.ClockRate)
{
- // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
- Dispose(false);
+ _channelConfig.ClockRate = (uint)Configuration.Speed.Hertz;
+ changed = true;
}
- ///
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- ///
- public void Dispose()
+ switch (Configuration.SpiMode)
{
- // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
- Dispose(true);
- GC.SuppressFinalize(this);
+ case SpiClockConfiguration.Mode.Mode0:
+ if ((_channelConfig.Options & SpiConfigOptions.MODE3) != SpiConfigOptions.MODE0)
+ {
+ _channelConfig.Options |= SpiConfigOptions.MODE0;
+ changed = true;
+ }
+ break;
+ case SpiClockConfiguration.Mode.Mode1:
+ if ((_channelConfig.Options & SpiConfigOptions.MODE3) != SpiConfigOptions.MODE1)
+ {
+ _channelConfig.Options = (_channelConfig.Options & ~SpiConfigOptions.MODE3) | SpiConfigOptions.MODE1;
+ changed = true;
+ }
+ break;
+ case SpiClockConfiguration.Mode.Mode2:
+ if ((_channelConfig.Options & SpiConfigOptions.MODE3) != SpiConfigOptions.MODE2)
+ {
+ _channelConfig.Options = (_channelConfig.Options & ~SpiConfigOptions.MODE3) | SpiConfigOptions.MODE2;
+ changed = true;
+ }
+ break;
+ case SpiClockConfiguration.Mode.Mode3:
+ if ((_channelConfig.Options & SpiConfigOptions.MODE3) != SpiConfigOptions.MODE3)
+ {
+ _channelConfig.Options = (_channelConfig.Options & ~SpiConfigOptions.MODE3) | SpiConfigOptions.MODE3;
+ changed = true;
+ }
+ break;
}
- internal void Open(SpiClockConfiguration config)
+ if (changed)
{
- Configuration = config;
+ CheckStatus(Mpsse.SPI_InitChannel(Handle, ref _channelConfig));
+ }
+ }
- if (CheckStatus(Functions.SPI_OpenChannel(ChannelNumber, out IntPtr handle)))
- {
- Handle = handle;
+ ///
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
- CheckStatus(Functions.SPI_InitChannel(Handle, ref _channelConfig));
- }
- }
+ internal void Open(SpiClockConfiguration config)
+ {
+ Configuration = config;
- private void CloseChannel()
+ if (CheckStatus(Mpsse.SPI_OpenChannel(ChannelNumber, out IntPtr handle)))
{
- if (Handle != IntPtr.Zero)
- {
- CheckStatus(Functions.SPI_CloseChannel(Handle));
- Handle = IntPtr.Zero;
- }
+ Handle = handle;
+
+ CheckStatus(Mpsse.SPI_InitChannel(Handle, ref _channelConfig));
}
+ }
- private SpiChannelConfig CreateChannelConfig(SpiClockConfiguration config)
+ private void CloseChannel()
+ {
+ if (Handle != IntPtr.Zero)
{
- // For now, we support CS on D3 and that's it
- Ft232h.SpiConfigOptions opts = SpiConfigOptions.CS_ACTIVELOW | SpiConfigOptions.CS_DBUS3;
-
- switch (config.SpiMode)
- {
- case SpiClockConfiguration.Mode.Mode0:
- opts = SpiConfigOptions.MODE0;
- break;
- case SpiClockConfiguration.Mode.Mode1:
- opts = SpiConfigOptions.MODE1;
- break;
- case SpiClockConfiguration.Mode.Mode2:
- opts = SpiConfigOptions.MODE2;
- break;
- case SpiClockConfiguration.Mode.Mode3:
- opts = SpiConfigOptions.MODE3;
- break;
- }
-
- return new SpiChannelConfig
- {
- ClockRate = (uint)config.Speed.Hertz,
- LatencyTimer = DefaultLatencyTimer,
- Options = opts
- };
+ CheckStatus(Mpsse.SPI_CloseChannel(Handle));
+ Handle = IntPtr.Zero;
}
+ }
- private SPITransferOptions CreateTransferOptions(ChipSelectMode mode)
+ private SpiChannelConfig CreateChannelConfig(SpiClockConfiguration config)
+ {
+ // for now we support CS on D3 and that's it
+ Ft232h.SpiConfigOptions opts = SpiConfigOptions.CS_ACTIVELOW | SpiConfigOptions.CS_DBUS3;
+
+ switch (config.SpiMode)
{
- SPITransferOptions opts = SPITransferOptions.SIZE_IN_BYTES;
-
- switch (mode)
- {
- case ChipSelectMode.ActiveLow:
- opts |= SPITransferOptions.CHIPSELECT_DISABLE;
- break;
- case ChipSelectMode.ActiveHigh:
- opts |= SPITransferOptions.CHIPSELECT_ENABLE;
- break;
- }
-
- return opts;
+ case SpiClockConfiguration.Mode.Mode0:
+ opts = SpiConfigOptions.MODE0;
+ break;
+ case SpiClockConfiguration.Mode.Mode1:
+ opts = SpiConfigOptions.MODE1;
+ break;
+ case SpiClockConfiguration.Mode.Mode2:
+ opts = SpiConfigOptions.MODE2;
+ break;
+ case SpiClockConfiguration.Mode.Mode3:
+ opts = SpiConfigOptions.MODE3;
+ break;
}
- ///
- /// Reads data from a device on the SPI bus.
- ///
- /// The digital output port representing the chip select line.
- /// The buffer to store the read data.
- /// The chip select mode (active low or active high).
- public void Read(IDigitalOutputPort chipSelect, Span readBuffer, ChipSelectMode csMode = ChipSelectMode.ActiveLow)
+ return new SpiChannelConfig
{
- var options = CreateTransferOptions(csMode);
+ ClockRate = (uint)config.Speed.Hertz,
+ LatencyTimer = DefaultLatencyTimer,
+ Options = opts
+ };
+ }
- chipSelect.State = csMode == ChipSelectMode.ActiveLow ? false : true;
+ private SPITransferOptions CreateTransferOptions(ChipSelectMode mode)
+ {
+ SPITransferOptions opts = SPITransferOptions.SIZE_IN_BYTES;
- var status = Functions.SPI_Read(
- Handle,
- MemoryMarshal.GetReference(readBuffer),
- readBuffer.Length,
- out _,
- options
- );
+ switch (mode)
+ {
+ case ChipSelectMode.ActiveLow:
+ opts |= SPITransferOptions.CHIPSELECT_DISABLE;
+ break;
+ case ChipSelectMode.ActiveHigh:
+ opts |= SPITransferOptions.CHIPSELECT_ENABLE;
+ break;
+ }
- chipSelect.State = csMode == ChipSelectMode.ActiveLow ? true : false;
+ return opts;
+ }
- CheckStatus(status);
- }
+ ///
+ public void Read(IDigitalOutputPort? chipSelect, Span readBuffer, ChipSelectMode csMode = ChipSelectMode.ActiveLow)
+ {
+ var options = CreateTransferOptions(csMode);
- ///
- /// Writes data to a device on the SPI bus.
- ///
- /// The digital output port representing the chip select line.
- /// The data to write to the device.
- /// The chip select mode (active low or active high).
- public void Write(IDigitalOutputPort chipSelect, Span writeBuffer, ChipSelectMode csMode = ChipSelectMode.ActiveLow)
+ if (chipSelect != null)
{
- var options = CreateTransferOptions(csMode);
-
chipSelect.State = csMode == ChipSelectMode.ActiveLow ? false : true;
+ }
- var status = Functions.SPI_Write(
- Handle,
- MemoryMarshal.GetReference(writeBuffer),
- writeBuffer.Length,
- out _,
- options
+ var status = Mpsse.SPI_Read(
+ Handle,
+ MemoryMarshal.GetReference(readBuffer),
+ readBuffer.Length,
+ out _,
+ options
);
+ if (chipSelect != null)
+ {
chipSelect.State = csMode == ChipSelectMode.ActiveLow ? true : false;
-
- CheckStatus(status);
}
- ///
- /// Exchanges data with a device on the SPI bus.
- ///
- /// The digital output port representing the chip select line.
- /// The data to write to the device.
- /// The buffer to store the read data.
- /// The chip select mode (active low or active high).
- public void Exchange(IDigitalOutputPort chipSelect, Span writeBuffer, Span readBuffer, ChipSelectMode csMode = ChipSelectMode.ActiveLow)
- {
- var options = CreateTransferOptions(csMode);
+ CheckStatus(status);
+ }
+ ///
+ public void Write(IDigitalOutputPort? chipSelect, Span writeBuffer, ChipSelectMode csMode = ChipSelectMode.ActiveLow)
+ {
+ var options = CreateTransferOptions(csMode);
+
+ if (chipSelect != null)
+ {
chipSelect.State = csMode == ChipSelectMode.ActiveLow ? false : true;
+ }
- var status = Functions.SPI_ReadWrite(
- Handle,
- MemoryMarshal.GetReference(readBuffer),
- MemoryMarshal.GetReference(writeBuffer),
- writeBuffer.Length,
- out _,
- options
+ var status = Mpsse.SPI_Write(
+ Handle,
+ MemoryMarshal.GetReference(writeBuffer),
+ writeBuffer.Length,
+ out _,
+ options
);
+ if (chipSelect != null)
+ {
chipSelect.State = csMode == ChipSelectMode.ActiveLow ? true : false;
-
- CheckStatus(status);
}
- ///
- /// Gets or sets the SPI bus configuration.
- ///
- public SpiClockConfiguration Configuration
+ CheckStatus(status);
+ }
+
+ ///
+ public void Exchange(IDigitalOutputPort? chipSelect, Span writeBuffer, Span readBuffer, ChipSelectMode csMode = ChipSelectMode.ActiveLow)
+ {
+ var options = CreateTransferOptions(csMode);
+
+ if (chipSelect != null)
{
- get { return _config; }
- set
- {
- _channelConfig = CreateChannelConfig(value);
- _config = value;
- this.Configuration.Changed += OnConfigurationChanged;
- }
+ chipSelect.State = csMode == ChipSelectMode.ActiveLow ? false : true;
}
- private void OnConfigurationChanged(object sender, EventArgs e)
+ var status = Mpsse.SPI_ReadWrite(
+ Handle,
+ MemoryMarshal.GetReference(readBuffer),
+ MemoryMarshal.GetReference(writeBuffer),
+ writeBuffer.Length,
+ out _,
+ options
+ );
+
+ if (chipSelect != null)
{
- var changed = false;
-
- if (Configuration.Speed.Hertz != _channelConfig.ClockRate)
- {
- _channelConfig.ClockRate = (uint)Configuration.Speed.Hertz;
- changed = true;
- }
-
- switch (Configuration.SpiMode)
- {
- case SpiClockConfiguration.Mode.Mode0:
- if ((_channelConfig.Options & SpiConfigOptions.MODE3) != SpiConfigOptions.MODE0)
- {
- _channelConfig.Options |= SpiConfigOptions.MODE0;
- changed = true;
- }
- break;
- case SpiClockConfiguration.Mode.Mode1:
- if ((_channelConfig.Options & SpiConfigOptions.MODE3) != SpiConfigOptions.MODE1)
- {
- _channelConfig.Options = (_channelConfig.Options & ~SpiConfigOptions.MODE3) | SpiConfigOptions.MODE1;
- changed = true;
- }
- break;
- case SpiClockConfiguration.Mode.Mode2:
- if ((_channelConfig.Options & SpiConfigOptions.MODE3) != SpiConfigOptions.MODE2)
- {
- _channelConfig.Options = (_channelConfig.Options & ~SpiConfigOptions.MODE3) | SpiConfigOptions.MODE2;
- changed = true;
- }
- break;
- case SpiClockConfiguration.Mode.Mode3:
- if ((_channelConfig.Options & SpiConfigOptions.MODE3) != SpiConfigOptions.MODE3)
- {
- _channelConfig.Options = (_channelConfig.Options & ~SpiConfigOptions.MODE3) | SpiConfigOptions.MODE3;
- changed = true;
- }
- break;
- }
-
- if (changed)
- {
- CheckStatus(Functions.SPI_InitChannel(Handle, ref _channelConfig));
- }
+ chipSelect.State = csMode == ChipSelectMode.ActiveLow ? true : false;
}
+
+ CheckStatus(status);
}
-}
+}
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232h.PinDefinitions.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232h.PinDefinitions.cs
index 2773ee9e05..7839cec451 100644
--- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232h.PinDefinitions.cs
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232h.PinDefinitions.cs
@@ -31,12 +31,15 @@ public class PinDefinitions : IPinDefinitions
///
public IPinController? Controller { get; set; }
+ private bool UseMpsseKeys { get; }
+
///
/// Creates a new PinDefinitions object.
///
/// The Ft232h controller associated with the pins.
internal PinDefinitions(Ft232h controller)
{
+ UseMpsseKeys = controller.UsingMpsse;
Controller = controller;
InitAllPins();
}
@@ -114,12 +117,54 @@ internal PinDefinitions(Ft232h controller)
public IPin D3 => new Pin(
Controller,
"D3",
- (byte)0x12,
+ (byte)0x13,
new List {
new SpiChannelInfo("SPI_CS0", SpiLineType.ChipSelect)
});
- // TODO: D4-D7 can be used as CS, and (probably??) GPIO. The docs are not terribly clear on this. Maybe just outputs and direct write the CS?
+ ///
+ /// Pin D4 definition.
+ ///
+ public IPin D4 => new Pin(
+ Controller,
+ "D4",
+ UseMpsseKeys ? (byte)0x14 : (ushort)(1 << 4),
+ new List {
+ new DigitalChannelInfo("D4", interruptCapable: false, pullUpCapable: false, pullDownCapable: false)
+ });
+
+ ///
+ /// Pin D5 definition.
+ ///
+ public IPin D5 => new Pin(
+ Controller,
+ "D5",
+ UseMpsseKeys ? (byte)0x15 : (ushort)(1 << 5),
+ new List {
+ new DigitalChannelInfo("D5", interruptCapable: false, pullUpCapable: false, pullDownCapable: false)
+ });
+
+ ///
+ /// Pin D6 definition.
+ ///
+ public IPin D6 => new Pin(
+ Controller,
+ "D6",
+ UseMpsseKeys ? (byte)0x16 : (ushort)(1 << 6),
+ new List {
+ new DigitalChannelInfo("D6", interruptCapable: false, pullUpCapable: false, pullDownCapable: false)
+ });
+
+ ///
+ /// Pin D7 definition.
+ ///
+ public IPin D7 => new Pin(
+ Controller,
+ "D7",
+ UseMpsseKeys ? (byte)0x17 : (ushort)(1 << 7),
+ new List {
+ new DigitalChannelInfo("D7", interruptCapable: false, pullUpCapable: false, pullDownCapable: false)
+ });
///
/// Pin SPI_COPI_D1 definition.
@@ -138,7 +183,7 @@ internal PinDefinitions(Ft232h controller)
public IPin C0 => new Pin(
Controller,
"C0",
- (byte)(1 << 0),
+ UseMpsseKeys ? (byte)(1 << 0) : (ushort)(1 << 8),
new List {
new DigitalChannelInfo("C0", interruptCapable: false, pullUpCapable: false, pullDownCapable: false)
});
@@ -149,7 +194,7 @@ internal PinDefinitions(Ft232h controller)
public IPin C1 => new Pin(
Controller,
"C1",
- (byte)(1 << 1),
+ UseMpsseKeys ? (byte)(1 << 1) : (ushort)(1 << 9),
new List {
new DigitalChannelInfo("C1", interruptCapable: false, pullUpCapable: false, pullDownCapable: false)
});
@@ -160,7 +205,7 @@ internal PinDefinitions(Ft232h controller)
public IPin C2 => new Pin(
Controller,
"C2",
- (byte)(1 << 2),
+ UseMpsseKeys ? (byte)(1 << 2) : (ushort)(1 << 10),
new List {
new DigitalChannelInfo("C2", interruptCapable: false, pullUpCapable: false, pullDownCapable: false)
});
@@ -171,7 +216,7 @@ internal PinDefinitions(Ft232h controller)
public IPin C3 => new Pin(
Controller,
"C3",
- (byte)(1 << 3),
+ UseMpsseKeys ? (byte)(1 << 3) : (ushort)(1 << 11),
new List {
new DigitalChannelInfo("C3", interruptCapable: false, pullUpCapable: false, pullDownCapable: false)
});
@@ -182,7 +227,7 @@ internal PinDefinitions(Ft232h controller)
public IPin C4 => new Pin(
Controller,
"C4",
- (byte)(1 << 4),
+ UseMpsseKeys ? (byte)(1 << 4) : (ushort)(1 << 12),
new List {
new DigitalChannelInfo("C4", interruptCapable: false, pullUpCapable: false, pullDownCapable: false)
});
@@ -193,7 +238,7 @@ internal PinDefinitions(Ft232h controller)
public IPin C5 => new Pin(
Controller,
"C5",
- (byte)(1 << 5),
+ UseMpsseKeys ? (byte)(1 << 5) : (ushort)(1 << 13),
new List {
new DigitalChannelInfo("C5", interruptCapable: false, pullUpCapable: false, pullDownCapable: false)
});
@@ -204,7 +249,7 @@ internal PinDefinitions(Ft232h controller)
public IPin C6 => new Pin(
Controller,
"C6",
- (byte)(1 << 6),
+ UseMpsseKeys ? (byte)(1 << 6) : (ushort)(1 << 14),
new List {
new DigitalChannelInfo("C6", interruptCapable: false, pullUpCapable: false, pullDownCapable: false)
});
@@ -215,7 +260,7 @@ internal PinDefinitions(Ft232h controller)
public IPin C7 => new Pin(
Controller,
"C7",
- (byte)(1 << 7),
+ UseMpsseKeys ? (byte)(1 << 7) : (ushort)(1 << 15),
new List {
new DigitalChannelInfo("C7", interruptCapable: false, pullUpCapable: false, pullDownCapable: false)
});
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232h.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232h.cs
index 3b1ee09a07..b80a3f53f2 100644
--- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232h.cs
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232h.cs
@@ -1,330 +1,159 @@
using Meadow.Hardware;
using Meadow.Units;
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-namespace Meadow.Foundation.ICs.IOExpanders
+namespace Meadow.Foundation.ICs.IOExpanders;
+
+///
+/// Represents a DS3502 digital potentiometer
+///
+public partial class Ft232h :
+ IDisposable,
+ IDigitalInputOutputController,
+ IDigitalOutputController,
+ ISpiController,
+ II2cController
{
- ///
- /// Represents an FT232H IO expander
- ///
- public partial class Ft232h :
- IDisposable,
- IDigitalInputOutputController,
- IDigitalOutputController,
- ISpiController,
- II2cController
- {
- private bool _isDisposed;
- private static int _instanceCount = 0;
- private Dictionary _i2cBuses = new Dictionary();
- private Dictionary _spiBuses = new Dictionary();
- private IFt232Bus? _activeBus = null;
-
- static Ft232h()
- {
- if (Interlocked.Increment(ref _instanceCount) == 1)
- {
- // only do this one time (no matter how many instances are created instances)
- Native.Functions.Init_libMPSSE();
- }
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- public Ft232h()
- {
- Pins = new PinDefinitions(this);
- EnumerateBuses();
- }
-
- ///
- /// The pins
- ///
- public PinDefinitions Pins { get; }
-
- private void EnumerateBuses()
- {
- _i2cBuses = GetI2CBuses();
- _spiBuses = GetSpiBuses();
- }
-
- private Dictionary GetI2CBuses()
- {
- Dictionary result = new Dictionary();
-
- if (Native.CheckStatus(Native.Functions.I2C_GetNumChannels(out int channels)))
- {
- if (channels > 0)
- {
- for (var c = 0; c < channels; c++)
- {
- if (Native.CheckStatus(Native.Functions.I2C_GetChannelInfo(c, out Native.FT_DEVICE_LIST_INFO_NODE info)))
- {
- result.Add(c, new Ft232I2cBus(c, info));
- }
- }
- }
- }
-
- return result;
- }
-
- private Dictionary GetSpiBuses()
- {
- Dictionary result = new Dictionary();
-
- if (Native.CheckStatus(Native.Functions.SPI_GetNumChannels(out int channels)))
- {
- if (channels > 0)
- {
- for (var c = 0; c < channels; c++)
- {
- if (Native.CheckStatus(Native.Functions.SPI_GetChannelInfo(c, out Native.FT_DEVICE_LIST_INFO_NODE info)))
- {
- result.Add(c, new Ft232SpiBus(c, info));
- }
- }
- }
- }
-
- return result;
- }
-
- ///
- /// Creates an I2C bus with the specified bus number and clock rate.
- ///
- /// The bus number.
- /// The I2C bus.
- public II2cBus CreateI2cBus(int busNumber = 0)
- {
- return CreateI2cBus(busNumber, I2CClockRate.Standard);
- }
+ private bool _isDisposed;
+ private IFtdiImpl _impl;
- ///
- /// Creates an I2C bus with the specified bus number and clock rate.
- ///
- /// The bus number.
- /// The I2C bus speed.
- /// The I2C bus.
- public II2cBus CreateI2cBus(int busNumber, I2cBusSpeed busSpeed)
- {
- // TODO: convert frequency
- return CreateI2cBus(busNumber, I2CClockRate.Standard);
- }
-
- ///
- /// Creates an I2C bus with the specified pins and bus speed.
- ///
- /// The I2C clock and data pins.
- /// The I2C bus speed.
- /// The I2C bus.
- public II2cBus CreateI2cBus(IPin[] pins, I2cBusSpeed busSpeed)
- {
- // TODO: map the pins to the bus number
- // TODO: convert frequency
- return CreateI2cBus(0, I2CClockRate.Standard);
- }
-
- ///
- /// Creates an I2C bus with the specified clock and data pins and bus speed.
- ///
- /// The clock pin.
- /// The data pin.
- /// The I2C bus speed.
- /// The I2C bus.
- public II2cBus CreateI2cBus(IPin clock, IPin data, I2cBusSpeed busSpeed)
- {
- // TODO: map the pins to the bus number
- // TODO: convert frequency
- return CreateI2cBus(0, I2CClockRate.Standard);
- }
-
- private II2cBus CreateI2cBus(int busNumber, I2CClockRate clock)
- {
- if (_activeBus != null)
- {
- throw new InvalidOperationException("The FT232 allows only one bus to be active at a time.");
- }
-
- if (_i2cBuses.Count == 0)
- {
- throw new InvalidOperationException("No I2C Busses found! Is the FT232 properly connected?");
- }
-
- if (!_i2cBuses.ContainsKey(busNumber)) throw new ArgumentOutOfRangeException(nameof(busNumber));
-
- var bus = _i2cBuses[busNumber];
- if (!bus.IsOpen)
- {
- bus.Open(clock);
- }
+ internal bool UsingMpsse { get; }
- _activeBus = bus;
-
- return bus;
- }
-
- ///
- /// Creates an SPI bus with the default configuration.
- ///
- /// The SPI bus.
- public ISpiBus CreateSpiBus()
- {
- return CreateSpiBus(0, DefaultClockConfiguration);
- }
-
- ///
- /// Creates an SPI bus with the specified clock, MOSI, MISO pins, and configuration.
- ///
- /// The clock pin.
- /// The MOSI (Master Out Slave In) pin.
- /// The MISO (Master In Slave Out) pin.
- /// The SPI configuration.
- /// The SPI bus.
- public ISpiBus CreateSpiBus(IPin clock, IPin mosi, IPin miso, SpiClockConfiguration config)
- {
- if (!clock.Supports(c => c.LineTypes.HasFlag(SpiLineType.Clock)))
- {
- throw new ArgumentException("Invalid Clock line");
- }
+ ///
+ /// The pins
+ ///
+ public PinDefinitions Pins { get; }
- // TODO: map the pins to the bus number
- return CreateSpiBus(0, config);
- }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Specifies whether to use Multi-Protocol Synchronous Serial Engine (MPSSE) mode (default is false).
+ public Ft232h(bool useMPSSE = false)
+ {
+ UsingMpsse = useMPSSE;
- ///
- /// Creates an SPI bus with the specified clock, MOSI, MISO pins, and configuration.
- ///
- /// The clock pin.
- /// The MOSI (Master Out Slave In) pin.
- /// The MISO (Master In Slave Out) pin.
- /// The SPI frequency.
- /// The SPI bus.
- public ISpiBus CreateSpiBus(IPin clock, IPin mosi, IPin miso, Frequency speed)
- {
- // TODO: map the pins to the bus number
- var config = new SpiClockConfiguration(speed);
- return CreateSpiBus(0, config);
- }
+ _impl = UsingMpsse ? new MpsseImpl() : new Ftd2xxImpl();
+ _impl.Initialize();
- ///
- /// The default SPI clock configuration
- ///
- public static SpiClockConfiguration DefaultClockConfiguration
- {
- get => new SpiClockConfiguration(
- new Frequency(Ft232SpiBus.DefaultClockRate, Frequency.UnitType.Hertz));
- }
+ Pins = new PinDefinitions(this);
+ }
- private ISpiBus CreateSpiBus(int busNumber, SpiClockConfiguration config)
- {
- if (_activeBus != null)
- {
- throw new InvalidOperationException("The FT232 allows only one bus to be active at a time.");
- }
+ ///
+ public II2cBus CreateI2cBus(int busNumber = 0)
+ {
+ return CreateI2cBus(busNumber, I2CClockRate.Standard);
+ }
- if (_spiBuses.Count == 0)
- {
- throw new InvalidOperationException("No SPI Busses found! Is the FT232 properly connected?");
- }
+ ///
+ public II2cBus CreateI2cBus(int busNumber, I2cBusSpeed busSpeed)
+ {
+ // TODO: convert frequency
+ return CreateI2cBus(busNumber, I2CClockRate.Standard);
+ }
- if (!_spiBuses.ContainsKey(busNumber)) throw new ArgumentOutOfRangeException(nameof(busNumber));
+ ///
+ public II2cBus CreateI2cBus(IPin[] pins, I2cBusSpeed busSpeed)
+ {
+ // TODO: map the pins to the bus number
+ // TODO: convert frequency
+ return CreateI2cBus(0, I2CClockRate.Standard);
+ }
- var bus = _spiBuses[busNumber];
- if (!bus.IsOpen)
- {
- bus.Open(config);
- }
+ ///
+ public II2cBus CreateI2cBus(IPin clock, IPin data, I2cBusSpeed busSpeed)
+ {
+ // TODO: map the pins to the bus number
+ // TODO: convert frequency
+ return CreateI2cBus(0, I2CClockRate.Standard);
+ }
- _activeBus = bus;
+ private II2cBus CreateI2cBus(int busNumber, I2CClockRate clock)
+ {
+ return _impl.CreateI2cBus(busNumber, clock);
+ }
- return bus;
- }
+ ///
+ public ISpiBus CreateSpiBus()
+ {
+ return CreateSpiBus(0, DefaultClockConfiguration);
+ }
- public IDigitalInputPort CreateDigitalInputPort(IPin pin)
+ ///
+ public ISpiBus CreateSpiBus(IPin clock, IPin mosi, IPin miso, SpiClockConfiguration config)
+ {
+ if (!clock.Supports(c => c.LineTypes.HasFlag(SpiLineType.Clock)))
{
- return CreateDigitalInputPort(pin, ResistorMode.Disabled);
+ throw new ArgumentException("Invalid Clock line");
}
- private bool _spiBusAutoCreated = false;
-
- ///
- public IDigitalInputPort CreateDigitalInputPort(IPin pin, ResistorMode resistorMode)
- {
- // MPSSE requires a bus, it can be either I2C or SPI, but that bus must be created before you can use GPIO
- // if no bus is yet open, we'll default to a SPI bus.
- // If this is created before an I2C comms bus, we need to let the caller know to create the comms bus first
-
- if (_activeBus == null)
- {
- var bus = CreateSpiBus(0, DefaultClockConfiguration);
- _spiBusAutoCreated = true;
- _activeBus = bus as IFt232Bus;
- }
-
- // TODO: do we need to set the direction make (see outputs) or are they defaulted to input?
+ // TODO: map the pins to the bus number
+ return CreateSpiBus(0, config);
+ }
- var info = pin.SupportedChannels?.FirstOrDefault(c => c is IDigitalChannelInfo) as IDigitalChannelInfo;
- return new Ft232DigitalInputPort(pin, info!, _activeBus!);
- }
+ ///
+ public ISpiBus CreateSpiBus(IPin clock, IPin mosi, IPin miso, Frequency speed)
+ {
+ // TODO: map the pins to the bus number
+ var config = new SpiClockConfiguration(speed);
+ return CreateSpiBus(0, config);
+ }
- ///
- public IDigitalOutputPort CreateDigitalOutputPort(IPin pin, bool initialState = false, OutputType initialOutputType = OutputType.PushPull)
- {
- // MPSSE requires a bus, it can be either I2C or SPI, but that bus must be created before you can use GPIO
- // if no bus is yet open, we'll default to a SPI bus.
- // If this is created before an I2C comms bus, we need to let the caller know to create the comms bus first
+ ///
+ public ISpiBus CreateSpiBus(int busNumber, SpiClockConfiguration config)
+ {
+ return _impl.CreateSpiBus(busNumber, config);
+ }
- if (_activeBus == null)
- {
- var bus = CreateSpiBus(0, DefaultClockConfiguration);
- _spiBusAutoCreated = true;
- _activeBus = bus as IFt232Bus;
- }
+ ///
+ public static SpiClockConfiguration DefaultClockConfiguration
+ {
+ get => new SpiClockConfiguration(
+ new Frequency(Ft232SpiBus.DefaultClockRate, Frequency.UnitType.Hertz));
+ }
- // update the global mask to make this an output
- _activeBus!.GpioDirectionMask |= (byte)pin.Key;
+ ///
+ public IDigitalInputPort CreateDigitalInputPort(IPin pin)
+ {
+ return CreateDigitalInputPort(pin, ResistorMode.Disabled);
+ }
- // update the direction
- Native.Functions.FT_WriteGPIO(_activeBus.Handle, _activeBus.GpioDirectionMask, 0);
+ ///
+ public IDigitalInputPort CreateDigitalInputPort(IPin pin, ResistorMode resistorMode)
+ {
+ // TODO: need to select the proper channel based on pin
+ return _impl.CreateDigitalInputPort(0, pin, resistorMode);
+ }
- var info = pin.SupportedChannels?.FirstOrDefault(c => c is IDigitalChannelInfo) as IDigitalChannelInfo;
- return new Ft232DigitalOutputPort(pin, info!, initialState, initialOutputType, _activeBus);
- }
+ ///
+ public IDigitalOutputPort CreateDigitalOutputPort(IPin pin, bool initialState = false, OutputType initialOutputType = OutputType.PushPull)
+ {
+ // TODO: need to select the proper channel based on pin
+ return _impl.CreateDigitalOutputPort(0, pin, initialState, initialOutputType);
+ }
- ///
- protected virtual void Dispose(bool disposing)
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_isDisposed)
{
- if (!_isDisposed)
- {
- foreach (var bus in _i2cBuses)
- {
- bus.Value?.Dispose();
- }
+ _impl.Dispose();
- if (Interlocked.Decrement(ref _instanceCount) == 0)
- {
- // last instance was disposed, clean house
- Native.Functions.Cleanup_libMPSSE();
- }
-
- _isDisposed = true;
- }
+ _isDisposed = true;
}
+ }
- ~Ft232h()
- {
- Dispose(false);
- }
+ ///
+ /// Finalizer for the Ft232h class, used to release unmanaged resources.
+ ///
+ ~Ft232h()
+ {
+ Dispose(false);
+ }
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
}
}
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232h.ft23xx.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232h.ft23xx.cs
new file mode 100644
index 0000000000..88c3d1595e
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232h.ft23xx.cs
@@ -0,0 +1,69 @@
+using Meadow.Hardware;
+
+namespace Meadow.Foundation.ICs.IOExpanders;
+
+internal class Ftd2xxImpl : IFtdiImpl
+{
+ private FtdiDeviceCollection _devices = default!;
+
+ public void Initialize()
+ {
+ _devices = new FtdiDeviceCollection();
+ _devices.Refresh();
+ }
+
+ public II2cBus CreateI2cBus(int channel, I2CClockRate clock)
+ {
+ if (_devices.Count == 0)
+ {
+ throw new DeviceNotFoundException();
+ }
+
+ _devices[channel].Open();
+
+ return new Ft23xxI2cBus(_devices[channel]);
+ }
+
+ public ISpiBus CreateSpiBus(int channel, SpiClockConfiguration config)
+ {
+ if (_devices.Count == 0)
+ {
+ throw new DeviceNotFoundException();
+ }
+
+ _devices[channel].Open();
+
+ return new Ft23xxSpiBus(_devices[channel], config);
+ }
+
+ public IDigitalInputPort CreateDigitalInputPort(int channel, IPin pin, ResistorMode resistorMode)
+ {
+ if (_devices.Count == 0)
+ {
+ throw new DeviceNotFoundException();
+ }
+
+ return new Ft23xxDigitalInputPort(_devices[channel], pin, resistorMode,
+ new DigitalChannelInfo(pin.Name, true, true, false, false, false, false));
+ }
+
+ public IDigitalOutputPort CreateDigitalOutputPort(int channel, IPin pin, bool initialState = false, OutputType initialOutputType = OutputType.PushPull)
+ {
+ if (_devices.Count == 0)
+ {
+ throw new DeviceNotFoundException();
+ }
+
+ return new Ft23xxDigitalOutputPort(_devices[channel], pin,
+ new DigitalChannelInfo(pin.Name, true, true, false, false, false, false),
+ initialState, initialOutputType);
+ }
+
+ public void Dispose()
+ {
+ foreach (var d in _devices)
+ {
+ d.Close();
+ }
+ }
+}
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232h.mpsse.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232h.mpsse.cs
new file mode 100644
index 0000000000..a69cf63072
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232h.mpsse.cs
@@ -0,0 +1,197 @@
+using Meadow.Hardware;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+
+namespace Meadow.Foundation.ICs.IOExpanders;
+
+internal class MpsseImpl : IFtdiImpl
+{
+ private static int _instanceCount = 0;
+ private bool _isDisposed = false;
+ private Dictionary _i2cBuses = new Dictionary();
+ private Dictionary _spiBuses = new Dictionary();
+ private bool _spiBusAutoCreated = false;
+
+ private IFt232Bus? _activeBus = null;
+
+ public void Initialize()
+ {
+ if (Interlocked.Increment(ref _instanceCount) == 1)
+ {
+ // only do this one time (no matter how many instances are created instances)
+ Native.Mpsse.Init_libMPSSE();
+ }
+
+ EnumerateBuses();
+ }
+
+ private void EnumerateBuses()
+ {
+ _i2cBuses = GetI2CBuses();
+ _spiBuses = GetSpiBuses();
+ }
+
+ private Dictionary GetI2CBuses()
+ {
+ Dictionary result = new Dictionary();
+
+ if (Native.CheckStatus(Native.Mpsse.I2C_GetNumChannels(out int channels)))
+ {
+ if (channels > 0)
+ {
+ for (var c = 0; c < channels; c++)
+ {
+ if (Native.CheckStatus(Native.Mpsse.I2C_GetChannelInfo(c, out Native.FT_DEVICE_LIST_INFO_NODE info)))
+ {
+ result.Add(c, new MpsseI2cBus(c, info));
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private Dictionary GetSpiBuses()
+ {
+ Dictionary result = new Dictionary();
+
+ if (Native.CheckStatus(Native.Mpsse.SPI_GetNumChannels(out int channels)))
+ {
+ if (channels > 0)
+ {
+ for (var c = 0; c < channels; c++)
+ {
+ if (Native.CheckStatus(Native.Mpsse.SPI_GetChannelInfo(c, out Native.FT_DEVICE_LIST_INFO_NODE info)))
+ {
+ result.Add(c, new Ft232SpiBus(c, info));
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public II2cBus CreateI2cBus(int busNumber, I2CClockRate clock)
+ {
+ // dev note: this fails on WIndows in all my testing
+ // it's a bug in the MPSSE DLL delivered by FTDI
+ // even using their C example, compiling it myself, it fails
+
+ if (_activeBus != null)
+ {
+ throw new InvalidOperationException("The FT232 allows only one bus to be active at a time.");
+ }
+
+ if (_i2cBuses.Count == 0)
+ {
+ throw new InvalidOperationException("No I2C Busses found! Is the FT232 properly connected?");
+ }
+
+ if (!_i2cBuses.ContainsKey(busNumber)) throw new ArgumentOutOfRangeException(nameof(busNumber));
+
+ var bus = _i2cBuses[busNumber];
+ if (!bus.IsOpen)
+ {
+ bus.Open(clock);
+ }
+
+ _activeBus = bus;
+
+ return bus;
+ }
+
+ public ISpiBus CreateSpiBus(int busNumber, SpiClockConfiguration config)
+ {
+ if (_activeBus != null)
+ {
+ throw new InvalidOperationException("The FT232 allows only one bus to be active at a time.");
+ }
+
+ if (_spiBuses.Count == 0)
+ {
+ throw new InvalidOperationException("No SPI Busses found! Is the FT232 properly connected?");
+ }
+
+ if (!_spiBuses.ContainsKey(busNumber)) throw new ArgumentOutOfRangeException(nameof(busNumber));
+
+ var bus = _spiBuses[busNumber];
+ if (!bus.IsOpen)
+ {
+ bus.Open(config);
+ }
+
+ _activeBus = bus;
+
+ return bus;
+ }
+
+ public IDigitalInputPort CreateDigitalInputPort(int channel, IPin pin, ResistorMode resistorMode)
+ {
+ // MPSSE requires a bus, it can be either I2C or SPI, but that bus must be created before you can use GPIO
+ // if no bus is yet open, we'll default to a SPI bus.
+ // If this is created before an I2C comms bus, we need to let the caller know to create the comms bus first
+
+ if (_activeBus == null)
+ {
+ var bus = CreateSpiBus(channel, Ft232h.DefaultClockConfiguration);
+ _spiBusAutoCreated = true;
+ _activeBus = bus as IFt232Bus;
+ }
+
+ // TODO: do we need to set the direction make (see outpuuts) or are they defaulted to input?
+
+ var info = pin.SupportedChannels?.FirstOrDefault(c => c is IDigitalChannelInfo) as IDigitalChannelInfo;
+ return new MpsseDigitalInputPort(pin, info!, _activeBus!);
+ }
+
+ public IDigitalOutputPort CreateDigitalOutputPort(int channel, IPin pin, bool initialState = false, OutputType initialOutputType = OutputType.PushPull)
+ {
+ // MPSSE requires a bus, it can be either I2C or SPI, but that bus must be created before you can use GPIO
+ // if no bus is yet open, we'll default to a SPI bus.
+ // If this is created before an I2C comms bus, we need to let the caller know to create the comms bus first
+
+ if (_activeBus == null)
+ {
+ var bus = CreateSpiBus(channel, Ft232h.DefaultClockConfiguration);
+ _spiBusAutoCreated = true;
+ _activeBus = bus as IFt232Bus;
+ }
+
+ // update the global mask to make this an output
+ _activeBus!.GpioDirectionMask |= (byte)pin.Key;
+
+ // update the direction
+ Native.Mpsse.FT_WriteGPIO(_activeBus.Handle, _activeBus.GpioDirectionMask, 0);
+
+ var info = pin.SupportedChannels?.FirstOrDefault(c => c is IDigitalChannelInfo) as IDigitalChannelInfo;
+ return new MpsseDigitalOutputPort(pin, info!, initialState, initialOutputType, _activeBus);
+ }
+
+ public void Dispose()
+ {
+ if (!_isDisposed)
+ {
+ foreach (var bus in _spiBuses)
+ {
+ bus.Value?.Dispose();
+ }
+
+ if (Interlocked.Decrement(ref _instanceCount) == 0)
+ {
+ if (_spiBusAutoCreated)
+ {
+ // TODO:
+ }
+
+ // last instance was disposed, clean house
+ Native.Mpsse.Cleanup_libMPSSE();
+ }
+
+ _isDisposed = true;
+ }
+ }
+}
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft23xxDigitalInputPort.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft23xxDigitalInputPort.cs
new file mode 100644
index 0000000000..4226f634de
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft23xxDigitalInputPort.cs
@@ -0,0 +1,50 @@
+using Meadow.Hardware;
+using System;
+
+namespace Meadow.Foundation.ICs.IOExpanders;
+
+///
+/// Represents a digital input port implementation for the FT23xx device.
+///
+public sealed class Ft23xxDigitalInputPort : DigitalInputPortBase
+{
+ private FtdiDevice _device;
+ private ResistorMode _resistor;
+
+ internal Ft23xxDigitalInputPort(FtdiDevice device, IPin pin, ResistorMode resistorMode, IDigitalChannelInfo channel)
+ : base(pin, channel)
+ {
+ Resistor = resistorMode;
+ _device = device;
+ }
+
+ ///
+ public override bool State
+ {
+ get
+ {
+ // reads all 8 pis at once
+ var state = _device.GetGpioState(true);
+ // the pin key is the mask
+ return (state & (byte)Pin.Key) != 0;
+ }
+ }
+
+ ///
+ public override ResistorMode Resistor
+ {
+ get => _resistor;
+ set
+ {
+ switch (value)
+ {
+ case ResistorMode.InternalPullUp:
+ case ResistorMode.InternalPullDown:
+ throw new NotSupportedException("Internal resistors are not supported");
+ default:
+ _resistor = value;
+ break;
+ }
+ }
+ }
+}
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft23xxDigitalOutputPort.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft23xxDigitalOutputPort.cs
new file mode 100644
index 0000000000..4f3982d9e2
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft23xxDigitalOutputPort.cs
@@ -0,0 +1,59 @@
+using Meadow.Hardware;
+using System;
+
+namespace Meadow.Foundation.ICs.IOExpanders;
+
+///
+/// Represents a digital output port implementation for the FT23xx device.
+///
+public sealed class Ft23xxDigitalOutputPort : DigitalOutputPortBase
+{
+ private FtdiDevice _device;
+ private bool _state;
+ private bool _isHighByte;
+ private byte _key;
+
+ internal Ft23xxDigitalOutputPort(FtdiDevice device, IPin pin, IDigitalChannelInfo channel, bool initialState, OutputType initialOutputType)
+ : base(pin, channel, initialState, initialOutputType)
+ {
+ _device = device;
+
+ // TODO: make sure the pin isn't already in use
+ var key = Convert.ToUInt16(Pin.Key);
+ if (key > 255)
+ {
+ _isHighByte = true;
+ _key = (byte)(key >> 8);
+ }
+ else
+ {
+ _isHighByte = false;
+ _key = (byte)(key & 0xff);
+ }
+
+ _device.GpioDirectionMask |= _key;
+ }
+
+ ///
+ public override bool State
+ {
+ get => _state;
+ set
+ {
+ byte s = _device.GpioState;
+
+ if (value)
+ {
+ s |= _key;
+ }
+ else
+ {
+ s &= (byte)~_key;
+ }
+
+ _device.SetGpioState(!_isHighByte, _device.GpioDirectionMask, s);
+ _device.GpioState = s;
+ _state = value;
+ }
+ }
+}
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft23xxI2cBus.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft23xxI2cBus.cs
new file mode 100644
index 0000000000..54b7964b06
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft23xxI2cBus.cs
@@ -0,0 +1,94 @@
+using Meadow.Hardware;
+using System;
+using System.IO;
+
+namespace Meadow.Foundation.ICs.IOExpanders;
+
+///
+/// Represents an I2C bus implementation using the FT23xx device.
+///
+public sealed class Ft23xxI2cBus : II2cBus, IDisposable
+{
+ private FtdiDevice _device;
+
+ ///
+ /// Gets the handle to the FT23xx device used by the I2C bus.
+ ///
+ public IntPtr Handle => _device.Handle;
+
+ ///
+ public I2cBusSpeed BusSpeed { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
+
+ internal Ft23xxI2cBus(FtdiDevice device)
+ {
+ if (device.Handle == IntPtr.Zero)
+ {
+ device.Open();
+ }
+
+ _device = device;
+
+ _device.InitializeI2C();
+ }
+
+ ///
+ public void Dispose()
+ {
+ _device.Close();
+ }
+
+ ///
+ public void Exchange(byte peripheralAddress, Span writeBuffer, Span readBuffer)
+ {
+ Write(peripheralAddress, writeBuffer);
+ Read(peripheralAddress, readBuffer);
+ }
+
+ ///
+ public void Read(byte peripheralAddress, Span readBuffer)
+ {
+ _device.I2cStart();
+ var ack = _device.I2cSendDeviceAddrAndCheckACK(peripheralAddress, true);
+ if (!ack)
+ {
+ _device.I2cStop();
+ throw new IOException($"Error reading device while setting up address");
+ }
+
+ for (int i = 0; i < readBuffer.Length - 1; i++)
+ {
+ readBuffer[i] = _device.I2CReadByte(true);
+ }
+
+ if (readBuffer.Length > 0)
+ {
+ readBuffer[readBuffer.Length - 1] = _device.I2CReadByte(false);
+ }
+
+ _device.I2cStop();
+ }
+
+ ///
+ public void Write(byte peripheralAddress, Span writeBuffer)
+ {
+ _device.I2cStart();
+ var ack = _device.I2cSendDeviceAddrAndCheckACK(peripheralAddress, false);
+ if (!ack)
+ {
+ _device.I2cStop();
+ throw new IOException($"Error writing device while setting up address");
+ }
+
+ for (int i = 0; i < writeBuffer.Length; i++)
+ {
+ ack = _device.I2cSendByteAndCheckACK(writeBuffer[i]);
+ if (!ack)
+ {
+ _device.I2cStop();
+ throw new IOException($"Error writing device on byte {i}");
+ }
+ }
+
+ _device.I2cStop();
+ }
+}
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft23xxSpiBus.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft23xxSpiBus.cs
new file mode 100644
index 0000000000..66923ba9c9
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft23xxSpiBus.cs
@@ -0,0 +1,67 @@
+using Meadow.Hardware;
+using Meadow.Units;
+using System;
+
+namespace Meadow.Foundation.ICs.IOExpanders;
+
+///
+/// Represents an SPI bus implementation using the FT23xx device.
+///
+public sealed class Ft23xxSpiBus : IFt232Bus, ISpiBus, IDisposable
+{
+ private FtdiDevice _device;
+
+ ///
+ /// Gets the handle to the FT23xx device used by the SPI bus.
+ ///
+ public IntPtr Handle => _device.Handle;
+
+ ///
+ public byte GpioDirectionMask { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
+
+ ///
+ public byte GpioState { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
+
+ ///
+ public Frequency[] SupportedSpeeds => throw new NotImplementedException();
+
+ ///
+ public SpiClockConfiguration Configuration { get; }
+
+ internal Ft23xxSpiBus(FtdiDevice device, SpiClockConfiguration config)
+ {
+ Configuration = config;
+
+ if (device.Handle == IntPtr.Zero)
+ {
+ device.Open();
+ }
+
+ _device = device;
+
+ _device.InitializeSpi(Configuration);
+ }
+
+ ///
+ public void Dispose()
+ {
+ }
+
+ ///
+ public void Exchange(IDigitalOutputPort? chipSelect, Span writeBuffer, Span readBuffer, ChipSelectMode csMode)
+ {
+ _device.SpiExchange(chipSelect, writeBuffer, readBuffer, csMode);
+ }
+
+ ///
+ public void Read(IDigitalOutputPort? chipSelect, Span readBuffer, ChipSelectMode csMode)
+ {
+ _device.SpiRead(chipSelect, readBuffer, csMode);
+ }
+
+ ///
+ public void Write(IDigitalOutputPort? chipSelect, Span writeBuffer, ChipSelectMode csMode)
+ {
+ _device.SpiWrite(chipSelect, writeBuffer, csMode);
+ }
+}
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/FtdiDevice.Gpio.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/FtdiDevice.Gpio.cs
new file mode 100644
index 0000000000..4aeb29edee
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/FtdiDevice.Gpio.cs
@@ -0,0 +1,93 @@
+using Meadow.Hardware;
+using System;
+using System.Threading;
+using static Meadow.Foundation.ICs.IOExpanders.Native.Ftd2xx;
+
+namespace Meadow.Foundation.ICs.IOExpanders;
+
+internal partial class FtdiDevice
+{
+ private bool? _useMpseeForGpio = null;
+
+ public byte GpioDirectionMask { get; set; }
+ public byte GpioState { get; set; }
+
+ public I2cBusSpeed BusSpeed { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
+
+ internal void InitializeGpio()
+ {
+ // for now we don't support GPIO channels C and D on the FT4232H
+
+ if (Handle == IntPtr.Zero) Open();
+
+ _useMpseeForGpio = true;
+
+ if (_useMpseeForGpio ?? true)
+ {
+ // Reset
+ Native.CheckStatus(
+ FT_SetBitMode(Handle, 0x00, Native.FT_BITMODE.FT_BITMODE_RESET));
+
+ // Enable MPSSE mode
+ Native.CheckStatus(
+ FT_SetBitMode(Handle, 0x00, Native.FT_BITMODE.FT_BITMODE_MPSSE));
+
+ Thread.Sleep(50);
+
+ ClearInputBuffer();
+ InitializeMpsse();
+ }
+ else
+ {
+ Native.CheckStatus(
+ FT_SetBitMode(Handle, 0x00, Native.FT_BITMODE.FT_BITMODE_RESET));
+
+ // Enable asynchronous bit bang mode, thise does allow to have different pin modes, put all pins as input
+ Native.CheckStatus(
+ FT_SetBitMode(Handle, 0x00, Native.FT_BITMODE.FT_BITMODE_ASYNC_BITBANG));
+
+ ClearInputBuffer();
+ }
+ }
+
+ internal byte GetGpioState(bool lowByte)
+ {
+ if (!_useMpseeForGpio.HasValue)
+ {
+ InitializeGpio();
+ }
+
+ if (_useMpseeForGpio ?? false)
+ {
+ Span outBuffer = stackalloc byte[2];
+ Span inBuffer = stackalloc byte[1];
+ outBuffer[0] = (byte)(lowByte ? Native.FT_OPCODE.ReadDataBitsLowByte : Native.FT_OPCODE.ReadDataBitsHighByte);
+ outBuffer[1] = (byte)Native.FT_OPCODE.SendImmediate;
+ Write(outBuffer);
+ ReadInto(inBuffer);
+ return inBuffer[0];
+ }
+
+ throw new NotImplementedException();
+ }
+
+ internal void SetGpioState(bool lowByte, byte direction, byte state)
+ {
+ if (!_useMpseeForGpio.HasValue)
+ {
+ InitializeGpio();
+ }
+
+ if (_useMpseeForGpio ?? false)
+ {
+ Span outBuffer = stackalloc byte[3];
+ outBuffer[0] = (byte)(lowByte ? Native.FT_OPCODE.SetDataBitsLowByte : Native.FT_OPCODE.SetDataBitsHighByte);
+ outBuffer[1] = state; //data
+ outBuffer[2] = direction; //direction 1 == output, 0 == input
+ Write(outBuffer);
+ return;
+ }
+
+ throw new NotImplementedException();
+ }
+}
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/FtdiDevice.I2c.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/FtdiDevice.I2c.cs
new file mode 100644
index 0000000000..296ebb9eba
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/FtdiDevice.I2c.cs
@@ -0,0 +1,398 @@
+using System;
+using static Meadow.Foundation.ICs.IOExpanders.Native;
+using static Meadow.Foundation.ICs.IOExpanders.Native.Ftd2xx;
+
+namespace Meadow.Foundation.ICs.IOExpanders;
+
+internal partial class FtdiDevice
+{
+ private byte GpioLowData = 0;
+ private byte GpioLowDir = 0;
+ // private byte GpioHighData = 0;
+ // private byte GpioHighDir = 0;
+ private uint _i2cFreqKbps = 400;
+
+ private const byte NumberCycles = 6;
+ private const byte MaskGpio = 0xF8;
+
+ internal static class PinDirection
+ {
+ public const byte SDAinSCLin = 0x00;
+ public const byte SDAinSCLout = 0x01;
+ public const byte SDAoutSCLin = 0x02;
+ public const byte SDAoutSCLout = 0x03;
+ }
+
+ internal static class PinData
+ {
+ public const byte SDAloSCLhi = 0x01;
+ public const byte SDAhiSCLhi = 0x03;
+ public const byte SDAloSCLlo = 0x00;
+ public const byte SDAhiSCLlo = 0x02;
+ }
+
+ public uint I2cBusFrequencyKbps
+ {
+ get => _i2cFreqKbps;
+ set
+ {
+ _i2cFreqKbps |= value;
+ InitializeI2CClocks();
+ }
+ }
+
+ public void InitializeI2C()
+ {
+ // TODO: make sure we're not already set up for SPI
+
+ CheckStatus(
+ FT_SetTimeouts(Handle, DefaultTimeoutMs, DefaultTimeoutMs));
+ CheckStatus(
+ FT_SetLatencyTimer(Handle, DefaultLatencyTimer));
+ CheckStatus(
+ FT_SetFlowControl(Handle, FT_FLOWCONTROL.FT_FLOW_RTS_CTS, 0x00, 0x00));
+ CheckStatus(
+ FT_SetBitMode(Handle, 0x00, FT_BITMODE.FT_BITMODE_RESET));
+ CheckStatus(
+ FT_SetBitMode(Handle, 0x00, FT_BITMODE.FT_BITMODE_MPSSE));
+
+ ClearInputBuffer();
+ InitializeI2CClocks();
+ InitializeMpsse();
+ }
+
+ private void InitializeI2CClocks()
+ {
+ // Now setup the clock and other elements
+ Span toSend = stackalloc byte[13];
+ int idx = 0;
+ // Disable clock divide by 5 for 60Mhz master clock
+ toSend[idx++] = (byte)FT_OPCODE.DisableClockDivideBy5;
+ // Turn off adaptive clocking
+ toSend[idx++] = (byte)FT_OPCODE.TurnOffAdaptiveClocking;
+ // Enable 3 phase data clock, used by I2C to allow data on both clock edges
+ toSend[idx++] = (byte)FT_OPCODE.Enable3PhaseDataClocking;
+ // The SK clock frequency can be worked out by below algorithm with divide by 5 set as off
+ // TCK period = 60MHz / (( 1 + [ (0xValueH * 256) OR 0xValueL] ) * 2)
+ // Command to set clock divisor
+ toSend[idx++] = (byte)FT_OPCODE.SetClockDivisor;
+ uint clockDivisor = (60000 / (I2cBusFrequencyKbps * 2)) - 1;
+ toSend[idx++] = (byte)(clockDivisor & 0x00FF);
+ toSend[idx++] = (byte)((clockDivisor >> 8) & 0x00FF);
+ // loopback off
+ toSend[idx++] = (byte)FT_OPCODE.DisconnectTDItoTDOforLoopback;
+ // Enable the FT232H's drive-zero mode with the following enable mask
+ toSend[idx++] = (byte)FT_OPCODE.SetIOOnlyDriveOn0AndTristateOn1;
+ // Low byte (ADx) enables - bits 0, 1 and 2
+ toSend[idx++] = 0x07;
+ // High byte (ACx) enables - all off
+ toSend[idx++] = 0x00;
+ // Command to set directions of lower 8 pins and force value on bits set as output
+ toSend[idx++] = (byte)FT_OPCODE.SetDataBitsLowByte;
+ if (DeviceType == FtDeviceType.Ft232H)
+ {
+ // SDA and SCL both output high(open drain)
+ GpioLowData = (byte)(PinData.SDAhiSCLhi | (GpioLowData & MaskGpio));
+ GpioLowDir = (byte)(PinDirection.SDAoutSCLout | (GpioLowDir & MaskGpio));
+ }
+ else
+ {
+ // SDA and SCL set low but as input to mimic open drain
+ GpioLowData = (byte)(PinData.SDAloSCLlo | (GpioLowData & MaskGpio));
+ GpioLowDir = (byte)(PinDirection.SDAinSCLin | (GpioLowDir & MaskGpio));
+ }
+
+ toSend[idx++] = GpioLowData;
+ toSend[idx++] = GpioLowDir;
+ Write(toSend);
+ }
+
+ internal void I2cStart()
+ {
+ int count;
+ int idx = 0;
+ // SDA high, SCL high
+ // The behavior is a bit different for FT232H and FT2232H/FT4232H
+ if (DeviceType == FtDeviceType.Ft232H)
+ {
+ GpioLowData = (byte)(PinData.SDAhiSCLhi | (GpioLowData & MaskGpio));
+ GpioLowDir = (byte)(PinDirection.SDAoutSCLout | (GpioLowDir & MaskGpio));
+ }
+ else
+ {
+ GpioLowData = (byte)(PinData.SDAloSCLlo | (GpioLowData & MaskGpio));
+ GpioLowDir = (byte)(PinDirection.SDAinSCLin | (GpioLowDir & MaskGpio));
+ }
+
+ Span toSend = stackalloc byte[(NumberCycles * 3 * 3) + 3];
+ for (count = 0; count < NumberCycles; count++)
+ {
+ toSend[idx++] = (byte)FT_OPCODE.SetDataBitsLowByte;
+ toSend[idx++] = GpioLowData;
+ toSend[idx++] = GpioLowDir;
+ }
+
+ // SDA lo, SCL high
+ // The behavior is a bit different for FT232H and FT2232H/FT4232H
+ if (DeviceType == FtDeviceType.Ft232H)
+ {
+ GpioLowData = (byte)(PinData.SDAloSCLhi | (GpioLowData & MaskGpio));
+ }
+ else
+ {
+ GpioLowDir = (byte)(PinDirection.SDAoutSCLin | (GpioLowDir & MaskGpio));
+ }
+
+ for (count = 0; count < NumberCycles; count++)
+ {
+ toSend[idx++] = (byte)FT_OPCODE.SetDataBitsLowByte;
+ toSend[idx++] = GpioLowData;
+ toSend[idx++] = GpioLowDir;
+ }
+
+ // SDA lo, SCL lo
+ // The behavior is a bit different for FT232H and FT2232H/FT4232H
+ if (DeviceType == FtDeviceType.Ft232H)
+ {
+ GpioLowData = (byte)(PinData.SDAloSCLlo | (GpioLowData & MaskGpio));
+ }
+ else
+ {
+ GpioLowDir = (byte)(PinDirection.SDAoutSCLout | (GpioLowDir & MaskGpio));
+ }
+
+ for (count = 0; count < NumberCycles; count++)
+ {
+ toSend[idx++] = (byte)FT_OPCODE.SetDataBitsLowByte;
+ toSend[idx++] = GpioLowData;
+ toSend[idx++] = GpioLowDir;
+ }
+
+ // Release SDA
+ // The behavior is a bit different for FT232H and FT2232H/FT4232H
+ if (DeviceType == FtDeviceType.Ft232H)
+ {
+ GpioLowData = (byte)(PinData.SDAhiSCLlo | (GpioLowData & MaskGpio));
+ }
+ else
+ {
+ GpioLowDir = (byte)(PinDirection.SDAinSCLout | (GpioLowDir & MaskGpio));
+ }
+
+ toSend[idx++] = (byte)FT_OPCODE.SetDataBitsLowByte;
+ toSend[idx++] = GpioLowData;
+ toSend[idx++] = GpioLowDir;
+
+ Write(toSend);
+ }
+
+ internal void I2cStop()
+ {
+ int count;
+ int idx = 0;
+ // SDA low, SCL low
+ GpioLowData = (byte)(PinData.SDAloSCLlo | (GpioLowData & MaskGpio));
+ GpioLowDir = (byte)(PinDirection.SDAoutSCLout | (GpioLowDir & MaskGpio));
+
+ Span toSend = stackalloc byte[NumberCycles * 3 * 3];
+ for (count = 0; count < NumberCycles; count++)
+ {
+ toSend[idx++] = (byte)FT_OPCODE.SetDataBitsLowByte;
+ toSend[idx++] = GpioLowData;
+ toSend[idx++] = GpioLowDir;
+ }
+
+ // SDA low, SCL high
+ // The behavior is a bit different for FT232H and FT2232H/FT4232H
+ if (DeviceType == FtDeviceType.Ft232H)
+ {
+ GpioLowData = (byte)(PinData.SDAloSCLhi | (GpioLowData & MaskGpio));
+ GpioLowDir = (byte)(PinDirection.SDAoutSCLout | (GpioLowDir & MaskGpio));
+ }
+ else
+ {
+ GpioLowData = (byte)(PinData.SDAloSCLlo | (GpioLowData & MaskGpio));
+ GpioLowDir = (byte)(PinDirection.SDAoutSCLin | (GpioLowDir & MaskGpio));
+ }
+
+ for (count = 0; count < NumberCycles; count++)
+ {
+ toSend[idx++] = (byte)FT_OPCODE.SetDataBitsLowByte;
+ toSend[idx++] = GpioLowData;
+ toSend[idx++] = GpioLowDir;
+ }
+
+ // SDA high, SCL high
+ // The behavior is a bit different for FT232H and FT2232H/FT4232H
+ if (DeviceType == FtDeviceType.Ft232H)
+ {
+ GpioLowData = (byte)(PinData.SDAhiSCLhi | (GpioLowData & MaskGpio));
+ GpioLowDir = (byte)(PinDirection.SDAoutSCLout | (GpioLowDir & MaskGpio));
+ }
+ else
+ {
+ GpioLowData = (byte)(PinData.SDAloSCLlo | (GpioLowData & MaskGpio));
+ GpioLowDir = (byte)(PinDirection.SDAinSCLin | (GpioLowDir & MaskGpio));
+ }
+
+ for (count = 0; count < NumberCycles; count++)
+ {
+ toSend[idx++] = (byte)FT_OPCODE.SetDataBitsLowByte;
+ toSend[idx++] = GpioLowData;
+ toSend[idx++] = GpioLowDir;
+ }
+
+ Write(toSend);
+ }
+
+ internal void I2cLineIdle()
+ {
+ int idx = 0;
+ // SDA low, SCL low
+ // The behavior is a bit different for FT232H and FT2232H/FT4232H
+ if (DeviceType == FtDeviceType.Ft232H)
+ {
+ GpioLowData = (byte)(PinData.SDAhiSCLhi | (GpioLowData & MaskGpio));
+ GpioLowDir = (byte)(PinDirection.SDAoutSCLout | (GpioLowDir & MaskGpio));
+ }
+ else
+ {
+ GpioLowData = (byte)(PinData.SDAloSCLlo | (GpioLowData & MaskGpio));
+ GpioLowDir = (byte)(PinDirection.SDAinSCLin | (GpioLowDir & MaskGpio));
+ }
+
+ Span toSend = stackalloc byte[3];
+ toSend[idx++] = (byte)FT_OPCODE.SetDataBitsLowByte;
+ toSend[idx++] = GpioLowData;
+ toSend[idx++] = GpioLowDir;
+ Write(toSend);
+ }
+
+ internal bool I2cSendByteAndCheckACK(byte data)
+ {
+ int idx = 0;
+ Span toSend = stackalloc byte[DeviceType == FtDeviceType.Ft232H ? 10 : 13];
+ Span toRead = stackalloc byte[1];
+ // The behavior is a bit different for FT232H and FT2232H/FT4232H
+ if (DeviceType == FtDeviceType.Ft232H)
+ {
+ // Just clock with one byte (0 = 1 byte)
+ toSend[idx++] = (byte)FT_OPCODE.ClockDataBytesOutOnMinusVeClockMSBFirst;
+ toSend[idx++] = 0;
+ toSend[idx++] = 0;
+ toSend[idx++] = data;
+ // Put line back to idle (data released, clock pulled low)
+ GpioLowData = (byte)(PinData.SDAhiSCLlo | (GpioLowData & MaskGpio));
+ GpioLowDir = (byte)(PinDirection.SDAoutSCLout | (GpioLowDir & MaskGpio));
+ toSend[idx++] = (byte)FT_OPCODE.SetDataBitsLowByte;
+ toSend[idx++] = GpioLowData;
+ toSend[idx++] = GpioLowDir;
+ // Clock in (0 = 1 byte)
+ toSend[idx++] = (byte)FT_OPCODE.ClockDataBitsInOnPlusVeClockMSBFirst;
+ toSend[idx++] = 0;
+ }
+ else
+ {
+ // Set directions and clock data
+ GpioLowData = (byte)(PinData.SDAloSCLlo | (GpioLowData & MaskGpio));
+ GpioLowDir = (byte)(PinDirection.SDAoutSCLout | (GpioLowDir & MaskGpio));
+ toSend[idx++] = (byte)FT_OPCODE.SetDataBitsLowByte;
+ toSend[idx++] = GpioLowData;
+ toSend[idx++] = GpioLowDir;
+ // Just clock with one byte (0 = 1 byte)
+ toSend[idx++] = (byte)FT_OPCODE.ClockDataBytesOutOnMinusVeClockMSBFirst;
+ toSend[idx++] = 0;
+ toSend[idx++] = 0;
+ toSend[idx++] = data;
+ // Put line back to idle (data released, clock pulled low)
+ // Set directions and clock data
+ GpioLowData = (byte)(PinData.SDAloSCLlo | (GpioLowData & MaskGpio));
+ GpioLowDir = (byte)(PinDirection.SDAinSCLout | (GpioLowDir & MaskGpio));
+ toSend[idx++] = (byte)FT_OPCODE.SetDataBitsLowByte;
+ toSend[idx++] = GpioLowData;
+ toSend[idx++] = GpioLowDir;
+ // Clock in (0 = 1 byte)
+ toSend[idx++] = (byte)FT_OPCODE.ClockDataBitsInOnPlusVeClockMSBFirst;
+ toSend[idx++] = 0;
+ }
+
+ // And ask it right away
+ toSend[idx++] = (byte)FT_OPCODE.SendImmediate;
+ Write(toSend);
+ ReadInto(toRead);
+ // Bit 0 equivalent to acknowledge, otherwise nack
+ return (toRead[0] & 0x01) == 0;
+ }
+
+ internal bool I2cSendDeviceAddrAndCheckACK(byte Address, bool Read)
+ {
+ // Set address for read or write
+ Address <<= 1;
+ if (Read == true)
+ {
+ Address |= 0x01;
+ }
+
+ return I2cSendByteAndCheckACK(Address);
+ }
+
+ internal byte I2CReadByte(bool ack)
+ {
+ int idx = 0;
+ Span toSend = stackalloc byte[DeviceType == FtDeviceType.Ft232H ? 10 : 16];
+ Span toRead = stackalloc byte[1];
+ // The behavior is a bit different for FT232H and FT2232H/FT4232H
+ if (DeviceType == FtDeviceType.Ft232H)
+ {
+ // Read one byte
+ toSend[idx++] = (byte)FT_OPCODE.ClockDataBytesInOnPlusVeClockMSBFirst;
+ toSend[idx++] = 0;
+ toSend[idx++] = 0;
+ // Send out either ack either nak
+ toSend[idx++] = (byte)FT_OPCODE.ClockDataBitsOutOnMinusVeClockMSBFirst;
+ toSend[idx++] = 0;
+ toSend[idx++] = (byte)(ack ? 0x00 : 0xFF);
+ // I2C lines back to idle state
+ toSend[idx++] = (byte)FT_OPCODE.SetDataBitsLowByte;
+ GpioLowData = (byte)(PinData.SDAhiSCLlo | (GpioLowData & MaskGpio));
+ GpioLowDir = (byte)(PinDirection.SDAoutSCLout | (GpioLowDir & MaskGpio));
+ toSend[idx++] = GpioLowData;
+ toSend[idx++] = GpioLowDir;
+ }
+ else
+ {
+ // Make sure no open gain
+ GpioLowData = (byte)(PinData.SDAloSCLlo | (GpioLowData & MaskGpio));
+ GpioLowDir = (byte)(PinDirection.SDAinSCLout | (GpioLowDir & MaskGpio));
+ toSend[idx++] = (byte)FT_OPCODE.SetDataBitsLowByte;
+ toSend[idx++] = GpioLowData;
+ toSend[idx++] = GpioLowDir;
+ // Read one byte
+ toSend[idx++] = (byte)FT_OPCODE.ClockDataBytesInOnPlusVeClockMSBFirst;
+ toSend[idx++] = 0;
+ toSend[idx++] = 0;
+ // Change direction
+ GpioLowData = (byte)(PinData.SDAloSCLlo | (GpioLowData & MaskGpio));
+ GpioLowDir = (byte)(PinDirection.SDAoutSCLout | (GpioLowDir & MaskGpio));
+ toSend[idx++] = (byte)FT_OPCODE.SetDataBitsLowByte;
+ toSend[idx++] = GpioLowData;
+ toSend[idx++] = GpioLowDir;
+ // Send out either ack either nak
+ toSend[idx++] = (byte)FT_OPCODE.ClockDataBitsOutOnMinusVeClockMSBFirst;
+ toSend[idx++] = 0;
+ toSend[idx++] = (byte)(ack ? 0x00 : 0xFF);
+ // I2C lines back to idle state
+ toSend[idx++] = (byte)FT_OPCODE.SetDataBitsLowByte;
+ GpioLowData = (byte)(PinData.SDAhiSCLlo | (GpioLowData & MaskGpio));
+ GpioLowDir = (byte)(PinDirection.SDAinSCLout | (GpioLowDir & MaskGpio));
+ toSend[idx++] = GpioLowData;
+ toSend[idx++] = GpioLowDir;
+ }
+
+ // And ask it right away
+ toSend[idx++] = (byte)FT_OPCODE.SendImmediate;
+ Write(toSend);
+ ReadInto(toRead);
+ return toRead[0];
+ }
+}
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/FtdiDevice.Spi.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/FtdiDevice.Spi.cs
new file mode 100644
index 0000000000..147811933c
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/FtdiDevice.Spi.cs
@@ -0,0 +1,210 @@
+using Meadow.Hardware;
+using System;
+using System.Threading;
+using static Meadow.Foundation.ICs.IOExpanders.Native;
+using static Meadow.Foundation.ICs.IOExpanders.Native.Ftd2xx;
+
+namespace Meadow.Foundation.ICs.IOExpanders;
+
+internal partial class FtdiDevice
+{
+ private SpiClockConfiguration? _spiConfig;
+
+ internal void InitializeSpi(SpiClockConfiguration config)
+ {
+ if (_spiConfig != null) return;
+
+ // TODO: make sure we're not already initialized for I2C
+
+ _spiConfig = config;
+
+ if (Handle == IntPtr.Zero) Open();
+
+ CheckStatus(
+ FT_SetLatencyTimer(Handle, 1));
+ CheckStatus(
+ FT_SetUSBParameters(Handle, 65535, 65535));
+ CheckStatus(
+ FT_SetChars(Handle, 0, 0, 0, 0));
+ CheckStatus(
+ FT_SetTimeouts(Handle, 3000, 3000));
+ CheckStatus(
+ FT_SetLatencyTimer(Handle, 1));
+ // Reset
+ CheckStatus(
+ FT_SetBitMode(Handle, 0x00, FT_BITMODE.FT_BITMODE_RESET));
+ // Enable MPSSE mode
+ CheckStatus(
+ FT_SetBitMode(Handle, 0x00, FT_BITMODE.FT_BITMODE_MPSSE));
+
+ // 50 ms according to thr doc for all USB to complete
+ Thread.Sleep(50);
+ ClearInputBuffer();
+ InitializeMpsse();
+
+ int idx = 0;
+ Span toSend = stackalloc byte[10];
+ toSend[idx++] = (byte)FT_OPCODE.DisableClockDivideBy5;
+ toSend[idx++] = (byte)FT_OPCODE.TurnOffAdaptiveClocking;
+ toSend[idx++] = (byte)FT_OPCODE.Disable3PhaseDataClocking;
+ toSend[idx++] = (byte)FT_OPCODE.SetDataBitsLowByte;
+ // Pin clock output, MISO output, MOSI input
+ GpioLowDir = (byte)((GpioLowDir & MaskGpio) | 0x03);
+ // clock, MOSI and MISO to 0
+ GpioLowData = (byte)(GpioLowData & MaskGpio);
+ toSend[idx++] = GpioLowDir;
+ toSend[idx++] = GpioLowData;
+ // The SK clock frequency can be worked out by below algorithm with divide by 5 set as off
+ // TCK period = 60MHz / (( 1 + [ (0xValueH * 256) OR 0xValueL] ) * 2)
+ // Command to set clock divisor
+ toSend[idx++] = (byte)FT_OPCODE.SetClockDivisor;
+ uint clockDivisor = (uint)((60000 / (_spiConfig.Speed.Hertz / 1000 * 2)) - 1);
+ toSend[idx++] = (byte)(clockDivisor & 0xFF);
+ toSend[idx++] = (byte)(clockDivisor >> 8);
+ // loopback off
+ toSend[idx++] = (byte)FT_OPCODE.DisconnectTDItoTDOforLoopback;
+ Write(toSend);
+ // Delay as in the documentation
+ Thread.Sleep(30);
+ }
+
+ internal void SpiWrite(IDigitalOutputPort? chipSelect, ReadOnlySpan writeBuffer, ChipSelectMode csMode)
+ {
+ if (_spiConfig == null)
+ {
+ throw new Exception("SPI not configured");
+ }
+
+ if (writeBuffer.Length > 65535)
+ {
+ throw new ArgumentException("Buffer too large, maximum size if 65535");
+ }
+
+ byte clock;
+ switch (_spiConfig.SpiMode)
+ {
+ default:
+ case SpiClockConfiguration.Mode.Mode3:
+ case SpiClockConfiguration.Mode.Mode0:
+ clock = (byte)FT_OPCODE.ClockDataBytesOutOnMinusVeClockMSBFirst;
+ break;
+ case SpiClockConfiguration.Mode.Mode2:
+ case SpiClockConfiguration.Mode.Mode1:
+ clock = (byte)FT_OPCODE.ClockDataBytesOutOnPlusVeClockMSBFirst;
+ break;
+ }
+
+ if (chipSelect != null)
+ {
+ // assert
+ chipSelect.State = csMode == ChipSelectMode.ActiveHigh ? true : false;
+ }
+
+ int idx = 0;
+ Span toSend = stackalloc byte[3 + writeBuffer.Length];
+ toSend[idx++] = clock;
+ toSend[idx++] = (byte)((writeBuffer.Length - 1) & 0xFF);
+ toSend[idx++] = (byte)((writeBuffer.Length - 1) >> 8);
+ writeBuffer.CopyTo(toSend.Slice(3));
+ Write(toSend);
+ if (chipSelect != null)
+ {
+ // deassert
+ chipSelect.State = csMode == ChipSelectMode.ActiveHigh ? false : true;
+ }
+ }
+
+ internal void SpiRead(IDigitalOutputPort? chipSelect, Span readBuffer, ChipSelectMode csMode)
+ {
+ if (_spiConfig == null)
+ {
+ throw new Exception("SPI not configured");
+ }
+
+ if (readBuffer.Length > 65535)
+ {
+ throw new ArgumentException("Buffer too large, maximum size if 65535");
+ }
+
+ byte clock;
+ switch (_spiConfig.SpiMode)
+ {
+ default:
+ case SpiClockConfiguration.Mode.Mode3:
+ case SpiClockConfiguration.Mode.Mode0:
+ clock = (byte)FT_OPCODE.ClockDataBytesInOnPlusVeClockMSBFirst;
+ break;
+ case SpiClockConfiguration.Mode.Mode2:
+ case SpiClockConfiguration.Mode.Mode1:
+ clock = (byte)FT_OPCODE.ClockDataBytesInOnMinusVeClockMSBFirst;
+ break;
+ }
+
+ if (chipSelect != null)
+ {
+ // assert
+ chipSelect.State = csMode == ChipSelectMode.ActiveHigh ? true : false;
+ }
+
+ int idx = 0;
+ Span toSend = stackalloc byte[3];
+ toSend[idx++] = clock;
+ toSend[idx++] = (byte)((readBuffer.Length - 1) & 0xFF);
+ toSend[idx++] = (byte)((readBuffer.Length - 1) >> 8);
+ Write(toSend);
+ ReadInto(readBuffer);
+
+ if (chipSelect != null)
+ {
+ // deassert
+ chipSelect.State = csMode == ChipSelectMode.ActiveHigh ? false : true;
+ }
+ }
+
+ internal void SpiExchange(IDigitalOutputPort? chipSelect, ReadOnlySpan writeBuffer, Span readBuffer, ChipSelectMode csMode)
+ {
+ if (_spiConfig == null)
+ {
+ throw new Exception("SPI not configured");
+ }
+
+ if ((readBuffer.Length > 65535) || (writeBuffer.Length > 65535))
+ {
+ throw new ArgumentException("Buffer too large, maximum size if 65535");
+ }
+
+ byte clock;
+ switch (_spiConfig.SpiMode)
+ {
+ default:
+ case SpiClockConfiguration.Mode.Mode3:
+ case SpiClockConfiguration.Mode.Mode0:
+ clock = (byte)FT_OPCODE.ClockDataBytesOutOnMinusBytesInOnPlusVeClockMSBFirst;
+ break;
+ case SpiClockConfiguration.Mode.Mode2:
+ case SpiClockConfiguration.Mode.Mode1:
+ clock = (byte)FT_OPCODE.ClockDataBytesOutOnPlusBytesInOnMinusVeClockMSBFirst;
+ break;
+ }
+
+ if (chipSelect != null)
+ {
+ // assert
+ chipSelect.State = csMode == ChipSelectMode.ActiveHigh ? true : false;
+ }
+
+ int idx = 0;
+ Span toSend = stackalloc byte[3 + writeBuffer.Length];
+ toSend[idx++] = clock;
+ toSend[idx++] = (byte)((writeBuffer.Length - 1) & 0xFF);
+ toSend[idx++] = (byte)((writeBuffer.Length - 1) >> 8);
+ writeBuffer.CopyTo(toSend.Slice(3));
+ Write(toSend);
+ ReadInto(readBuffer);
+ if (chipSelect != null)
+ {
+ // deassert
+ chipSelect.State = csMode == ChipSelectMode.ActiveHigh ? false : true;
+ }
+ }
+}
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/FtdiDevice.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/FtdiDevice.cs
new file mode 100644
index 0000000000..99dfcffacd
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/FtdiDevice.cs
@@ -0,0 +1,138 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using static Meadow.Foundation.ICs.IOExpanders.Native;
+using static Meadow.Foundation.ICs.IOExpanders.Native.Ftd2xx;
+
+namespace Meadow.Foundation.ICs.IOExpanders;
+
+internal partial class FtdiDevice
+{
+ private const int DefaultTimeoutMs = 5000;
+ private const int DefaultLatencyTimer = 16;
+
+ internal uint Index { get; }
+ internal uint Flags { get; }
+ internal FtDeviceType DeviceType { get; }
+ internal uint ID { get; }
+ internal uint LocID { get; }
+ internal string SerialNumber { get; }
+ internal string Description { get; }
+ internal IntPtr Handle { get; private set; }
+
+ internal FtdiDevice(
+ uint index,
+ uint flags,
+ FtDeviceType deviceType,
+ uint id,
+ uint locid,
+ string serialNumber,
+ string description,
+ IntPtr handle
+ )
+ {
+ Index = index;
+ Flags = flags;
+ DeviceType = deviceType;
+ ID = id;
+ LocID = locid;
+ SerialNumber = serialNumber;
+ Description = description;
+ Handle = handle;
+ }
+
+ public void Open()
+ {
+ if (Handle == IntPtr.Zero)
+ {
+ Native.CheckStatus(
+ FT_OpenEx(LocID, Native.FT_OPEN_TYPE.FT_OPEN_BY_LOCATION, out IntPtr handle)
+ );
+ Handle = handle;
+ }
+ }
+
+ private void InitializeMpsse()
+ {
+ Span writeBuffer = stackalloc byte[1];
+ writeBuffer[0] = 0xAA;
+ Write(writeBuffer);
+ Span readBuffer = stackalloc byte[2];
+ ReadInto(readBuffer);
+ if (!((readBuffer[0] == 0xFA) && (readBuffer[1] == 0xAA)))
+ {
+ throw new IOException($"Failed to setup device {Description} in MPSSE mode using magic 0xAA sync");
+ }
+
+ // Second with 0xAB
+ writeBuffer[0] = 0xAB;
+ Write(writeBuffer);
+ ReadInto(readBuffer);
+ if (!((readBuffer[0] == 0xFA) && (readBuffer[1] == 0xAB)))
+ {
+ throw new IOException($"Failed to setup device {Description}, status in MPSSE mode using magic 0xAB sync");
+ }
+ }
+
+ public int ReadInto(Span buffer)
+ {
+ var totalRead = 0;
+ uint read = 0;
+
+ while (totalRead < buffer.Length)
+ {
+ var available = GetAvailableBytes();
+ if (available > 0)
+ {
+ CheckStatus(
+ FT_Read(Handle, in buffer[totalRead], available, ref read));
+
+ totalRead += (int)read;
+ }
+ }
+
+ return totalRead;
+ }
+
+ public void Write(ReadOnlySpan data)
+ {
+ uint written = 0;
+
+ CheckStatus(
+ FT_Write(Handle, in MemoryMarshal.GetReference(data), (ushort)data.Length, ref written));
+ }
+
+ private void ClearInputBuffer()
+ {
+ var available = GetAvailableBytes();
+
+ if (available > 0)
+ {
+ var toRead = new byte[available];
+ uint bytesRead = 0;
+ CheckStatus(
+ FT_Read(Handle, in toRead[0], available, ref bytesRead));
+ }
+ }
+
+ private uint GetAvailableBytes()
+ {
+ uint availableBytes = 0;
+
+ CheckStatus(
+ FT_GetQueueStatus(Handle, ref availableBytes));
+
+ return availableBytes;
+ }
+
+ public void Close()
+ {
+ if (Handle != IntPtr.Zero)
+ {
+ CheckStatus(
+ FT_Close(Handle));
+
+ Handle = IntPtr.Zero;
+ }
+ }
+}
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/FtdiDeviceCollection.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/FtdiDeviceCollection.cs
new file mode 100644
index 0000000000..0ea1c4c4f3
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/FtdiDeviceCollection.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+using static Meadow.Foundation.ICs.IOExpanders.Native.Ftd2xx;
+
+namespace Meadow.Foundation.ICs.IOExpanders;
+
+internal class FtdiDeviceCollection : IEnumerable
+{
+ private List _devices = new();
+
+ public int Count => _devices.Count;
+
+ public FtdiDevice this[int index]
+ {
+ get => _devices[index];
+ }
+
+ public void Refresh()
+ {
+ _devices.Clear();
+
+ uint count;
+
+ try
+ {
+ Native.CheckStatus(FT_CreateDeviceInfoList(out count));
+ }
+ catch (DllNotFoundException)
+ {
+ throw new DriverNotInstalledException();
+ }
+
+ ReadOnlySpan serialNumberBuffer = stackalloc byte[16];
+ ReadOnlySpan descriptionBuffer = stackalloc byte[64];
+
+ for (uint index = 0; index < count; index++)
+ {
+ Native.CheckStatus(FT_GetDeviceInfoDetail(
+ index,
+ out uint flags,
+ out FtDeviceType deviceType,
+ out uint id,
+ out uint locid,
+ in MemoryMarshal.GetReference(serialNumberBuffer),
+ in MemoryMarshal.GetReference(descriptionBuffer),
+ out IntPtr handle));
+
+ switch (deviceType)
+ {
+ case FtDeviceType.Ft232H:
+ case FtDeviceType.Ft2232:
+ case FtDeviceType.Ft2232H:
+ case FtDeviceType.Ft4232H:
+ // valid, add to list
+ break;
+ default:
+ continue;
+ }
+
+ // no idea why the buffer isn't all zeros after the null terminator - thanks FTDI!
+ var serialNumber = Encoding.ASCII.GetString(serialNumberBuffer.ToArray(), 0, serialNumberBuffer.IndexOf((byte)0));
+ var description = Encoding.ASCII.GetString(descriptionBuffer.ToArray(), 0, descriptionBuffer.IndexOf((byte)0));
+
+ _devices.Add(new FtdiDevice(index, flags, deviceType, id, locid, serialNumber, description, handle));
+ }
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return _devices.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+}
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/IFt232Bus.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/IFt232Bus.cs
index d91ee18fd5..bade933078 100644
--- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/IFt232Bus.cs
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/IFt232Bus.cs
@@ -1,11 +1,24 @@
using System;
-namespace Meadow.Foundation.ICs.IOExpanders
+namespace Meadow.Foundation.ICs.IOExpanders;
+
+///
+/// Represents an interface for interacting with an FT232 bus.
+///
+internal interface IFt232Bus
{
- internal interface IFt232Bus
- {
- public IntPtr Handle { get; }
- public byte GpioDirectionMask { get; set; }
- public byte GpioState { get; set; }
- }
-}
\ No newline at end of file
+ ///
+ /// Gets the handle to the FT232 device.
+ ///
+ IntPtr Handle { get; }
+
+ ///
+ /// Gets or sets the GPIO direction mask for the FT232 device.
+ ///
+ byte GpioDirectionMask { get; set; }
+
+ ///
+ /// Gets or sets the state of the GPIO pins on the FT232 device.
+ ///
+ byte GpioState { get; set; }
+}
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/IFtdiImpl.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/IFtdiImpl.cs
new file mode 100644
index 0000000000..0ce8faca27
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/IFtdiImpl.cs
@@ -0,0 +1,13 @@
+using Meadow.Hardware;
+using System;
+
+namespace Meadow.Foundation.ICs.IOExpanders;
+
+internal interface IFtdiImpl : IDisposable
+{
+ void Initialize();
+ II2cBus CreateI2cBus(int busNumber, I2CClockRate clock);
+ ISpiBus CreateSpiBus(int busNumber, SpiClockConfiguration config);
+ IDigitalInputPort CreateDigitalInputPort(int channel, IPin pin, ResistorMode resistorMode);
+ IDigitalOutputPort CreateDigitalOutputPort(int channel, IPin pin, bool initialState = false, OutputType initialOutputType = OutputType.PushPull);
+}
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232DigitalInputPort.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/MpsseDigitalInputPort.cs
similarity index 89%
rename from Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232DigitalInputPort.cs
rename to Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/MpsseDigitalInputPort.cs
index 0b86f372e1..312af6ae0b 100644
--- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Ft232DigitalInputPort.cs
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/MpsseDigitalInputPort.cs
@@ -6,17 +6,17 @@ namespace Meadow.Foundation.ICs.IOExpanders
///
/// Represents a digital input port implementation for the FT232 bus.
///
- public sealed class Ft232DigitalInputPort : DigitalInterruptPortBase
+ public sealed class MpsseDigitalInputPort : DigitalInterruptPortBase
{
private readonly IFt232Bus _bus;
///
- /// Instantiates a .
+ /// Instantiates a .
///
/// The pin connected to the input port.
/// The digital channel info associated with the pin.
/// The FT232 bus instance.
- internal Ft232DigitalInputPort(IPin pin, IDigitalChannelInfo info, IFt232Bus bus)
+ internal MpsseDigitalInputPort(IPin pin, IDigitalChannelInfo info, IFt232Bus bus)
: base(pin, info)
{
_bus = bus;
@@ -31,7 +31,7 @@ public override bool State
get
{
// reads all 8 pis at once
- Native.Functions.FT_ReadGPIO(_bus.Handle, out byte state);
+ Native.Mpsse.FT_ReadGPIO(_bus.Handle, out byte state);
// the pin key is the mask
return (state & (byte)Pin.Key) != 0;
}
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/MpsseI2cBus.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/MpsseI2cBus.cs
new file mode 100644
index 0000000000..1e7b13911c
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/MpsseI2cBus.cs
@@ -0,0 +1,137 @@
+using Meadow.Hardware;
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using static Meadow.Foundation.ICs.IOExpanders.Native;
+
+namespace Meadow.Foundation.ICs.IOExpanders;
+
+///
+/// Represents an I2C bus implementation using the MPSSE mode of the FT232 device.
+///
+public sealed class MpsseI2cBus : IFt232Bus, II2cBus, IDisposable
+{
+ private const byte DefaultLatencyTimer = 10;
+ private const I2CChannelOptions DefaultChannelOptions = I2CChannelOptions.None;
+
+ private bool _isDisposed;
+
+ ///
+ public IntPtr Handle { get; private set; }
+
+ ///
+ public byte GpioDirectionMask { get; set; }
+
+ ///
+ public byte GpioState { get; set; }
+ internal bool IsOpen { get; private set; } = false;
+ internal int ChannelNumber { get; }
+ private FT_DEVICE_LIST_INFO_NODE InfoNode { get; }
+
+ internal MpsseI2cBus(int channelNumber, FT_DEVICE_LIST_INFO_NODE info)
+ {
+ ChannelNumber = channelNumber;
+ InfoNode = info;
+ }
+
+ ///
+ public I2cBusSpeed BusSpeed { get; set; }
+
+ private void Dispose(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+ CloseChannel();
+
+ _isDisposed = true;
+ }
+ }
+
+ ///
+ /// Finalizer for the MpsseI2cBus class, used to release unmanaged resources.
+ ///
+ ~MpsseI2cBus()
+ {
+ // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
+ Dispose(false);
+ }
+
+ ///
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ internal void Open(I2CClockRate clockRate = I2CClockRate.Standard)
+ {
+ if (CheckStatus(Mpsse.I2C_OpenChannel(ChannelNumber, out IntPtr handle)))
+ {
+ Handle = handle;
+
+ var config = new I2CChannelConfig
+ {
+ ClockRate = clockRate,
+ LatencyTimer = DefaultLatencyTimer,
+ Options = DefaultChannelOptions
+ };
+
+ CheckStatus(Mpsse.I2C_InitChannel(Handle, ref config));
+
+ IsOpen = true;
+ }
+ }
+
+ private void CloseChannel()
+ {
+ if (Handle != IntPtr.Zero)
+ {
+ CheckStatus(Mpsse.I2C_CloseChannel(Handle));
+ Handle = IntPtr.Zero;
+ }
+ }
+
+ ///
+ public void Exchange(byte peripheralAddress, Span writeBuffer, Span readBuffer)
+ {
+ Write(peripheralAddress, writeBuffer);
+ Read(peripheralAddress, readBuffer);
+ }
+
+ ///
+ public void Read(byte peripheralAddress, Span readBuffer)
+ {
+ var status = Mpsse.I2C_DeviceRead(
+ Handle,
+ peripheralAddress,
+ readBuffer.Length,
+ MemoryMarshal.GetReference(readBuffer),
+ out int transferred,
+ I2CTransferOptions.FAST_TRANSFER | I2CTransferOptions.FAST_TRANSFER_BYTES
+ //I2CTransferOptions.START_BIT | I2CTransferOptions.STOP_BIT | I2CTransferOptions.NACK_LAST_BYTE
+ // I2CTransferOptions.START_BIT | I2CTransferOptions.STOP_BIT | I2CTransferOptions.FAST_TRANSFER | I2CTransferOptions.NACK_LAST_BYTE
+ );
+
+ Debug.WriteLine($"transferred: {transferred}");
+ CheckStatus(status);
+ }
+
+ ///
+ public void Write(byte peripheralAddress, Span writeBuffer)
+ {
+ var status = Mpsse.I2C_DeviceWrite(
+ Handle,
+ peripheralAddress,
+ writeBuffer.Length,
+ MemoryMarshal.GetReference(writeBuffer),
+ out int transferred,
+ I2CTransferOptions.FAST_TRANSFER | I2CTransferOptions.FAST_TRANSFER_BYTES
+ //I2CTransferOptions.START_BIT | I2CTransferOptions.BREAK_ON_NACK
+ //I2CTransferOptions.START_BIT | I2CTransferOptions.STOP_BIT | I2CTransferOptions.NACK_LAST_BYTE
+ );
+
+ Debug.WriteLine($"transferred: {transferred}");
+ // CheckStatus(status);
+ }
+}
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Native.Ftd2xx.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Native.Ftd2xx.cs
new file mode 100644
index 0000000000..39d8391e8c
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Native.Ftd2xx.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Meadow.Foundation.ICs.IOExpanders;
+
+internal static partial class Native
+{
+ public class Ftd2xx
+ {
+ public enum FtDeviceType
+ {
+ Ft232BOrFt245B = 0,
+ Ft8U232AmOrFTtU245Am,
+ Ft8U100Ax,
+ UnknownDevice,
+ Ft2232,
+ Ft232ROrFt245R,
+ Ft2232H,
+ Ft4232H,
+ Ft232H,
+ FtXSeries,
+ Ft4222HMode0or2With2Interfaces,
+ Ft4222HMode1or2With4Interfaces,
+ Ft4222HMode3With1Interface,
+ Ft4222OtpProgrammerBoard,
+ }
+
+ private const string FTDI_LIB = "ftd2xx";
+
+ [DllImport(FTDI_LIB, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
+ public static extern FT_STATUS FT_CreateDeviceInfoList(out uint numdevs);
+
+ [DllImport(FTDI_LIB, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
+ public static extern FT_STATUS FT_GetDeviceInfoDetail(uint index, out uint flags, out FtDeviceType chiptype, out uint id, out uint locid, in byte serialnumber, in byte description, out IntPtr ftHandle);
+
+ [DllImport(FTDI_LIB, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
+ public static extern FT_STATUS FT_OpenEx(uint pvArg1, FT_OPEN_TYPE dwFlags, out IntPtr ftHandle);
+
+ [DllImport(FTDI_LIB, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
+ public static extern FT_STATUS FT_Close(IntPtr ftHandle);
+
+ [DllImport(FTDI_LIB, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
+ public static extern FT_STATUS FT_SetTimeouts(IntPtr ftHandle, uint dwReadTimeout, uint dwWriteTimeout);
+
+ [DllImport(FTDI_LIB, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
+ public static extern FT_STATUS FT_SetLatencyTimer(IntPtr ftHandle, byte ucLatency);
+
+ [DllImport(FTDI_LIB, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
+ public static extern FT_STATUS FT_SetFlowControl(IntPtr ftHandle, FT_FLOWCONTROL usFlowControl, byte uXon, byte uXoff);
+
+ [DllImport(FTDI_LIB, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
+ public static extern FT_STATUS FT_SetBitMode(IntPtr ftHandle, byte ucMask, FT_BITMODE ucMode);
+
+ [DllImport(FTDI_LIB, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
+ public static extern FT_STATUS FT_GetBitMode(IntPtr ftHandle, ref byte ucMode);
+
+ [DllImport(FTDI_LIB, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
+ public static extern FT_STATUS FT_GetQueueStatus(IntPtr ftHandle, ref uint lpdwAmountInRxQueue);
+
+ [DllImport(FTDI_LIB, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
+ public static extern FT_STATUS FT_Read(IntPtr ftHandle, in byte lpBuffer, uint dwBytesToRead, ref uint lpdwBytesReturned);
+
+ [DllImport(FTDI_LIB, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
+ public static extern FT_STATUS FT_Write(IntPtr ftHandle, in byte lpBuffer, uint dwBytesToWrite, ref uint lpdwBytesWritten);
+
+ [DllImport(FTDI_LIB, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
+ public static extern FT_STATUS FT_SetChars(IntPtr ftHandle, byte uEventCh, byte uEventChEn, byte uErrorCh, byte uErrorChEn);
+
+ [DllImport(FTDI_LIB, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
+ public static extern FT_STATUS FT_SetUSBParameters(IntPtr ftHandle, uint dwInTransferSize, uint dwOutTransferSize);
+ }
+}
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Native.Functions.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Native.Mpsse.cs
similarity index 99%
rename from Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Native.Functions.cs
rename to Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Native.Mpsse.cs
index 0c0d75659d..01f0c95108 100644
--- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Native.Functions.cs
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Native.Mpsse.cs
@@ -6,8 +6,7 @@ namespace Meadow.Foundation.ICs.IOExpanders
{
internal static partial class Native
{
-
- public class Functions
+ public static class Mpsse
{
private const string MPSSE_LIB = "libmpsse";
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Native.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Native.cs
index b2514a077f..b928a2ebe6 100644
--- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Native.cs
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Driver/Native.cs
@@ -15,6 +15,13 @@ public static bool CheckStatus(Native.FT_STATUS status)
throw new Exception($"Native error: {status}");
}
+ public enum FT_OPEN_TYPE
+ {
+ FT_OPEN_BY_SERIAL_NUMBER = 1,
+ FT_OPEN_BY_DESCRIPTION = 2,
+ FT_OPEN_BY_LOCATION = 4
+ }
+
public enum FT_DEVICE_TYPE
{
FT_DEVICE_BM,
@@ -57,6 +64,90 @@ public enum FT_FLAGS
FT_FLAGS_HISPEED = 2
}
+ [Flags]
+ public enum FT_FLOWCONTROL : ushort
+ {
+ FT_FLOW_NONE = 0x0000,
+ FT_FLOW_RTS_CTS = 0x0100,
+ FT_FLOW_DTR_DSR = 0x0200,
+ FT_FLOW_XON_XOFF = 0x0400,
+ }
+
+ [Flags]
+ public enum FT_BITMODE
+ {
+ FT_BITMODE_RESET = 0x00,
+ FT_BITMODE_ASYNC_BITBANG = 0x01,
+ FT_BITMODE_MPSSE = 0x02,
+ FT_BITMODE_SYNC_BITBANG = 0x04,
+ FT_BITMODE_MCU_HOST = 0x08,
+ FT_BITMODE_FAST_SERIAL = 0x10,
+ FT_BITMODE_CBUS_BITBANG = 0x20,
+ FT_BITMODE_SYNC_FIFO = 0x40,
+
+ }
+ internal enum FT_OPCODE
+ {
+ ClockDataBytesOutOnPlusVeClockMSBFirst = 0x10,
+ ClockDataBytesOutOnMinusVeClockMSBFirst = 0x11,
+ ClockDataBitsOutOnPlusVeClockMSBFirst = 0x12,
+ ClockDataBitsOutOnMinusVeClockMSBFirst = 0x13,
+ ClockDataBytesInOnPlusVeClockMSBFirst = 0x20,
+ ClockDataBytesInOnMinusVeClockMSBFirst = 0x24,
+ ClockDataBitsInOnPlusVeClockMSBFirst = 0x22,
+ ClockDataBitsInOnMinusVeClockMSBFirst = 0x26,
+ ClockDataBytesOutOnMinusBytesInOnPlusVeClockMSBFirst = 0x31,
+ ClockDataBytesOutOnPlusBytesInOnMinusVeClockMSBFirst = 0x34,
+ ClockDataBitsOutOnMinusBitsInOnPlusVeClockMSBFirst = 0x33,
+ ClockDataBitsOutOnPlusBitsInOnMinusVeClockMSBFirst = 0x36,
+ ClockDataBytesOutOnPlusVeClockLSBFirst = 0x18,
+ ClockDataBytesOutOnMinusVeClockLSBFirst = 0x19,
+ ClockDataBitsOutOnPlusVeClockLSBFirst = 0x1A,
+ ClockDataBitsOutOnMinusVeClockLSBFirst = 0x1B,
+ ClockDataBytesInOnPlusVeClockLSBFirst = 0x28,
+ ClockDataBytesInOnMinusVeClockLSBFirst = 0x2C,
+ ClockDataBitsInOnPlusVeClockLSBFirst = 0x2A,
+ ClockDataBitsInOnMinusVeClockSBFirst = 0x2E,
+ ClockDataBytesOutOnMinusBytesInOnPlusVeClockLSBFirst = 0x39,
+ ClockDataBytesOutOnPlusBytesInOnMinusVeClockLSBFirst = 0x3C,
+ ClockDataBitsOutOnMinusBitsInOnPlusVeClockLSBFirst = 0x3B,
+ ClockDataBitsOutOnPlusBitsInOnMinusVeClockLSBFirst = 0x3E,
+ ClockDataBytesOutOnPlusVeClockTMSPinLSBFirst = 0x4A,
+ ClockDataBytesOutOnMinusVeClockTMSPinSBFirst = 0x4B,
+ ClockDataBytesOutOnPlusDataInOnPlusVeClockTMSPinSBFirst = 0x6A,
+ ClockDataBytesOutOnMinusDataInOnPlusVeClockTMSPinSBFirst = 0x6B,
+ ClockDataBytesOutOnPlusDataInOnMinusVeClockTMSPinSBFirst = 0x6E,
+ ClockDataBytesOutOnMinusDataInOnMinusVeClockTMSPinSBFirst = 0x6F,
+ SetDataBitsLowByte = 0x80,
+ SetDataBitsHighByte = 0x82,
+ ReadDataBitsLowByte = 0x81,
+ ReadDataBitsHighByte = 0x83,
+ ConnectTDItoTDOforLoopback = 0x84,
+ DisconnectTDItoTDOforLoopback = 0x85,
+ SetTCKSKDivisor = 0x86,
+ SetClockDivisor = 0x86,
+ CPUModeReadShortAddress = 0x90,
+ CPUModeReadExtendedAddress = 0x91,
+ CPUModeWriteShortAddress = 0x92,
+ CPUModeWriteExtendedAddress = 0x93,
+ SendImmediate = 0x87,
+ WaitOnIOHigh = 0x88,
+ WaitOnIOLow = 0x89,
+ DisableClockDivideBy5 = 0x8A,
+ EnableClockDivideBy5 = 0x8B,
+ Enable3PhaseDataClocking = 0x8C,
+ Disable3PhaseDataClocking = 0x8D,
+ ClockForNBitsWithNoDataTransfer = 0x8E,
+ ClockForNx8BitsWithNoDataTransfer = 0x8F,
+ ClockContinuouslyAndWaitOnIOHigh = 0x94,
+ ClockContinuouslyAndWaitOnIOLow = 0x95,
+ TurnOnAdaptiveClocking = 0x96,
+ TurnOffAdaptiveClocking = 0x97,
+ ClockForNx8BitsWithNoDataTransferOrUntilGPIOL1IsHigh = 0x9C,
+ ClockForNx8BitsWithNoDataTransferOrUntilGPIOL1IsLow = 0x9D,
+ SetIOOnlyDriveOn0AndTristateOn1 = 0x9E,
+ }
+
public enum FT_STATUS
{
FT_OK,
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Samples/Ft232h_Windows_Sample/Ft232h_Windows_Sample.csproj b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Samples/Ft232h_Windows_Sample/Ft232h_Windows_Sample.csproj
index e14f4211ea..b50f7e4a35 100644
--- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Samples/Ft232h_Windows_Sample/Ft232h_Windows_Sample.csproj
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Samples/Ft232h_Windows_Sample/Ft232h_Windows_Sample.csproj
@@ -14,6 +14,9 @@
+
+
+
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Samples/Ft232h_Windows_Sample/Program.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Samples/Ft232h_Windows_Sample/Program.cs
index e3b8a036a9..14eaaffede 100644
--- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Samples/Ft232h_Windows_Sample/Program.cs
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Samples/Ft232h_Windows_Sample/Program.cs
@@ -1,29 +1,79 @@
// See https://aka.ms/new-console-template for more information
using Meadow;
+using Meadow.Foundation.Displays;
using Meadow.Foundation.ICs.IOExpanders;
+using Meadow.Foundation.Sensors.Atmospheric;
+using Meadow.Hardware;
+using System.Diagnostics;
-Resolver.Log.Info("HELLO FROM THE WILDERNESS FT232H DRIVER!");
+Console.WriteLine("HELLO FROM THE WILDERNESS FT232H DRIVER!");
var ft232 = new Ft232h();
-/*
-var i2cChannels = ft232.GetI2CChannels();
-Resolver.Log.Info($"{i2cChannels.Length} I2C CHANNELS");
-foreach (var c in i2cChannels)
+//await TestBME280(ft232);
+await TestIli9341(ft232);
+//await TestGpio(ft232);
+
+async Task TestBME280(Ft232h expander)
{
- Resolver.Log.Info($"Serial #: {c.SerialNumber}");
- Resolver.Log.Info($"Description #: {c.Description}");
+ var bme = new Bme280(expander.CreateI2cBus());
+
+ while (true)
+ {
+ var reading = await bme.Read();
+ Debug.WriteLine($"Temp: {reading.Temperature.Value.Fahrenheit}F Humidity: {reading.Humidity.Value.Percent}%");
+ await Task.Delay(1000);
+ }
}
-var spiChannels = ft232.GetSpiChannels();
-Resolver.Log.Info($"{spiChannels.Length} SPI CHANNELS");
-foreach (var c in spiChannels)
+async Task TestGpio(Ft232h expander)
{
- Resolver.Log.Info($"Serial #: {c.SerialNumber}");
- Resolver.Log.Info($"Description #: {c.Description}");
-}
-*/
+ var outputs = new List
+ {
+ expander.CreateDigitalOutputPort(expander.Pins.C0),
+ expander.CreateDigitalOutputPort(expander.Pins.C1),
+ expander.CreateDigitalOutputPort(expander.Pins.C2),
+ expander.CreateDigitalOutputPort(expander.Pins.C3),
+ expander.CreateDigitalOutputPort(expander.Pins.C4),
+ expander.CreateDigitalOutputPort(expander.Pins.C5),
+ expander.CreateDigitalOutputPort(expander.Pins.C6),
+ expander.CreateDigitalOutputPort(expander.Pins.D7),
+};
+
+ var s = false;
-Console.ReadKey();
+ while (true)
+ {
+ for (var i = 0; i < outputs.Count; i++)
+ {
+ var setTo = (i % 2 == 0) ? s : !s;
+ outputs[i].State = setTo;
+ }
+
+ await Task.Delay(1000);
+ s = !s;
+ }
+}
+
+async Task TestIli9341(Ft232h expander)
+{
+ var ili = new Ili9341(
+ expander.CreateSpiBus(),
+ expander.CreateDigitalOutputPort(expander.Pins.C0),
+ expander.CreateDigitalOutputPort(expander.Pins.C2),
+ expander.CreateDigitalOutputPort(expander.Pins.C1),
+ 480,
+ 320
+ );
+ while (true)
+ {
+ ili.Fill(Color.Red);
+ await Task.Delay(1000);
+ ili.Fill(Color.Green);
+ await Task.Delay(1000);
+ ili.Fill(Color.Blue);
+ await Task.Delay(1000);
+ }
+}
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Tests/FT232.Unit.Tests/FT232.Unit.Tests.csproj b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Tests/FT232.Unit.Tests/FT232.Unit.Tests.csproj
new file mode 100644
index 0000000000..3c190ebe21
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Tests/FT232.Unit.Tests/FT232.Unit.Tests.csproj
@@ -0,0 +1,29 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Tests/FT232.Unit.Tests/UnitTest1.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Tests/FT232.Unit.Tests/UnitTest1.cs
new file mode 100644
index 0000000000..acf2f26d54
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Tests/FT232.Unit.Tests/UnitTest1.cs
@@ -0,0 +1,65 @@
+using Meadow.Foundation.ICs.IOExpanders;
+
+namespace FT232.Unit.Tests;
+
+public class Ftd2xxTests
+{
+ [Fact]
+ public void GetDeviceList()
+ {
+ // assumes an FT232 is connected
+ var devices = new FtdiDeviceCollection();
+ devices.Refresh();
+ Assert.True(devices.Count > 0);
+ }
+
+ [Fact]
+ public void OpenI2CBus()
+ {
+ // assumes an FT232 is connected
+ var ftdi = new Ft232h(false);
+ var bus = ftdi.CreateI2cBus();
+ Assert.NotNull(bus);
+ }
+
+ [Fact]
+ public void OpenSPIBus()
+ {
+ // assumes an FT232 is connected
+ var ftdi = new Ft232h(false);
+ var bus = ftdi.CreateSpiBus();
+ Assert.NotNull(bus);
+ }
+
+ [Fact]
+ public void NoDeviceForSpiCheck()
+ {
+ // assumes no FT232 is connected
+ var ftdi = new Ft232h(false);
+ Assert.Throws(() =>
+ {
+ var bus = ftdi.CreateSpiBus();
+ });
+ }
+
+ [Fact]
+ public void NoDeviceForI2CCheck()
+ {
+ // assumes no FT232 is connected
+ var ftdi = new Ft232h(false);
+ Assert.Throws(() =>
+ {
+ var bus = ftdi.CreateI2cBus();
+ });
+ }
+
+ [Fact]
+ public void NoDriverCheck()
+ {
+ // assumes no FT232 driver is installed (rename C:\Windows\System32\ftd2xx.dll)
+ Assert.Throws(() =>
+ {
+ var ftdi = new Ft232h(false);
+ });
+ }
+}
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Tests/FT232.Unit.Tests/Usings.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Tests/FT232.Unit.Tests/Usings.cs
new file mode 100644
index 0000000000..8c927eb747
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ft232h/Tests/FT232.Unit.Tests/Usings.cs
@@ -0,0 +1 @@
+global using Xunit;
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Datasheet/Grid-EYE_SPECIFICATIONS(Reference).pdf b/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Datasheet/Grid-EYE_SPECIFICATIONS(Reference).pdf
new file mode 100644
index 0000000000..29cdd3953d
Binary files /dev/null and b/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Datasheet/Grid-EYE_SPECIFICATIONS(Reference).pdf differ
diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Driver/Amg8833.Enums.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Driver/Amg8833.Enums.cs
new file mode 100644
index 0000000000..8901576180
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Driver/Amg8833.Enums.cs
@@ -0,0 +1,62 @@
+namespace Meadow.Foundation.Sensors.Camera;
+
+public partial class Amg8833
+{
+ internal static class Constants
+ {
+ public const int StartupDelayMs = 100;
+ public const double ThermistorConversion = 0.0625;
+ public const int PixelArraySize = 64;
+ public const double PixelTempConversion = 0.25;
+ }
+
+ internal enum InterruptModes : byte
+ {
+ Difference = 0x00,
+ Absolute = 0x01 << 1
+ }
+
+ internal enum Addresses : byte
+ {
+ Address_0x69 = 0x69,
+ Default = Address_0x69
+ }
+
+ internal enum Registers : byte
+ {
+ PCTL = 0x00,
+ RST = 0x01,
+ FPSC = 0x02,
+ INTC = 0x03,
+ STAT = 0x04,
+ SCLR = 0x05,
+ RESERVED = 0x06,
+ AVE = 0x07,
+ INTHL = 0x08,
+ INTHH = 0x09,
+ INTLL = 0x0A,
+ INTLH = 0x0B,
+ IHYSL = 0x0C,
+ IHYSH = 0x0D,
+ TTHL = 0x0E,
+ TTHH = 0x0F,
+ INT_OFFSET = 0x010,
+ PIXEL_OFFSET = 0x80
+ };
+
+ internal enum Modes : byte
+ {
+ Normal = 0x00,
+ Sleep = 0x01,
+ StandBy_60 = 0x20,
+ StandBy_10 = 0x21
+ }
+
+ internal enum Commands : byte
+ {
+ RST_FlagReset = 0x30,
+ RST_InitialReset = 0x3f,
+ FPS_One = 0x01,
+ FPS_Ten = 0x00,
+ }
+}
diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Driver/Amg8833.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Driver/Amg8833.cs
new file mode 100644
index 0000000000..f2338dab42
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Driver/Amg8833.cs
@@ -0,0 +1,136 @@
+using Meadow.Hardware;
+using System;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+namespace Meadow.Foundation.Sensors.Camera;
+
+///
+/// Represents a Panasonic Grid-EYE infrared array sensor
+///
+public partial class Amg8833 : II2cPeripheral
+{
+ private II2cCommunications _i2cComms;
+
+ ///
+ /// The default I2C address for the peripheral
+ ///
+ public byte DefaultI2cAddress => (byte)Addresses.Default;
+
+ ///
+ /// Creates an instance of an AMG8833 sensor
+ ///
+ ///
+ public Amg8833(II2cBus i2cBus)
+ {
+ _i2cComms = new I2cCommunications(i2cBus, (byte)Addresses.Default);
+
+ Initialize();
+ }
+
+ private void Initialize()
+ {
+ SetPowerMode(Modes.Normal);
+ WriteRegister(Registers.RST, Commands.RST_InitialReset);
+ DisableInterrupts();
+ WriteRegister(Registers.FPSC, Commands.FPS_Ten);
+
+ Thread.Sleep(Constants.StartupDelayMs);
+ }
+
+ private void SetPowerMode(Modes mode)
+ {
+ WriteRegister(Registers.PCTL, Modes.Normal);
+ }
+
+ private void DisableInterrupts()
+ {
+ WriteRegister(Registers.INTC, (byte)0x00);
+ }
+
+ ///
+ /// Reads the temperature of the on-board thermistor
+ ///
+ ///
+ public Units.Temperature ReadThermistor()
+ {
+ // read 2 bytes
+ var raw = _i2cComms.ReadRegisterAsUShort((byte)Registers.TTHL);
+ // see the data sheet on this calculation
+ var converted = Convert12BitUShortToFloat(raw) * Constants.ThermistorConversion;
+ return new Units.Temperature(converted, Units.Temperature.UnitType.Celsius);
+ }
+
+ ///
+ /// Reads the raw sensor data into the provided buffer.
+ ///
+ /// A span of 64 16-but values
+ public void ReadRawPixels(Span buffer)
+ {
+ if (buffer.Length != Constants.PixelArraySize)
+ {
+ throw new ArgumentOutOfRangeException($"Expected a buffer of {Constants.PixelArraySize} shorts");
+ }
+
+ Span tx = stackalloc byte[1];
+
+ tx[0] = (byte)Registers.PIXEL_OFFSET;
+ _i2cComms.Write(tx);
+ var rx = MemoryMarshal.Cast(buffer);
+ _i2cComms.Read(rx);
+ }
+
+ ///
+ /// Reads the temperature of each pixel in the sensor
+ ///
+ ///
+ public Units.Temperature[] ReadPixels()
+ {
+ var temps = new Units.Temperature[Constants.PixelArraySize];
+ Span raw = stackalloc short[Constants.PixelArraySize];
+
+ ReadRawPixels(raw);
+
+ for (var i = 0; i < raw.Length; i++)
+ {
+ var r = Convert12BitUShortToFloat((ushort)raw[i]) * Constants.PixelTempConversion;
+ temps[i] = new Units.Temperature(r, Units.Temperature.UnitType.Celsius);
+ }
+
+ return temps;
+ }
+
+ private float Convert12BitUShortToFloat(ushort s)
+ {
+ // take 11 bits of value
+ var absolute = s & 0x7ff;
+ // apply any negative
+
+ if ((absolute & 0x800) != 0)
+ {
+ return 0 - (float)absolute;
+ }
+
+ return absolute;
+ }
+
+ private void EnableInterrupts(InterruptModes mode)
+ {
+ WriteRegister(Registers.INTC, (byte)(0x01 | (byte)mode));
+ }
+
+ private void WriteRegister(Registers register, byte value)
+ {
+ _i2cComms.WriteRegister((byte)register, value);
+ }
+
+ private void WriteRegister(Registers register, Modes mode)
+ {
+ WriteRegister(register, (byte)mode);
+ }
+
+ private void WriteRegister(Registers register, Commands command)
+ {
+ WriteRegister(register, (byte)command);
+ }
+}
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Driver/Readme.md b/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Driver/Readme.md
new file mode 100644
index 0000000000..3561f6ace2
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Driver/Readme.md
@@ -0,0 +1,13 @@
+# Meadow.Foundation.Sensors.Camera.Amg8833
+
+
+## How to Contribute
+
+- **Found a bug?** [Report an issue](https://github.com/WildernessLabs/Meadow_Issues/issues)
+- Have a **feature idea or driver request?** [Open a new feature request](https://github.com/WildernessLabs/Meadow_Issues/issues)
+- Want to **contribute code?** Fork the [Meadow.Foundation](https://github.com/WildernessLabs/Meadow.Foundation) repository and submit a pull request against the `develop` branch
+
+
+## Need Help?
+
+If you have questions or need assistance, please join the Wilderness Labs [community on Slack](http://slackinvite.wildernesslabs.co/).
diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Driver/Sensors.Camera.Amg8833.csproj b/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Driver/Sensors.Camera.Amg8833.csproj
new file mode 100644
index 0000000000..ce8b3bcd08
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Driver/Sensors.Camera.Amg8833.csproj
@@ -0,0 +1,26 @@
+
+
+ enable
+ 10.0
+ Apache-2.0
+ true
+ icon.png
+ Wilderness Labs, Inc
+ netstandard2.1
+ Library
+ Amg8833
+ Wilderness Labs, Inc
+ http://developer.wildernesslabs.co/Meadow/Meadow.Foundation/
+ Meadow.Foundation.Sensors.Camera.Amg8833
+ https://github.com/WildernessLabs/Meadow.Foundation
+ Meadow.Foundation,Amg8833,Camera,Infrared,Thermal
+ 1.6.0.1
+ true
+ Grid-EYE Amg8833 I2C infrared array sensor
+
+
+
+
+
+
+
diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Samples/Amg8833_Sample/Amg8833_Sample.csproj b/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Samples/Amg8833_Sample/Amg8833_Sample.csproj
new file mode 100644
index 0000000000..70318612b5
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Samples/Amg8833_Sample/Amg8833_Sample.csproj
@@ -0,0 +1,20 @@
+
+
+ https://github.com/WildernessLabs/Meadow.Foundation
+ Wilderness Labs, Inc
+ Wilderness Labs, Inc
+ true
+ netstandard2.1
+ Library
+ App
+
+
+
+
+
+
+
+ Always
+
+
+
diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Samples/Amg8833_Sample/MeadowApp.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Samples/Amg8833_Sample/MeadowApp.cs
new file mode 100644
index 0000000000..6200fe500d
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Samples/Amg8833_Sample/MeadowApp.cs
@@ -0,0 +1,38 @@
+using Meadow;
+using Meadow.Devices;
+using Meadow.Foundation.Sensors.Camera;
+using Meadow.Hardware;
+using System.Threading.Tasks;
+
+namespace Sensors.Cameras.Amg8833_Sample
+{
+ public class MeadowApp : App
+ {
+ //
+
+ Amg8833 camera;
+
+ public override Task Initialize()
+ {
+ Resolver.Log.Info("Initialize...");
+
+ var i2cBus = Device.CreateI2cBus(I2cBusSpeed.Fast);
+ camera = new Amg8833(i2cBus);
+
+ return Task.CompletedTask;
+ }
+
+ public override Task Run()
+ {
+ while (true)
+ {
+ var pixels = camera.ReadPixels();
+
+ //output the first 4 pixels
+ Resolver.Log.Info($"{pixels[0].Celsius:F1}°C, {pixels[1].Celsius:F1}°C, {pixels[2].Celsius:F1}°C, {pixels[3].Celsius:F1}°C");
+ }
+ }
+
+ //
+ }
+}
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Samples/Amg8833_Sample/meadow.config.yaml b/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Samples/Amg8833_Sample/meadow.config.yaml
new file mode 100644
index 0000000000..32363cb69c
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Amg8833/Samples/Amg8833_Sample/meadow.config.yaml
@@ -0,0 +1,2 @@
+MonoControl:
+ Options: --jit
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Mlx90640/Samples/Mlx90640_Sample/MeadowApp.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Mlx90640/Samples/Mlx90640_Sample/MeadowApp.cs
index 55b1576328..4aa0f31ce4 100644
--- a/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Mlx90640/Samples/Mlx90640_Sample/MeadowApp.cs
+++ b/Source/Meadow.Foundation.Peripherals/Sensors.Camera.Mlx90640/Samples/Mlx90640_Sample/MeadowApp.cs
@@ -6,7 +6,7 @@
using System.Threading;
using System.Threading.Tasks;
-namespace Sensors.Temperature.MLX90640_Sample
+namespace Sensors.Cameras.Mlx90640_Sample
{
public class MeadowApp : App
{
diff --git a/Source/Meadow.Foundation.sln b/Source/Meadow.Foundation.sln
index 5ef89589b2..4a292225a4 100644
--- a/Source/Meadow.Foundation.sln
+++ b/Source/Meadow.Foundation.sln
@@ -1423,6 +1423,28 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{EF92
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DFRobotGravityDOMeter_Sample", "Meadow.Foundation.Peripherals\Sensors.Environmental.DFRobotGravityDOMeter\Samples\DFRobotGravityDOMeter_Sample\DFRobotGravityDOMeter_Sample.csproj", "{2C3B5884-5874-4073-AF55-ED01EA4D9FFA}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Amg8833", "Amg8833", "{6CBC4642-3BD1-463B-B759-9AB8A5B22D5C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sensors.Camera.Amg8833", "Meadow.Foundation.Peripherals\Sensors.Camera.Amg8833\Driver\Sensors.Camera.Amg8833.csproj", "{F15F6DFE-37D1-4E44-973E-E9F7295DAAC5}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{E608C8E7-1B76-4859-9AF9-C868B5C61994}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Cp2112", "Cp2112", "{087005C6-1E96-490D-94FF-90CF75F10E53}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ICs.IOExpanders.Cp2112", "Meadow.Foundation.Peripherals\ICs.IOExpanders.Cp2112\Driver\ICs.IOExpanders.Cp2112.csproj", "{5A740B20-BD2A-4D8C-B37D-1F5AA0E5355B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Linux", "..\..\Meadow.Core\source\implementations\linux\Meadow.Linux\Meadow.Linux.csproj", "{CD273618-53A8-493C-9478-61C173210C84}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AsciiConsole", "AsciiConsole", "{E775FF9F-8AC5-4994-B45A-20B50E2B03E2}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Displays.AsciiConsole", "Meadow.Foundation.Peripherals\Displays.AsciiConsole\Driver\Meadow.Displays.AsciiConsole.csproj", "{EB5F7986-54CD-4DC0-B045-9C3E1C32E686}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{40687DAC-F3EF-4F70-850A-0429B9586648}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsciiConsoleDisplay_Sample", "Meadow.Foundation.Peripherals\Displays.AsciiConsole\Samples\AsciiConsoleDisplay_Sample\AsciiConsoleDisplay_Sample.csproj", "{2F20AD5B-5879-432B-B44F-8540DAEE5C43}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Amg8833_Sample", "Meadow.Foundation.Peripherals\Sensors.Camera.Amg8833\Samples\Amg8833_Sample\Amg8833_Sample.csproj", "{866EA918-39E3-4440-877C-70A3205A3D00}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -3437,6 +3459,40 @@ Global
{2C3B5884-5874-4073-AF55-ED01EA4D9FFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2C3B5884-5874-4073-AF55-ED01EA4D9FFA}.Release|Any CPU.Build.0 = Release|Any CPU
{2C3B5884-5874-4073-AF55-ED01EA4D9FFA}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {F15F6DFE-37D1-4E44-973E-E9F7295DAAC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F15F6DFE-37D1-4E44-973E-E9F7295DAAC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F15F6DFE-37D1-4E44-973E-E9F7295DAAC5}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {F15F6DFE-37D1-4E44-973E-E9F7295DAAC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F15F6DFE-37D1-4E44-973E-E9F7295DAAC5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F15F6DFE-37D1-4E44-973E-E9F7295DAAC5}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {5A740B20-BD2A-4D8C-B37D-1F5AA0E5355B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5A740B20-BD2A-4D8C-B37D-1F5AA0E5355B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5A740B20-BD2A-4D8C-B37D-1F5AA0E5355B}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {5A740B20-BD2A-4D8C-B37D-1F5AA0E5355B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5A740B20-BD2A-4D8C-B37D-1F5AA0E5355B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5A740B20-BD2A-4D8C-B37D-1F5AA0E5355B}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {CD273618-53A8-493C-9478-61C173210C84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD273618-53A8-493C-9478-61C173210C84}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD273618-53A8-493C-9478-61C173210C84}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {CD273618-53A8-493C-9478-61C173210C84}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD273618-53A8-493C-9478-61C173210C84}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CD273618-53A8-493C-9478-61C173210C84}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {EB5F7986-54CD-4DC0-B045-9C3E1C32E686}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EB5F7986-54CD-4DC0-B045-9C3E1C32E686}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EB5F7986-54CD-4DC0-B045-9C3E1C32E686}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {EB5F7986-54CD-4DC0-B045-9C3E1C32E686}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EB5F7986-54CD-4DC0-B045-9C3E1C32E686}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EB5F7986-54CD-4DC0-B045-9C3E1C32E686}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {2F20AD5B-5879-432B-B44F-8540DAEE5C43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2F20AD5B-5879-432B-B44F-8540DAEE5C43}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2F20AD5B-5879-432B-B44F-8540DAEE5C43}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2F20AD5B-5879-432B-B44F-8540DAEE5C43}.Release|Any CPU.Build.0 = Release|Any CPU
+ {866EA918-39E3-4440-877C-70A3205A3D00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {866EA918-39E3-4440-877C-70A3205A3D00}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {866EA918-39E3-4440-877C-70A3205A3D00}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {866EA918-39E3-4440-877C-70A3205A3D00}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {866EA918-39E3-4440-877C-70A3205A3D00}.Release|Any CPU.Build.0 = Release|Any CPU
+ {866EA918-39E3-4440-877C-70A3205A3D00}.Release|Any CPU.Deploy.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -4147,6 +4203,17 @@ Global
{CA8E6A39-0CDD-4730-BC37-E33F3348FC1D} = {E144FE5A-48B1-496B-8042-CD0099C04512}
{EF92E69C-0E9A-4E11-86B2-C0537F440C74} = {E144FE5A-48B1-496B-8042-CD0099C04512}
{2C3B5884-5874-4073-AF55-ED01EA4D9FFA} = {EF92E69C-0E9A-4E11-86B2-C0537F440C74}
+ {6CBC4642-3BD1-463B-B759-9AB8A5B22D5C} = {91DE515A-DFF9-4B75-809C-F56C51222310}
+ {F15F6DFE-37D1-4E44-973E-E9F7295DAAC5} = {6CBC4642-3BD1-463B-B759-9AB8A5B22D5C}
+ {E608C8E7-1B76-4859-9AF9-C868B5C61994} = {6CBC4642-3BD1-463B-B759-9AB8A5B22D5C}
+ {087005C6-1E96-490D-94FF-90CF75F10E53} = {86B81B21-8C90-4A99-B113-FA71F8A6CD8D}
+ {5A740B20-BD2A-4D8C-B37D-1F5AA0E5355B} = {087005C6-1E96-490D-94FF-90CF75F10E53}
+ {CD273618-53A8-493C-9478-61C173210C84} = {65C50059-6C22-43E9-88DE-AD73F7F108C8}
+ {E775FF9F-8AC5-4994-B45A-20B50E2B03E2} = {2B794146-DFEE-475A-B919-7D3ED48587B8}
+ {EB5F7986-54CD-4DC0-B045-9C3E1C32E686} = {E775FF9F-8AC5-4994-B45A-20B50E2B03E2}
+ {40687DAC-F3EF-4F70-850A-0429B9586648} = {E775FF9F-8AC5-4994-B45A-20B50E2B03E2}
+ {2F20AD5B-5879-432B-B44F-8540DAEE5C43} = {40687DAC-F3EF-4F70-850A-0429B9586648}
+ {866EA918-39E3-4440-877C-70A3205A3D00} = {E608C8E7-1B76-4859-9AF9-C868B5C61994}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AF7CA16F-8C38-4546-87A2-5DAAF58A1520}