Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 167 additions & 0 deletions src/devices/CharacterLcd/Aip31068Lcd.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Device;
using System.Device.I2c;
using System.Drawing;

namespace Iot.Device.CharacterLcd
{
/// <summary>
/// 16x2 LCD controller based on the AIP31068, which exposes an HD44780 compatible interface with
/// an integrated I2C controller.
/// </summary>
public class Aip31068Lcd : Hd44780
{
private const byte ContrastMask = 0x3F;
private const byte ContrastSetCommand = 0x70;
private const byte PowerIconContrastCommand = 0x50;
private const byte IconDisplayFlag = 0x08;
private const byte BoosterFlag = 0x04;
private const byte FollowerControlCommand = 0x6C;
private const byte InternalOscillatorCommand = 0x14;
private const byte DefaultContrast = 0x20;

private byte _contrast;
private bool _iconDisplayEnabled;
private bool _boosterEnabled;

// Static members must appear before non-static members (SA1204)
private static byte NormalizeContrast(byte value)
{
return value > ContrastMask ? ContrastMask : (byte)(value & ContrastMask);
}

/// <summary>
/// Initializes a new instance of the <see cref="Aip31068Lcd"/> class using the specified I2C device.
/// </summary>
/// <param name="device">The I2C device used to communicate with the LCD controller.</param>
/// <param name="contrast">Initial contrast value. Valid range: 0-63.</param>
/// <param name="iconDisplayEnabled">True to enable the icon display, false to disable it.</param>
/// <param name="boosterEnabled">True to enable the internal voltage booster, false to disable it.</param>
public Aip31068Lcd(I2cDevice device, byte contrast = DefaultContrast, bool iconDisplayEnabled = false, bool boosterEnabled = true)
: base(new Size(16, 2), LcdInterface.CreateI2c(device, true))
{
_contrast = NormalizeContrast(contrast);
_iconDisplayEnabled = iconDisplayEnabled;
_boosterEnabled = boosterEnabled;

InitializeController();
}

/// <summary>
/// Initializes a new instance of the <see cref="Aip31068Lcd"/> class using an existing LCD interface.
/// </summary>
/// <param name="lcdInterface">The LCD interface used to communicate with the controller.</param>
/// <param name="contrast">Initial contrast value. Valid range: 0-63.</param>
/// <param name="iconDisplayEnabled">True to enable the icon display, false to disable it.</param>
/// <param name="boosterEnabled">True to enable the internal voltage booster, false to disable it.</param>
public Aip31068Lcd(LcdInterface lcdInterface, byte contrast = DefaultContrast, bool iconDisplayEnabled = false, bool boosterEnabled = true)
: base(new Size(16, 2), lcdInterface)
{
_contrast = NormalizeContrast(contrast);
_iconDisplayEnabled = iconDisplayEnabled;
_boosterEnabled = boosterEnabled;

InitializeController();
}

/// <summary>
/// Gets or sets the display contrast (0-63).
/// </summary>
public byte Contrast
{
get => _contrast;
set
{
byte normalized = NormalizeContrast(value);
if (_contrast == normalized)
{
return;
}

_contrast = normalized;
UpdateContrastConfiguration();
}
}

/// <summary>
/// Gets or sets a value indicating whether the icon display is enabled.
/// </summary>
public bool IconDisplayEnabled
{
get => _iconDisplayEnabled;
set
{
if (_iconDisplayEnabled == value)
{
return;
}

_iconDisplayEnabled = value;
UpdateContrastConfiguration();
}
}

/// <summary>
/// Gets or sets a value indicating whether the voltage booster is enabled.
/// </summary>
public bool BoosterEnabled
{
get => _boosterEnabled;
set
{
if (_boosterEnabled == value)
{
return;
}

_boosterEnabled = value;
UpdateContrastConfiguration();
}
}

private void InitializeController()
{
EnterExtendedInstructionSet();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you point where in the documentation this can be found? Will make it easier for maintenance. Including the delay. Thanks!

SendCommandAndWait(InternalOscillatorCommand);
SendContrastCommands();
SendCommandAndWait(FollowerControlCommand);
DelayHelper.DelayMilliseconds(200, allowThreadYield: true);
ExitExtendedInstructionSet();

// Re-issue the standard configuration commands expected after extended setup.
SendCommandAndWait((byte)_displayControl);
Clear();
SendCommandAndWait((byte)_displayMode);
}

private void UpdateContrastConfiguration()
{
EnterExtendedInstructionSet();
SendContrastCommands();
ExitExtendedInstructionSet();
}

private void SendContrastCommands()
{
SendCommandAndWait((byte)(ContrastSetCommand | (_contrast & 0x0F)));
byte value = (byte)(PowerIconContrastCommand
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, a pointer on the doc for maintenance would definitely help!

| (_iconDisplayEnabled ? IconDisplayFlag : 0)
| (_boosterEnabled ? BoosterFlag : 0)
| ((_contrast >> 4) & 0x03));
SendCommandAndWait(value);
}

private void EnterExtendedInstructionSet()
{
SendCommandAndWait((byte)(_displayFunction | DisplayFunction.ExtendedInstructionSet));
}

private void ExitExtendedInstructionSet()
{
SendCommandAndWait((byte)(_displayFunction & ~DisplayFunction.ExtendedInstructionSet));
}
}
}

