diff --git a/src/devices/Display/BarColor.cs b/src/devices/Display/BarColor.cs new file mode 100644 index 0000000000000000000000000000000000000000..5d0e2151402738141ba43056b019a4bf7ddf5464 --- /dev/null +++ b/src/devices/Display/BarColor.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Iot.Device.Display +{ + /// + /// Describes LED colors in a Bargraph. + /// + public enum BarColor + { + /// + /// Disable LED. + /// + Off = 0, + + /// + /// Enable red LED. + /// + Red = 1, + + /// + /// Enable green LED. + /// + Green = 2, + + /// + /// Enable both green and red LEDs, producing a yellow color. + /// + Yellow = 3 + } +} diff --git a/src/devices/Display/BiColorBarGraph.cs b/src/devices/Display/BiColorBarGraph.cs new file mode 100644 index 0000000000000000000000000000000000000000..eb9c40db1536207be41ce00ef2d7b6b254631649 --- /dev/null +++ b/src/devices/Display/BiColorBarGraph.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 +{ + /// + /// Represents a 24-Segment bargraph that can display multiple colors. + /// + // Product: https://www.adafruit.com/product/1721 + public class BiColorBarGraph : Ht16k33 + { + private readonly byte[] _displayBuffer = new byte[7]; + private readonly BarColor[] _biColorSegment = new BarColor[24]; + + /// + /// Initialize BarGraph display + /// + /// The to create with. + public BiColorBarGraph(I2cDevice i2cDevice) + : base(i2cDevice) + { + } + + /// + /// Indexer for . + /// + public BarColor this[int index] + { + get => _biColorSegment[index]; + set + { + _biColorSegment[index] = value; + UpdateBuffer(index); + if (BufferingEnabled) + { + Flush(); + } + } + } + + /// + /// Enable all LEDs. + /// + public void Fill(BarColor color) + { + byte fill = 0xFF; + switch (color) + { + case BarColor.Red: + _displayBuffer[1] = fill; + _displayBuffer[3] = fill; + _displayBuffer[5] = fill; + break; + case BarColor.Green: + _displayBuffer[2] = fill; + _displayBuffer[4] = fill; + _displayBuffer[6] = fill; + break; + case BarColor.Yellow: + Span displayBuffer = _displayBuffer; + displayBuffer.Fill(fill); + displayBuffer[0] = 0x00; + break; + default: + break; + } + + if (BufferingEnabled) + { + _i2cDevice.Write(_displayBuffer); + } + } + + /// + public override void Clear() + { + _displayBuffer.AsSpan().Clear(); + if (BufferingEnabled) + { + _i2cDevice.Write(_displayBuffer); + } + } + + /// + public override void Flush() => _i2cDevice.Write(_displayBuffer); + + /// + public override void Write(ReadOnlySpan data, int startAddress = 0) + { + // Note: first byte is command data; does not affect display + foreach (byte b in data) + { + _displayBuffer[startAddress++] = b; + } + + if (BufferingEnabled) + { + Flush(); + } + } + + /* + Task: Update data for the bargraph + + The following diagram shows the intended orientation of the bargraph. + + pins x x x x x x x x x x x x x x x x x x x x x x x x + 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + + Each bargraph has three segments, four LEDs each. + For the 24-segment bargraph, that's six segments of four LEDs. + Each segment is addressed separately, with 4 bits (of a byte). + The first and last 4 bits of each byte represent different segments. + Each bit represents an LED. If the bits are on, the LEDs are on. + + Each bar contains two LEDs. These are separately controlled. + That means that there are eight bits to consider for each segement, + four for each color. All of which can be separately on or off. + + There are seven (7) bytes in the buffer. + + The first byte of the buffer is for control/command information. + It should always be `0` unless specifically sending commands (like for blinking). + + Each of the following bytes are paired, the first for red and the second for green. + Each of the bytes in the pair are split, the first half for one segment, + and the second half for the matching segement on the other bargraph. + + The bytes are laid out this way: + - first segment: first four bits of bytes[2] for red; first four bits of bytes[3] for green + - second segment: first four bits of bytes[4] for red; first four bits of bytes[5] for green + - third segment: first four bits of bytes[6] for red; first four bits of bytes[7] for green + ------------- // boundary of the bargraph units + - fourth segment: second four bits of bytes[2] for red; second four bits of bytes[3] for green + - fifth segment: second four bits of bytes[4] for red; second four bits of bytes[5] for green + - sixth segment: second four bits of bytes[6] for red; second four bits of bytes[7] for green + + This is more obvious if you write some variation of value to the i2cdevice: + + byte[] buffer = + { + 0, 255, 0, 0, 255, 255, 255 + }; + */ + private void UpdateBuffer(int index) + { + // Tasks: + // Determine the location of the bar (for `index`). + // Produce a mask for the correct bit (within four bits). + // bitmask the correct bits dependending on the desired result + // Some basic math to use: + // 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]; + int unit = index / 12; + int segment = index / 4; + int third = segment % 3; + int bit = index % 4 + (index / 12) * 4; + int mask = 1 << bit; + int bufferIndex = (third * 2) + 1; + byte red = _displayBuffer[bufferIndex]; + byte green = _displayBuffer[bufferIndex + 1]; + + switch (value) + { + case BarColor.Off: + _displayBuffer[bufferIndex] = (byte)(red & ~mask); + _displayBuffer[bufferIndex + 1] = (byte)(green & ~mask); + break; + case BarColor.Red: + _displayBuffer[bufferIndex] = (byte)(red ^ mask); + break; + case BarColor.Green: + _displayBuffer[bufferIndex + 1] = (byte)(green ^ mask); + break; + case BarColor.Yellow: + _displayBuffer[bufferIndex] = (byte)(red ^ mask); + _displayBuffer[bufferIndex + 1] = (byte)(green ^ mask); + break; + default: + break; + } + } + } +} diff --git a/src/devices/Display/Display.csproj b/src/devices/Display/Display.csproj index 3dee55a98f280e77966e94f6b6901c95b1123364..549ca7a5f96dc82fa1e7fddce12080539ea68c04 100644 --- a/src/devices/Display/Display.csproj +++ b/src/devices/Display/Display.csproj @@ -13,8 +13,10 @@ + + - \ No newline at end of file + diff --git a/src/devices/Display/Display.sln b/src/devices/Display/Display.sln index 5d63314ec84d4f24b844af30a88f8860478d89b8..6f1824c7f9278852eca04c8024be2468db6f0211 100644 --- a/src/devices/Display/Display.sln +++ b/src/devices/Display/Display.sln @@ -7,6 +7,8 @@ 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}") = "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}" EndProject Global diff --git a/src/devices/Display/README.md b/src/devices/Display/README.md index f7e5f68962e7046f274d7fd1bfa12300b9a0aa9e..c14b0aac115d6662a1a610add6654a8c10c1884f 100644 --- a/src/devices/Display/README.md +++ b/src/devices/Display/README.md @@ -1,27 +1,24 @@ -# Segment display driver (HT16K33) +# HT16K33 -- LED Matrix Display Driver -This project contains multipurpose LED display drivers and binding implementations for concrete display configurations. - -## Documentation - -The **HT16K33** is LED display driver that supports multiple LED configurations and I2C communication. +The [Ht16k33](https://cdn-shop.adafruit.com/datasheets/ht16K33v110.pdf) is a memory mapping and multi-function LED controller driver. It is used as a [backpack driver for several Adafruit products](https://www.adafruit.com/?q=Ht16k33). It supports multiple LED configurations and I2C communication. Adafruit sells multiple display backpacks built upon this driver: -- [Adafruit LED / SEGMENTED category](https://www.adafruit.com/category/103) -- **Large4Digit7SegmentDisplay** is a binding that supports the **Adafruit 1.2" 4-Digit 7-Segment Display w/I2C Backpack** that comes in 3 colors: -- [Adafruit 1.2" 4-Digit 7-Segment Display w/I2C Backpack - Yellow](https://www.adafruit.com/product/1268) -- [Adafruit 1.2" 4-Digit 7-Segment Display w/I2C Backpack - Green](https://www.adafruit.com/product/1269) -- [Adafruit 1.2" 4-Digit 7-Segment Display w/I2C Backpack - Red](https://www.adafruit.com/product/1270) -- [HT16K33 datasheet](https://cdn-shop.adafruit.com/datasheets/ht16K33v110.pdf) +- [1.2" 4-Digit 7-Segment Display w/I2C Backpack - Yellow](https://www.adafruit.com/product/1268) +- [1.2" 4-Digit 7-Segment Display w/I2C Backpack - Green](https://www.adafruit.com/product/1269) +- [1.2" 4-Digit 7-Segment Display w/I2C Backpack - Red](https://www.adafruit.com/product/1270) +- [Bi-Color (Red/Green) 24-Bar Bargraph w/I2C Backpack Kit](https://www.adafruit.com/product/1721) -More information on wiring can be found on the respective product pages. +More information on wiring can be found on the respective product pages and at [adafruit/Adafruit_CircuitPython_HT16K33 +](https://github.com/adafruit/Adafruit_CircuitPython_HT16K33) (Adafruit-maintained Python bindings). -## Usage +## 7-Segment Display Usage + +![Adafruit 1.2" 4-Digit 7-Segment Display w/I2C Backpack - Green](https://cdn-shop.adafruit.com/970x728/1268-00.jpg) ```csharp // Initialize display (busId = 1 for Raspberry Pi 2 & 3) -var display = new Large4Digit7SegmentDisplay(I2cDevice.Create(new I2cConnectionSettings(busId: 1, address: Ht16k33.DefaultI2cAddress)); +using var display = new Large4Digit7SegmentDisplay(I2cDevice.Create(new I2cConnectionSettings(busId: 1, address: Ht16k33.DefaultI2cAddress)); // Set max brightness (automatically turns on display) display.Brightness = display.MaxBrightness; @@ -41,7 +38,25 @@ display.Dots = Dot.DecimalPoint; // Send buffer to the device display.Flush(); +``` -// Dispose display object (the device itself will not be turned off until powered down) -display.Dispose(); +## Bi-Color Bargraph Usage + +![Bi-Color (Red/Green) 24-Bar Bargraph w/I2C Backpack Kit](https://cdn-shop.adafruit.com/970x728/1721-00.jpg) + +```csharp +using BiColorBarGraph bargraph = new(I2cDevice.Create(new I2cConnectionSettings(busId: 1, Ht16k33.DefaultI2cAddress))) + { + // Set max brightness + Brightness = Ht16k33.MaxBrightness, + BufferingEnabled = true + }; + +bargraph.Clear(); + +bargraph[0] = BarColor.RED; +bargraph[1] = BarColor.GREEN; +bargraph[2] = BarColor.YELLOW; +bargraph[3] = BarColor.OFF; +bargraph[4] = BarColor.RED; ``` diff --git a/src/devices/Display/samples/BiColorBargraph.sample.csproj b/src/devices/Display/samples/BiColorBargraph.sample.csproj new file mode 100644 index 0000000000000000000000000000000000000000..9256c3d484cfa30bd7f8e7e164d7008567de737b --- /dev/null +++ b/src/devices/Display/samples/BiColorBargraph.sample.csproj @@ -0,0 +1,11 @@ + + + Exe + $(DefaultSampleTfms) + false + + + + + + diff --git a/src/devices/Display/samples/Large4Digit7SegmentDisplay.sample.csproj b/src/devices/Display/samples/Large4Digit7SegmentDisplay.sample.csproj index 6b37da30b7c8ebc08dc683c468a97c665c71fb4b..ce9bfe6c769cf7d7cece7b03dc5fa83bc1973111 100644 --- a/src/devices/Display/samples/Large4Digit7SegmentDisplay.sample.csproj +++ b/src/devices/Display/samples/Large4Digit7SegmentDisplay.sample.csproj @@ -2,8 +2,10 @@ Exe $(DefaultSampleTfms) + false + - \ No newline at end of file + diff --git a/src/devices/Display/samples/Program.BiColorBargraph.cs b/src/devices/Display/samples/Program.BiColorBargraph.cs new file mode 100644 index 0000000000000000000000000000000000000000..e4f9c400201074d812691f34c3bf2711ea601020 --- /dev/null +++ b/src/devices/Display/samples/Program.BiColorBargraph.cs @@ -0,0 +1,85 @@ +// 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; +using System.Threading; +using Iot.Device.Display; + +// Initialize display (busId = 1 for Raspberry Pi 2+) +using BiColorBarGraph bargraph = new(I2cDevice.Create(new I2cConnectionSettings(busId: 1, Ht16k33.DefaultI2cAddress))) + { + // Set max brightness + Brightness = Ht16k33.MaxBrightness, + BufferingEnabled = true + }; + +bargraph.Clear(); + +bargraph[0] = BarColor.Red; +bargraph[1] = BarColor.Green; +bargraph[2] = BarColor.Yellow; +bargraph[3] = BarColor.Off; +bargraph[4] = BarColor.Red; + +Thread.Sleep(1000); +bargraph.Clear(); + +for (int i = 0; i < 24; i++) +{ + if (i % 2 is 1) + { + continue; + } + + int num = i % 4; + BarColor color = (BarColor)(i % 3 + 1); + bargraph[i] = color; + Thread.Sleep(100); +} + +for (int i = 23; i >= 0; i--) +{ + if (i % 2 is 0) + { + continue; + } + + int num = i % 4; + BarColor color = (BarColor)(i % 3 + 1); + bargraph[i] = color; + Thread.Sleep(100); +} + +Thread.Sleep(1000); +bargraph.Clear(); + +bargraph[0] = BarColor.Red; +bargraph[6] = BarColor.Green; +bargraph[11] = BarColor.Yellow; +bargraph[12] = BarColor.Yellow; +bargraph[18] = BarColor.Green; +bargraph[23] = BarColor.Red; + +Thread.Sleep(1000); +bargraph.Clear(); + +byte[] customBuffer = +{ + 0, 255, 0, 0, 255, 255, 255 +}; + +bargraph.Write(customBuffer); + +Thread.Sleep(1000); +bargraph.Clear(); + +bargraph.Fill(BarColor.Red); +Thread.Sleep(1000); +bargraph.Clear(); +bargraph.Fill(BarColor.Green); +Thread.Sleep(1000); +bargraph.Clear(); +bargraph.Fill(BarColor.Yellow); +Thread.Sleep(1000); +bargraph.Clear(); diff --git a/src/devices/Ht16k33/README.md b/src/devices/Ht16k33/README.md new file mode 100644 index 0000000000000000000000000000000000000000..40dd03d84d32440e381d6678ddcafd156536a728 --- /dev/null +++ b/src/devices/Ht16k33/README.md @@ -0,0 +1,5 @@ +# Ht16k33 + +The [Ht16k33](https://cdn-shop.adafruit.com/datasheets/ht16K33v110.pdf) is a memory mapping and multi-function LED controller driver. It is used as a [backpack driver for several Adafruit products](https://www.adafruit.com/?q=Ht16k33). + +See the [Display](../Display/) folder for implementations of various products that use the Ht16k33.