Skip to content

Commit 0c2f2a8

Browse files
asp2286Ellerbach
andauthored
Add AIP31068 LCD binding with contrast control, sample, and docs (#2434)
* Add AIP31068 LCD binding with sample and docs * Update src/devices/CharacterLcd/README.md Co-authored-by: Laurent Ellerbach <[email protected]> * chore: save WIP before rebase --------- Co-authored-by: Laurent Ellerbach <[email protected]>
1 parent cb648ad commit 0c2f2a8

File tree

5 files changed

+295
-0
lines changed

5 files changed

+295
-0
lines changed
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Device;
5+
using System.Device.I2c;
6+
using System.Drawing;
7+
8+
namespace Iot.Device.CharacterLcd
9+
{
10+
/// <summary>
11+
/// 16x2 LCD controller based on the AIP31068, which exposes an HD44780 compatible interface with
12+
/// an integrated I2C controller.
13+
/// </summary>
14+
public class Aip31068Lcd : Hd44780
15+
{
16+
private const byte ContrastMask = 0x3F;
17+
private const byte ContrastSetCommand = 0x70;
18+
private const byte PowerIconContrastCommand = 0x50;
19+
private const byte IconDisplayFlag = 0x08;
20+
private const byte BoosterFlag = 0x04;
21+
private const byte FollowerControlCommand = 0x6C;
22+
private const byte InternalOscillatorCommand = 0x14;
23+
private const byte DefaultContrast = 0x20;
24+
25+
private byte _contrast;
26+
private bool _iconDisplayEnabled;
27+
private bool _boosterEnabled;
28+
29+
// Static members must appear before non-static members (SA1204)
30+
private static byte NormalizeContrast(byte value)
31+
{
32+
return value > ContrastMask ? ContrastMask : (byte)(value & ContrastMask);
33+
}
34+
35+
/// <summary>
36+
/// Initializes a new instance of the <see cref="Aip31068Lcd"/> class using the specified I2C device.
37+
/// </summary>
38+
/// <param name="device">The I2C device used to communicate with the LCD controller.</param>
39+
/// <param name="contrast">Initial contrast value. Valid range: 0-63.</param>
40+
/// <param name="iconDisplayEnabled">True to enable the icon display, false to disable it.</param>
41+
/// <param name="boosterEnabled">True to enable the internal voltage booster, false to disable it.</param>
42+
public Aip31068Lcd(I2cDevice device, byte contrast = DefaultContrast, bool iconDisplayEnabled = false, bool boosterEnabled = true)
43+
: base(new Size(16, 2), LcdInterface.CreateI2c(device, true))
44+
{
45+
_contrast = NormalizeContrast(contrast);
46+
_iconDisplayEnabled = iconDisplayEnabled;
47+
_boosterEnabled = boosterEnabled;
48+
49+
InitializeController();
50+
}
51+
52+
/// <summary>
53+
/// Initializes a new instance of the <see cref="Aip31068Lcd"/> class using an existing LCD interface.
54+
/// </summary>
55+
/// <param name="lcdInterface">The LCD interface used to communicate with the controller.</param>
56+
/// <param name="contrast">Initial contrast value. Valid range: 0-63.</param>
57+
/// <param name="iconDisplayEnabled">True to enable the icon display, false to disable it.</param>
58+
/// <param name="boosterEnabled">True to enable the internal voltage booster, false to disable it.</param>
59+
public Aip31068Lcd(LcdInterface lcdInterface, byte contrast = DefaultContrast, bool iconDisplayEnabled = false, bool boosterEnabled = true)
60+
: base(new Size(16, 2), lcdInterface)
61+
{
62+
_contrast = NormalizeContrast(contrast);
63+
_iconDisplayEnabled = iconDisplayEnabled;
64+
_boosterEnabled = boosterEnabled;
65+
66+
InitializeController();
67+
}
68+
69+
/// <summary>
70+
/// Gets or sets the display contrast (0-63).
71+
/// </summary>
72+
public byte Contrast
73+
{
74+
get => _contrast;
75+
set
76+
{
77+
byte normalized = NormalizeContrast(value);
78+
if (_contrast == normalized)
79+
{
80+
return;
81+
}
82+
83+
_contrast = normalized;
84+
UpdateContrastConfiguration();
85+
}
86+
}
87+
88+
/// <summary>
89+
/// Gets or sets a value indicating whether the icon display is enabled.
90+
/// </summary>
91+
public bool IconDisplayEnabled
92+
{
93+
get => _iconDisplayEnabled;
94+
set
95+
{
96+
if (_iconDisplayEnabled == value)
97+
{
98+
return;
99+
}
100+
101+
_iconDisplayEnabled = value;
102+
UpdateContrastConfiguration();
103+
}
104+
}
105+
106+
/// <summary>
107+
/// Gets or sets a value indicating whether the voltage booster is enabled.
108+
/// </summary>
109+
public bool BoosterEnabled
110+
{
111+
get => _boosterEnabled;
112+
set
113+
{
114+
if (_boosterEnabled == value)
115+
{
116+
return;
117+
}
118+
119+
_boosterEnabled = value;
120+
UpdateContrastConfiguration();
121+
}
122+
}
123+
124+
/// <summary>
125+
/// Performs the extended-instruction initialization sequence required by AIP31068L-compatible controllers.
126+
/// </summary>
127+
/// <remarks>
128+
/// Sequence (extended mode): enter extended instruction set; program bias/oscillator; set contrast low nibble
129+
/// (0x70 | C[3:0]) and high bits with Ion/Bon (0x50 | Ion | Bon | C[5:4]); enable follower (Fon, Rab[2:0]);
130+
/// wait for VLCD to stabilize; return to basic instruction set.
131+
/// References (AIP31068L Product Specification):
132+
/// - Table 3. Instruction Table (p.18/28)
133+
/// - Section 4.5 "INITIALIZING BY INSTRUCTION" (p.20/28)
134+
/// - Section 5.2 "BIAS VOLTAGE DIVIDE CIRCUIT" (p.23/28)
135+
/// Datasheet: https://www.orientdisplay.com/wp-content/uploads/2022/08/AIP31068L.pdf
136+
/// </remarks>
137+
private void InitializeController()
138+
{
139+
EnterExtendedInstructionSet();
140+
SendCommandAndWait(InternalOscillatorCommand);
141+
SendContrastCommands();
142+
SendCommandAndWait(FollowerControlCommand);
143+
DelayHelper.DelayMilliseconds(200, allowThreadYield: true);
144+
ExitExtendedInstructionSet();
145+
146+
// Re-issue the standard configuration commands expected after extended setup.
147+
SendCommandAndWait((byte)_displayControl);
148+
Clear();
149+
SendCommandAndWait((byte)_displayMode);
150+
}
151+
152+
private void UpdateContrastConfiguration()
153+
{
154+
EnterExtendedInstructionSet();
155+
SendContrastCommands();
156+
ExitExtendedInstructionSet();
157+
}
158+
159+
/// <summary>
160+
/// Writes the electronic volume (contrast) low and high bits while in the extended instruction set.
161+
/// </summary>
162+
/// <remarks>
163+
/// Issues 0x70 | C[3:0] (low nibble) then 0x50 | Ion | Bon | C[5:4] (high bits and power/icon controls).
164+
/// References (AIP31068L Product Specification): Table 3. Instruction Table (p.18/28) and
165+
/// Section 4.5 "INITIALIZING BY INSTRUCTION" (p.20/28).
166+
/// Datasheet: https://www.orientdisplay.com/wp-content/uploads/2022/08/AIP31068L.pdf
167+
/// </remarks>
168+
private void SendContrastCommands()
169+
{
170+
SendCommandAndWait((byte)(ContrastSetCommand | (_contrast & 0x0F)));
171+
byte value = (byte)(PowerIconContrastCommand
172+
| (_iconDisplayEnabled ? IconDisplayFlag : 0)
173+
| (_boosterEnabled ? BoosterFlag : 0)
174+
| ((_contrast >> 4) & 0x03));
175+
SendCommandAndWait(value);
176+
}
177+
178+
private void EnterExtendedInstructionSet()
179+
{
180+
SendCommandAndWait((byte)(_displayFunction | DisplayFunction.ExtendedInstructionSet));
181+
}
182+
183+
private void ExitExtendedInstructionSet()
184+
{
185+
SendCommandAndWait((byte)(_displayFunction & ~DisplayFunction.ExtendedInstructionSet));
186+
}
187+
}
188+
}
189+

src/devices/CharacterLcd/CharacterLcd.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<Compile Include="Hd44780.cs" />
99
<Compile Include="ICharacterLcd.cs" />
1010
<Compile Include="CharacterLcdExtensions.cs" />
11+
<Compile Include="Aip31068Lcd.cs" />
1112
<Compile Include="Lcd1602.cs" />
1213
<Compile Include="Lcd2004.cs" />
1314
<Compile Include="LcdConsole.cs" />

src/devices/CharacterLcd/README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,18 @@ using LcdRgb lcd = new LcdRgb(new Size(16, 2), i2cLcdDevice, i2cRgbDevice);
4444
}
4545
```
4646

47+
AIP31068 based LCDs expose the same HD44780 compatible instruction set but require an extended
48+
initialization sequence. The `Aip31068Lcd` binding performs the necessary configuration and allows
49+
adjusting the display contrast:
50+
51+
```csharp
52+
var i2cDevice = I2cDevice.Create(new I2cConnectionSettings(busId: 1, deviceAddress: 0x3E));
53+
using Aip31068Lcd lcd = new(i2cDevice);
54+
lcd.Clear();
55+
lcd.Write("Hello from AIP31068!");
56+
lcd.Contrast = 36; // optional: tune the contrast (0-63)
57+
```
58+
4759
PCF8574T/PCF8574AT Sample
4860
The I2C backpack based on the PCF8574T/AT IC uses specific pin mapping, to consume this device binding on this backpack use like so
4961

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Device.I2c;
6+
using System.Threading;
7+
8+
namespace Iot.Device.CharacterLcd.Samples
9+
{
10+
internal static class Aip31068Sample
11+
{
12+
private const int DefaultBusId = 1;
13+
private const int DefaultAddress = 0x3E;
14+
15+
public static void Run()
16+
{
17+
using I2cDevice device = I2cDevice.Create(new I2cConnectionSettings(DefaultBusId, DefaultAddress));
18+
using Aip31068Lcd lcd = new(device);
19+
20+
lcd.Clear();
21+
lcd.SetCursorPosition(0, 0);
22+
lcd.Write("AIP31068 sample");
23+
DisplayContrast(lcd);
24+
25+
Console.WriteLine("AIP31068 LCD sample ready.");
26+
Console.WriteLine("Use '+' or '-' to adjust contrast, 'B' to toggle the booster, 'I' to toggle icons.");
27+
Console.WriteLine("Press Esc to exit.");
28+
29+
while (true)
30+
{
31+
if (!Console.KeyAvailable)
32+
{
33+
Thread.Sleep(50);
34+
continue;
35+
}
36+
37+
ConsoleKeyInfo key = Console.ReadKey(intercept: true);
38+
if (key.Key == ConsoleKey.Escape)
39+
{
40+
break;
41+
}
42+
43+
switch (key.Key)
44+
{
45+
case ConsoleKey.Add:
46+
case ConsoleKey.OemPlus:
47+
AdjustContrast(lcd, +1);
48+
break;
49+
case ConsoleKey.Subtract:
50+
case ConsoleKey.OemMinus:
51+
AdjustContrast(lcd, -1);
52+
break;
53+
case ConsoleKey.B:
54+
lcd.BoosterEnabled = !lcd.BoosterEnabled;
55+
Console.WriteLine($"Booster {(lcd.BoosterEnabled ? "enabled" : "disabled")}.");
56+
break;
57+
case ConsoleKey.I:
58+
lcd.IconDisplayEnabled = !lcd.IconDisplayEnabled;
59+
Console.WriteLine($"Icon display {(lcd.IconDisplayEnabled ? "enabled" : "disabled")}.");
60+
break;
61+
default:
62+
continue;
63+
}
64+
65+
DisplayContrast(lcd);
66+
}
67+
68+
lcd.Clear();
69+
lcd.Write("Goodbye!");
70+
}
71+
72+
private static void AdjustContrast(Aip31068Lcd lcd, int delta)
73+
{
74+
int contrast = lcd.Contrast + delta;
75+
contrast = Math.Clamp(contrast, 0, 63);
76+
lcd.Contrast = (byte)contrast;
77+
Console.WriteLine($"Contrast set to {lcd.Contrast}.");
78+
}
79+
80+
private static void DisplayContrast(Aip31068Lcd lcd)
81+
{
82+
lcd.SetCursorPosition(0, 1);
83+
lcd.Write($"Contrast: {lcd.Contrast:D2} ");
84+
}
85+
}
86+
}
87+

src/devices/CharacterLcd/samples/Program.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
// UsingGroveRgbDisplay();
2020
// UsingHd44780OverI2C();
2121
// UsingHd44780OverI2CAndArduino();
22+
// UsingAip31068Lcd();
2223
UsingShiftRegister();
2324

2425
void UsingGpioPins()
@@ -87,6 +88,11 @@ void UsingHd44780OverI2CAndArduino()
8788
ExtendedSample.Test(hd44780);
8889
}
8990

91+
void UsingAip31068Lcd()
92+
{
93+
Aip31068Sample.Run();
94+
}
95+
9096
void UsingShiftRegister()
9197
{
9298
int registerSelectPin = 1;

0 commit comments

Comments
 (0)