1 change: 1 addition & 0 deletions src/devices/CharacterLcd/CharacterLcd.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<Compile Include="Hd44780.cs" />
<Compile Include="ICharacterLcd.cs" />
<Compile Include="CharacterLcdExtensions.cs" />
<Compile Include="Aip31068Lcd.cs" />
<Compile Include="Lcd1602.cs" />
<Compile Include="Lcd2004.cs" />
<Compile Include="LcdConsole.cs" />
Expand Down
12 changes: 12 additions & 0 deletions src/devices/CharacterLcd/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ using LcdRgb lcd = new LcdRgb(new Size(16, 2), i2cLcdDevice, i2cRgbDevice);
}
```

AIP31068 based LCDs expose the same HD44780 compatible instruction set but require an extended
initialization sequence. The <code>Aip31068Lcd</code> binding performs the necessary configuration and allows
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
initialization sequence. The <code>Aip31068Lcd</code> binding performs the necessary configuration and allows
initialization sequence. The `Aip31068Lcd` binding performs the necessary configuration and allows

adjusting the display contrast:

```csharp
var i2cDevice = I2cDevice.Create(new I2cConnectionSettings(busId: 1, deviceAddress: 0x3E));
using Aip31068Lcd lcd = new(i2cDevice);
lcd.Clear();
lcd.Write("Hello from AIP31068!");
lcd.Contrast = 36; // optional: tune the contrast (0-63)
```

PCF8574T/PCF8574AT Sample
The I2C backpack based on the PCF8574T/AT IC uses specific pin mapping, to consume this device binding on this backpack use like so

Expand Down
87 changes: 87 additions & 0 deletions src/devices/CharacterLcd/samples/Aip31068Sample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// 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;

namespace Iot.Device.CharacterLcd.Samples
{
internal static class Aip31068Sample
{
private const int DefaultBusId = 1;
private const int DefaultAddress = 0x3E;

public static void Run()
{
using I2cDevice device = I2cDevice.Create(new I2cConnectionSettings(DefaultBusId, DefaultAddress));
using Aip31068Lcd lcd = new(device);

lcd.Clear();
lcd.SetCursorPosition(0, 0);
lcd.Write("AIP31068 sample");
DisplayContrast(lcd);

Console.WriteLine("AIP31068 LCD sample ready.");
Console.WriteLine("Use '+' or '-' to adjust contrast, 'B' to toggle the booster, 'I' to toggle icons.");
Console.WriteLine("Press Esc to exit.");

while (true)
{
if (!Console.KeyAvailable)
{
Thread.Sleep(50);
continue;
}

ConsoleKeyInfo key = Console.ReadKey(intercept: true);
if (key.Key == ConsoleKey.Escape)
{
break;
}

switch (key.Key)
{
case ConsoleKey.Add:
case ConsoleKey.OemPlus:
AdjustContrast(lcd, +1);
break;
case ConsoleKey.Subtract:
case ConsoleKey.OemMinus:
AdjustContrast(lcd, -1);
break;
case ConsoleKey.B:
lcd.BoosterEnabled = !lcd.BoosterEnabled;
Console.WriteLine($"Booster {(lcd.BoosterEnabled ? "enabled" : "disabled")}.");
break;
case ConsoleKey.I:
lcd.IconDisplayEnabled = !lcd.IconDisplayEnabled;
Console.WriteLine($"Icon display {(lcd.IconDisplayEnabled ? "enabled" : "disabled")}.");
break;
default:
continue;
}

DisplayContrast(lcd);
}

lcd.Clear();
lcd.Write("Goodbye!");
}

private static void AdjustContrast(Aip31068Lcd lcd, int delta)
{
int contrast = lcd.Contrast + delta;
contrast = Math.Clamp(contrast, 0, 63);
lcd.Contrast = (byte)contrast;
Console.WriteLine($"Contrast set to {lcd.Contrast}.");
}

private static void DisplayContrast(Aip31068Lcd lcd)
{
lcd.SetCursorPosition(0, 1);
lcd.Write($"Contrast: {lcd.Contrast:D2} ");
}
}
}

6 changes: 6 additions & 0 deletions src/devices/CharacterLcd/samples/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
// UsingGroveRgbDisplay();
// UsingHd44780OverI2C();
// UsingHd44780OverI2CAndArduino();
// UsingAip31068Lcd();
UsingShiftRegister();

void UsingGpioPins()
Expand Down Expand Up @@ -87,6 +88,11 @@ void UsingHd44780OverI2CAndArduino()
ExtendedSample.Test(hd44780);
}

void UsingAip31068Lcd()
{
Aip31068Sample.Run();
}

void UsingShiftRegister()
{
int registerSelectPin = 1;
Expand Down
Loading