forked from nanoframework/nanoFramework.IoT.Device
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Mpr121.cs
231 lines (203 loc) · 9.21 KB
/
Mpr121.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
// 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.Buffers.Binary;
using System.Device.I2c;
using System.Threading;
namespace Iot.Device.Mpr121
{
/// <summary>
/// Supports MPR121 Proximity Capacitive Touch Sensor Controller.
/// </summary>
public class Mpr121 : IDisposable
{
/// <summary>
/// MPR121 Default I2C Address.
/// </summary>
public static readonly byte DefaultI2cAddress = 0x5A;
private static readonly int CHANNELS_NUMBER = 12;// Enum.GetValues(typeof(Channels)).Length;
private I2cDevice _i2cDevice;
private Timer _timer;
private bool[] _statuses;
private int _periodRefresh;
/// <summary>
/// Notifies about a the channel statuses have been changed.
/// Refresh period can be changed by setting PeriodRefresh property.
/// </summary>
public event ChannelStatusesChanged ChannelStatusesChanged;
/// <summary>
/// Gets or sets the period in milliseconds to refresh the channels statuses.
/// </summary>
/// <remark>
/// Set value 0 to stop the automatically refreshing. Setting the value greater than 0 will start/update auto-refresh.
/// </remark>
public int PeriodRefresh
{
get => _periodRefresh;
set
{
_periodRefresh = value;
if (_periodRefresh > 0)
{
_timer.Change(TimeSpan.FromMilliseconds(_periodRefresh), TimeSpan.FromMilliseconds(_periodRefresh));
}
else
{
// Disable the auto-refresh.
_timer.Change(Timeout.Infinite, Timeout.Infinite);
}
}
}
/// <summary>
/// Initialize a MPR121 controller.
/// </summary>
/// <param name="i2cDevice">The i2c device.</param>
/// <param name="periodRefresh">The period in milliseconds of refresing the channel statuses.</param>
/// <param name="configuration">The controller configuration.</param>
public Mpr121(I2cDevice i2cDevice, int periodRefresh = -1, Mpr121Configuration? configuration = null)
{
configuration = configuration ?? GetDefaultConfiguration();
_i2cDevice = i2cDevice ?? throw new ArgumentNullException(nameof(i2cDevice));
_timer = new Timer(RefreshChannelStatuses, this, Timeout.Infinite, Timeout.Infinite);
_statuses = new bool[CHANNELS_NUMBER]; // new Dictionary<Channels, bool>();
for (int i = 0; i < CHANNELS_NUMBER; i++)
{
_statuses[i] = false;
}
InitializeController(configuration);
PeriodRefresh = periodRefresh;
}
/// <inheritdoc/>
public void Dispose()
{
_i2cDevice?.Dispose();
_i2cDevice = null!;
_timer?.Dispose();
_timer = null!;
}
/// <summary>
/// Reads the channel statuses of MPR121 controller.
/// </summary>
public bool[] ReadChannelStatuses()
{
RefreshChannelStatuses();
bool[] statuses = new bool[CHANNELS_NUMBER];
Array.Copy(_statuses, statuses, CHANNELS_NUMBER);
return statuses;
}
/// <summary>
/// Reads the channel status of MPR121 controller.
/// </summary>
/// <param name="channel">The channel to read status.</param>
/// <remark>
/// Please use ReadChannelStatuses() if you need to read statuses of multiple channels.
/// Using this method several times to read status for several channels can affect the performance.
/// </remark>
public bool ReadChannelStatus(Channels channel)
{
RefreshChannelStatuses();
return _statuses[(int)channel];
}
private static Mpr121Configuration GetDefaultConfiguration()
{
return new Mpr121Configuration()
{
MaxHalfDeltaRising = 0x01,
NoiseHalfDeltaRising = 0x01,
NoiseCountLimitRising = 0x00,
FilterDelayCountLimitRising = 0x00,
MaxHalfDeltaFalling = 0x01,
NoiseHalfDeltaFalling = 0x01,
NoiseCountLimitFalling = 0xFF,
FilterDelayCountLimitFalling = 0x01,
ElectrodeTouchThreshold = 0x0F,
ElectrodeReleaseThreshold = 0x0A,
ChargeDischargeTimeConfiguration = 0x04,
ElectrodeConfiguration = 0x0C
};
}
private void InitializeController(Mpr121Configuration configuration)
{
SetRegister(Registers.MHDR, configuration.MaxHalfDeltaRising);
SetRegister(Registers.NHDR, configuration.NoiseHalfDeltaRising);
SetRegister(Registers.NCLR, configuration.NoiseCountLimitRising);
SetRegister(Registers.FDLR, configuration.FilterDelayCountLimitRising);
SetRegister(Registers.MHDF, configuration.MaxHalfDeltaFalling);
SetRegister(Registers.NHDF, configuration.NoiseHalfDeltaFalling);
SetRegister(Registers.NCLF, configuration.NoiseCountLimitFalling);
SetRegister(Registers.FDLF, configuration.FilterDelayCountLimitFalling);
SetRegister(Registers.E0TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E0RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.E1TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E1RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.E2TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E2RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.E3TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E3RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.E4TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E4RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.E5TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E5RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.E6TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E6RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.E7TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E7RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.E8TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E8RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.E9TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E9RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.E10TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E10RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.E11TTH, configuration.ElectrodeTouchThreshold);
SetRegister(Registers.E11RTH, configuration.ElectrodeReleaseThreshold);
SetRegister(Registers.CDTC, configuration.ChargeDischargeTimeConfiguration);
SetRegister(Registers.ELECONF, configuration.ElectrodeConfiguration);
}
/// <summary>
/// The callback function for timer to refresh channels statuses.
/// </summary>
private void RefreshChannelStatuses(object? state) => RefreshChannelStatuses();
/// <summary>
/// Refresh the channel statuses.
/// </summary>
private void RefreshChannelStatuses()
{
// Pause the auto-refresh to prevent possible collisions.
var periodRefresh = PeriodRefresh;
PeriodRefresh = 0;
SpanByte buffer = new byte[2];
_i2cDevice.Read(buffer);
short rawStatus = BinaryPrimitives.ReadInt16LittleEndian(buffer);
bool isStatusChanged = false;
for (var i = 0; i < CHANNELS_NUMBER; i++)
{
bool status = ((1 << i) & rawStatus) > 0;
if (_statuses[i] != status)
{
_statuses[i] = status;
isStatusChanged = true;
}
}
if (isStatusChanged)
{
OnChannelStatusesChanged();
}
// Resume the auto-refresh.
PeriodRefresh = periodRefresh;
}
private void SetRegister(Registers register, byte value)
{
SpanByte data = new byte[]
{
(byte)register, value
};
_i2cDevice.Write(data);
}
private void OnChannelStatusesChanged()
{
bool[] statuses = new bool[CHANNELS_NUMBER];
Array.Copy(_statuses, statuses, CHANNELS_NUMBER);
ChannelStatusesChanged?.Invoke(this, new ChannelStatusesChangedEventArgs(statuses));
}
}
}