Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Matrix binding for HT16K33 #1916

Merged
merged 21 commits into from Jan 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion .markdown-link-check.json
Expand Up @@ -11,6 +11,7 @@
{ "pattern": "http://ecee.colorado.edu/~mcclurel/Philips_I2C_IO_Expanders_AN469_2.pdf" },
{ "pattern": "https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/datasheet/core/MPU-6886-000193%2Bv1.1_GHIC_en.pdf" },
{ "pattern": "https://www.digikey.com/" },
{ "pattern": "https://www.aliexpress.com/" }
{ "pattern": "https://www.aliexpress.com/" },
{ "pattern": "https://www.emvlab.org/tlvutils/" }
]
}
27 changes: 27 additions & 0 deletions THIRD-PARTY-NOTICES.TXT
Expand Up @@ -69,3 +69,30 @@ 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.

License notice for LED matrices with the HT16K33 chip
-----------------------------------------------------

https://github.com/adafruit/Adafruit_CircuitPython_HT16K33

The MIT License (MIT)

Copyright (c) 2016 Radomir Dopieralski & Tony DiCola for Adafruit Industries

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
42 changes: 42 additions & 0 deletions src/Iot.Device.Bindings/CompatibilitySuppressions.xml
Expand Up @@ -231,4 +231,46 @@
<Right>lib/netstandard2.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Iot.Device.Display.BiColorBarGraph.Fill(Iot.Device.Display.BarColor)</Target>
<Left>lib/net6.0/Iot.Device.Bindings.dll</Left>
<Right>lib/net6.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Iot.Device.Display.BarColor</Target>
<Left>lib/net6.0/Iot.Device.Bindings.dll</Left>
<Right>lib/net6.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Iot.Device.Display.BiColorBarGraph.Fill(Iot.Device.Display.BarColor)</Target>
<Left>lib/netcoreapp3.1/Iot.Device.Bindings.dll</Left>
<Right>lib/netcoreapp3.1/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Iot.Device.Display.BarColor</Target>
<Left>lib/netcoreapp3.1/Iot.Device.Bindings.dll</Left>
<Right>lib/netcoreapp3.1/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Iot.Device.Display.BiColorBarGraph.Fill(Iot.Device.Display.BarColor)</Target>
<Left>lib/netstandard2.0/Iot.Device.Bindings.dll</Left>
<Right>lib/netstandard2.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Iot.Device.Display.BarColor</Target>
<Left>lib/netstandard2.0/Iot.Device.Bindings.dll</Left>
<Right>lib/netstandard2.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
</Suppressions>
24 changes: 12 additions & 12 deletions src/devices/Display/BiColorBarGraph.cs
Expand Up @@ -13,7 +13,7 @@ namespace Iot.Device.Display
public class BiColorBarGraph : Ht16k33
{
private readonly byte[] _displayBuffer = new byte[7];
private readonly BarColor[] _biColorSegment = new BarColor[24];
private readonly LedColor[] _biColorSegment = new LedColor[24];

/// <summary>
/// Initialize BarGraph display
Expand All @@ -25,9 +25,9 @@ public BiColorBarGraph(I2cDevice i2cDevice)
}

