diff --git a/src/devices/Display/Display.csproj b/src/devices/Display/Display.csproj index b727bb84dd..216e36e803 100644 --- a/src/devices/Display/Display.csproj +++ b/src/devices/Display/Display.csproj @@ -3,6 +3,12 @@ $(DefaultBindingTfms) false + + portable + + + portable + @@ -11,6 +17,8 @@ + + @@ -22,6 +30,7 @@ + diff --git a/src/devices/Display/Display.sln b/src/devices/Display/Display.sln index 65a050ce8c..55483632ba 100644 --- a/src/devices/Display/Display.sln +++ b/src/devices/Display/Display.sln @@ -1,21 +1,24 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26124.0 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.35122.118 MinimumVisualStudioVersion = 15.0.26124.0 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{20F2E85F-D100-4EE6-BF7D-8BB8748D626C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Large4Digit7SegmentDisplay.sample", "samples\Large4Digit7SegmentDisplay\Large4Digit7SegmentDisplay.sample.csproj", "{C4D7AED7-B340-4DCD-974C-5150C6D5F074}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Large4Digit7SegmentDisplay.sample", "samples\Large4Digit7SegmentDisplay\Large4Digit7SegmentDisplay.sample.csproj", "{C4D7AED7-B340-4DCD-974C-5150C6D5F074}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Matrix.sample", "samples\Matrix\Matrix.sample.csproj", "{C4D7AED7-B340-4DCD-974C-5150C6D5F074}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Matrix.sample", "samples\Matrix\Matrix.sample.csproj", "{938399AC-21ED-4CAD-BC8B-EA75031FCFC3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Matrix8x8Bicolor.sample", "samples\Matrix8x8Bicolor\Matrix8x8Bicolor.sample.csproj", "{C4D7AED7-B340-4DCD-974C-5150C6D5F074}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Matrix8x8Bicolor.sample", "samples\Matrix8x8Bicolor\Matrix8x8Bicolor.sample.csproj", "{59564F6F-CA0A-4138-8D36-EDF770591081}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BiColorBargraph.sample", "samples\BiColorBargraph.sample.csproj", "{C4D7AED7-B340-4DCD-974C-5150C6D5F074}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BiColorBargraph.sample", "samples\BiColorBargraph.sample.csproj", "{A03ED5B0-4A75-418D-B168-290A6568B0D5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Display", "Display.csproj", "{4AAE8B9E-8540-4582-8083-D1F14104BAAC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Display", "Display.csproj", "{4AAE8B9E-8540-4582-8083-D1F14104BAAC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LedSegmentDisplay5641AS.Sample", "samples\LedSegmentDisplay5641AS.Sample\LedSegmentDisplay5641AS.Sample.csproj", "{9C7E6C39-38F5-40B9-8A9C-6D75749ACFAD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Button", "..\Button\Button.csproj", "{273B1DA5-52BA-4488-A82F-A24E2C0BEDE9}" EndProject - Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -25,9 +28,6 @@ Global Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {C4D7AED7-B340-4DCD-974C-5150C6D5F074}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C4D7AED7-B340-4DCD-974C-5150C6D5F074}.Debug|Any CPU.Build.0 = Debug|Any CPU @@ -41,6 +41,30 @@ Global {C4D7AED7-B340-4DCD-974C-5150C6D5F074}.Release|x64.Build.0 = Release|Any CPU {C4D7AED7-B340-4DCD-974C-5150C6D5F074}.Release|x86.ActiveCfg = Release|Any CPU {C4D7AED7-B340-4DCD-974C-5150C6D5F074}.Release|x86.Build.0 = Release|Any CPU + {938399AC-21ED-4CAD-BC8B-EA75031FCFC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {938399AC-21ED-4CAD-BC8B-EA75031FCFC3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {938399AC-21ED-4CAD-BC8B-EA75031FCFC3}.Debug|x64.ActiveCfg = Debug|Any CPU + {938399AC-21ED-4CAD-BC8B-EA75031FCFC3}.Debug|x64.Build.0 = Debug|Any CPU + {938399AC-21ED-4CAD-BC8B-EA75031FCFC3}.Debug|x86.ActiveCfg = Debug|Any CPU + {938399AC-21ED-4CAD-BC8B-EA75031FCFC3}.Debug|x86.Build.0 = Debug|Any CPU + {938399AC-21ED-4CAD-BC8B-EA75031FCFC3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {938399AC-21ED-4CAD-BC8B-EA75031FCFC3}.Release|Any CPU.Build.0 = Release|Any CPU + {938399AC-21ED-4CAD-BC8B-EA75031FCFC3}.Release|x64.ActiveCfg = Release|Any CPU + {938399AC-21ED-4CAD-BC8B-EA75031FCFC3}.Release|x64.Build.0 = Release|Any CPU + {938399AC-21ED-4CAD-BC8B-EA75031FCFC3}.Release|x86.ActiveCfg = Release|Any CPU + {938399AC-21ED-4CAD-BC8B-EA75031FCFC3}.Release|x86.Build.0 = Release|Any CPU + {59564F6F-CA0A-4138-8D36-EDF770591081}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {59564F6F-CA0A-4138-8D36-EDF770591081}.Debug|Any CPU.Build.0 = Debug|Any CPU + {59564F6F-CA0A-4138-8D36-EDF770591081}.Debug|x64.ActiveCfg = Debug|Any CPU + {59564F6F-CA0A-4138-8D36-EDF770591081}.Debug|x64.Build.0 = Debug|Any CPU + {59564F6F-CA0A-4138-8D36-EDF770591081}.Debug|x86.ActiveCfg = Debug|Any CPU + {59564F6F-CA0A-4138-8D36-EDF770591081}.Debug|x86.Build.0 = Debug|Any CPU + {59564F6F-CA0A-4138-8D36-EDF770591081}.Release|Any CPU.ActiveCfg = Release|Any CPU + {59564F6F-CA0A-4138-8D36-EDF770591081}.Release|Any CPU.Build.0 = Release|Any CPU + {59564F6F-CA0A-4138-8D36-EDF770591081}.Release|x64.ActiveCfg = Release|Any CPU + {59564F6F-CA0A-4138-8D36-EDF770591081}.Release|x64.Build.0 = Release|Any CPU + {59564F6F-CA0A-4138-8D36-EDF770591081}.Release|x86.ActiveCfg = Release|Any CPU + {59564F6F-CA0A-4138-8D36-EDF770591081}.Release|x86.Build.0 = Release|Any CPU {4AAE8B9E-8540-4582-8083-D1F14104BAAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4AAE8B9E-8540-4582-8083-D1F14104BAAC}.Debug|Any CPU.Build.0 = Debug|Any CPU {4AAE8B9E-8540-4582-8083-D1F14104BAAC}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -53,8 +77,40 @@ Global {4AAE8B9E-8540-4582-8083-D1F14104BAAC}.Release|x64.Build.0 = Release|Any CPU {4AAE8B9E-8540-4582-8083-D1F14104BAAC}.Release|x86.ActiveCfg = Release|Any CPU {4AAE8B9E-8540-4582-8083-D1F14104BAAC}.Release|x86.Build.0 = Release|Any CPU + {9C7E6C39-38F5-40B9-8A9C-6D75749ACFAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9C7E6C39-38F5-40B9-8A9C-6D75749ACFAD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9C7E6C39-38F5-40B9-8A9C-6D75749ACFAD}.Debug|x64.ActiveCfg = Debug|Any CPU + {9C7E6C39-38F5-40B9-8A9C-6D75749ACFAD}.Debug|x64.Build.0 = Debug|Any CPU + {9C7E6C39-38F5-40B9-8A9C-6D75749ACFAD}.Debug|x86.ActiveCfg = Debug|Any CPU + {9C7E6C39-38F5-40B9-8A9C-6D75749ACFAD}.Debug|x86.Build.0 = Debug|Any CPU + {9C7E6C39-38F5-40B9-8A9C-6D75749ACFAD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9C7E6C39-38F5-40B9-8A9C-6D75749ACFAD}.Release|Any CPU.Build.0 = Release|Any CPU + {9C7E6C39-38F5-40B9-8A9C-6D75749ACFAD}.Release|x64.ActiveCfg = Release|Any CPU + {9C7E6C39-38F5-40B9-8A9C-6D75749ACFAD}.Release|x64.Build.0 = Release|Any CPU + {9C7E6C39-38F5-40B9-8A9C-6D75749ACFAD}.Release|x86.ActiveCfg = Release|Any CPU + {9C7E6C39-38F5-40B9-8A9C-6D75749ACFAD}.Release|x86.Build.0 = Release|Any CPU + {273B1DA5-52BA-4488-A82F-A24E2C0BEDE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {273B1DA5-52BA-4488-A82F-A24E2C0BEDE9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {273B1DA5-52BA-4488-A82F-A24E2C0BEDE9}.Debug|x64.ActiveCfg = Debug|Any CPU + {273B1DA5-52BA-4488-A82F-A24E2C0BEDE9}.Debug|x64.Build.0 = Debug|Any CPU + {273B1DA5-52BA-4488-A82F-A24E2C0BEDE9}.Debug|x86.ActiveCfg = Debug|Any CPU + {273B1DA5-52BA-4488-A82F-A24E2C0BEDE9}.Debug|x86.Build.0 = Debug|Any CPU + {273B1DA5-52BA-4488-A82F-A24E2C0BEDE9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {273B1DA5-52BA-4488-A82F-A24E2C0BEDE9}.Release|Any CPU.Build.0 = Release|Any CPU + {273B1DA5-52BA-4488-A82F-A24E2C0BEDE9}.Release|x64.ActiveCfg = Release|Any CPU + {273B1DA5-52BA-4488-A82F-A24E2C0BEDE9}.Release|x64.Build.0 = Release|Any CPU + {273B1DA5-52BA-4488-A82F-A24E2C0BEDE9}.Release|x86.ActiveCfg = Release|Any CPU + {273B1DA5-52BA-4488-A82F-A24E2C0BEDE9}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {C4D7AED7-B340-4DCD-974C-5150C6D5F074} = {20F2E85F-D100-4EE6-BF7D-8BB8748D626C} + {9C7E6C39-38F5-40B9-8A9C-6D75749ACFAD} = {20F2E85F-D100-4EE6-BF7D-8BB8748D626C} + {273B1DA5-52BA-4488-A82F-A24E2C0BEDE9} = {20F2E85F-D100-4EE6-BF7D-8BB8748D626C} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DE260CF9-1B56-49A0-AAB0-61D3E71B4E88} EndGlobalSection EndGlobal diff --git a/src/devices/Display/LedSegmentDisplay5641AS.cs b/src/devices/Display/LedSegmentDisplay5641AS.cs new file mode 100644 index 0000000000..9abb48957c --- /dev/null +++ b/src/devices/Display/LedSegmentDisplay5641AS.cs @@ -0,0 +1,198 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Device.Gpio; +using System.Linq; +using System.Threading; +using Iot.Device.Display; + +namespace Display +{ + /// + /// LED 4-digit 7-segment display, model 5641AS. This class handles driverless, direct-pin access over GPIO. + /// + /// + /// Data Sheet: + /// http://www.xlitx.com/datasheet/5641AS.pdf + /// + public class LedSegmentDisplay5641AS : ISevenSegmentDisplay, IDisposable + { + private readonly LedSegmentDisplay5641ASPinScheme _pinScheme; + private readonly GpioController _gpioController; + private readonly bool _shouldDispose; + private bool _disposedValue; + + private List _segmentPins; + private List _digitPins; + + // address, Segment(s) + private Dictionary _displayBuffer; + + /// + public int NumberOfDigits => _pinScheme.DigitCount; + + /// + public Segment this[int address] + { + get => _displayBuffer[address]; + set => WriteDigitAtAddress(address, value); + } + + /// + /// Initializes a new instance of the class. + /// + public LedSegmentDisplay5641AS(LedSegmentDisplay5641ASPinScheme pinScheme, GpioController? gpioController = null, bool shouldDispose = true) + { + _pinScheme = pinScheme; + _gpioController = gpioController ?? new GpioController(); + _shouldDispose = gpioController is null || shouldDispose; + + _segmentPins = new(pinScheme.SegmentCountPerDigit); + _digitPins = new(pinScheme.DigitCount); + _displayBuffer = new Dictionary(pinScheme.DigitCount); + + Open(); + } + + /// + /// Opens GPIO pins defined in as + /// + private void Open() + { + foreach (var segmentPinNumBoard in _pinScheme.Segments) + { + _segmentPins.Add(_gpioController.OpenPin(segmentPinNumBoard, PinMode.Output)); + } + + foreach (var digitPinNumBoard in _pinScheme.Digits) + { + _digitPins.Add(_gpioController.OpenPin(digitPinNumBoard, PinMode.Output)); + } + } + + /// + /// Closes GPIO pins defined in + /// + private void Close() + { + foreach (var segmentPinNumBoard in _pinScheme.Segments) + { + _gpioController.ClosePin(segmentPinNumBoard); + } + + foreach (var digitPinNumBoard in _pinScheme.Digits) + { + _gpioController.ClosePin(digitPinNumBoard); + } + + _segmentPins = new(_pinScheme.SegmentCountPerDigit); + _digitPins = new(_pinScheme.DigitCount); + } + + /// + /// Clears display by writing to all segments + /// + public void Clear() + { + foreach (var seg in _segmentPins) + { + seg?.Write(PinValue.Low); + } + } + + /// + public void Write(ReadOnlySpan digits, int startAddress = 0) + { + if (digits.Length > NumberOfDigits) + { + throw new ArgumentOutOfRangeException(nameof(digits), $"Too many digits specified (max {NumberOfDigits} supported"); + } + + if (startAddress > NumberOfDigits - 1) + { + throw new ArgumentOutOfRangeException(nameof(startAddress), $"Specified digit address out of range (max {NumberOfDigits} supported"); + } + + // Characters must be written to the device in reverse order to be displayed in the intended order + Segment[] digitsToWrite = digits.ToArray().Reverse().ToArray(); + + for (int idx = startAddress; idx < NumberOfDigits; idx++) + { + WriteDigitAtAddress(idx, digitsToWrite[idx]); + Thread.Sleep(5); + } + } + + /// + public void Write(ReadOnlySpan characters, int startAddress = 0) + { + if (characters.Length > NumberOfDigits) + { + throw new ArgumentException($"Too many characters specified (max {NumberOfDigits} supported)", nameof(characters)); + } + + Write(characters.ToArray().Select(c => (Segment)c).ToArray(), startAddress); + } + + private void WriteDigitAtAddress(int digitAddress, Segment segmentsToWrite) + { + ActivateDigit(digitAddress); + + ReadOnlySpan mapping = SegmentHelpers.GetPinValuesFromSegment(segmentsToWrite); + SetPinValues(mapping); + + _displayBuffer[digitAddress] = segmentsToWrite; + } + + private void ActivateDigit(int idx) + { + GpioPin digit = _digitPins[idx]; + WriteAll(_digitPins, _pinScheme.DigitDisplayOff); + digit.Write(!_pinScheme.DigitDisplayOff); + + static void WriteAll(List pins, PinValue pinValue) => pins.ForEach(p => p.Write(pinValue)); + } + + private void SetPinValues(ReadOnlySpan pinValues) + { + for (int i = 0; i < pinValues.Length; i++) + { + PinValue val = pinValues[i]; + _segmentPins[i].Write(val); + } + } + + /// + /// Dispose pattern + /// + /// Indicates type is under active disposal + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + if (_shouldDispose) + { + _gpioController?.Dispose(); + } + else + { + Close(); + } + } + + _disposedValue = true; + } + } + + /// + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/devices/Display/LedSegmentDisplay5641ASPinScheme.cs b/src/devices/Display/LedSegmentDisplay5641ASPinScheme.cs new file mode 100644 index 0000000000..c33b5a8189 --- /dev/null +++ b/src/devices/Display/LedSegmentDisplay5641ASPinScheme.cs @@ -0,0 +1,177 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Device.Gpio; + +namespace Display +{ + /// + /// Display mode of the segment display, representing current flow through the pins + /// + public enum Led7SegmentDisplayMode + { + /// + /// A common anode display is ON when a high value is sent to the pin + /// + CommonAnode = 0, + + /// + /// A common cathode display is OFF when a high value is sent to the pin. Common for LEDs. + /// + CommonCathode = 1 + } + + /// Default pin scheme for the 5641AS 4-digit 7-segment display + /// + /// + /// AAA + /// F B + /// F B + /// GGG + /// E C + /// E C + /// DDD DP + /// + /// 12 11 10 9 8 7 + /// |~~~~ | | | | | | ~~~~| + /// | | + /// | | + /// |~~~~ | | | | | | ~~~~| + /// 1 2 3 4 5 6 + /// + /// Segments + /// A = 11 + /// B = 7 + /// C = 4 + /// D = 2 + /// E = 1 + /// F = 10 + /// G = 5 + /// DP = 3 + /// + /// Digits + /// Dig1 = 12 + /// Dig2 = 9 + /// Dig3 = 8 + /// Dig4 = 6 + /// + /// + /// + public class LedSegmentDisplay5641ASPinScheme + { + /// + /// Creates the pin scheme for the 5641AS display, indicating which pin on the board + /// is mapped to each pin on the display. See data sheet and remarks for pin layout. + /// + public LedSegmentDisplay5641ASPinScheme(int boardPinA, int boardPinB, int boardPinC, int boardPinD, + int boardPinE, int boardPinF, int boardPinG, int boardPinDP, + int boardPinDig1, int boardPinDig2, int boardPinDig3, int boardPinDig4) + { + DisplayMode = Led7SegmentDisplayMode.CommonCathode; + + A = boardPinA; + B = boardPinB; + C = boardPinC; + D = boardPinD; + E = boardPinE; + F = boardPinF; + G = boardPinG; + DP = boardPinDP; + + Dig1 = boardPinDig1; + Dig2 = boardPinDig2; + Dig3 = boardPinDig3; + Dig4 = boardPinDig4; + } + + /// + /// Board pin number connected to Top segment 'A' + /// + public int A { get; set; } + + /// + /// Board pin number connected to TopRight segment 'B' + /// + public int B { get; set; } + + /// + /// Board pin number connected to BottomRight segment 'C' + /// + public int C { get; set; } + + /// + /// Board pin number connected to Bottom segment 'D' + /// + public int D { get; set; } + + /// + /// Board pin number connected to BottomLeft segment 'E' + /// + public int E { get; set; } + + /// + /// Board pin number connected to TopLeft segment 'F' + /// + public int F { get; set; } + + /// + /// Board pin number connected to Middle segment 'G' + /// + public int G { get; set; } + + /// + /// Board pin number connected to Dot segment 'DP' + /// + public int DP { get; set; } + + /// + /// Board pin number connected to Digit1 (leftmost) + /// + public int Dig1 { get; set; } + + /// + /// Board pin number connected to Digit2 (second from left) + /// + public int Dig2 { get; set; } + + /// + /// Board pin number connected to Digit3 (second from right) + /// + public int Dig3 { get; set; } + + /// + /// Board pin number connected to Digit4 (rightmost) + /// + public int Dig4 { get; set; } + + /// + /// Collection of all segment pin assignments, shared across all digits + /// + public int[] Segments => new int[] { A, B, C, D, E, F, G, DP }; + + /// + /// Collection of all digit pin assignments + /// + public int[] Digits => new int[] { Dig1, Dig2, Dig3, Dig4 }; + + /// + /// Number of digit spaces on the display + /// + public int DigitCount => Digits.Length; + + /// + /// Number of segments per digit + /// + public int SegmentCountPerDigit => Segments.Length; + + /// + /// The configured display mode of the device + /// + public Led7SegmentDisplayMode DisplayMode { get; set; } + + /// + /// A common cathode display is OFF when a high value is sent to the pin. Applies to the digit only + /// + public PinValue DigitDisplayOff => DisplayMode == Led7SegmentDisplayMode.CommonCathode ? PinValue.High : PinValue.Low; + } +} diff --git a/src/devices/Display/README.md b/src/devices/Display/README.md index ef399da8cd..bb1a345a06 100644 --- a/src/devices/Display/README.md +++ b/src/devices/Display/README.md @@ -1,10 +1,14 @@ -# HT16K33 - LED Matrix Display Driver +# LED Displays - 5641AS, HT16K33 and LED Matrix Display Driver + +A number of LED displays have supported devices or can be extended as necessary. + +## HT16K33 - LED Matrix Display Driver The [Ht16k33](https://cdn-shop.adafruit.com/datasheets/ht16K33v110.pdf) is a multi-function LED controller driver. It is used as a [backpack driver for several Adafruit products](https://www.adafruit.com/?q=Ht16k33). It uses the I2C protocol. This binding and samples are based on [adafruit/Adafruit_CircuitPython_HT16K33](https://github.com/adafruit/Adafruit_CircuitPython_HT16K33). -## 7-Segment Display +### 7-Segment Display These [bright crisp displays](https://www.adafruit.com/product/1270) are good for showing numeric output. Besides the four 7-segments there is a top right dot (perhaps useful as a degrees symbol) and two sets of colon-dots (good for time-based projects). They come in several colors. @@ -36,7 +40,7 @@ display.Dots = Dot.DecimalPoint; display.Flush(); ``` -## 14-Segment Display +### 14-Segment Display This [display](https://shop.pimoroni.com/products/four-letter-phat?variant=39256047178) is good for showing alpha-numeric output, and its additional segments provide a wider range of characters @@ -44,7 +48,7 @@ This [display](https://shop.pimoroni.com/products/four-letter-phat?variant=39256 Checkout a [sample](samples/Large4Digit14SegmentDisplay/Program.cs). -## 8x8 and 16x8 LED Matrix +### 8x8 and 16x8 LED Matrix Make a [scrolling sign or a small video display](https://www.adafruit.com/product/1614) with [16x8](https://www.adafruit.com/product/2040), [8x8](https://www.adafruit.com/product/1632), and [Bicolor](https://www.adafruit.com/product/902) LED matrices. They are quite visible but not so large it won't plug into a breadboard! @@ -72,7 +76,7 @@ matrix[4, 3] = 1; matrix[7, 7] = 1; ``` -## Bi-Color Bargraph Usage +### Bi-Color Bargraph Usage Make a [small linear display](https://www.adafruit.com/product/1721) with multiple colors using this elegant bi-color LED bargraph. Every bar has two LEDs inside so you can have it display red, green, yellow or with fast multiplexing (provided by the HT16K33 driver chip) any color in between. @@ -95,3 +99,23 @@ bargraph[2] = LedColor.YELLOW; bargraph[3] = LedColor.OFF; bargraph[4] = LedColor.RED; ``` + +## Other Displays + +### GPIO Devices + +The [5641AS](http://www.xlitx.com/datasheet/5641AS.pdf) segment display is similar to above devices but without colon and degrees LEDs. It can be used without a driver. + +![5641AS Segment Display](samples/LedSegmentDisplay5641AS.Sample/5641AS.jpg) + +The following code initializes the pin scheme for the device - mapping pins on the device to the GPIO pins on the board - and creates the display. + +```c# +var scheme = new LedSegmentDisplay5641ASPinScheme(16, 21, 6, 19, 26, 20, 5, 13, + 22, 27, 17, 4); + +using var gpio = new System.Device.Gpio.GpioController(); +using var display = new LedSegmentDisplay5641AS(scheme, gpio, false); +``` + +See the sample project for pin diagram and a stopwatch example. diff --git a/src/devices/Display/SegmentHelpers.cs b/src/devices/Display/SegmentHelpers.cs new file mode 100644 index 0000000000..eb150a6274 --- /dev/null +++ b/src/devices/Display/SegmentHelpers.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Device.Gpio; +using System.Linq; +using Iot.Device.Display; + +namespace Display +{ + internal static class SegmentHelpers + { + // Dictionary allows key-based lookup + private static readonly Dictionary SegIndex = new Dictionary() + { + { Segment.Top, 0 }, + { Segment.TopRight, 1 }, + { Segment.BottomRight, 2 }, + { Segment.Bottom, 3 }, + { Segment.BottomLeft, 4 }, + { Segment.TopLeft, 5 }, + { Segment.Middle, 6 }, + { Segment.Dot, 7 }, // DP is required for each digit but set independently + }; + + public static ReadOnlySpan GetPinValuesFromSegment(Segment segments) + { + var pinValues = Enumerable.Repeat(PinValue.Low, 8).ToArray(); + foreach (var kvp in SegIndex) + { + if (segments.HasFlag(kvp.Key)) + { + pinValues[kvp.Value] = 1; + } + } + + return pinValues; + } + } +} diff --git a/src/devices/Display/samples/LedSegmentDisplay5641AS.Sample/5641AS.jpg b/src/devices/Display/samples/LedSegmentDisplay5641AS.Sample/5641AS.jpg new file mode 100644 index 0000000000..52b542461b Binary files /dev/null and b/src/devices/Display/samples/LedSegmentDisplay5641AS.Sample/5641AS.jpg differ diff --git a/src/devices/Display/samples/LedSegmentDisplay5641AS.Sample/LedSegmentDisplay5641AS.Sample.csproj b/src/devices/Display/samples/LedSegmentDisplay5641AS.Sample/LedSegmentDisplay5641AS.Sample.csproj new file mode 100644 index 0000000000..9ca7f2ea31 --- /dev/null +++ b/src/devices/Display/samples/LedSegmentDisplay5641AS.Sample/LedSegmentDisplay5641AS.Sample.csproj @@ -0,0 +1,24 @@ + + + + Exe + $(DefaultSampleTfms) + false + + + + portable + + + + + + + + + + + + + + diff --git a/src/devices/Display/samples/LedSegmentDisplay5641AS.Sample/Program.cs b/src/devices/Display/samples/LedSegmentDisplay5641AS.Sample/Program.cs new file mode 100644 index 0000000000..a8aac5bd3c --- /dev/null +++ b/src/devices/Display/samples/LedSegmentDisplay5641AS.Sample/Program.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/* This sample displays the output of a stopwatch on the 5641AS segment display. + * It includes a button for starting and stopping the stopwatch, but this is optional + * and is only included to show how to respond to events within the refresh loop. + * Refer to the Fritzing diagram for pinout. +*/ +using LedSEgmentDisplay5641AS.Sample; + +var segmentStopwatch = new SegmentStopwatch(); +segmentStopwatch.Run(); diff --git a/src/devices/Display/samples/LedSegmentDisplay5641AS.Sample/Readme.md b/src/devices/Display/samples/LedSegmentDisplay5641AS.Sample/Readme.md new file mode 100644 index 0000000000..3b12e69775 --- /dev/null +++ b/src/devices/Display/samples/LedSegmentDisplay5641AS.Sample/Readme.md @@ -0,0 +1,38 @@ +# Example commands for deploying to Raspberry Pi + +## Build / Publish + +Run from repository root (below: ```C:\code\iot```) + +```pwsh +# Change '--no-self-contained' to '--self-contained' if runtime is not installed on Pi +dotnet publish C:\code\iot\src\devices\Display\samples\LedSegmentDisplay5641AS.Sample\LedSegmentDisplay5641AS.Sample.csproj ` + -c Debug -f net6.0 -o debug -r linux-arm64 --no-self-contained +``` + +## Deploy + +Create folder on Pi: `led-display` + +```pwsh +sftp user@192.168.1.xxx +put -R C:\code\iot\debug\* led-display +# or just changes to sample project: +# put C:\code\iot\debug\Led* led-display +``` + +## Run + +```pwsh +ssh user@192.168.1.xxx +cd led-display +chmod +x LedSegmentDisplay5641AS.Sample +./LedSegmentDisplay5641AS.Sample +# CTRL+C to end +``` + +## Debug + +[Debug .NET Core on Linux](https://learn.microsoft.com/en-us/visualstudio/debugger/remote-debugging-dotnet-core-linux-with-ssh?view=vs-2022) + +Ensure PDB files are created and deployed. diff --git a/src/devices/Display/samples/LedSegmentDisplay5641AS.Sample/SegDisplayStopwatchSample5641AS-diagram.png b/src/devices/Display/samples/LedSegmentDisplay5641AS.Sample/SegDisplayStopwatchSample5641AS-diagram.png new file mode 100644 index 0000000000..4c14c2d28a Binary files /dev/null and b/src/devices/Display/samples/LedSegmentDisplay5641AS.Sample/SegDisplayStopwatchSample5641AS-diagram.png differ diff --git a/src/devices/Display/samples/LedSegmentDisplay5641AS.Sample/SegmentStopwatch.cs b/src/devices/Display/samples/LedSegmentDisplay5641AS.Sample/SegmentStopwatch.cs new file mode 100644 index 0000000000..716bcb575c --- /dev/null +++ b/src/devices/Display/samples/LedSegmentDisplay5641AS.Sample/SegmentStopwatch.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using Display; +using Iot.Device.Button; +using Iot.Device.Display; + +namespace LedSEgmentDisplay5641AS.Sample +{ + internal class SegmentStopwatch + { + private readonly object _lockObj = new object(); + private readonly Stopwatch _sw; + + private readonly List _toWrite; + private bool _timerRunning = false; + + public SegmentStopwatch() + { + _toWrite = new List(4); + _sw = new Stopwatch(); + } + + public void Run() + { + bool[] decimalsEnabled = new bool[] { false, true, false, false }; + + var scheme = new LedSegmentDisplay5641ASPinScheme(16, 21, 6, 19, 26, 20, 5, 13, + 22, 27, 17, 4); + + using var gpio = new System.Device.Gpio.GpioController(); + using var display = new LedSegmentDisplay5641AS(scheme, gpio, false); + GpioButton timerButton = new GpioButton(12, gpio: gpio); + timerButton.Press += TimerButton_Press; + + display.Clear(); + Thread.Sleep(2000); + + do + { + lock (_lockObj) + { + _toWrite.Clear(); + } + + // Use the Segment[] overload to set DP per digit + Segment[] elapsedString = FontHelper.GetString(_sw.Elapsed.ToString("ssff")).Cast().ToArray(); + elapsedString[1] |= Segment.Dot; + + /* Use Font[] overload for general string writes without DP + Font[] elapsedString = FontHelper.GetString(_sw.Elapsed.ToString("ssff")); + */ + + display.Write(elapsedString); + Thread.Sleep(1); + } + while (true); + } + + private void TimerButton_Press(object? sender, EventArgs e) + { + lock (_lockObj) + { + if (_timerRunning) + { + _sw.Stop(); + } + else + { + _sw.Start(); + } + + _timerRunning = !_timerRunning; + } + } + } +}