forked from dotnet/iot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
LcdInterface.I2c.cs
147 lines (130 loc) · 6.51 KB
/
LcdInterface.I2c.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Device.I2c;
namespace Iot.Device.CharacterLcd
{
public abstract partial class LcdInterface : IDisposable
{
/// <summary>
/// Built-in I2c access to the Hd44780 compatible controller. The Philips/NXP LCD driver ICs
/// (such as the PCF2119x) are examples of this support.
/// </summary>
private sealed class I2c : LcdInterface
{
[Flags]
private enum ControlByteFlags : byte
{
/// <summary>
/// When set, another control byte will follow the next data/command byte.
/// Otherwise the last control byte will be used.
/// </summary>
/// <remarks>
/// This is only relevant when sending multiple bytes of data to the register.
/// When a new I2c transmission is made, the first byte is always assumed to
/// be a control byte. This flag is needed if you want to flip the "RS" bit
/// in a stream of bytes.
/// </remarks>
ControlByteFollows = 0b_1000_0000,
/// <summary>
/// When set the data register will be selected (i.e. equivalent to
/// RS pin being high). Otherwise the instruction/command register
/// will be updated.
/// </summary>
RegisterSelect = 0b_0100_0000
// No other bits are used
}
// Philips created the I2c bus and likely were the first to provide character LCD
// controllers with I2c functionality built-in. This driver was written using the
// PCF2119x datasheet as reference. Other chipsets are compatible with the addressing
// scheme (Sitronix ST7036, Aiptek AIP31068L, probably others).
private readonly I2cDevice _device;
// While the LCD controller can be set to 4 bit mode there really isn't a way to
// mess with that from the I2c pins as far as I know. Other drivers try to set the
// controller up for 8 bit mode, but it appears they are doing so only because they've
// copied existing HD44780 drivers.
public I2c(I2cDevice device) => _device = device;
public override bool EightBitMode => true;
public override bool BacklightOn
{
// Setting the backlight on or off is not supported with 8 bit commands, according to the docs.
get => true;
set
{
// Ignore setting the backlight. Exceptions are not expected by user code here, as it is normal to
// enable this during initialization, so that it is enabled whether switching it is supported or not.
}
}
public override void SendCommand(byte command)
{
Span<byte> buffer = stackalloc byte[]
{
0x00,
command
};
_device.Write(buffer);
}
public override void SendCommands(ReadOnlySpan<byte> commands)
{
// There is a limit to how much data the controller can accept at once. Haven't found documentation
// for this yet, can probably iterate a bit more on this to find a true "max". Not adding additional
// logic like SendData as we don't expect a need to send more than a handful of commands at a time.
if (commands.Length > 20)
{
throw new ArgumentOutOfRangeException(nameof(commands), "Too many commands in one request.");
}
Span<byte> buffer = stackalloc byte[commands.Length + 1];
buffer[0] = 0x00;
commands.CopyTo(buffer.Slice(1));
_device.Write(buffer);
}
public override void SendData(byte value)
{
Span<byte> buffer = stackalloc byte[]
{
(byte)ControlByteFlags.RegisterSelect,
value
};
_device.Write(buffer);
}
public override void SendData(ReadOnlySpan<byte> values)
{
// There is a limit to how much data the controller can accept at once. Haven't found documentation
// for this yet, can probably iterate a bit more on this to find a true "max". 40 was too much.
const int MaxCopy = 20;
Span<byte> buffer = stackalloc byte[MaxCopy + 1];
buffer[0] = (byte)ControlByteFlags.RegisterSelect;
Span<byte> bufferData = buffer.Slice(1);
while (values.Length > 0)
{
ReadOnlySpan<byte> currentValues = values.Slice(0, values.Length > MaxCopy ? MaxCopy : values.Length);
values = values.Slice(currentValues.Length);
currentValues.CopyTo(bufferData);
_device.Write(buffer.Slice(0, currentValues.Length + 1));
}
}
public override void SendData(ReadOnlySpan<char> values)
{
// There is a limit to how much data the controller can accept at once. Haven't found documentation
// for this yet, can probably iterate a bit more on this to find a true "max". 40 was too much.
const int MaxCopy = 20;
Span<byte> buffer = stackalloc byte[MaxCopy + 1];
buffer[0] = (byte)ControlByteFlags.RegisterSelect;
Span<byte> bufferData = buffer.Slice(1);
while (values.Length > 0)
{
ReadOnlySpan<char> buff = values.Slice(0, values.Length > MaxCopy ? MaxCopy : values.Length);
// As we are in a while loop, we can't use stackalloc
Span<byte> currentValues = new byte[buff.Length];
for (int i = 0; i < buff.Length; i++)
{
currentValues[i] = (byte)buff[i];
}
values = values.Slice(currentValues.Length);
currentValues.CopyTo(bufferData);
_device.Write(buffer.Slice(0, currentValues.Length + 1));
}
}
}
}
}