diff --git a/src/Iot.Device.Bindings/CompatibilitySuppressions.xml b/src/Iot.Device.Bindings/CompatibilitySuppressions.xml index bd888108bf..1c4c65ce8c 100644 --- a/src/Iot.Device.Bindings/CompatibilitySuppressions.xml +++ b/src/Iot.Device.Bindings/CompatibilitySuppressions.xml @@ -8,6 +8,20 @@ lib/net6.0/Iot.Device.Bindings.dll true + + CP0001 + T:Iot.Device.Tm1637.Character + lib/net6.0/Iot.Device.Bindings.dll + lib/net6.0/Iot.Device.Bindings.dll + true + + + CP0001 + T:Iot.Device.Tm1637.Tm1637 + lib/net6.0/Iot.Device.Bindings.dll + lib/net6.0/Iot.Device.Bindings.dll + true + CP0001 T:Iot.Device.Pn532.AsTarget.TargetBaudRateInialized @@ -15,6 +29,20 @@ lib/netstandard2.0/Iot.Device.Bindings.dll true + + CP0001 + T:Iot.Device.Tm1637.Character + lib/netstandard2.0/Iot.Device.Bindings.dll + lib/netstandard2.0/Iot.Device.Bindings.dll + true + + + CP0001 + T:Iot.Device.Tm1637.Tm1637 + lib/netstandard2.0/Iot.Device.Bindings.dll + lib/netstandard2.0/Iot.Device.Bindings.dll + true + CP0002 F:Iot.Device.Axp192.BatteryStatus.Overwinered diff --git a/src/devices/Device-Index.md b/src/devices/Device-Index.md index 2a91b5100f..98d829c073 100755 --- a/src/devices/Device-Index.md +++ b/src/devices/Device-Index.md @@ -119,7 +119,6 @@ * [TCA954X - TCA954X Low-Voltage Multi-Channel I2C Switch with Reset](Tca954x/README.md) * [TCS3472x Sensors](Tcs3472x/README.md) * [TLC1543 - 10-bit ADC with 11 input channels](Tlc1543/README.md) -* [TM1637 - Segment Display](Tm1637/README.md) * [TSL256x - Illuminance sensor](Tsl256x/README.md) * [VL53L0X - distance sensor](Vl53L0X/README.md) * [VL53L1X - distance sensor](Vl53L1X/README.md) diff --git a/src/devices/Mhz19b/README.md b/src/devices/Mhz19b/README.md index b0e2bc9042..f7631fea00 100644 --- a/src/devices/Mhz19b/README.md +++ b/src/devices/Mhz19b/README.md @@ -37,6 +37,7 @@ The MH-Z19B gas module provides a serial communication interface (UART) which ca |GND |6 (GND) |7 (GND) | |UART |8 (TXD0) |2 (RXD) | |UART |10 (RXD0) |3 (TXD) | + Table: MH-Z19B to RPi 3 connection The binding supports the connection through an UART interface (e.g. ```/dev/serial0```) or (serial port) stream. diff --git a/src/devices/README.md b/src/devices/README.md index b1020d3166..8767622097 100755 --- a/src/devices/README.md +++ b/src/devices/README.md @@ -207,7 +207,6 @@ Our vision: the majority of .NET bindings are written completely in .NET languag * [SkiaSharp graphics library adapter](SkiaSharpAdapter/README.md) * [Solomon Systech Ssd1351 - CMOS OLED](Ssd1351/README.md) * [Solomon Systech SSD13xx OLED display family](Ssd13xx/README.md) -* [TM1637 - Segment Display](Tm1637/README.md) * [Ws28xx / SK6812 LED drivers](Ws28xx/README.md) ### GPIO Expanders diff --git a/src/devices/Tm1637/DataCommand.cs b/src/devices/Tm1637/DataCommand.cs deleted file mode 100644 index a4288b79f6..0000000000 --- a/src/devices/Tm1637/DataCommand.cs +++ /dev/null @@ -1,22 +0,0 @@ -// 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.Text; - -namespace Iot.Device.Tm1637 -{ - /// - /// Internal registers to be send to the TM1637 - /// - internal enum DataCommand - { - DataCommandSetting = 0b0100_0000, - DisplayAndControlCommandSetting = 0b1000_0000, - AddressCommandSetting = 0b1100_0000, - ReadKeyScanData = 0b0100_0010, - FixAddress = 0b0100_0100, - TestMode = 0b0100_1000, - } -} diff --git a/src/devices/Tm1637/DisplayCommand.cs b/src/devices/Tm1637/DisplayCommand.cs deleted file mode 100644 index 2ea4ad646e..0000000000 --- a/src/devices/Tm1637/DisplayCommand.cs +++ /dev/null @@ -1,18 +0,0 @@ -// 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.Text; - -namespace Iot.Device.Tm1637 -{ - /// - /// Switch on or off the 8 segments LCD - /// - internal enum DisplayCommand - { - DisplayOn = 0b1000_1000, - DisplayOff = 0b1000_0000, - } -} diff --git a/src/devices/Tm1637/README.md b/src/devices/Tm1637/README.md deleted file mode 100644 index 4f0fba350f..0000000000 --- a/src/devices/Tm1637/README.md +++ /dev/null @@ -1,103 +0,0 @@ -# TM1637 - Segment Display - -TM1637 is a segments display with 6 characters Led controler. It used a 2 wire implementation, one for the clock (CLK), one for the data (DIO). This chip can act as well as a key reader. This part has not been implemented. Only the Led display has been implemented. Most of its modern usages are for 4 to 6 segment displays. - -## Documentation - -- TM1637 [datasheet](https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/datasheet/unit/digi_clock/TM1637.pdf) - -### Device family - -You can find this display as [Grove](http://wiki.seeedstudio.com/Grove-4-Digit_Display/) elements as well as very cheap with no brand. A search on your favorite online shop will give you lots of options. Those simple displays are used a lot for simple clock for example. - -## Usage - -You need to create a ```Tm1637``` class with 2 pins, the clock one and the data one. - -```csharp -Tm1637 tm1637 = new Tm1637(21, 20); -``` - -![tm1637](./tm1637_bb.png) - -### Screen on, off, brightness - -It is possible and you have to turn the screen on when you want to use the Tm1637. - -```csharp -tm1637.Brightness = 7; -tm1637.ScreenOn = true; -tm1637.ClearDisplay(); -``` - -As an example, this will blink the screen: - -```csharp -for (int i = 0; i < 10; i++) -{ - tm1637.ScreenOn = !tm1637.ScreenOn; - tm1637.Display(rawData); - Thread.Sleep(500); -} -``` - -When adjusting the screen brightness from 0 to 7 where 7 is the maximum, the command is immediate. - -You can clear the display as well: - -```csharp -tm1637.ClearDisplay(); -``` - -### Displaying pre build characters - -Characters are prebuild from 0 to F to facilitate hexadecimal displays on the segments. The following example will display the number 4 then 2 with a dot then A and F. - -```csharp -Character[] toDisplay = new Character[4] { - Character.Digit4, - Character.Digit2 | Character.Dot, - Character.Digit3, - Character.Digit8 -}; -tm1637.Display(toDisplay); -``` - -The maximum size of the buffer is 6. - -### Displaying raw data - -You can as well display raw data like in the following example: - -```csharp -// Displays couple of raw data -Character[] rawData = new Character[6] { - // All led on including the dot - (Character)0b1111_1111, - // All led off - (Character)0b0000_0000, - // top blanck, right on, turning like this including dot - (Character)0b1010_1010, - // top on, right black, turning like this no dot - (Character)0b0101_0101, - // half one half off - Character.SegmentTop | Character.SegmentTopRight | Character.SegmentBottomRight | Character.SegmentBottom, - // half off half on - Character.SegmentTopLeft|Character.SegmentBottomLeft|Character.SegmentMiddle | Character.Dot, -}; -// If you have a 4 display, only the fisrt 4 will be displayed -// on a 6 segment one, all 6 will be displayed -tm1637.Display(rawData); -``` - -The maximum size of the buffer is 6. - -### Segment order - -You can change the order of the characters. In some cases, especially when you have displays with 6 segments split with 2 displays of 3, the order may not be the one you expect. - -```csharp -tm1637.CharacterOrder = new byte[] { 2, 1, 0, 5, 4, 3 }; -``` - -Make sure you have a length of 6 and all numbers from 0 to 5. diff --git a/src/devices/Tm1637/Tm1637.cs b/src/devices/Tm1637/Tm1637.cs deleted file mode 100644 index 57874fed85..0000000000 --- a/src/devices/Tm1637/Tm1637.cs +++ /dev/null @@ -1,343 +0,0 @@ -// 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; -using System.Device.Gpio; -using System.Diagnostics; -using System.Linq; -using System.Runtime.InteropServices; -// using System.Runtime.InteropServices.WindowsRuntime; -namespace Iot.Device.Tm1637 -{ - /// - /// Represents Tm1637 segment display - /// - public sealed class Tm1637 : IDisposable - { - /// - /// The number of characters that the TM1637 can handle - /// - public static byte MaxCharacters => 6; - - // According to the doc, the clock pulse width minimum is 400 ns - // And waiting time between clk up and down is 1 µs - private const byte ClockWidthMicroseconds = 1; - - private readonly int _pinClk; - private readonly int _pinDio; - private GpioController _controller; - private bool _shouldDispose; - - private byte _brightness; - - // Default character order is from 0 to 5 - private byte[] _charactersOrder = new byte[6] { 0, 1, 2, 3, 4, 5 }; - - // To store what has been displayed last. Used when change on brightness or - // screen on/off is used - private byte[] _lastDisplay = new byte[6] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - private bool _screenOn; - - /// - /// Initialize a TM1637 - /// - /// The clock pin - /// The data pin - /// Use the logical or physical pin layout - /// A Gpio Controller if you want to use a specific one - /// True to dispose the Gpio Controller - public Tm1637(int pinClk, int pinDio, PinNumberingScheme pinNumberingScheme = PinNumberingScheme.Logical, - GpioController? gpioController = null, bool shouldDispose = true) - { - _pinClk = pinClk; - _pinDio = pinDio; - _controller = gpioController ?? new GpioController(pinNumberingScheme); - _shouldDispose = shouldDispose || gpioController is null; - _controller.OpenPin(_pinClk, PinMode.Output); - _controller.OpenPin(_pinDio, PinMode.Output); - _brightness = 7; - } - - /// - /// Order of characters, expect a 6 length byte array - /// 0 to 5, any order. Most of the time 4 segments do - /// not need to be changed but the 6 ones may be in different order - /// like 0 1 2 5 4 3. In this case, this byte array has be be in this order - /// - public byte[] CharacterOrder - { - get => _charactersOrder; - set - { - if (value.Length != MaxCharacters) - { - throw new ArgumentException($"Value must be 6 bytes.", nameof(CharacterOrder)); - } - - // Check if we have all values from 0 to 5 - bool allExist = true; - for (int i = 0; i < MaxCharacters; i++) - { - allExist &= Array.Exists(value, e => e == i); - } - - if (!allExist) - { - throw new ArgumentException( - $"{nameof(CharacterOrder)} needs to have all existing characters from 0 to 5"); - } - - value.CopyTo(_charactersOrder, 0); - } - } - - /// - /// Set the screen on or off - /// - public bool ScreenOn - { - get => _screenOn; - set - { - _screenOn = value; - DisplayRaw(0, _lastDisplay[0]); - } - } - - /// - /// Adjust the screen brightness from 0 to 7 - /// - public byte Brightness - { - get => _brightness; - set - { - if (value > 7) - { - throw new ArgumentException("Value must be less than 8.", nameof(Brightness)); - } - - _brightness = value; - DisplayRaw(0, _lastDisplay[0]); - } - } - - private PinValue WriteByte(byte data) - { - // We send data by 8 bits - for (byte i = 0; i < 8; i++) - { - _controller.Write(_pinClk, PinValue.Low); - DelayHelper.DelayMicroseconds(ClockWidthMicroseconds, true); - // LSB first - if ((data & 0x01) == 0x01) - { - _controller.Write(_pinDio, PinValue.High); - } - else - { - _controller.Write(_pinDio, PinValue.Low); - } - - // LSB first - data >>= 1; - _controller.Write(_pinClk, PinValue.High); - DelayHelper.DelayMicroseconds(ClockWidthMicroseconds, true); - } - - // Wait for the acknowledge - _controller.Write(_pinClk, PinValue.Low); - _controller.Write(_pinDio, PinValue.High); - _controller.Write(_pinClk, PinValue.High); - DelayHelper.DelayMicroseconds(ClockWidthMicroseconds, true); - _controller.SetPinMode(_pinDio, PinMode.Input); - - // Wait 1 µs, it's the waiting time between clk up and down - // That's according to the documentation - DelayHelper.DelayMicroseconds(ClockWidthMicroseconds, true); - - var ack = _controller.Read(_pinDio); - if (ack == PinValue.Low) - { - // We get acknowledge from the device - _controller.SetPinMode(_pinDio, PinMode.Output); - _controller.Write(_pinDio, PinValue.Low); - } - - _controller.Write(_pinClk, PinValue.High); - DelayHelper.DelayMicroseconds(ClockWidthMicroseconds, true); - _controller.Write(_pinClk, PinValue.Low); - DelayHelper.DelayMicroseconds(ClockWidthMicroseconds, true); - - _controller.SetPinMode(_pinDio, PinMode.Output); - return ack; - } - - private void StartTransmission() - { - _controller.Write(_pinClk, PinValue.High); - _controller.Write(_pinDio, PinValue.High); - DelayHelper.DelayMicroseconds(ClockWidthMicroseconds, true); - _controller.Write(_pinDio, PinValue.Low); - } - - private void StopTransmission() - { - _controller.Write(_pinClk, PinValue.Low); - _controller.Write(_pinDio, PinValue.Low); - DelayHelper.DelayMicroseconds(ClockWidthMicroseconds, true); - _controller.Write(_pinClk, PinValue.High); - _controller.Write(_pinDio, PinValue.High); - } - - /// - /// Displays segments starting at first segment with byte array containing raw data for each segment including the dot - /// - /// Segment representation: - /// - /// bit 0 = a _a_ - /// bit 1 = b | | - /// bit 2 = c f b - /// bit 3 = d |_g_| - /// bit 4 = e | | - /// bit 5 = f e c - /// bit 6 = g |_d_| .dp - /// bit 7 = dp - /// - /// Representation of the number 0 so lighting segments a, b, c, d, e and F is then 0x3f - /// - /// - /// The raw data array to display, size of the array has to be 6 maximum - private void Display(ReadOnlySpan rawData) - { - if (rawData.Length > MaxCharacters) - { - throw new ArgumentException($"Maximum number of segments for TM1637 is {MaxCharacters}", nameof(rawData)); - } - - // Prepare the buffer with the right order to transfer - byte[] toTransfer = new byte[MaxCharacters]; - - for (int i = 0; i < rawData.Length; i++) - { - toTransfer[_charactersOrder[i]] = rawData[i]; - } - - for (int j = rawData.Length; j < MaxCharacters; j++) - { - toTransfer[_charactersOrder[j]] = (byte)Character.Nothing; - } - - _lastDisplay = toTransfer; - - StartTransmission(); - // First command is set data - WriteByte((byte)DataCommand.DataCommandSetting); - StopTransmission(); - StartTransmission(); - // Second command is set address to automatic - WriteByte((byte)DataCommand.DataCommandSetting); - // Transfer the data - for (int i = 0; i < MaxCharacters; i++) - { - WriteByte(toTransfer[i]); - } - - StopTransmission(); - StartTransmission(); - // Set the display on/off and the brightness - WriteByte((byte)((ScreenOn ? DisplayCommand.DisplayOn : DisplayCommand.DisplayOff) + _brightness)); - StopTransmission(); - } - - /// - /// Displays a series of prebuild characters including the dot or not - /// You can build your won characters with the primitives like Bottom, Top, Dot - /// - /// The Character to display - public void Display(ReadOnlySpan rawData) - { - Display(MemoryMarshal.AsBytes(rawData)); - } - - /// - /// Displays a raw data at a specific segment position from 0 to 5 - /// - /// - /// Segment representation: - /// - /// bit 0 = a _a_ - /// bit 1 = b | | - /// bit 2 = c f b - /// bit 3 = d |_g_| - /// bit 4 = e | | - /// bit 5 = f e c - /// bit 6 = g |_d_| .dp - /// bit 7 = dp - /// - /// Representation of the number 0 so lighting segments a, b, c, d, e and F is then 0x3f - /// - /// The character position from 0 to 5 - /// The segment characters to display - public void Display(byte characterPosition, Character rawData) - { - if (characterPosition > MaxCharacters) - { - throw new ArgumentException($"Maximum number of characters for TM1637 is {MaxCharacters}", nameof(characterPosition)); - } - - // Recreate the buffer in correct order - _lastDisplay[_charactersOrder[characterPosition]] = (byte)rawData; - DisplayRaw(_charactersOrder[characterPosition], (byte)rawData); - } - - private void DisplayRaw(byte characterAddress, byte rawData) - { - StartTransmission(); - // First command for fix address - WriteByte((byte)DataCommand.FixAddress); - StopTransmission(); - StartTransmission(); - // Set the address to transfer - WriteByte((byte)(DataCommand.AddressCommandSetting + characterAddress)); - // Transfer the byte - WriteByte(rawData); - StopTransmission(); - StartTransmission(); - // Set the display on/off and the brightness - WriteByte((byte)((ScreenOn ? DisplayCommand.DisplayOn : DisplayCommand.DisplayOff) + _brightness)); - StopTransmission(); - } - - /// - /// Clear the display - /// - public void ClearDisplay() - { - // 6 segments with nothing/space displayed - Span clearDisplay = stackalloc byte[] - { - (byte)Character.Nothing, - (byte)Character.Nothing, - (byte)Character.Nothing, - (byte)Character.Nothing, - (byte)Character.Nothing, - (byte)Character.Nothing, - }; - Display(clearDisplay); - } - - /// - /// Cleanup - /// - public void Dispose() - { - if (_shouldDispose) - { - _controller?.Dispose(); - _controller = null!; - } - } - } -} diff --git a/src/devices/Tm1637/Tm1637.sln b/src/devices/Tm1637/Tm1637.sln deleted file mode 100644 index 226861d28b..0000000000 --- a/src/devices/Tm1637/Tm1637.sln +++ /dev/null @@ -1,53 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26124.0 -MinimumVisualStudioVersion = 15.0.26124.0 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{AE1E5E02-C391-4828-B33A-B65A7FFF98C5}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tm1637.sample", "samples\Tm1637.sample.csproj", "{DF5C54BA-36EE-4258-BC9F-24E1B7B0507E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tm1637", "Tm1637.csproj", "{5646DA32-786B-403D-B462-83258731DB6C}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {DF5C54BA-36EE-4258-BC9F-24E1B7B0507E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DF5C54BA-36EE-4258-BC9F-24E1B7B0507E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DF5C54BA-36EE-4258-BC9F-24E1B7B0507E}.Debug|x64.ActiveCfg = Debug|Any CPU - {DF5C54BA-36EE-4258-BC9F-24E1B7B0507E}.Debug|x64.Build.0 = Debug|Any CPU - {DF5C54BA-36EE-4258-BC9F-24E1B7B0507E}.Debug|x86.ActiveCfg = Debug|Any CPU - {DF5C54BA-36EE-4258-BC9F-24E1B7B0507E}.Debug|x86.Build.0 = Debug|Any CPU - {DF5C54BA-36EE-4258-BC9F-24E1B7B0507E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DF5C54BA-36EE-4258-BC9F-24E1B7B0507E}.Release|Any CPU.Build.0 = Release|Any CPU - {DF5C54BA-36EE-4258-BC9F-24E1B7B0507E}.Release|x64.ActiveCfg = Release|Any CPU - {DF5C54BA-36EE-4258-BC9F-24E1B7B0507E}.Release|x64.Build.0 = Release|Any CPU - {DF5C54BA-36EE-4258-BC9F-24E1B7B0507E}.Release|x86.ActiveCfg = Release|Any CPU - {DF5C54BA-36EE-4258-BC9F-24E1B7B0507E}.Release|x86.Build.0 = Release|Any CPU - {5646DA32-786B-403D-B462-83258731DB6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5646DA32-786B-403D-B462-83258731DB6C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5646DA32-786B-403D-B462-83258731DB6C}.Debug|x64.ActiveCfg = Debug|Any CPU - {5646DA32-786B-403D-B462-83258731DB6C}.Debug|x64.Build.0 = Debug|Any CPU - {5646DA32-786B-403D-B462-83258731DB6C}.Debug|x86.ActiveCfg = Debug|Any CPU - {5646DA32-786B-403D-B462-83258731DB6C}.Debug|x86.Build.0 = Debug|Any CPU - {5646DA32-786B-403D-B462-83258731DB6C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5646DA32-786B-403D-B462-83258731DB6C}.Release|Any CPU.Build.0 = Release|Any CPU - {5646DA32-786B-403D-B462-83258731DB6C}.Release|x64.ActiveCfg = Release|Any CPU - {5646DA32-786B-403D-B462-83258731DB6C}.Release|x64.Build.0 = Release|Any CPU - {5646DA32-786B-403D-B462-83258731DB6C}.Release|x86.ActiveCfg = Release|Any CPU - {5646DA32-786B-403D-B462-83258731DB6C}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {DF5C54BA-36EE-4258-BC9F-24E1B7B0507E} = {AE1E5E02-C391-4828-B33A-B65A7FFF98C5} - EndGlobalSection -EndGlobal diff --git a/src/devices/Tm1637/samples/Program.cs b/src/devices/Tm1637/samples/Program.cs deleted file mode 100644 index 94b4924be2..0000000000 --- a/src/devices/Tm1637/samples/Program.cs +++ /dev/null @@ -1,101 +0,0 @@ -// 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.Threading; -using Iot.Device.Tm1637; - -Console.WriteLine("Hello Tm1637!"); -using Tm1637 tm1637 = new(20, 21); -tm1637.Brightness = 7; -tm1637.ScreenOn = true; -tm1637.ClearDisplay(); -// Displays 4 Characters -// If you have a 4 character display, all 4 will be displayed as well as on a 6 -Character[] toDisplay = new Character[4] -{ - Character.Digit4, Character.Digit2 | Character.Dot, Character.Digit3, Character.Digit8 -}; -tm1637.Display(toDisplay); -Thread.Sleep(3000); - -// Display a character at a specific segment position -// If you have a 4 display, only the fisrt 4 will be displayed as like as [0123] -// on a 6 segment one, all 6 will be displayed as like as [012345] -tm1637.Display(0, Character.Digit0); -tm1637.Display(1, Character.Digit1); -tm1637.Display(2, Character.Digit2); -tm1637.Display(3, Character.Digit3); -tm1637.Display(4, Character.Digit4); -tm1637.Display(5, Character.Digit5); -Thread.Sleep(3000); - -// Changing order of the segments -tm1637.CharacterOrder = new byte[] { 2, 1, 0, 5, 4, 3 }; - -// Displays couple of raw data -Character[] rawData = new Character[6] -{ - // All led on including the dot - (Character)0b1111_1111, - // All led off - (Character)0b0000_0000, - // top blanck, right on, turning like this including dot - (Character)0b1010_1010, - // top on, right black, turning like this no dot - (Character)0b0101_0101, - // half one half off - Character.SegmentTop | Character.SegmentTopRight | Character.SegmentBottomRight | - Character.SegmentBottom, - // half off half on - Character.SegmentTopLeft | Character.SegmentBottomLeft | Character.SegmentMiddle | Character.Dot, -}; -// If you have a 4 display, only the fisrt 4 will be displayed -// on a 6 segment one, all 6 will be displayed -tm1637.Display(rawData); -Thread.Sleep(3000); -for (int i = 0; i < 6; i++) -{ - rawData[i] = (Character)Enum.Parse(typeof(Character), $"Digit{i}"); -} - -tm1637.Display(rawData); -Thread.Sleep(3000); - -// If you have a 4 display, only the fisrt 4 will be displayed, as like as [6549] -// on a 6 segment one, all 6 will be displayed, as like as [654987] -for (int i = 0; i < 6; i++) -{ - tm1637.Display((byte)i, (Character)Enum.Parse(typeof(Character), $"Digit{4 + i}")); -} - -Thread.Sleep(3000); - -// Revert order of the segments -tm1637.CharacterOrder = new byte[] { 0, 1, 2, 3, 4, 5 }; - -// Blink the screen by switching on and off -for (int i = 0; i < 10; i++) -{ - tm1637.ScreenOn = !tm1637.ScreenOn; - tm1637.Display(rawData); - Thread.Sleep(500); -} - -tm1637.ScreenOn = true; - -long bright = 0; -while (!Console.KeyAvailable) -{ - var dt = DateTime.Now; - toDisplay[0] = (Character)Enum.Parse(typeof(Character), $"Digit{dt.Minute / 10}"); - toDisplay[1] = (Character)Enum.Parse(typeof(Character), $"Digit{dt.Minute % 10}") | Character.Dot; - toDisplay[2] = (Character)Enum.Parse(typeof(Character), $"Digit{dt.Second / 10}"); - toDisplay[3] = (Character)Enum.Parse(typeof(Character), $"Digit{dt.Second % 10}"); - tm1637.Brightness = (byte)(bright++ % 8); - tm1637.Display(toDisplay); - Thread.Sleep(100); -} - -tm1637.ScreenOn = false; -tm1637.ClearDisplay(); diff --git a/src/devices/Tm1637/Character.cs b/src/devices/Tm16xx/Character.cs similarity index 98% rename from src/devices/Tm1637/Character.cs rename to src/devices/Tm16xx/Character.cs index 8cec4082db..8acd5ff744 100644 --- a/src/devices/Tm1637/Character.cs +++ b/src/devices/Tm16xx/Character.cs @@ -2,10 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; -using System.Text; -namespace Iot.Device.Tm1637 +namespace Iot.Device.Tm16xx { /// /// diff --git a/src/devices/Tm16xx/LedSegment.cs b/src/devices/Tm16xx/LedSegment.cs new file mode 100644 index 0000000000..d0ca9639db --- /dev/null +++ b/src/devices/Tm16xx/LedSegment.cs @@ -0,0 +1,26 @@ +// 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.Tm16xx +{ + /// + /// Specifies the segment of the screen. + /// + public enum LedSegment + { + /// + /// 7-Segment without dot. + /// + Led7Segment, + + /// + /// 8-Segment with dot. + /// + Led8Segment, + + /// + /// 16-Segment. Each pair of bytes represent a character to display. + /// + Led16Segment + } +} diff --git a/src/devices/Tm16xx/README.md b/src/devices/Tm16xx/README.md new file mode 100644 index 0000000000..94f18d590e --- /dev/null +++ b/src/devices/Tm16xx/README.md @@ -0,0 +1,90 @@ +# TitanmecLed - Segment Display + +Titanmec is a company that released multiple LED controllers. Currently, LED displaying function of TM1637 and TM1650 are supported by this library. + +## Device Family + +| |Segment|Max Characters|Protocol|Switch|Segment Choose|Brightness|Display Format| +|--|--| +|TM1637|8|6|I2C like|Yes|No|Value range: [0,7]. 7 is the brightest and the default.|One byte for each segment.| +|TM1650|7 or 8|4|I2C like|Yes|7 or 8|Value range: [0,7]. 0 is the brightest and the default. 7 is the second brightest, and then 6 to 1.|One byte for each segment.| + +### TM1637 + +TM1637 is a segments display with 6 characters Led controler. It used a 2 wire implementation, one for the clock (CLK), one for the data (DIO). This chip can act as well as a key reader. This part has not been implemented. Only the Led display has been implemented. Most of its modern usages are for 4 to 6 segment displays. + +[Datasheet - English](http://olimex.cl/website_MCI/static/documents/Datasheet_TM1637.pdf) + +[Datasheet - Chinese (Official)](http://www.titanmec.com/index.php/product/view/id/530/typeid/88.html) + +You can find this display as [Grove](http://wiki.seeedstudio.com/Grove-4-Digit_Display/) elements as well as very cheap with no brand. A search on your favorite online shop will give you lots of options. Those simple displays are used a lot for simple clock for example. + +Character order is set to 0,1,2,3,4,5 as default. For 4 segments set board, most of time, it is not necessary to change the order. But 6 segments boards may come with 0,1,2,5,4,3. In this case, update the order by set the value to ```CharacterOrder``` property. + +### TM1650 + +TM1637 is a segments display with 4 characters Led controler. It used a 2 wire implementation, one for the clock (CLK), one for the data (DIO). This chip can act as well as a key reader. This part has not been implemented. Only the Led display has been implemented. + +[Datasheet - Chinese (Official)](http://www.titanmec.com/index.php/product/view/id/540/typeid/88.html) + +Character order is set to 0,1,2,3 as default. Most of time, it is not necessary to change the order. + +## Usage + +![picture](./picture.png) + +### Constructor + +After connecting the led chip to the board, an instance of the chip need to be created with 2 pins, the clock pin and the data pin. + +```csharp +Tm1637 led = new Tm1637(21, 20); +// or +Tm1650 led = new Tm1650(21, 20); +``` + +If the instance of GpioController need to be shared, the instance of the GpioController can be specified through the argument of the constructor. + +### Segment order + +The order of the characters can be changed. In some cases of TM1637, especially when you have displays with 6 segments split with 2 displays of 3, the order may not be the one you expect. Make sure you have a length of 6 and all numbers from 0 to 5. + +```csharp +led.CharacterOrder = new byte[] { 2, 1, 0, 5, 4, 3 }; +``` + +### Adjust settings + +```csharp +led.ScreenBrightness = 7; +led.IsScreenOn = true; +led.LedSegment = LedSegment.Led8Segment; //Tm1650 only +``` + +For reducing communication, calling SetScreen method instead of changing properties above one by one. + +### Display + +Displaying one segment with index specified or a sequence of segments at once. Both raw data in byte and [Character.cs](Character.cs) are supported. + +```csharp +Character[] toDisplay = new Character[4] { + Character.Digit4, + Character.Digit2 | Character.Dot, + Character.Digit3, + Character.Digit8 +}; +led.Display(toDisplay); + +led.Display(2, 0b0111_1111); //displays an 8 as the 3rd segment. +``` + +Calls ClearDisplay method to clear the display. + +```csharp +led.ClearDisplay(); +``` + +### Dispose + +The instance need to be disposed. diff --git a/src/devices/Tm16xx/Tm1637.cs b/src/devices/Tm16xx/Tm1637.cs new file mode 100644 index 0000000000..f923db5c57 --- /dev/null +++ b/src/devices/Tm16xx/Tm1637.cs @@ -0,0 +1,299 @@ +// 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; + +namespace Iot.Device.Tm16xx +{ + /// + /// Represents Titanmec TM1637 device. + /// + public class Tm1637 : Tm16xxI2CLikeBase + { + #region Const + + // According to the doc, the clock pulse width minimum is 400 ns + // And waiting time between clk up and down is 1 µs + private const byte ClockWidthMicroseconds = 1; + + private const byte MemoryAddressPrefix = 0b1100_0000; + private const byte FixedAddressWritingCommand = 0b0100_0100; + private const byte SequencedWritingCommand = 0b0100_0000; + + // To store what has been displayed last. Used when change on brightness or screen on/off is used. + private byte _lastDisplay = 0; + private byte _lastDisplayIndex = 0; + private byte _displayState = 0; + + #endregion + + #region State + + /// + /// Sets the screen on or off. + /// + [Obsolete($"This is kept for code backward compatible. Uses IsScreenOn instead.")] + public bool ScreenOn + { + get => IsScreenOn; + set => IsScreenOn = value; + } + + /// + /// Gets or sets the screen brightness. Value must be in range from 0 to 7. For TM1637, 7 is the brightest. + /// + [Obsolete($"This is kept for code backward compatible. Uses ScreenBrightness instead.")] + public byte Brightness + { + get => ScreenBrightness; + set => ScreenBrightness = value; + } + + /// + /// Gets or sets the segment mode. Due to the Led7Segment is the only supported, this property could be ignored for Tm1637. + /// + public override LedSegment LedSegment + { + get => LedSegment.Led7Segment; + set + { + if (value == LedSegment.Led7Segment) + { + return; + } + + throw new ArgumentOutOfRangeException(nameof(value), value, $"{nameof(Tm1637)} can only support 7-segment mode."); + } + } + + /// + /// Updates all screen settings at once to reduce communication. + /// + /// Screen brightness. Value must be in range from 0 to 7. Default value is 7. + /// Whether the screen is on. Default value is , aka screen on. + /// Set to to leave the update command with the next display command, in order to save once communication. Default value is . If a Display method is called next, this can be set to . + public void SetScreen(byte brightness = 7, bool screenOn = true, bool waitForNextDisplay = false) + { + if (brightness > 7) + { + throw new ArgumentException("Value must be less than 8.", nameof(brightness)); + } + + _brightness = brightness; + _screenOn = screenOn; + if (waitForNextDisplay) + { + return; + } + + OnStateChanged(); + } + + private protected override void OnStateChanged() + { + // Generates the state code for this time and future. + _displayState = (byte)((_screenOn ? 0b1000_1000 : 0b1000_0000) + _brightness); + + // Sends display command. + DisplayOneInternal(_lastDisplayIndex, _lastDisplay); + } + + #endregion + + #region Constructors + + /// + /// Initializes an instance of TM1637 with Gpio controller specified. + /// + /// The clock pin. + /// The data pin. + /// The instance of the Gpio controller which will not be disposed with this object. + public Tm1637(int pinClk, int pinDio, GpioController controller) + : this(pinClk, pinDio, gpioController: controller) + { + } + + /// + /// Initializes an instance of TM1637 with new Gpio controller which will be disposed with this object. Pin numbering scheme is set to logical. + /// + /// The clock pin. + /// The data pin. + public Tm1637(int pinClk, int pinDio) + : this(pinClk, pinDio, PinNumberingScheme.Logical, null, true) + { + } + + /// + /// Initializes an instance of TM1637 with new Gpio controller which will be disposed with this object. + /// + /// The clock pin. + /// The data pin. + /// Uses the logical or physical pin layout for new created Gpio controller. + public Tm1637(int pinClk, int pinDio, PinNumberingScheme pinNumberingScheme = PinNumberingScheme.Logical) + : this(pinClk, pinDio, pinNumberingScheme, null, true) + { + } + + /// + /// Initializes an instance of TM1637. + /// + /// The clock pin. + /// The data pin. + /// Uses the logical or physical pin layout for new created Gpio controller. + /// The instance of the gpio controller. Set to to create a new one. + /// Sets to to dispose the Gpio controller with this object. If the is set to , this parameter will be ignored and the new created Gpio controller will always be disposed with this object. + public Tm1637(int pinClk, int pinDio, PinNumberingScheme pinNumberingScheme = PinNumberingScheme.Logical, GpioController? gpioController = null, bool shouldDispose = true) + : base(pinClk, pinDio, ClockWidthMicroseconds, pinNumberingScheme, gpioController, shouldDispose) + { + _maxCharacters = 6; + _brightness = 7; + _screenOn = true; + _characterOrder = new byte[6] { 0, 1, 2, 3, 4, 5 }; + } + #endregion + + #region Display + + /// + /// Displays segments starting at first segment with byte array containing raw data for each segment including the dot. + /// + /// Segment representation: + /// + /// bit 0 = a _a_ + /// bit 1 = b | | + /// bit 2 = c f b + /// bit 3 = d |_g_| + /// bit 4 = e | | + /// bit 5 = f e c + /// bit 6 = g |_d_| .dp + /// bit 7 = dp + /// + /// Representation of the number 0 so lighting segments a, b, c, d, e and F is then 0x3f. + /// + /// + /// The raw data array to display, size of the array has to be 6 maximum. + public override void Display(ReadOnlySpan rawData) + { + if (rawData.Length > _maxCharacters) + { + throw new ArgumentException($"Maximum number of characters for TM1637 is {_maxCharacters}.", nameof(rawData)); + } + + // Prepare the buffer with the right order to transfer + byte[] toTransfer = new byte[_maxCharacters]; + + for (int i = 0; i < rawData.Length; i++) + { + toTransfer[_characterOrder[i]] = rawData[i]; + } + + for (int j = rawData.Length; j < _maxCharacters; j++) + { + toTransfer[_characterOrder[j]] = 0; + } + + _lastDisplayIndex = 0; + _lastDisplay = toTransfer[0]; + + DisplayMultipleInternal(toTransfer); + } + + /// + /// Displays a raw data at a specific segment position from 0 to 3. + /// + /// + /// Segment representation: + /// + /// bit 0 = a _a_ + /// bit 1 = b | | + /// bit 2 = c f b + /// bit 3 = d |_g_| + /// bit 4 = e | | + /// bit 5 = f e c + /// bit 6 = g |_d_| .dp + /// bit 7 = dp + /// + /// Representation of the number 0 so lighting segments a, b, c, d, e and F is then 0x3f. + /// + /// The character position from 0 to 5. + /// The segment character to display. + public override void Display(byte characterPosition, byte rawData) + { + if (characterPosition > _maxCharacters) + { + throw new ArgumentException($"Maximum number of characters for TM1637 is {_maxCharacters}.", nameof(characterPosition)); + } + + _lastDisplay = rawData; + _lastDisplayIndex = characterPosition; + DisplayOneInternal(characterPosition, rawData); + } + + /// + public override void ClearDisplay() + { + _lastDisplay = 0; + _lastDisplayIndex = 0; + DisplayMultipleInternal(0, 0, 0, 0, 0, 0); + } + + // Displays without updating _lastDisplayIndex and _lastDisplay. + private void DisplayOneInternal(int index, byte rawData) + { + StartTransmission(); + // First command for fixed address mode + WriteByteAndWaitAcknowledge(FixedAddressWritingCommand); + StopTransmission(); + StartTransmission(); + // Set the address to transfer + WriteByteAndWaitAcknowledge((byte)(MemoryAddressPrefix + index)); + // Transfer the byte + WriteByteAndWaitAcknowledge(rawData); + StopTransmission(); + StartTransmission(); + // Set the display on/off and the brightness + WriteByteAndWaitAcknowledge(_displayState); + StopTransmission(); + } + + private void DisplayMultipleInternal(params byte[] rawData) + { + StartTransmission(); + // First command for sequence mode + WriteByteAndWaitAcknowledge(SequencedWritingCommand); + StopTransmission(); + StartTransmission(); + // Set the address to transfer + WriteByteAndWaitAcknowledge(MemoryAddressPrefix); + // Transfer the data + for (int i = 0; i < _maxCharacters; i++) + { + WriteByteAndWaitAcknowledge(rawData[i]); + } + + StopTransmission(); + StartTransmission(); + // Set the display on/off and the brightness + WriteByteAndWaitAcknowledge(_displayState); + StopTransmission(); + } + #endregion + + #region Low level IO + + private protected override IEnumerable ByteToBitConverter(byte data) + { + for (var i = 0; i < 7; i++) + { + yield return (data & 0b0000_0001) != 0; + data >>= 1; + } + + yield return (data & 0b0000_0001) != 0; + } + + #endregion + } +} diff --git a/src/devices/Tm16xx/Tm1650.cs b/src/devices/Tm16xx/Tm1650.cs new file mode 100644 index 0000000000..2c97fa3c6b --- /dev/null +++ b/src/devices/Tm16xx/Tm1650.cs @@ -0,0 +1,250 @@ +// 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; + +namespace Iot.Device.Tm16xx +{ + /// + /// Represents Titanmec TM1650 device. + /// + public class Tm1650 : Tm16xxI2CLikeBase + { + #region Const + + private const int MemoryAddressPrefix = 0b0110_1000; + private const byte ModeCommand = 0b0100_1000; + + // According to the doc, the clock pulse width minimum is 400 ns + // And waiting time between clk up and down is 1 µs + private const byte ClockWidthMicroseconds = 1; + private bool _use7Segment; + private byte[] _characterMemoryAddress = new byte[0]; + #endregion + + #region Character memory address + + private protected override void OnCharacterOrderChanged() + { + _characterMemoryAddress = Array.ConvertAll(_characterOrder, i => (byte)(MemoryAddressPrefix | (i << 1))); + } + + #endregion + + #region State + + /// + /// Gets or sets the segment mode. Led7Segment and Led8Segment are supported. + /// + public override LedSegment LedSegment + { + get => _use7Segment ? LedSegment.Led7Segment : LedSegment.Led8Segment; + + set + { + switch (value) + { + case LedSegment.Led7Segment: + _use7Segment = true; + break; + case LedSegment.Led8Segment: + _use7Segment = false; + break; + default: + throw new ArgumentOutOfRangeException(nameof(value), value, $"{nameof(Tm1650)} can only support 7-segment and 8-segment modes."); + } + + OnStateChanged(); + } + } + + /// + /// Updates all screen settings at once to reduce communication. + /// + /// Screen brightness. Value must be in range from 0 to 7. Default value is 0. + /// Whether use 7 segment or 8. Default value is , aka 8 segment. + /// Whether the screen is on. Default value is , aka screen on. + public void SetScreen(byte brightness = 0, bool use7Segment = false, bool screenOn = true) + { + if (brightness > 7) + { + throw new ArgumentException("Value must be less than 8.", nameof(brightness)); + } + + _brightness = brightness; + _use7Segment = use7Segment; + _screenOn = screenOn; + OnStateChanged(); + } + + private protected override void OnStateChanged() + { + // Sends display command. + var displayCommand = (byte)((_brightness << 4) | + (_use7Segment ? 8 : 0) | + (_screenOn ? 1 : 0)); + + StartTransmission(); + WriteByteAndWaitAcknowledge(ModeCommand); + WriteByteAndWaitAcknowledge(displayCommand); + StopTransmission(); + } + + #endregion + + #region Constructors + + /// + /// Initializes an instance of TM1650 with Gpio controller specified. + /// + /// The clock pin. + /// The data pin. + /// The instance of the Gpio controller which will not be disposed with this object. + public Tm1650(int pinClk, int pinDio, GpioController controller) + : this(pinClk, pinDio, gpioController: controller) + { + } + + /// + /// Initializes an instance of TM1650 with new Gpio controller which will be disposed with this object. Pin numbering scheme is set to logical. + /// + /// The clock pin. + /// The data pin. + public Tm1650(int pinClk, int pinDio) + : this(pinClk, pinDio, PinNumberingScheme.Logical, null, true) + { + } + + /// + /// Initializes an instance of TM1650 with new Gpio controller which will be disposed with this object. + /// + /// The clock pin. + /// The data pin. + /// Uses the logical or physical pin layout for new created Gpio controller. + public Tm1650(int pinClk, int pinDio, PinNumberingScheme pinNumberingScheme = PinNumberingScheme.Logical) + : this(pinClk, pinDio, pinNumberingScheme, null, true) + { + } + + /// + /// Initializes an instance of TM1650. + /// + /// The clock pin. + /// The data pin. + /// Uses the logical or physical pin layout for new created Gpio controller. + /// The instance of the gpio controller. Set to to create a new one. + /// Sets to to dispose the Gpio controller with this object. If the is set to , this parameter will be ignored and the new created Gpio controller will always be disposed with this object. + public Tm1650(int pinClk, int pinDio, PinNumberingScheme pinNumberingScheme = PinNumberingScheme.Logical, GpioController? gpioController = null, bool shouldDispose = true) + : base(pinClk, pinDio, ClockWidthMicroseconds, pinNumberingScheme, gpioController, shouldDispose) + { + _maxCharacters = 4; + _characterOrder = new byte[4] { 0, 1, 2, 3 }; + // ReSharper disable once VirtualMemberCallInConstructor + OnCharacterOrderChanged(); + SetScreen(); + } + #endregion + + #region Display + + /// + /// Displays segments starting at first segment with byte array containing raw data for each segment including the dot. + /// + /// Segment representation: + /// + /// bit 0 = a _a_ + /// bit 1 = b | | + /// bit 2 = c f b + /// bit 3 = d |_g_| + /// bit 4 = e | | + /// bit 5 = f e c + /// bit 6 = g |_d_| .dp + /// bit 7 = dp + /// + /// Representation of the number 0 so lighting segments a, b, c, d, e and F is then 0x3f. + /// + /// + /// The raw data array to display, size of the array has to be 4 maximum. + public override void Display(ReadOnlySpan rawData) + { + if (rawData.Length > _maxCharacters) + { + throw new ArgumentException($"Maximum number of characters for TM1650 is {_maxCharacters}.", nameof(rawData)); + } + + for (int i = 0; i < rawData.Length; i++) + { + DisplayOneInternal(i, rawData[i]); + } + + for (int j = rawData.Length; j < _maxCharacters; j++) + { + DisplayOneInternal(j, (byte)Character.Nothing); + } + } + + /// + /// Displays a raw data at a specific segment position from 0 to 3. + /// + /// + /// Segment representation: + /// + /// bit 0 = a _a_ + /// bit 1 = b | | + /// bit 2 = c f b + /// bit 3 = d |_g_| + /// bit 4 = e | | + /// bit 5 = f e c + /// bit 6 = g |_d_| .dp + /// bit 7 = dp + /// + /// Representation of the number 0 so lighting segments a, b, c, d, e and F is then 0x3f. + /// + /// The character position from 0 to 3. + /// The segment character to display. + public override void Display(byte characterPosition, byte rawData) + { + if (characterPosition > _maxCharacters) + { + throw new ArgumentException($"Maximum number of characters for TM1650 is {_maxCharacters}.", nameof(characterPosition)); + } + + DisplayOneInternal(characterPosition, rawData); + } + + /// + public override void ClearDisplay() + { + DisplayOneInternal(0, 0); + DisplayOneInternal(1, 0); + DisplayOneInternal(2, 0); + DisplayOneInternal(3, 0); + } + + private void DisplayOneInternal(int index, byte rawData) + { + StartTransmission(); + WriteByteAndWaitAcknowledge(_characterMemoryAddress[index]); + WriteByteAndWaitAcknowledge(rawData); + StopTransmission(); + } + #endregion + + #region Low level IO + + private protected override IEnumerable ByteToBitConverter(byte data) + { + for (var i = 0; i < 7; i++) + { + yield return (data & 0b1000_0000) != 0; + data <<= 1; + } + + yield return (data & 0b1000_0000) != 0; + } + + #endregion + } +} diff --git a/src/devices/Tm1637/Tm1637.csproj b/src/devices/Tm16xx/Tm16xx.csproj similarity index 53% rename from src/devices/Tm1637/Tm1637.csproj rename to src/devices/Tm16xx/Tm16xx.csproj index 57bb92aa83..2105b00edc 100644 --- a/src/devices/Tm1637/Tm1637.csproj +++ b/src/devices/Tm16xx/Tm16xx.csproj @@ -1,9 +1,12 @@ - + $(DefaultBindingTfms) + false + latest + \ No newline at end of file diff --git a/src/devices/Tm16xx/Tm16xx.sln b/src/devices/Tm16xx/Tm16xx.sln new file mode 100644 index 0000000000..a9bd5cc3cd --- /dev/null +++ b/src/devices/Tm16xx/Tm16xx.sln @@ -0,0 +1,56 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33530.505 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tm16xx", "Tm16xx.csproj", "{B82C190A-642B-465B-BD3F-DB56FFF22253}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{6A4DE7B1-03F3-4EE0-BF73-A0BAEF88BA2B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tm1637.sample", "samples\Tm1637.sample.csproj", "{C158540C-003C-4240-92DF-52DD91D0ACDD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B82C190A-642B-465B-BD3F-DB56FFF22253}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B82C190A-642B-465B-BD3F-DB56FFF22253}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B82C190A-642B-465B-BD3F-DB56FFF22253}.Debug|x64.ActiveCfg = Debug|Any CPU + {B82C190A-642B-465B-BD3F-DB56FFF22253}.Debug|x64.Build.0 = Debug|Any CPU + {B82C190A-642B-465B-BD3F-DB56FFF22253}.Debug|x86.ActiveCfg = Debug|Any CPU + {B82C190A-642B-465B-BD3F-DB56FFF22253}.Debug|x86.Build.0 = Debug|Any CPU + {B82C190A-642B-465B-BD3F-DB56FFF22253}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B82C190A-642B-465B-BD3F-DB56FFF22253}.Release|Any CPU.Build.0 = Release|Any CPU + {B82C190A-642B-465B-BD3F-DB56FFF22253}.Release|x64.ActiveCfg = Release|Any CPU + {B82C190A-642B-465B-BD3F-DB56FFF22253}.Release|x64.Build.0 = Release|Any CPU + {B82C190A-642B-465B-BD3F-DB56FFF22253}.Release|x86.ActiveCfg = Release|Any CPU + {B82C190A-642B-465B-BD3F-DB56FFF22253}.Release|x86.Build.0 = Release|Any CPU + {C158540C-003C-4240-92DF-52DD91D0ACDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C158540C-003C-4240-92DF-52DD91D0ACDD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C158540C-003C-4240-92DF-52DD91D0ACDD}.Debug|x64.ActiveCfg = Debug|Any CPU + {C158540C-003C-4240-92DF-52DD91D0ACDD}.Debug|x64.Build.0 = Debug|Any CPU + {C158540C-003C-4240-92DF-52DD91D0ACDD}.Debug|x86.ActiveCfg = Debug|Any CPU + {C158540C-003C-4240-92DF-52DD91D0ACDD}.Debug|x86.Build.0 = Debug|Any CPU + {C158540C-003C-4240-92DF-52DD91D0ACDD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C158540C-003C-4240-92DF-52DD91D0ACDD}.Release|Any CPU.Build.0 = Release|Any CPU + {C158540C-003C-4240-92DF-52DD91D0ACDD}.Release|x64.ActiveCfg = Release|Any CPU + {C158540C-003C-4240-92DF-52DD91D0ACDD}.Release|x64.Build.0 = Release|Any CPU + {C158540C-003C-4240-92DF-52DD91D0ACDD}.Release|x86.ActiveCfg = Release|Any CPU + {C158540C-003C-4240-92DF-52DD91D0ACDD}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {C158540C-003C-4240-92DF-52DD91D0ACDD} = {6A4DE7B1-03F3-4EE0-BF73-A0BAEF88BA2B} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0846CF4F-8A39-4B64-893E-238E98CF9DCC} + EndGlobalSection +EndGlobal diff --git a/src/devices/Tm16xx/Tm16xxBase.cs b/src/devices/Tm16xx/Tm16xxBase.cs new file mode 100644 index 0000000000..3e2946d806 --- /dev/null +++ b/src/devices/Tm16xx/Tm16xxBase.cs @@ -0,0 +1,107 @@ +// 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.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Iot.Device.Tm16xx +{ + /// + /// Represents Titanmec led devices. This is an abstract class. + /// + /// Not all derived classes support all methods declared in this abstract class. Check the document before use. + /// Some derived class may need to be disposed after use. + public abstract class Tm16xxBase + { + /// + /// Gets or sets whether exception should be thrown when IO error occurred. + /// + public virtual bool ThrowWhenIoError { get; set; } + + /// + /// Gets or sets the order of characters. + /// + public abstract byte[] CharacterOrder { get; set; } + + /// + /// Gets or sets the switch of the screen. + /// + public abstract bool IsScreenOn { get; set; } + + /// + /// Gets or sets the brightness. + /// + /// The value range, the mapping of value and brightness are not consistent in different devices. Check the document before use. + public abstract byte ScreenBrightness { get; set; } + + /// + /// Gets or sets the segment mode. + /// + /// Not all derived classes support supports all kinds of segment mode. Check the document before use. + public abstract LedSegment LedSegment { get; set; } + + #region One Byte Display + + /// + /// Displays a series of raw data. Each byte represents a character. + /// + /// The raw data to display. Size of the array has to be equal or less than the count of the supported characters on the led board. + public abstract void Display(ReadOnlySpan rawData); + + /// + /// Displays a series of pre-build characters. + /// + /// The characters to display. Size of the array has to be equal or less than the count of the supported characters on the led board. + public virtual void Display(ReadOnlySpan characters) + { + Display(MemoryMarshal.AsBytes(characters)); + } + + /// + /// Displays a series of raw data. Each byte represents a character. + /// + /// The raw data array to display. Size of the array has to be equal or less than the count of the supported characters on the led board. + public virtual void Display(params byte[] rawData) + { + Display(rawData.AsSpan()); + } + + /// + /// Displays a series of pre-build characters. + /// + /// The character array to display. Size of the array has to be equal or less than the count of the supported characters on the led board. + public virtual void Display(params Character[] characters) + { + Display(characters.AsSpan()); + } + + /// + /// Displays a raw data byte on the specified position. + /// + /// The position to display. + /// The raw data to display. + public abstract void Display(byte characterPosition, byte rawData); + + /// + /// Displays a pre-build character on the specified position. + /// + /// The position to display. + /// The character to display. + public virtual void Display(byte characterPosition, Character character) + { + Display(characterPosition, (byte)character); + } + + #endregion + + /// + /// Clears the display. + /// + public abstract void ClearDisplay(); + + } +} diff --git a/src/devices/Tm16xx/Tm16xxI2CLikeBase.cs b/src/devices/Tm16xx/Tm16xxI2CLikeBase.cs new file mode 100644 index 0000000000..2dd1029dbf --- /dev/null +++ b/src/devices/Tm16xx/Tm16xxI2CLikeBase.cs @@ -0,0 +1,274 @@ +// 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; +using System.Device.Gpio; +using System.IO; + +namespace Iot.Device.Tm16xx +{ + /// + /// Represents some Titanmec led devices which use I2C like communication protocol. This is an abstract class. + /// + /// The communication protocol of some Titanmec led devices is like I2C, using one cable for data and one for clock, but not following the standard of I2C. + public abstract class Tm16xxI2CLikeBase : Tm16xxBase, IDisposable + { + private readonly int _clockWidthMicroseconds; + private readonly bool _shouldDispose; + private bool _disposedValue; + private protected byte[] _characterOrder = new byte[0]; + private protected bool _screenOn; + private protected byte _brightness; + private protected int _maxCharacters; + + #region Gpio + + private protected int PinClk { get; } + private protected int PinDio { get; } + private protected GpioController Controller { get; } + + #endregion + + #region CharacterOrder + + private protected virtual void OnCharacterOrderChanged() + { + } + + /// + public override byte[] CharacterOrder + { + get => _characterOrder; + set + { + if (value.Length != _maxCharacters) + { + throw new ArgumentException($"Value must be {_maxCharacters} bytes.", nameof(CharacterOrder)); + } + + // Check if we have all values from 0 to 5 + var allExist = true; + for (var i = 0; i < _maxCharacters; i++) + { + allExist &= Array.Exists(value, e => e == i); + } + + if (!allExist) + { + throw new ArgumentException( + $"{nameof(CharacterOrder)} needs to have all existing characters from 0 to {_maxCharacters - 1}.", nameof(CharacterOrder)); + } + + value.CopyTo(_characterOrder, 0); + OnCharacterOrderChanged(); + } + } + + #endregion + + #region State + + private protected abstract void OnStateChanged(); + + /// + public override bool IsScreenOn + { + get => _screenOn; + set + { + if (_screenOn != value) + { + _screenOn = value; + OnStateChanged(); + } + } + } + + /// + public override byte ScreenBrightness + { + get => _brightness; + set + { + if (_brightness != value) + { + _brightness = value; + OnStateChanged(); + } + } + } + + #endregion + + #region Constructors + + /// + /// Initializes an instance with Gpio controller specified. + /// + /// The clock pin. + /// The data pin. + /// Waiting time between clock up and down. + /// The instance of the Gpio controller which will not be disposed with this object. + protected Tm16xxI2CLikeBase(int pinClk, int pinDio, int clockWidthMicroseconds, GpioController controller) + : this(pinClk, pinDio, clockWidthMicroseconds, PinNumberingScheme.Logical, controller, false) + { + } + + /// + /// Initializes an instance with new Gpio controller which will be disposed with this object. Pin numbering scheme is set to logical. + /// + /// The clock pin. + /// The data pin. + /// Waiting time between clock up and down. + protected Tm16xxI2CLikeBase(int pinClk, int pinDio, int clockWidthMicroseconds) + : this(pinClk, pinDio, clockWidthMicroseconds, PinNumberingScheme.Logical, null, true) + { + } + + /// + /// Initializes an instance with new Gpio controller which will be disposed with this object. + /// + /// The clock pin. + /// The data pin. + /// Waiting time between clock up and down. + /// Uses the logical or physical pin layout for new created Gpio controller. + protected Tm16xxI2CLikeBase(int pinClk, int pinDio, int clockWidthMicroseconds, PinNumberingScheme pinNumberingScheme = PinNumberingScheme.Logical) + : this(pinClk, pinDio, clockWidthMicroseconds, pinNumberingScheme, null, true) + { + } + + /// + /// Initializes an instance. + /// + /// The clock pin. + /// The data pin. + /// Waiting time between clock up and down. + /// Uses the logical or physical pin layout for new created Gpio controller. + /// The instance of the gpio controller. Set to to create a new one. + /// Sets to to dispose the Gpio controller with this object. If the is set to , this parameter will be ignored and the new created Gpio controller will always be disposed with this object. + protected Tm16xxI2CLikeBase(int pinClk, int pinDio, int clockWidthMicroseconds, + PinNumberingScheme pinNumberingScheme = PinNumberingScheme.Logical, + GpioController? gpioController = null, bool shouldDispose = true) + { + PinClk = pinClk; + PinDio = pinDio; + _clockWidthMicroseconds = clockWidthMicroseconds; + Controller = gpioController ?? new GpioController(pinNumberingScheme); + _shouldDispose = shouldDispose || gpioController is null; + Controller.OpenPin(pinClk, PinMode.Output); + Controller.OpenPin(pinDio, PinMode.Output); + Controller.Write(pinClk, PinValue.High); + Controller.Write(pinDio, PinValue.High); + } + + #endregion + + #region Dispose + + private protected virtual void BeforeDisposing(bool willDisposeGpioController) + { + } + + /// + /// Disposes the class. + /// + /// True to dispose. + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + BeforeDisposing(_shouldDispose); + if (_shouldDispose) + { + Controller?.Dispose(); + } + } + + _disposedValue = true; + } + } + + /// + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + #endregion + + #region Low level IO + + private protected virtual void StartTransmission() + { + // should already be high + Controller.Write(PinClk, PinValue.High); + + // should already be high + Controller.Write(PinDio, PinValue.High); + DelayHelper.DelayMicroseconds(_clockWidthMicroseconds, true); + Controller.Write(PinDio, PinValue.Low); + DelayHelper.DelayMicroseconds(_clockWidthMicroseconds, true); + } + + private protected virtual void StopTransmission() + { + // should be changed from high to low + Controller.Write(PinClk, PinValue.Low); + + // just changed to output, state is not sure + Controller.Write(PinDio, PinValue.Low); + DelayHelper.DelayMicroseconds(_clockWidthMicroseconds, true); + Controller.Write(PinClk, PinValue.High); + DelayHelper.DelayMicroseconds(_clockWidthMicroseconds, true); + Controller.Write(PinDio, PinValue.High); + } + + private protected virtual PinValue WriteByteAndWaitAcknowledge(byte data) + { + WriteByte(data); + return WaitAcknowledge(); + } + + private protected virtual void WriteByte(byte data) + { + foreach (bool bit in ByteToBitConverter(data)) + { + Controller.Write(PinClk, PinValue.Low); + DelayHelper.DelayMicroseconds(_clockWidthMicroseconds, true); + Controller.Write(PinDio, bit ? PinValue.High : PinValue.Low); + Controller.Write(PinClk, PinValue.High); + DelayHelper.DelayMicroseconds(_clockWidthMicroseconds, true); + } + } + + private protected abstract IEnumerable ByteToBitConverter(byte data); + + private protected virtual PinValue WaitAcknowledge() + { + Controller.Write(PinClk, PinValue.Low); + Controller.Write(PinDio, PinValue.High); + Controller.SetPinMode(PinDio, PinMode.Input); + // Wait for the acknowledge + DelayHelper.DelayMicroseconds(_clockWidthMicroseconds, true); + var ack = Controller.Read(PinDio); + if (ThrowWhenIoError && ack == PinValue.High) + { + throw new IOException("Device reports an IO error."); + } + + Controller.SetPinMode(PinDio, PinMode.Output); + Controller.Write(PinClk, PinValue.High); + DelayHelper.DelayMicroseconds(_clockWidthMicroseconds, true); + + return ack; + } + + #endregion + } +} diff --git a/src/devices/Tm1637/category.txt b/src/devices/Tm16xx/category.txt similarity index 50% rename from src/devices/Tm1637/category.txt rename to src/devices/Tm16xx/category.txt index f698ddcf70..e3b08054dd 100644 --- a/src/devices/Tm1637/category.txt +++ b/src/devices/Tm16xx/category.txt @@ -1,2 +1,2 @@ display -segment +segment \ No newline at end of file diff --git a/src/devices/Tm1637/tm1637_bb.png b/src/devices/Tm16xx/picture.png similarity index 100% rename from src/devices/Tm1637/tm1637_bb.png rename to src/devices/Tm16xx/picture.png diff --git a/src/devices/Tm16xx/samples/Program.cs b/src/devices/Tm16xx/samples/Program.cs new file mode 100644 index 0000000000..2e71cd5201 --- /dev/null +++ b/src/devices/Tm16xx/samples/Program.cs @@ -0,0 +1,73 @@ +// 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.Threading; +using Iot.Device.Tm16xx; + +namespace Samples +{ + internal class Program + { + public static void Main(string[] args) + { + Console.WriteLine("This is a demo using TM1637 on GPIO-23 for CLK and GPIO-24 for DIO."); + + // Create an instance of TM1637, using Gpio 23 for clock and 24 for IO. + using (Tm1637 tm1637 = new Tm1637(23, 24)) + { + // When creating the instance without GpioController provided, a new instance of GpioController is created using PinNumberingScheme.Logical from default constructor. The instance of GpioController is disposed with the instance of Tm16xx. + // Provides an instance of GpioController when constructing Tm16xx instance when specified factory of GpioController is required or for reusing. The instance of GpioContoller provided is not disposed with the instance of Tm16xx. + // Some board need a delay for self initializing. + Thread.Sleep(100); + + // Set to the brightest for the next displaying. + // Note: Setting state by using properties is also supported but using SetScreen is recommended. By using SetScreen instead, not supported properties could be ignored and meaningless device communications are avoided. + tm1637.SetScreen(7, true, true); + // Set waitForNextDisplay to true: no data is sent to device but leave it till next digit updates. + // This will save one communication because the protocol of Tm1637 is defineded to send screen state and digits together. + // No standalone command for changing screen state only without updating at least one digit. + // If the screen state need to be changed immediately, leave waitForNextDisplay as false. + // The default value of waitForNextDisplay is false. + + // Display 12.34 + tm1637.Display(Character.Digit1, Character.Digit2 | Character.Dot, Character.Digit3, Character.Digit4); + + Thread.Sleep(3000); + + // Update digits one by one to ABCD + tm1637.Display((byte)0, Character.A); + Thread.Sleep(300); + tm1637.Display(1, Character.B); + Thread.Sleep(300); + tm1637.Display(2, Character.C); + Thread.Sleep(300); + tm1637.Display(3, Character.D); + + Thread.Sleep(3000); + + // Flash + for (int i = 0; i < 5; i++) + { + // turn off the screen + tm1637.IsScreenOn = false; + Thread.Sleep(200); + + // turn on the screen and set the brightness to 3 + tm1637.SetScreen(3, true, false); + Thread.Sleep(200); + + // turn on the screen and set the brightness to 7 + tm1637.SetScreen(7, true, false); + Thread.Sleep(200); + } + + Console.WriteLine("Press any key to quit..."); + Console.ReadKey(true); + + // Clear before quit. + tm1637.ClearDisplay(); + } + } + } +} diff --git a/src/devices/Tm1637/samples/Tm1637.sample.csproj b/src/devices/Tm16xx/samples/Tm1637.sample.csproj similarity index 79% rename from src/devices/Tm1637/samples/Tm1637.sample.csproj rename to src/devices/Tm16xx/samples/Tm1637.sample.csproj index e65a757c04..92cfde911b 100644 --- a/src/devices/Tm1637/samples/Tm1637.sample.csproj +++ b/src/devices/Tm16xx/samples/Tm1637.sample.csproj @@ -4,6 +4,6 @@ $(DefaultSampleTfms) - + \ No newline at end of file diff --git a/src/devices/Tm1637/tm1637.fzz b/src/devices/Tm16xx/tm1637.fzz similarity index 100% rename from src/devices/Tm1637/tm1637.fzz rename to src/devices/Tm16xx/tm1637.fzz