Skip to content

Commit

Permalink
New Device: LED segment display 5641AS (#2350)
Browse files Browse the repository at this point in the history
* Binding added for LED 4-digit 7-segment display model 5641AS using GPIO only

* Sample code added

* Fritzing diagram for 5641AS

* Displays readme updated with new device and with a general title

* Corrected sample project with proper tf

* Corrected headers in README.md to match required conventions

* Added missing enum

* Corrected missing files and compiler errors introduced with prior tf fix

* Fixed sample image link, resized image

* Privatize Open() and Close() to make it transparent to user

* Improve readme header

* Implemented digit address indexer; simplified available Write commands including removal of special overload for DP handling; added ReadMe for sample

* Markdown compliance

* Markdown compliance
  • Loading branch information
jabberhams authored Jan 4, 2025
1 parent 9c6b628 commit 41e17fb
Show file tree
Hide file tree
Showing 12 changed files with 677 additions and 16 deletions.
9 changes: 9 additions & 0 deletions src/devices/Display/Display.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
<TargetFrameworks>$(DefaultBindingTfms)</TargetFrameworks>
<EnableDefaultItems>false</EnableDefaultItems>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net6.0|AnyCPU'">
<DebugType>portable</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|netstandard2.0|AnyCPU'">
<DebugType>portable</DebugType>
</PropertyGroup>
<ItemGroup>
<Compile Include="Alignment.cs" />
<Compile Include="BiColorBarGraph.cs" />
Expand All @@ -11,6 +17,8 @@
<Compile Include="Dot.cs" />
<Compile Include="Font.cs" />
<Compile Include="Font14.cs" />
<Compile Include="LedSegmentDisplay5641AS.cs" />
<Compile Include="LedSegmentDisplay5641ASPinScheme.cs" />
<Compile Include="Segment.cs" />
<Compile Include="Segment14.cs" />
<Compile Include="FontHelper.cs" />
Expand All @@ -22,6 +30,7 @@
<Compile Include="Matrix16x8.cs" />
<Compile Include="Matrix8x8.cs" />
<Compile Include="Matrix8x8Bicolor.cs" />
<Compile Include="SegmentHelpers.cs" />
</ItemGroup>
<ItemGroup>
<None Include="README.md" />
Expand Down
78 changes: 67 additions & 11 deletions src/devices/Display/Display.sln
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
198 changes: 198 additions & 0 deletions src/devices/Display/LedSegmentDisplay5641AS.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// LED 4-digit 7-segment display, model 5641AS. This class handles driverless, direct-pin access over GPIO.
/// </summary>
/// <remarks>
/// Data Sheet:
/// http://www.xlitx.com/datasheet/5641AS.pdf
/// </remarks>
public class LedSegmentDisplay5641AS : ISevenSegmentDisplay, IDisposable
{
private readonly LedSegmentDisplay5641ASPinScheme _pinScheme;
private readonly GpioController _gpioController;
private readonly bool _shouldDispose;
private bool _disposedValue;

private List<GpioPin> _segmentPins;
private List<GpioPin> _digitPins;

// address, Segment(s)
private Dictionary<int, Segment> _displayBuffer;

/// <inheritdoc />
public int NumberOfDigits => _pinScheme.DigitCount;

/// <inheritdoc />
public Segment this[int address]
{
get => _displayBuffer[address];
set => WriteDigitAtAddress(address, value);
}

/// <summary>
/// Initializes a new instance of the <see cref="LedSegmentDisplay5641AS"/> class.
/// </summary>
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<int, Segment>(pinScheme.DigitCount);

Open();
}

/// <summary>
/// Opens GPIO pins defined in <see cref="LedSegmentDisplay5641ASPinScheme"/> as <see cref="PinMode.Output"/>
/// </summary>
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));
}
}

/// <summary>
/// Closes GPIO pins defined in <see cref="LedSegmentDisplay5641ASPinScheme"/>
/// </summary>
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);
}

/// <summary>
/// Clears display by writing <see cref="PinValue.Low"/> to all segments
/// </summary>
public void Clear()
{
foreach (var seg in _segmentPins)
{
seg?.Write(PinValue.Low);
}
}

/// <inheritdoc/>
public void Write(ReadOnlySpan<Segment> 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);
}
}

/// <inheritdoc/>
public void Write(ReadOnlySpan<Font> 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<PinValue> 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<GpioPin> pins, PinValue pinValue) => pins.ForEach(p => p.Write(pinValue));
}

private void SetPinValues(ReadOnlySpan<PinValue> pinValues)
{
for (int i = 0; i < pinValues.Length; i++)
{
PinValue val = pinValues[i];
_segmentPins[i].Write(val);
}
}

/// <summary>
/// Dispose pattern
/// </summary>
/// <param name="disposing">Indicates type is under active disposal</param>
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
if (_shouldDispose)
{
_gpioController?.Dispose();
}
else
{
Close();
}
}

_disposedValue = true;
}
}

/// <inheritdoc />
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}
Loading

0 comments on commit 41e17fb

Please sign in to comment.