未验证 提交 5528eb89 编写于 作者: H HumphreyJ 提交者: GitHub

added ht1632 (#1711)

Co-authored-by: NJose Perez Rodriguez <joperezr@microsoft.com>
上级 803d4ec7
// 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.Ht1632
{
/// <summary>
/// Clock modes
/// </summary>
public enum ClockMode : byte
{
/// <summary>
/// Set secondary mode and clock source from external clock, the system clock input from OSC pin and synchronous signal input from SYN pin
/// </summary>
Secondary = Command.Secondary,
/// <summary>
/// Set primary mode and clock source from on-chip RC oscillator, the system clock output to OSC pin and synchronous signal output to SYN pin (default)
/// </summary>
RcPrimary = Command.RcPrimary,
/// <summary>
/// Set primary mode and clock source from external clock, the system clock input from OSC pin and synchronous signal output to SYN pin
/// </summary>
ExternalClockPrimary = Command.ExternalClockPrimary,
}
}
// 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.Ht1632
{
/// <summary>
/// Com modes
/// </summary>
public enum ComOption : byte
{
/// <summary>
/// N-MOS open drain output and 8 COM option (default)
/// </summary>
NMos8Com = Command.NMos8Com,
/// <summary>
/// N-MOS open drain output and 16 COM option
/// </summary>
NMos16Com = Command.NMos16Com,
/// <summary>
/// P-MOS open drain output and 8 COM option
/// </summary>
PMos8Com = Command.PMos8Com,
/// <summary>
/// P-MOS open drain output and 16 COM option
/// </summary>
PMos16Com = Command.PMos16Com,
}
}
// 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.Ht1632
{
/// <summary>
/// Command Summary
/// </summary>
public enum Command : byte
{
// References middle 8-bits of command codes like:
// 100 0000-0000-X
// ~~~~ ~~~~
/// <summary>
/// Turn off both system oscillator and LED duty cycle generator (default)
/// </summary>
SystemDisabled = 0b_0000_0000,
/// <summary>
/// Turn on system oscillator
/// </summary>
SystemEnabled = 0b_0000_0001,
/// <summary>
/// Turn off LED duty cycle generator (default)
/// </summary>
LedOff = 0b_0000_0010,
/// <summary>
/// Turn on LED duty cycle generator
/// </summary>
LedOn = 0b_0000_0011,
/// <summary>
/// Turn off blinking function (default)
/// </summary>
BlinkOff = 0b_0000_1000,
/// <summary>
/// Turn on blinking function
/// </summary>
BlinkOn = 0b_0000_1001,
/// <summary>
/// Set secondary mode and clock source from external clock, the system clock input from OSC pin and synchronous signal input from SYN pin
/// </summary>
Secondary = 0b_0001_0000,
/// <summary>
/// Set primary mode and clock source from on-chip RC oscillator, the system clock output to OSC pin and synchronous signal output to SYN pin (default)
/// </summary>
RcPrimary = 0b_0001_1000,
/// <summary>
/// Set primary mode and clock source from external clock, the system clock input from OSC pin and synchronous signal output to SYN pin
/// </summary>
ExternalClockPrimary = 0b_0001_1100,
/// <summary>
/// N-MOS open drain output and 8 COM option (default)
/// </summary>
NMos8Com = 0b_0010_0000,
/// <summary>
/// N-MOS open drain output and 16 COM option
/// </summary>
NMos16Com = 0b_0010_0100,
/// <summary>
/// P-MOS open drain output and 8 COM option
/// </summary>
PMos8Com = 0b_0010_1000,
/// <summary>
/// P-MOS open drain output and 16 COM option
/// </summary>
PMos16Com = 0b_0010_1100,
/// <summary>
/// PWM 1/16 duty
/// </summary>
PwmDuty1 = 0b_1010_0000,
/// <summary>
/// PWM 2/16 duty
/// </summary>
PwmDuty2 = 0b_1010_0001,
/// <summary>
/// PWM 3/16 duty
/// </summary>
PwmDuty3 = 0b_1010_0010,
/// <summary>
/// PWM 4/16 duty
/// </summary>
PwmDuty4 = 0b_1010_0011,
/// <summary>
/// PWM 5/16 duty
/// </summary>
PwmDuty5 = 0b_1010_0100,
/// <summary>
/// PWM 6/16 duty
/// </summary>
PwmDuty6 = 0b_1010_0101,
/// <summary>
/// PWM 7/16 duty
/// </summary>
PwmDuty7 = 0b_1010_0110,
/// <summary>
/// PWM 8/16 duty
/// </summary>
PwmDuty8 = 0b_1010_0111,
/// <summary>
/// PWM 9/16 duty
/// </summary>
PwmDuty9 = 0b_1010_1000,
/// <summary>
/// PWM 10/16 duty
/// </summary>
PwmDuty10 = 0b_1010_1001,
/// <summary>
/// PWM 11/16 duty
/// </summary>
PwmDuty11 = 0b_1010_1010,
/// <summary>
/// PWM 12/16 duty
/// </summary>
PwmDuty12 = 0b_1010_1011,
/// <summary>
/// PWM 13/16 duty
/// </summary>
PwmDuty13 = 0b_1010_1100,
/// <summary>
/// PWM 14/16 duty
/// </summary>
PwmDuty14 = 0b_1010_1101,
/// <summary>
/// PWM 15/16 duty
/// </summary>
PwmDuty15 = 0b_1010_1110,
/// <summary>
/// PWM 16/16 duty (default)
/// </summary>
PwmDuty16 = 0b_1010_1111,
}
}
// 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.Gpio;
namespace Iot.Device.Ht1632
{
/// <summary>
/// HT1632C
/// 32x8 and 24x16 LED Driver
/// </summary>
public class Ht1632 : IDisposable
{
// Datasheet: https://www.holtek.com/documents/10179/116711/HT1632D_32D-2v100.pdf
/// <summary>
/// Set if system oscillator is on. (default is off)
/// </summary>
public bool Enabled { set => WriteCommand(value ? Command.SystemEnabled : Command.SystemDisabled); }
/// <summary>
/// Set if LED duty cycle generator is on (default is off)
/// </summary>
public bool LedOn { set => WriteCommand(value ? Command.LedOn : Command.LedOff); }
/// <summary>
/// Set if blinking function is on (default is off)
/// </summary>
public bool Blink { set => WriteCommand(value ? Command.BlinkOn : Command.BlinkOff); }
/// <summary>
/// Set clock mode (default is RC-Primary)
/// </summary>
public ClockMode ClockMode { set => WriteCommand((Command)value); }
/// <summary>
/// Set COM option (default is N-MOS open drain output and 8 COM)
/// </summary>
public ComOption ComOption { set => WriteCommand((Command)value); }
/// <summary>
/// Set row PWM duty (1/16 to 16/16, default is 16)
/// </summary>
public byte PwmDuty { set => WriteCommand(s_pwmCommandMap[value - 1]); }
private static readonly Command[] s_pwmCommandMap = new[]
{
Command.PwmDuty1,
Command.PwmDuty2,
Command.PwmDuty3,
Command.PwmDuty4,
Command.PwmDuty5,
Command.PwmDuty6,
Command.PwmDuty7,
Command.PwmDuty8,
Command.PwmDuty9,
Command.PwmDuty10,
Command.PwmDuty11,
Command.PwmDuty12,
Command.PwmDuty13,
Command.PwmDuty14,
Command.PwmDuty15,
Command.PwmDuty16,
};
private readonly Ht1632PinMapping _pinMapping;
private readonly int _cs;
private readonly int _wr;
private readonly int _data;
private readonly bool _shouldDispose;
private GpioController? _controller;
/// <summary>
/// HT1632C 32x8 and 24x16 LED Driver
/// </summary>
/// <param name="pinMapping">The pin mapping to use by the binding.</param>
/// <param name="gpioController">The GPIO Controller used for interrupt handling.</param>
/// <param name="shouldDispose">True (the default) if the GPIO controller shall be disposed when disposing this instance.</param>
public Ht1632(Ht1632PinMapping pinMapping, GpioController? gpioController = null, bool shouldDispose = true)
{
_shouldDispose = shouldDispose || gpioController is null;
_controller = gpioController ?? new GpioController();
_pinMapping = pinMapping;
_cs = _pinMapping.ChipSelect;
_wr = _pinMapping.WriteClock;
_data = _pinMapping.SerialData;
SetupPins();
}
/// <summary>
/// Command Mode
/// </summary>
/// <param name="commands">Commands to sent</param>
public void WriteCommand(params Command[] commands)
{
Begin();
WriteBits((byte)Id.COMMAND, 3);
foreach (Command command in commands)
{
WriteBits((byte)(command), 8);
WriteBits(0, 1);
}
End();
}
/// <summary>
/// WRITE Mode - Successive Address Writing
/// </summary>
/// <param name="address">Memory Address (MA) - 0b00_A6_A5_A4_A3_A2_A1_A0</param>
/// <param name="data">Data (MA, MA+1, ...) - 0b0000_D0_D1_D2_D3</param>
public void WriteData(byte address, params byte[] data)
{
Begin();
WriteBits((byte)Id.WRITE, 3);
WriteBits(address, 7);
foreach (byte d in data)
{
WriteBits(d, 4);
}
End();
}
/// <inheritdoc/>
public void Dispose()
{
if (_shouldDispose)
{
_controller?.Dispose();
_controller = null;
}
}
private void SetupPins()
{
if (_cs >= 0 && _wr >= 0 && _data >= 0)
{
_controller?.OpenPin(_cs, PinMode.Output);
_controller?.OpenPin(_wr, PinMode.Output);
_controller?.OpenPin(_data, PinMode.Output);
_controller?.Write(_cs, 1);
_controller?.Write(_wr, 1);
_controller?.Write(_data, 1);
}
else
{
throw new Exception($"{nameof(Ht1632)} -- {nameof(Ht1632PinMapping)} values must be non-zero; Values: {nameof(Ht1632PinMapping.ChipSelect)}: {_cs}; {nameof(Ht1632PinMapping.WriteClock)}: {_wr}; {nameof(Ht1632PinMapping.SerialData)}: {_data};.");
}
}
private void Begin()
{
_controller?.Write(_cs, 0);
}
private void End()
{
_controller?.Write(_cs, 1);
}
private void WriteBits(byte bits, int count = 8)
{
for (var i = count - 1; i >= 0; i--)
{
var bit = bits >> i & 1;
_controller?.Write(_wr, 0);
_controller?.Write(_data, bit);
_controller?.Write(_wr, 1);
}
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(DefaultBindingTfms)</TargetFrameworks>
<EnableDefaultItems>false</EnableDefaultItems>
<RootNamespace>Iot.Device.Ht1632</RootNamespace>
<!--Disabling default items so samples source won't get build by the main library-->
</PropertyGroup>
<ItemGroup>
<Compile Include="*.cs" />
</ItemGroup>
<ItemGroup>
<None Include="README.md" />
</ItemGroup>
</Project>
\ No newline at end of file

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31702.278
MinimumVisualStudioVersion = 15.0.26124.0
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DA6C1243-517A-4827-A7B0-05DDCEFA88A5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ht1632", "Ht1632.csproj", "{93FDF758-A983-47E1-9D5D-12312B9FDD5E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ht1632.Sample", "samples\Ht1632.Sample.csproj", "{FDEFDC73-80AF-4FBF-95CC-DEE8824AD9DA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{93FDF758-A983-47E1-9D5D-12312B9FDD5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{93FDF758-A983-47E1-9D5D-12312B9FDD5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{93FDF758-A983-47E1-9D5D-12312B9FDD5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{93FDF758-A983-47E1-9D5D-12312B9FDD5E}.Release|Any CPU.Build.0 = Release|Any CPU
{FDEFDC73-80AF-4FBF-95CC-DEE8824AD9DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FDEFDC73-80AF-4FBF-95CC-DEE8824AD9DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FDEFDC73-80AF-4FBF-95CC-DEE8824AD9DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FDEFDC73-80AF-4FBF-95CC-DEE8824AD9DA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{FDEFDC73-80AF-4FBF-95CC-DEE8824AD9DA} = {DA6C1243-517A-4827-A7B0-05DDCEFA88A5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9BF5A2BC-71AD-48D1-A25B-2ACDB1049516}
EndGlobalSection
EndGlobal
// 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 SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
namespace Iot.Device.Ht1632
{
/// <summary>
/// Extension for sending image
/// </summary>
public static class Ht1632ImageSending
{
/// <summary>
/// Method for whether pixel is lit or not.
/// </summary>
/// <param name="pixel">Input pixel</param>
/// <returns>True if lit.</returns>
public delegate bool BrightnessConvertor(IPixel pixel);
/// <summary>
/// Show image with 8-Com mode
/// </summary>
/// <param name="ht1632">HT1632 device</param>
/// <param name="image">Image to show. Width at least 8 pixels, height at least 32 pixels </param>
/// <param name="brightnessConvertor">Method for whether pixel is lit or not. Use <see cref="LinearBrightnessConvertor"/> if null.</param>
public static void ShowImageWith8Com<TPixel>(this Ht1632 ht1632, Image<TPixel> image, BrightnessConvertor? brightnessConvertor = null)
where TPixel : unmanaged, IPixel<TPixel> => ht1632.ShowImage(image, 8, 32, brightnessConvertor ?? LinearBrightnessConvertor);
/// <summary>
/// Show image with 16-Com mode
/// </summary>
/// <param name="ht1632">HT1632 device</param>
/// <param name="image">Image to show. Width at least 16 pixels, height at least 24 pixels </param>
/// <param name="brightnessConvertor">Method for whether pixel is lit or not. Use <see cref="LinearBrightnessConvertor"/> if null.</param>
public static void ShowImageWith16Com<TPixel>(this Ht1632 ht1632, Image<TPixel> image, BrightnessConvertor? brightnessConvertor = null)
where TPixel : unmanaged, IPixel<TPixel> => ht1632.ShowImage(image, 16, 24, brightnessConvertor ?? LinearBrightnessConvertor);
/// <summary>
/// Lit if average value of RGB is greater than half.
/// </summary>
/// <param name="pixel">Input pixel</param>
/// <returns>True if average value of RGB is greater than half.</returns>
public static bool LinearBrightnessConvertor(IPixel pixel)
{
var vector = pixel.ToScaledVector4();
return vector.X + vector.Y + vector.Z > 1.5;
}
private static void ShowImage<TPixel>(this Ht1632 ht1632, Image<TPixel> image, int com, int row, BrightnessConvertor brightnessConvertor)
where TPixel : unmanaged, IPixel<TPixel>
{
if (image.Width < com || image.Height < row)
{
throw new Exception($"Image is too small. Width: {image.Width}/{com}, height: {image.Height}/{row}.");
}
var data = new byte[row * com / 4];
for (var y = 0; y < row; y++)
{
for (var x = 0; x < com; x += 4)
{
var value = (byte)(
(brightnessConvertor(image[x + 0, y]) ? 0b_1000 : 0) |
(brightnessConvertor(image[x + 1, y]) ? 0b_0100 : 0) |
(brightnessConvertor(image[x + 2, y]) ? 0b_0010 : 0) |
(brightnessConvertor(image[x + 3, y]) ? 0b_0001 : 0));
var index = (x + com * y) / 4;
data[index] = value;
}
}
ht1632.WriteData(0, data);
}
}
}
// 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.Ht1632
{
/// <summary>
/// Represents pin mapping for the Ht1632 binding
/// </summary>
public struct Ht1632PinMapping
{
/// <param name="cs">Chip select input with pull-high resistor</param>
/// <param name="wr">WRITE clock input with pull-high resistor</param>
/// <param name="data">Serial data input or output with pull-high resistor</param>
public Ht1632PinMapping(int cs, int wr, int data)
{
ChipSelect = cs;
WriteClock = wr;
SerialData = data;
}
/// <summary>
/// Chip select input with pull-high resistor
/// </summary>
public int ChipSelect { get; set; }
/// <summary>
/// WRITE clock input with pull-high resistor
/// </summary>
public int WriteClock { get; set; }
/// <summary>
/// Serial data input or output with pull-high resistor
/// </summary>
public int SerialData { get; set; }
}
}
// 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.Ht1632
{
/// <summary>
/// Mode ID
/// </summary>
internal enum Id : byte
{
// References first 3-bits of command codes like:
// 100 0000-0000-X
// ~~~
/// <summary>
/// READ Mode
/// </summary>
READ = 0b_110,
/// <summary>
/// WRITE Mode
/// </summary>
WRITE = 0b_101,
/// <summary>
/// READ-MODIFY-WRITE Mode
/// </summary>
READ_MODIFY_WRITE = 0b_101,
/// <summary>
/// Command Mode
/// </summary>
COMMAND = 0b_100,
}
}
# Holtek HT1632 - 32×8 & 24×16 LED Driver
The devices are a memory mapping LED display controller/driver, which can select a number of ROW and commons. These are 32 ROW & 8 Commons and 24 ROW & 16 Commons. The devices support 16-gradation LEDs for each out line using PWM control with software instructions. A serial interface is conveniently provided for the command mode and data mode. Only three or four lines are required for the interface between the host controller and the devices. The display can be extended by cascading the devices for wider applications.
## Documentation
- [Datasheet](https://www.holtek.com/documents/10179/116711/HT1632D_32D-2v100.pdf)
## Binding Notes
This binding currently only supports writing commands and raw data with GPIO.
- [X] GPIO
- [ ] SPI
- [X] WRITE Mode
- [ ] READ Mode
- [ ] READ-MODIFY-WRITE Mode
## Usage
### Initialization
```csharp
using var ht1632 = new Ht1632(new Ht1632PinMapping(cs: 27, wr: 22, data: 17), new GpioController())
{
ComOption = ComOption.NMos16Com,
ClockMode = ClockMode.RcPrimary,
Enabled = true,
PwmDuty = 1,
Blink = false,
LedOn = true
};
```
### Send data
HT1632 has 4-bit RAM, one byte corresponds to one address. Only lower 4 bits are valid.
```csharp
var data = new byte[24 * 16 / 4];
var random = new Random();
for (var i = 0; i < data.Length; i++)
{
data[i] = (byte)random.Next();
}
ht1632.WriteData(0, data);
```
### Show image
```csharp
var image = Image.Load<Rgb24>("./dotnet-bot.bmp");
ht1632.ShowImageWith16Com(image);
```
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Iot.Device.Ht1632.Sample</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ht1632.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="dotnet-bot.bmp">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
// 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.Gpio;
using Iot.Device.Ht1632;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
Console.WriteLine("Initialize HT1632 with 24-ROW x 16-COM, RC-primary, PWM 1/16 duty.");
Console.WriteLine("cs: 27, wr: 22, data: 17");
using var ht1632 = new Ht1632(new Ht1632PinMapping(cs: 27, wr: 22, data: 17), new GpioController())
{
ComOption = ComOption.NMos16Com,
ClockMode = ClockMode.RcPrimary,
Enabled = true,
PwmDuty = 1,
Blink = false,
LedOn = true
};
Clear();
Console.ReadLine();
RandomDots();
Console.ReadLine();
ShowImage();
void Clear()
{
Console.WriteLine();
Console.WriteLine("Clear");
// HT1632 has 4-bit RAM, one byte corresponds to one address
// Only lower 4 bits are valid
var data = new byte[24 * 16 / 4];
ht1632.WriteData(0, data);
}
void RandomDots()
{
Console.WriteLine("Random dots");
var data = new byte[24 * 16 / 4];
var random = new Random();
for (var i = 0; i < data.Length; i++)
{
data[i] = (byte)random.Next();
}
ht1632.WriteData(0, data);
}
void ShowImage()
{
Console.WriteLine("Show image");
var image = Image.Load<Rgb24>("./dotnet-bot.bmp");
ht1632.ShowImageWith16Com(image);
}
# Schematic
![Schematic](Schematic_HT1632Ctest.png)
## Results
![Random dots](random.jpg)
![.NET bot](bot.jpg)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册