/// <summary>
/// Indexer for <see cref="BiColorBarGraph"/>.
/// Indexer for bargraph.
/// </summary>
public BarColor this[int index]
public LedColor this[int index]
{
get => _biColorSegment[index];
set
Expand All @@ -44,22 +44,22 @@ public BiColorBarGraph(I2cDevice i2cDevice)
/// <summary>
/// Enable all LEDs.
/// </summary>
public void Fill(BarColor color)
public void Fill(LedColor color)
{
byte fill = 0xFF;
switch (color)
{
case BarColor.Red:
case LedColor.Red:
_displayBuffer[1] = fill;
_displayBuffer[3] = fill;
_displayBuffer[5] = fill;
break;
case BarColor.Green:
case LedColor.Green:
_displayBuffer[2] = fill;
_displayBuffer[4] = fill;
_displayBuffer[6] = fill;
break;
case BarColor.Yellow:
case LedColor.Yellow:
Span<byte> displayBuffer = _displayBuffer;
displayBuffer.Fill(fill);
displayBuffer[0] = 0x00;
Expand Down Expand Up @@ -155,7 +155,7 @@ private void UpdateBuffer(int index)
// x = index % 4 // which third (for example, for the 24 bar graph, there are six thirds)
// y = x % 3 // which third of the bar segment to use
// z = index / 12 // which of the bar segments to use
BarColor value = _biColorSegment[index];
LedColor value = _biColorSegment[index];
int unit = index / 12;
int segment = index / 4;
int third = segment % 3;
Expand All @@ -167,17 +167,17 @@ private void UpdateBuffer(int index)

switch (value)
{
case BarColor.Off:
case LedColor.Off:
_displayBuffer[bufferIndex] = (byte)(red & ~mask);
_displayBuffer[bufferIndex + 1] = (byte)(green & ~mask);
break;
case BarColor.Red:
case LedColor.Red:
_displayBuffer[bufferIndex] = (byte)(red ^ mask);
break;
case BarColor.Green:
case LedColor.Green:
_displayBuffer[bufferIndex + 1] = (byte)(green ^ mask);
break;
case BarColor.Yellow:
case LedColor.Yellow:
_displayBuffer[bufferIndex] = (byte)(red ^ mask);
_displayBuffer[bufferIndex + 1] = (byte)(green ^ mask);
break;
Expand Down
7 changes: 5 additions & 2 deletions src/devices/Display/Display.csproj
Expand Up @@ -5,6 +5,7 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="Alignment.cs" />
<Compile Include="BiColorBarGraph.cs" />
<Compile Include="BlinkRate.cs" />
<Compile Include="Ht16k33.cs" />
<Compile Include="Dot.cs" />
Expand All @@ -13,8 +14,10 @@
<Compile Include="FontHelper.cs" />
<Compile Include="ISevenSegmentDisplay.cs" />
<Compile Include="Large4Digit7SegmentDisplay.cs" />
<Compile Include="BiColorBarGraph.cs" />
<Compile Include="BarColor.cs" />
<Compile Include="LedColor.cs" />
<Compile Include="Matrix16x8.cs" />
<Compile Include="Matrix8x8.cs" />
<Compile Include="Matrix8x8Bicolor.cs" />
</ItemGroup>
<ItemGroup>
<None Include="README.md" />
Expand Down
3 changes: 3 additions & 0 deletions src/devices/Display/Display.sln
Expand Up @@ -7,6 +7,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{20F2
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Large4Digit7SegmentDisplay.sample", "samples\Large4Digit7SegmentDisplay.sample.csproj", "{C4D7AED7-B340-4DCD-974C-5150C6D5F074}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Matrix.sample", "samples\Matrix.sample.csproj", "{C4D7AED7-B340-4DCD-974C-5150C6D5F074}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Matrix8x8Bicolor.sample", "samples\Matrix8x8Bicolor.sample.csproj", "{C4D7AED7-B340-4DCD-974C-5150C6D5F074}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BiColorBargraph.sample", "samples\BiColorBargraph.sample.csproj", "{C4D7AED7-B340-4DCD-974C-5150C6D5F074}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Display", "Display.csproj", "{4AAE8B9E-8540-4582-8083-D1F14104BAAC}"
Expand Down
Expand Up @@ -4,9 +4,9 @@
namespace Iot.Device.Display
{
/// <summary>
/// Describes LED colors in a Bargraph.
/// Describes LED colors in an LED matrix or bargraph.
/// </summary>
public enum BarColor
public enum LedColor
{
/// <summary>
/// Disable LED.
Expand Down
189 changes: 189 additions & 0 deletions src/devices/Display/Matrix16x8.cs
@@ -0,0 +1,189 @@
// 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.Device.I2c;

namespace Iot.Device.Display
{
/// <summary>
/// Represents an 8x16 LED matrix using the HT16K33 LED Matrix driver.
/// </summary>
// Product: https://www.adafruit.com/product/2042
public class Matrix16x8 : Ht16k33
{
private readonly byte[] _displayBuffer = new byte[17];

/// <summary>
/// Initialize Matrix display
/// </summary>
/// <param name="i2cDevice">The <see cref="I2cDevice"/> to create with.</param>
public Matrix16x8(I2cDevice i2cDevice)
: base(i2cDevice)
{
}

/// <summary>
/// Width of matrix.
/// </summary>
public int Width => 16;

/// <summary>
/// Height of matrix.
/// </summary>
public int Height => 8;

/// <summary>
/// Indexer for updating matrix.
/// </summary>
public int this[int x, int y]
{
set
{
UpdateBuffer(x, y, value);
FlushIfBuffering();
}
}

/// <summary>
/// Enable all LEDs.
/// </summary>
public void Fill()
{
Span<byte> displayBuffer = _displayBuffer;
displayBuffer.Fill(0xFF);
displayBuffer[0] = 0x00;
FlushIfBuffering();
}

/// <inheritdoc/>
public override void Clear()
{
_displayBuffer.AsSpan().Clear();
FlushIfBuffering();
}

/// <inheritdoc/>
public override void Flush() => _i2cDevice.Write(_displayBuffer);

/// <inheritdoc/>
public override void Write(ReadOnlySpan<byte> data, int startAddress = 0)
{
// startAddress is intended to mean which matrix to write to
// only 8 bytes are supported at a time
int rowIndex = 0;
foreach (byte b in data)
{
int row = rowIndex * 2 + 1 + startAddress;
_displayBuffer[row] = b;
rowIndex++;
}

FlushIfBuffering();
}

private void FlushIfBuffering()
{
if (BufferingEnabled)
{
Flush();
}
}

private void UpdateBuffer(int x, int y, int value)
{
/*
Task: Update data for 8x16 matrix

The following diagram shows the intended orientation of the matrix
and its x,y address scheme.

LED used:
- https://www.adafruit.com/product/2043
- https://cdn-shop.adafruit.com/datasheets/KWM-30881XUGB.pdf

← x →
0,0 7,0 15,0
x x x x x x x x | x x x x x x x x 0 ↑
x x x x x x x x | x x x x x x x x 1 y
sdl x x x x x x x x | x x x x x x x x 2 ↓
sda x x x x x x x x | x x x x x x x x 3
gnd x x x x x x x x | x x x x x x x x 4
vcc x x x x x x x x | x x x x x x x x 5 ↑
x x x x x x x x | x x x x x x x x 6 y
x x x x x x x x | x x x x x x x x 7 ↓
0 1 2 3 4 5 6 7 8 9 101112131415
← x → 15,7

The line splits the first and second matrice.

The underlying data is structured with each
long side taking two bytes (split between the two matrices).

For example a value of `1` for the first byte
will light up the top left LED.
A value of `128` for the second byte will light up
the top right value.

The diagram demonstrates the layout of the unit and
the underlying data structure that supports it.

1 x x x x x x x x | 2 x x x x x x x x
3 x x x x x x x x | 4 x x x x x x x x
5 x x x x x x x x | 6 x x x x x x x x
7 x x x x x x x x | 8 x x x x x x x x
9 x x x x x x x x | 10 x x x x x x x x
11 x x x x x x x x | 12 x x x x x x x x
13 x x x x x x x x | 14 x x x x x x x x
15 x x x x x x x x | 16 x x x x x x x x

We now need to marry those together.
We need to accomodate two things:
- The bytes are structured right/left
- x values >=8 jump to the next byte

x - columns
y - rows

bytes for rows (counting from top to bottom; y values):
0 -> 1
1 -> 3
2 -> 5
3 -> 7
4 -> 9
5 -> 11
6 -> 13
7 -> 15

x > 7

0 -> 2
1 -> 4
2 -> 6
3 -> 8
4 -> 10
5 -> 12
6 -> 14
7 -> 16
*/

// Is x is greater than one matrix/byte
// then jump to the next matrix/byte
int row = x < 8 ? (y * 2) + 1 : (y * 2) + 2;
// Same thing here; get an 8-bit mask
int mask = x < 8 ? 1 << x : 1 << (x - 8);
byte data = _displayBuffer[row];

if (value > 0)
{
// Ensure bit is set
_displayBuffer[row] = (byte)(data | mask);
}
else
{
// Unset bit
_displayBuffer[row] = (byte)(data & ~mask);
}
}
}
}