diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Driver/Controls/Circle.cs b/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Driver/Controls/Circle.cs index dff34de8e5..f13cc1fba4 100644 --- a/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Driver/Controls/Circle.cs +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Driver/Controls/Circle.cs @@ -1,17 +1,13 @@ namespace Meadow.Foundation.Graphics.MicroLayout; -public struct Coordinate2D -{ - public int X { get; set; } - public int Y { get; set; } -} - /// /// Represents a circle in the user interface. /// public class Circle : ThemedControl { - private Color _foreColor; + private Color foreColor; + private Point center; + private int radius; /// /// Gets or sets a value indicating whether the Circle is filled with the foreground color. @@ -25,7 +21,7 @@ public class Circle : ThemedControl /// The Y coordinate of the circles's center. /// The radius of the circle. public Circle(int centerX, int centerY, int radius) - : base(centerX - radius, centerY - radius, radius * 2, radius * 2) + : this(new Point(centerX, centerY), radius) { } @@ -34,9 +30,11 @@ public Circle(int centerX, int centerY, int radius) /// /// The coordinate of the circles's center. /// The radius of the circle. - public Circle(Coordinate2D center, int radius) + public Circle(Point center, int radius) : base(center.X - radius, center.Y - radius, radius * 2, radius * 2) { + this.center = center; + this.radius = radius; } /// @@ -56,8 +54,8 @@ public override void ApplyTheme(DisplayTheme theme) /// public Color ForeColor { - get => _foreColor; - set => SetInvalidatingProperty(ref _foreColor, value); + get => foreColor; + set => SetInvalidatingProperty(ref foreColor, value); } /// @@ -65,14 +63,14 @@ public Color ForeColor /// public int Radius { - get => Width / 2; + get => radius; set { - // keep centered - var coeff = (value > Radius) ? -1 : 1; - var offset = value - Radius; - - Width = value * 2; + radius = value; + Left = center.X - radius; + Width = radius * 2; + Top = center.Y - radius; + Height = radius * 2; } } @@ -84,10 +82,7 @@ protected override void OnDraw(MicroGraphics graphics) { if (ForeColor != Color.Transparent) { - var radius = (Right - Left) / 2; - var centerX = Left + radius; - var centerY = Top + radius; - graphics.DrawCircle(centerX, centerY, radius, ForeColor, IsFilled); + graphics.DrawCircle(center.X, center.Y, radius, ForeColor, IsFilled); } } } diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Driver/Controls/Crosshair.cs b/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Driver/Controls/Crosshair.cs new file mode 100644 index 0000000000..cbf2162691 --- /dev/null +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Driver/Controls/Crosshair.cs @@ -0,0 +1,52 @@ +namespace Meadow.Foundation.Graphics.MicroLayout; + +/// +/// Represents a circle in the user interface. +/// +public class Crosshair : ThemedControl +{ + private Color _foreColor = Color.Black; + private int _lineWidth; + + /// + /// Initializes a new instance of the class with the specified dimensions and center point. + /// + /// The X coordinate of the crosshair's center. + /// The Y coordinate of the crosshair's center. + /// The with and height of the crosshair. + /// The line width of the crosshair + public Crosshair(int centerX, int centerY, int size = 20, int linewidth = 3) + : base(centerX, centerY, size, size) + { + _lineWidth = linewidth; + } + + /// + public override void ApplyTheme(DisplayTheme theme) + { + if (theme != null) + { + if (theme.ForegroundColor != null) this.ForeColor = theme.ForegroundColor.Value; + } + } + + /// + /// Gets or sets the foreground color of the Crosshair. + /// + public Color ForeColor + { + get => _foreColor; + set => SetInvalidatingProperty(ref _foreColor, value); + } + + /// + protected override void OnDraw(MicroGraphics graphics) + { + if (ForeColor != Color.Transparent) + { + // position is the center of the crosshair + graphics.DrawRectangle(Left - Width / 2, Top - _lineWidth / 2, Width, _lineWidth, ForeColor, true); + graphics.DrawRectangle(Left - _lineWidth / 2, Top - Height / 2, _lineWidth, Height, ForeColor, true); + } + } +} 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 f364ee6468..c453ee6e1f 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 @@ -12,9 +12,13 @@ public class DisplayScreen { private readonly IPixelDisplay _display; private readonly MicroGraphics _graphics; - private readonly ITouchScreen? _touchScreen; private bool _updateInProgress = false; + /// + /// Gets the Touchscreen associated with the display screen + /// + public ITouchScreen? TouchScreen { get; } + /// /// Gets the collection of controls on the display screen. /// @@ -56,12 +60,12 @@ public DisplayScreen(IPixelDisplay physicalDisplay, RotationType rotation = Rota _graphics.Rotation = rotation; - _touchScreen = touchScreen; + TouchScreen = touchScreen; - if (_touchScreen != null) + if (TouchScreen != null) { - _touchScreen.TouchDown += _touchScreen_TouchDown; - _touchScreen.TouchUp += _touchScreen_TouchUp; + TouchScreen.TouchDown += _touchScreen_TouchDown; + TouchScreen.TouchUp += _touchScreen_TouchUp; } if (theme?.Font != null) @@ -81,24 +85,31 @@ public DisplayScreen(IPixelDisplay physicalDisplay, RotationType rotation = Rota } } - private void _touchScreen_TouchUp(int x, int y) + private void _touchScreen_TouchUp(ITouchScreen source, TouchPoint point) { - lock (Controls.SyncRoot) + if (Monitor.TryEnter(Controls.SyncRoot, 100)) { - foreach (var control in Controls) + try { - if (control is IClickableControl c) + foreach (var control in Controls) { - if (control.Contains(x, y)) + if (control is IClickableControl c) { - c.Pressed = false; + if (control.Contains(point.ScreenX, point.ScreenY)) + { + c.Pressed = false; + } } } } + finally + { + Monitor.Exit(Controls.SyncRoot); + } } } - private void _touchScreen_TouchDown(int x, int y) + private void _touchScreen_TouchDown(ITouchScreen source, TouchPoint point) { lock (Controls.SyncRoot) { @@ -106,7 +117,7 @@ private void _touchScreen_TouchDown(int x, int y) { if (control is IClickableControl c) { - if (control.Contains(x, y)) + if (control.Contains(point.ScreenX, point.ScreenY)) { c.Pressed = true; } diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Samples/Ili9488Charts_Sample/Ili9488Charts_Sample.csproj b/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Samples/Ili9488Charts_Sample/Ili9488Charts_Sample.csproj index 95c0965525..26b8a6655d 100644 --- a/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Samples/Ili9488Charts_Sample/Ili9488Charts_Sample.csproj +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Samples/Ili9488Charts_Sample/Ili9488Charts_Sample.csproj @@ -15,10 +15,4 @@ - - - PreserveNewest - - - diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Samples/Ili9488Charts_Sample/libmpsse.dll b/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Samples/Ili9488Charts_Sample/libmpsse.dll deleted file mode 100644 index a3d19379e8..0000000000 Binary files a/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Samples/Ili9488Charts_Sample/libmpsse.dll and /dev/null differ diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Samples/MAX7219_Sample/MAX7219_Sample.csproj b/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Samples/MAX7219_Sample/MAX7219_Sample.csproj index f10c615d58..8d8e294c89 100644 --- a/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Samples/MAX7219_Sample/MAX7219_Sample.csproj +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Samples/MAX7219_Sample/MAX7219_Sample.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net7.0-windows enable enable @@ -15,10 +15,4 @@ - - - PreserveNewest - - - diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Samples/MAX7219_Sample/libmpsse.dll b/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Samples/MAX7219_Sample/libmpsse.dll deleted file mode 100644 index a3d19379e8..0000000000 Binary files a/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Samples/MAX7219_Sample/libmpsse.dll and /dev/null differ diff --git a/Source/Meadow.Foundation.Peripherals/Displays.Gtk/Driver/Gtk.cs b/Source/Meadow.Foundation.Peripherals/Displays.Gtk/Driver/Gtk.cs index 4e55c7b192..1b6ede763e 100644 --- a/Source/Meadow.Foundation.Peripherals/Displays.Gtk/Driver/Gtk.cs +++ b/Source/Meadow.Foundation.Peripherals/Displays.Gtk/Driver/Gtk.cs @@ -12,18 +12,14 @@ namespace Meadow.Foundation.Displays; /// public class GtkDisplay : IPixelDisplay, ITouchScreen { - /// - /// Event fired when the display gets a mouse down - /// + /// public event Hardware.TouchEventHandler TouchDown = default!; - /// - /// Event fired when the display gets a mouse up - /// + /// public event Hardware.TouchEventHandler TouchUp = default!; - /// - /// Event fired when the display gets a mouse click - /// + /// public event Hardware.TouchEventHandler TouchClick = default!; + /// + public event Hardware.TouchEventHandler TouchMoved = default!; private Window _window = default!; private IPixelBuffer _pixelBuffer = default!; @@ -34,6 +30,9 @@ public class GtkDisplay : IPixelDisplay, ITouchScreen private EventWaitHandle ShowComplete { get; } = new EventWaitHandle(true, EventResetMode.ManualReset); + /// + public RotationType Rotation => RotationType.Normal; + /// public IPixelBuffer PixelBuffer => _pixelBuffer; @@ -49,6 +48,9 @@ public class GtkDisplay : IPixelDisplay, ITouchScreen /// public ColorMode SupportedColorModes => ColorMode.Format24bppRgb888 | ColorMode.Format16bppRgb565 | ColorMode.Format32bppRgba8888; + /// + public bool IsTouched { get; private set; } + static GtkDisplay() { Application.Init(); @@ -108,15 +110,15 @@ private void Initialize(int width, int height, ColorMode mode) private void RaiseTouchDown(double x, double y) { _leftButtonState = true; - TouchDown?.Invoke((int)x, (int)y); + TouchDown?.Invoke(this, TouchPoint.FromScreenData((int)x, (int)y, 0, (int)x, (int)y, 0)); } private void RaiseTouchUp(double x, double y) { - TouchUp?.Invoke((int)x, (int)y); + TouchUp?.Invoke(this, TouchPoint.FromScreenData((int)x, (int)y, 0, (int)x, (int)y, 0)); if (_leftButtonState) { - TouchClick?.Invoke((int)x, (int)y); + TouchClick?.Invoke(this, TouchPoint.FromScreenData((int)x, (int)y, 0, (int)x, (int)y, 0)); } _leftButtonState = false; @@ -133,9 +135,11 @@ private void WindowWidgetEvent(object o, WidgetEventArgs args) { case Gdk.ModifierType.None: RaiseTouchDown(b.X, b.Y); + IsTouched = true; break; case Gdk.ModifierType.Button1Mask: RaiseTouchUp(b.X, b.Y); + IsTouched = false; break; } break; diff --git a/Source/Meadow.Foundation.Peripherals/Displays.WinForms/Driver/WinForms.cs b/Source/Meadow.Foundation.Peripherals/Displays.WinForms/Driver/WinForms.cs index 453b13dd21..89ae850c3f 100644 --- a/Source/Meadow.Foundation.Peripherals/Displays.WinForms/Driver/WinForms.cs +++ b/Source/Meadow.Foundation.Peripherals/Displays.WinForms/Driver/WinForms.cs @@ -8,21 +8,20 @@ namespace Meadow.Foundation.Displays; /// public class WinFormsDisplay : Form, IPixelDisplay, ITouchScreen { - /// - /// Event fired when the display gets a mouse down - /// - public event TouchEventHandler TouchDown = default!; - /// - /// Event fired when the display gets a mouse up - /// - public event TouchEventHandler TouchUp = default!; - /// - /// Event fired when the display gets a mouse click - /// - public event TouchEventHandler TouchClick = default!; + /// + public event TouchEventHandler? TouchDown = default!; + /// + public event TouchEventHandler? TouchUp = default!; + /// + public event TouchEventHandler? TouchClick = default!; + /// + public event TouchEventHandler? TouchMoved = default!; private readonly WinFormsPixelBuffer _buffer; + /// + public RotationType Rotation => RotationType.Normal; + /// public ColorMode ColorMode => PixelBuffer.ColorMode; @@ -32,6 +31,9 @@ public class WinFormsDisplay : Form, IPixelDisplay, ITouchScreen /// public ColorMode SupportedColorModes => ColorMode.Format24bppRgb888; + /// + public bool IsTouched { get; private set; } + /// /// Create a new WinFormsDisplay /// @@ -64,21 +66,23 @@ protected override void Dispose(bool disposing) /// protected override void OnMouseDown(MouseEventArgs e) { - TouchDown?.Invoke(e.X, e.Y); + TouchDown?.Invoke(this, TouchPoint.FromScreenData(e.X, e.Y, 0, e.X, e.Y, 0)); + IsTouched = true; base.OnMouseDown(e); } /// protected override void OnMouseUp(MouseEventArgs e) { - TouchUp?.Invoke(e.X, e.Y); + TouchUp?.Invoke(this, TouchPoint.FromScreenData(e.X, e.Y, 0, e.X, e.Y, 0)); + IsTouched = false; base.OnMouseUp(e); } /// protected override void OnClick(EventArgs e) { - TouchClick?.Invoke(-1, -1); + TouchClick?.Invoke(this, TouchPoint.FromScreenData(-1, -1, 0, -1, -1, 0)); base.OnClick(e); } diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ftxxxx/Samples/Ft232h_Sample/Ft232h_Sample.csproj b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ftxxxx/Samples/Ft232h_Sample/Ft232h_Sample.csproj index 0f8aa6074a..2f1fbbc706 100644 --- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ftxxxx/Samples/Ft232h_Sample/Ft232h_Sample.csproj +++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Ftxxxx/Samples/Ft232h_Sample/Ft232h_Sample.csproj @@ -7,12 +7,6 @@ enable - - - PreserveNewest - - - diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Tsc2004/Samples/Tsc2004_Sample/MeadowApp.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Tsc2004/Samples/Tsc2004_Sample/MeadowApp.cs index 76eacf5dcd..e5e8df8d99 100644 --- a/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Tsc2004/Samples/Tsc2004_Sample/MeadowApp.cs +++ b/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Tsc2004/Samples/Tsc2004_Sample/MeadowApp.cs @@ -7,13 +7,13 @@ using System.Threading; using System.Threading.Tasks; -namespace Bbq10Keyboard_Sample +namespace Tsc2004_Sample { public class MeadowApp : App { // - Tsc2004 touchScreen; + private Tsc2004 touchScreen; public override Task Initialize() { diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Tsc2004/Samples/Tsc2004_Sample/Tsc2004_Sample.csproj b/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Tsc2004/Samples/Tsc2004_Sample/Tsc2004_Sample.csproj index 60c6a684d5..ec3c2ddc26 100644 --- a/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Tsc2004/Samples/Tsc2004_Sample/Tsc2004_Sample.csproj +++ b/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Tsc2004/Samples/Tsc2004_Sample/Tsc2004_Sample.csproj @@ -9,9 +9,4 @@ - - - Always - - diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Tsc2004/Samples/Tsc2004_Sample/meadow.config.yaml b/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Tsc2004/Samples/Tsc2004_Sample/meadow.config.yaml deleted file mode 100644 index 32363cb69c..0000000000 --- a/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Tsc2004/Samples/Tsc2004_Sample/meadow.config.yaml +++ /dev/null @@ -1,2 +0,0 @@ -MonoControl: - Options: --jit \ No newline at end of file diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Xpt2046/Datasheet/XPT2046.pdf b/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Xpt2046/Datasheet/XPT2046.pdf new file mode 100644 index 0000000000..f625c06d33 Binary files /dev/null and b/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Xpt2046/Datasheet/XPT2046.pdf differ diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Xpt2046/Driver/Sensors.Hid.Xpt2046.csproj b/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Xpt2046/Driver/Sensors.Hid.Xpt2046.csproj new file mode 100644 index 0000000000..7d68887bdc --- /dev/null +++ b/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Xpt2046/Driver/Sensors.Hid.Xpt2046.csproj @@ -0,0 +1,25 @@ + + + 1.8.0 + enable + 10.0 + Apache-2.0 + true + icon.png + Wilderness Labs, Inc + netstandard2.1 + Library + Xpt2046 + Wilderness Labs, Inc + http://developer.wildernesslabs.co/Meadow/Meadow.Foundation/ + Meadow.Foundation.Sensors.Hid.Xpt2046 + https://github.com/WildernessLabs/Meadow.Foundation + Meadow.Foundation,touch,screen,display,capacitive,XPT2046 + true + Xpt2046 SPI, 4-wire resistive touch screen controller + + + + + + diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Xpt2046/Driver/Xpt2046.Enums.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Xpt2046/Driver/Xpt2046.Enums.cs new file mode 100644 index 0000000000..4695a3b457 --- /dev/null +++ b/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Xpt2046/Driver/Xpt2046.Enums.cs @@ -0,0 +1,26 @@ +using System; + +namespace Meadow.Foundation.Sensors.Hid; + +public partial class Xpt2046 +{ + private enum Mode + { + Bits_12 = 0, + Bits_8 = 8 + } + + private enum VoltageReference + { + SingleEnded = 0, + Differential = 4 + } + + [Flags] + private enum PowerState + { + PowerDown = 0, + Adc = 1, + Reference = 2, + } +} \ No newline at end of file diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Xpt2046/Driver/Xpt2046.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Xpt2046/Driver/Xpt2046.cs new file mode 100644 index 0000000000..98bc62514a --- /dev/null +++ b/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Xpt2046/Driver/Xpt2046.cs @@ -0,0 +1,242 @@ +using Meadow.Hardware; +using Meadow.Peripherals.Displays; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +namespace Meadow.Foundation.Sensors.Hid +{ + /// + /// Represents an XPT2046 4-wire touch screen controller + /// + public partial class Xpt2046 : ICalibratableTouchscreen + { + /// + public event TouchEventHandler? TouchDown = null; + /// + public event TouchEventHandler? TouchUp = null; + /// + public event TouchEventHandler? TouchClick = null; + /// + public event TouchEventHandler? TouchMoved = null; + + private const int SamplePeriodMilliseconds = 100; + private const byte StartBit = 0x80; + + private readonly ISpiBus spiBus; + private readonly IDigitalInterruptPort touchInterrupt; + private readonly IDigitalOutputPort? chipSelect = null; + private Timer sampleTimer; + private bool isSampling = false; + private bool firstTouch = true; + private TouchPoint? lastTouchPosition; + private TouchPoint? penultimatePosition; + private float mX, mY, cX, cY; // linear calibration coefficeints + + /// + public RotationType Rotation { get; } + /// + public bool IsCalibrated { get; private set; } + + /// + public bool IsTouched => isSampling; + + /// + /// Creates an instance of an Xpt2046 + /// + /// The ISpiBus connected to the touchscreen controller + /// The interrupt port connected to the touchscreen controller + /// The chip select port for the touchscreen controller + /// The touchscreen rotation (not the display rotation) + public Xpt2046( + ISpiBus spiBus, + IDigitalInterruptPort touchInterrupt, + IDigitalOutputPort? chipSelect, + RotationType rotation = RotationType.Normal) + { + sampleTimer = new Timer(SampleTimerProc, null, -1, -1); + + this.spiBus = spiBus; + this.touchInterrupt = touchInterrupt; + this.chipSelect = chipSelect; + Rotation = rotation; + + touchInterrupt.Changed += OnTouchInterrupt; + } + + private TouchPoint ConvertRawToTouchPoint(ushort rawX, ushort rawY, ushort rawZ) + { + int x, y; + + // rotate + switch (Rotation) + { + case RotationType._90Degrees: + x = 4095 - rawY; + y = rawX; + break; + case RotationType._180Degrees: + x = 4095 - rawX; + y = 4095 - rawY; + break; + case RotationType._270Degrees: + x = rawY; + y = 4095 - rawX; + break; + default: + x = rawX; + y = rawY; + break; + } + + if (!IsCalibrated) + { + return TouchPoint.FromRawData(x, y, rawZ); + } + + // scale for calibration + var scaledX = ((x * mX) + cX); + var scaledY = ((y * mY) + cY); + + return TouchPoint.FromScreenData((int)scaledX, (int)scaledY, rawZ, rawX, rawY, rawZ); + } + + private void OnTouchInterrupt(object sender, DigitalPortResult e) + { + // high is not touched, low is touched + if (!isSampling) + { + sampleTimer.Change(0, -1); + } + } + + private void SampleTimerProc(object o) + { + if (touchInterrupt.State) + { + // the actual "last" reading for up is often garbage, so go back one before that if we have it + if (penultimatePosition != null) + { + TouchUp?.Invoke(this, penultimatePosition.Value); + } + else if (lastTouchPosition != null) + { + TouchUp?.Invoke(this, lastTouchPosition.Value); + } + isSampling = false; + firstTouch = true; + lastTouchPosition = null; + penultimatePosition = null; + return; + } + + isSampling = true; + + var z = ReadZ(); + var x = ReadX(); + var y = ReadY(); + EnableIrq(); + + var position = ConvertRawToTouchPoint(x, y, z); + + try + { + if (firstTouch) + { + firstTouch = false; + lastTouchPosition = position; + if (lastTouchPosition != null) + { + TouchDown?.Invoke(this, lastTouchPosition.Value); + } + } + else + { + if (!position.Equals(lastTouchPosition)) + { + penultimatePosition = lastTouchPosition; + lastTouchPosition = position; + if (lastTouchPosition != null) + { + TouchMoved?.Invoke(this, lastTouchPosition.Value); + } + } + } + } + catch (Exception ex) + { + Resolver.Log.Warn($"Touchscreen event handler error: {ex.Message}"); + // ignore any unhandled handler exceptions + } + + sampleTimer.Change(SamplePeriodMilliseconds, -1); + } + + private ushort ReadX() + { + return ReadChannel(Channel.X); + } + + private ushort ReadY() + { + return ReadChannel(Channel.Y); + } + + private ushort ReadZ() + { + return ReadChannel(Channel.Z2); + } + + private void EnableIrq() + { + ReadChannel(Channel.Temp, PowerState.PowerDown); + } + + private ushort ReadChannel(Channel channel, PowerState postSamplePowerState = PowerState.Adc, Mode mode = Mode.Bits_12, VoltageReference vref = VoltageReference.Differential) + { + Span txBuffer = stackalloc byte[3]; + Span rxBuffer = stackalloc byte[3]; + + txBuffer[0] = (byte)(StartBit | (byte)channel | (byte)mode | (byte)vref | (byte)postSamplePowerState); + + spiBus.Exchange(chipSelect, txBuffer, rxBuffer); + + return (ushort)((rxBuffer[1] >> 3) << 8 | (rxBuffer[2] >> 3)); + } + + /// + public void SetCalibrationData(IEnumerable data) + { + var points = data.ToArray(); + if (points.Length != 2) { throw new ArgumentException("This touchscreen requires exactly 2 calibration points"); } + + // basic point validation + if (points[1].RawX - points[0].RawX == 0 || + points[1].RawY - points[0].RawY == 0 || + points[1].ScreenX - points[0].ScreenX == 0 || + points[1].ScreenY - points[0].ScreenY == 0) + { + throw new ArgumentOutOfRangeException("Invalid calibration data"); + } + + // simple 2-point linear calibration (fine for small screens) + + mX = (points[1].ScreenX - points[0].ScreenX) / (float)(points[1].RawX - points[0].RawX); + cX = points[0].ScreenX - (points[0].RawX * mX); + mY = (points[1].ScreenY - points[0].ScreenY) / (float)(points[1].RawY - points[0].RawY); + cY = points[0].ScreenY - (points[0].RawY * mY); + + IsCalibrated = true; + } + + private enum Channel : byte + { + Temp = 0x00, + X = 1 << 4, + Z1 = 3 << 4, + Z2 = 4 << 4, + Y = 5 << 4, + } + } +} \ No newline at end of file diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Xpt2046/Samples/Xpt2046_Sample/MeadowApp.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Xpt2046/Samples/Xpt2046_Sample/MeadowApp.cs new file mode 100644 index 0000000000..cd3a949a1a --- /dev/null +++ b/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Xpt2046/Samples/Xpt2046_Sample/MeadowApp.cs @@ -0,0 +1,38 @@ +using Meadow; +using Meadow.Devices; +using Meadow.Foundation.Sensors.Hid; +using Meadow.Hardware; +using System.Threading.Tasks; + +namespace Xpt2046_Sample +{ + public class MeadowApp : App + { + // + + private Xpt2046 touchScreen; + + public override Task Initialize() + { + Resolver.Log.Info("Initialize..."); + + var i2cBus = Device.CreateI2cBus(I2cBusSpeed.Fast); + + touchScreen = new Xpt2046( + Device.CreateSpiBus(), + Device.Pins.D04.CreateDigitalInterruptPort(InterruptMode.EdgeFalling, ResistorMode.InternalPullUp), + Device.Pins.D05.CreateDigitalOutputPort(true)); + + touchScreen.TouchDown += TouchScreen_TouchDown; + + return Task.CompletedTask; + } + + private void TouchScreen_TouchDown(ITouchScreen sender, TouchPoint point) + { + Resolver.Log.Info($"Touch at location: X:{point.ScreenX}, Y:{point.ScreenY}"); + } + + // + } +} \ No newline at end of file diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Xpt2046/Samples/Xpt2046_Sample/Xpt2046_Sample.csproj b/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Xpt2046/Samples/Xpt2046_Sample/Xpt2046_Sample.csproj new file mode 100644 index 0000000000..daf355f523 --- /dev/null +++ b/Source/Meadow.Foundation.Peripherals/Sensors.Hid.Xpt2046/Samples/Xpt2046_Sample/Xpt2046_Sample.csproj @@ -0,0 +1,12 @@ + + + netstandard2.1 + true + Library + App + + + + + + diff --git a/Source/Meadow.Foundation.sln b/Source/Meadow.Foundation.sln index 4523e93c07..eb2f244f49 100644 --- a/Source/Meadow.Foundation.sln +++ b/Source/Meadow.Foundation.sln @@ -1055,6 +1055,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sw18AB_Sample", "Meadow.Fou EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sht4x_Sample", "Meadow.Foundation.Peripherals\Sensors.Atmospheric.Sht4x\Samples\Sht4x_Sample\Sht4x_Sample.csproj", "{831C87F3-4925-4311-83A4-C07CE4CD109E}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet", "..\..\MQTTnet\Source\MQTTnet\MQTTnet.csproj", "{810BF526-53F8-4DFB-A32A-A67CB5FFF785}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sensors.Light.AnalogLightSensor_Sample", "Meadow.Foundation.Core.Samples\Sensors.Light.AnalogLightSensor_Sample\Sensors.Light.AnalogLightSensor_Sample.csproj", "{5A2538C7-D110-4DFB-A77B-C328CDDF7848}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Thermistor", "Thermistor", "{FF251CAE-8D0B-45E2-BAA4-FE2991D527F7}" @@ -1115,10 +1117,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Modbus", "..\..\Mead EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.F7", "..\..\Meadow.Core\source\implementations\f7\Meadow.F7\Meadow.F7.csproj", "{404AAC33-B6F1-4EA3-A246-FD9D8A95E93F}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ft232h", "Ft232h", "{4E0052C2-559D-4509-9C42-C6451B1F1FFC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{67B5150B-2FDE-4EA8-93EE-8BB31D5D7789}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WinForms", "WinForms", "{BBF136B7-361C-4FC2-AACA-6B6ED79BDCF1}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Displays.WinForms", "Meadow.Foundation.Peripherals\Displays.WinForms\Driver\Displays.WinForms.csproj", "{2DFB37A0-07FE-479F-8B30-D2B5F7E209D2}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gtk", "Gtk", "{14ACE12B-5B23-4FF3-B3C2-BFECB21064A6}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Displays.Gtk", "Meadow.Foundation.Peripherals\Displays.Gtk\Driver\Displays.Gtk.csproj", "{768E2B04-FBE8-4464-B5DE-F5AA010E9D33}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Epd5in65f_Sample", "Meadow.Foundation.Peripherals\Displays.ePaperWaveShare\Samples\Epd5in65f_Sample\Epd5in65f_Sample.csproj", "{16A387A9-80AA-4080-85F6-10F53D6EC99E}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Pmsa003i", "Pmsa003i", "{AD6DD7B6-B896-44B9-904E-9330C2C842C7}" @@ -1443,19 +1453,19 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{FC2E EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pct2075_Sample", "Meadow.Foundation.Peripherals\Sensors.Temperature.Pct2075\Samples\Pct2075_Sample\Pct2075_Sample.csproj", "{2B29B1A8-8903-4335-A5CC-6FFBD9069C2D}" EndProject -Project("{9344BDBB-3E7F-41FC-A0DD-8665D75EE146}") = "Displays.WinForms", "Meadow.Foundation.Peripherals\Displays.WinForms\Driver\Displays.WinForms.csproj", "{FAE78BFF-B2D4-4AAD-9E45-A52A4AB6AC28}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Touchscreen", "Touchscreen", "{B773E1A0-FA17-4D5A-BAF9-29CA3CF00789}" EndProject -Project("{9344BDBB-3E7F-41FC-A0DD-8665D75EE146}") = "Displays.Gtk", "Meadow.Foundation.Peripherals\Displays.Gtk\Driver\Displays.Gtk.csproj", "{07DE237C-5288-4577-9443-D2B7D43D3775}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Xpt2046", "Xpt2046", "{FB71B923-4A40-4C68-BEA9-846A57813ED2}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ftxxxx", "Ftxxxx", "{FB4CEFDD-BEE3-4858-8A53-D92C4D1B6297}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sensors.Hid.Xpt2046", "Meadow.Foundation.Peripherals\Sensors.Hid.Xpt2046\Driver\Sensors.Hid.Xpt2046.csproj", "{C399419B-04D5-40A2-9501-F21DEDAF3EC6}" EndProject -Project("{9344BDBB-3E7F-41FC-A0DD-8665D75EE146}") = "MQTTnet", "..\..\MQTTnet\Source\MQTTnet\MQTTnet.csproj", "{6768E5DB-C1BA-43FB-A7FF-668982CF3CC6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ICs.IOExpanders.Ftxxxx", "Meadow.Foundation.Peripherals\ICs.IOExpanders.Ftxxxx\Driver\ICs.IOExpanders.Ftxxxx.csproj", "{5D1417F3-5B31-4560-9943-DF5F74FA14C0}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{5DCA2786-ED9A-47E9-8A07-6F3FF2B73A53}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ft232h_Sample", "Meadow.Foundation.Peripherals\ICs.IOExpanders.Ftxxxx\Samples\Ft232h_Sample\Ft232h_Sample.csproj", "{0189CCB2-916B-4448-BC3B-665736BB8806}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICs.IOExpanders.Ftxxxx", "Meadow.Foundation.Peripherals\ICs.IOExpanders.Ftxxxx\Driver\ICs.IOExpanders.Ftxxxx.csproj", "{D0167772-53C1-4ECB-A486-E36D354FC887}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{927B2E17-5220-45B0-B1D7-0BB92A430E68}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ft232h_Sample", "Meadow.Foundation.Peripherals\ICs.IOExpanders.Ftxxxx\Samples\Ft232h_Sample\Ft232h_Sample.csproj", "{BE8B85B9-5FC3-439F-86A8-4D4E028B9DC3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xpt2046_Sample", "Meadow.Foundation.Peripherals\Sensors.Hid.Xpt2046\Samples\Xpt2046_Sample\Xpt2046_Sample.csproj", "{0592DDD2-FBEE-45DC-9DB0-191EF38FE12C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -2865,6 +2875,10 @@ Global {831C87F3-4925-4311-83A4-C07CE4CD109E}.Release|Any CPU.ActiveCfg = Release|Any CPU {831C87F3-4925-4311-83A4-C07CE4CD109E}.Release|Any CPU.Build.0 = Release|Any CPU {831C87F3-4925-4311-83A4-C07CE4CD109E}.Release|Any CPU.Deploy.0 = Release|Any CPU + {810BF526-53F8-4DFB-A32A-A67CB5FFF785}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {810BF526-53F8-4DFB-A32A-A67CB5FFF785}.Debug|Any CPU.Build.0 = Debug|Any CPU + {810BF526-53F8-4DFB-A32A-A67CB5FFF785}.Release|Any CPU.ActiveCfg = Release|Any CPU + {810BF526-53F8-4DFB-A32A-A67CB5FFF785}.Release|Any CPU.Build.0 = Release|Any CPU {5A2538C7-D110-4DFB-A77B-C328CDDF7848}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5A2538C7-D110-4DFB-A77B-C328CDDF7848}.Debug|Any CPU.Build.0 = Debug|Any CPU {5A2538C7-D110-4DFB-A77B-C328CDDF7848}.Debug|Any CPU.Deploy.0 = Debug|Any CPU @@ -2969,6 +2983,14 @@ Global {404AAC33-B6F1-4EA3-A246-FD9D8A95E93F}.Release|Any CPU.ActiveCfg = Release|Any CPU {404AAC33-B6F1-4EA3-A246-FD9D8A95E93F}.Release|Any CPU.Build.0 = Release|Any CPU {404AAC33-B6F1-4EA3-A246-FD9D8A95E93F}.Release|Any CPU.Deploy.0 = Release|Any CPU + {2DFB37A0-07FE-479F-8B30-D2B5F7E209D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2DFB37A0-07FE-479F-8B30-D2B5F7E209D2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2DFB37A0-07FE-479F-8B30-D2B5F7E209D2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2DFB37A0-07FE-479F-8B30-D2B5F7E209D2}.Release|Any CPU.Build.0 = Release|Any CPU + {768E2B04-FBE8-4464-B5DE-F5AA010E9D33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {768E2B04-FBE8-4464-B5DE-F5AA010E9D33}.Debug|Any CPU.Build.0 = Debug|Any CPU + {768E2B04-FBE8-4464-B5DE-F5AA010E9D33}.Release|Any CPU.ActiveCfg = Release|Any CPU + {768E2B04-FBE8-4464-B5DE-F5AA010E9D33}.Release|Any CPU.Build.0 = Release|Any CPU {16A387A9-80AA-4080-85F6-10F53D6EC99E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {16A387A9-80AA-4080-85F6-10F53D6EC99E}.Debug|Any CPU.Build.0 = Debug|Any CPU {16A387A9-80AA-4080-85F6-10F53D6EC99E}.Debug|Any CPU.Deploy.0 = Debug|Any CPU @@ -3499,26 +3521,28 @@ Global {2B29B1A8-8903-4335-A5CC-6FFBD9069C2D}.Release|Any CPU.ActiveCfg = Release|Any CPU {2B29B1A8-8903-4335-A5CC-6FFBD9069C2D}.Release|Any CPU.Build.0 = Release|Any CPU {2B29B1A8-8903-4335-A5CC-6FFBD9069C2D}.Release|Any CPU.Deploy.0 = Release|Any CPU - {FAE78BFF-B2D4-4AAD-9E45-A52A4AB6AC28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FAE78BFF-B2D4-4AAD-9E45-A52A4AB6AC28}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FAE78BFF-B2D4-4AAD-9E45-A52A4AB6AC28}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FAE78BFF-B2D4-4AAD-9E45-A52A4AB6AC28}.Release|Any CPU.Build.0 = Release|Any CPU - {07DE237C-5288-4577-9443-D2B7D43D3775}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {07DE237C-5288-4577-9443-D2B7D43D3775}.Debug|Any CPU.Build.0 = Debug|Any CPU - {07DE237C-5288-4577-9443-D2B7D43D3775}.Release|Any CPU.ActiveCfg = Release|Any CPU - {07DE237C-5288-4577-9443-D2B7D43D3775}.Release|Any CPU.Build.0 = Release|Any CPU - {6768E5DB-C1BA-43FB-A7FF-668982CF3CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6768E5DB-C1BA-43FB-A7FF-668982CF3CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6768E5DB-C1BA-43FB-A7FF-668982CF3CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6768E5DB-C1BA-43FB-A7FF-668982CF3CC6}.Release|Any CPU.Build.0 = Release|Any CPU - {D0167772-53C1-4ECB-A486-E36D354FC887}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D0167772-53C1-4ECB-A486-E36D354FC887}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D0167772-53C1-4ECB-A486-E36D354FC887}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D0167772-53C1-4ECB-A486-E36D354FC887}.Release|Any CPU.Build.0 = Release|Any CPU - {BE8B85B9-5FC3-439F-86A8-4D4E028B9DC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BE8B85B9-5FC3-439F-86A8-4D4E028B9DC3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BE8B85B9-5FC3-439F-86A8-4D4E028B9DC3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BE8B85B9-5FC3-439F-86A8-4D4E028B9DC3}.Release|Any CPU.Build.0 = Release|Any CPU + {C399419B-04D5-40A2-9501-F21DEDAF3EC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C399419B-04D5-40A2-9501-F21DEDAF3EC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C399419B-04D5-40A2-9501-F21DEDAF3EC6}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {C399419B-04D5-40A2-9501-F21DEDAF3EC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C399419B-04D5-40A2-9501-F21DEDAF3EC6}.Release|Any CPU.Build.0 = Release|Any CPU + {C399419B-04D5-40A2-9501-F21DEDAF3EC6}.Release|Any CPU.Deploy.0 = Release|Any CPU + {5D1417F3-5B31-4560-9943-DF5F74FA14C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5D1417F3-5B31-4560-9943-DF5F74FA14C0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5D1417F3-5B31-4560-9943-DF5F74FA14C0}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {5D1417F3-5B31-4560-9943-DF5F74FA14C0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5D1417F3-5B31-4560-9943-DF5F74FA14C0}.Release|Any CPU.Build.0 = Release|Any CPU + {5D1417F3-5B31-4560-9943-DF5F74FA14C0}.Release|Any CPU.Deploy.0 = Release|Any CPU + {0189CCB2-916B-4448-BC3B-665736BB8806}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0189CCB2-916B-4448-BC3B-665736BB8806}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0189CCB2-916B-4448-BC3B-665736BB8806}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0189CCB2-916B-4448-BC3B-665736BB8806}.Release|Any CPU.Build.0 = Release|Any CPU + {0592DDD2-FBEE-45DC-9DB0-191EF38FE12C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0592DDD2-FBEE-45DC-9DB0-191EF38FE12C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0592DDD2-FBEE-45DC-9DB0-191EF38FE12C}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {0592DDD2-FBEE-45DC-9DB0-191EF38FE12C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0592DDD2-FBEE-45DC-9DB0-191EF38FE12C}.Release|Any CPU.Build.0 = Release|Any CPU + {0592DDD2-FBEE-45DC-9DB0-191EF38FE12C}.Release|Any CPU.Deploy.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -3912,7 +3936,7 @@ Global {6AF2F8E5-423F-4EF7-9AB6-1978792598B7} = {8FB70D12-1111-480B-B64C-B8EB65977F58} {BD5FABCC-4C2A-4DA6-A506-7152C72D29E7} = {8FB70D12-1111-480B-B64C-B8EB65977F58} {598D3695-BEB7-4BD7-AE6A-C296F36265E7} = {0BB488AA-E23E-4697-BBD9-4A17B21BFCD7} - {372B055D-2279-47D2-B1A8-AB1E5670202F} = {818BF624-10A7-45B2-9BEE-4C411CD9CA06} + {372B055D-2279-47D2-B1A8-AB1E5670202F} = {B773E1A0-FA17-4D5A-BAF9-29CA3CF00789} {8908AAA5-53DD-4526-AAF8-01308F1316EF} = {372B055D-2279-47D2-B1A8-AB1E5670202F} {B167E0C6-F2D8-4B4F-B814-578B72B083BE} = {372B055D-2279-47D2-B1A8-AB1E5670202F} {C6D1270C-92AB-4FE2-BE93-29DD52F97AD4} = {B167E0C6-F2D8-4B4F-B814-578B72B083BE} @@ -4045,6 +4069,7 @@ Global {BBC875BB-E04F-4F9E-A7CC-023B0B1D2BC4} = {5F4B9D7E-81B4-4562-BEDC-3499A8DDE648} {3102742F-8C87-4C84-B257-1A49DDB3D6BD} = {5F4B9D7E-81B4-4562-BEDC-3499A8DDE648} {831C87F3-4925-4311-83A4-C07CE4CD109E} = {4657A98F-6D05-48EB-864D-E3D0DCA658C5} + {810BF526-53F8-4DFB-A32A-A67CB5FFF785} = {65C50059-6C22-43E9-88DE-AD73F7F108C8} {5A2538C7-D110-4DFB-A77B-C328CDDF7848} = {7EBB4434-F29C-4316-BEDC-F28F07CE4AC8} {FF251CAE-8D0B-45E2-BAA4-FE2991D527F7} = {DBC6C89D-A932-4F99-9382-7405A0045988} {CB8A3C9C-C1EA-4877-ABFC-76DF58FA7BA6} = {FF251CAE-8D0B-45E2-BAA4-FE2991D527F7} @@ -4075,8 +4100,12 @@ Global {E718B06A-FEAF-4E11-9344-E81272E214AA} = {F9B62A9D-4DDC-4646-9B43-06D471E0F82C} {DD749A41-50A3-4DB4-A84F-4372BDFCB742} = {65C50059-6C22-43E9-88DE-AD73F7F108C8} {404AAC33-B6F1-4EA3-A246-FD9D8A95E93F} = {65C50059-6C22-43E9-88DE-AD73F7F108C8} + {4E0052C2-559D-4509-9C42-C6451B1F1FFC} = {86B81B21-8C90-4A99-B113-FA71F8A6CD8D} + {67B5150B-2FDE-4EA8-93EE-8BB31D5D7789} = {4E0052C2-559D-4509-9C42-C6451B1F1FFC} {BBF136B7-361C-4FC2-AACA-6B6ED79BDCF1} = {2B794146-DFEE-475A-B919-7D3ED48587B8} + {2DFB37A0-07FE-479F-8B30-D2B5F7E209D2} = {BBF136B7-361C-4FC2-AACA-6B6ED79BDCF1} {14ACE12B-5B23-4FF3-B3C2-BFECB21064A6} = {2B794146-DFEE-475A-B919-7D3ED48587B8} + {768E2B04-FBE8-4464-B5DE-F5AA010E9D33} = {14ACE12B-5B23-4FF3-B3C2-BFECB21064A6} {16A387A9-80AA-4080-85F6-10F53D6EC99E} = {7311794D-7D2F-47E8-A5B0-C216CBD64A13} {AD6DD7B6-B896-44B9-904E-9330C2C842C7} = {78E463DA-0FA1-4AAE-A281-D3297C9388C9} {A3F37EFB-9686-4074-9962-5F731C52D919} = {AD6DD7B6-B896-44B9-904E-9330C2C842C7} @@ -4239,13 +4268,13 @@ Global {3C999A94-227A-470F-935E-966E375E40BB} = {7471C6BF-1995-4E56-91E9-86DAAA46C386} {FC2EEFA9-030B-4EF0-AFB7-1CC61876D1EE} = {7471C6BF-1995-4E56-91E9-86DAAA46C386} {2B29B1A8-8903-4335-A5CC-6FFBD9069C2D} = {FC2EEFA9-030B-4EF0-AFB7-1CC61876D1EE} - {FAE78BFF-B2D4-4AAD-9E45-A52A4AB6AC28} = {BBF136B7-361C-4FC2-AACA-6B6ED79BDCF1} - {07DE237C-5288-4577-9443-D2B7D43D3775} = {14ACE12B-5B23-4FF3-B3C2-BFECB21064A6} - {FB4CEFDD-BEE3-4858-8A53-D92C4D1B6297} = {86B81B21-8C90-4A99-B113-FA71F8A6CD8D} - {6768E5DB-C1BA-43FB-A7FF-668982CF3CC6} = {65C50059-6C22-43E9-88DE-AD73F7F108C8} - {5DCA2786-ED9A-47E9-8A07-6F3FF2B73A53} = {FB4CEFDD-BEE3-4858-8A53-D92C4D1B6297} - {D0167772-53C1-4ECB-A486-E36D354FC887} = {FB4CEFDD-BEE3-4858-8A53-D92C4D1B6297} - {BE8B85B9-5FC3-439F-86A8-4D4E028B9DC3} = {5DCA2786-ED9A-47E9-8A07-6F3FF2B73A53} + {B773E1A0-FA17-4D5A-BAF9-29CA3CF00789} = {818BF624-10A7-45B2-9BEE-4C411CD9CA06} + {FB71B923-4A40-4C68-BEA9-846A57813ED2} = {B773E1A0-FA17-4D5A-BAF9-29CA3CF00789} + {C399419B-04D5-40A2-9501-F21DEDAF3EC6} = {FB71B923-4A40-4C68-BEA9-846A57813ED2} + {5D1417F3-5B31-4560-9943-DF5F74FA14C0} = {4E0052C2-559D-4509-9C42-C6451B1F1FFC} + {0189CCB2-916B-4448-BC3B-665736BB8806} = {67B5150B-2FDE-4EA8-93EE-8BB31D5D7789} + {927B2E17-5220-45B0-B1D7-0BB92A430E68} = {FB71B923-4A40-4C68-BEA9-846A57813ED2} + {0592DDD2-FBEE-45DC-9DB0-191EF38FE12C} = {927B2E17-5220-45B0-B1D7-0BB92A430E68} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {AF7CA16F-8C38-4546-87A2-5DAAF58A1520}