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}