forked from nanoframework/nanoFramework.IoT.Device
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Max31865.cs
277 lines (233 loc) · 10.5 KB
/
Max31865.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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
// 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.Spi;
using System.Threading;
using UnitsNet;
namespace Iot.Device.Max31865
{
/// <summary>
/// MAX31865 Resistance Temperature Detector to Digital Converter
/// </summary>
/// <remarks>
/// Documentation https://datasheets.maximintegrated.com/en/ds/MAX31865.pdf
/// </remarks>
public class Max31865 : IDisposable
{
// Callender Van Dusen coefficiants A/B for temperature conversion
private const double TemperatureCoefficiantA = 3.9083e-3;
private const double TemperatureCoefficiantB = -5.775e-7;
private readonly PlatinumResistanceThermometerType _prtType;
private readonly ResistanceTemperatureDetectorWires _rtdWires;
private readonly ConversionFilterMode _filterMode;
private readonly double _referenceResistor;
private readonly bool _shouldDispose;
private SpiDevice _spiDevice;
/// <summary>
/// MAX31865 Spi Clock Frequency
/// </summary>
public const int SpiClockFrequency = 5_000_000;
/// <summary>
/// MAX31865 SPI Mode 1
/// </summary>
public const SpiMode SpiMode1 = SpiMode.Mode1;
/// <summary>
/// MAX31865 SPI Mode 3
/// </summary>
public const SpiMode SpiMode3 = SpiMode.Mode3;
/// <summary>
/// MAX31865 SPI Data Flow
/// </summary>
public const DataFlow SpiDataFlow = DataFlow.MsbFirst;
/// <summary>
/// MAX31865 Temperature
/// </summary>
public Temperature Temperature { get => Temperature.FromDegreesCelsius(GetTemperature()); }
/// <summary>
/// Creates a new instance of the MAX31865.
/// </summary>
/// <param name="spiDevice">The communications channel to a device on a SPI bus</param>
/// <param name="platinumResistanceThermometerType">The type of Platinum Resistance Thermometer</param>
/// <param name="resistanceTemperatureDetectorWires">The number of wires the Platinum Resistance Thermometer has</param>
/// <param name="referenceResistor">The reference resistor value in Ohms.</param>
/// <param name="filterMode">Noise rejection filter mode</param>
/// <param name="shouldDispose">True to dispose the SPI device</param>
public Max31865(SpiDevice spiDevice, PlatinumResistanceThermometerType platinumResistanceThermometerType, ResistanceTemperatureDetectorWires resistanceTemperatureDetectorWires, ElectricResistance referenceResistor, ConversionFilterMode filterMode = ConversionFilterMode.Filter60Hz, bool shouldDispose = true)
{
_spiDevice = spiDevice ?? throw new ArgumentNullException(nameof(spiDevice));
_prtType = platinumResistanceThermometerType;
_rtdWires = resistanceTemperatureDetectorWires;
_filterMode = filterMode;
_referenceResistor = referenceResistor.Ohms;
_shouldDispose = shouldDispose;
Initialize();
}
/// <inheritdoc/>
public void Dispose()
{
if (_shouldDispose)
{
_spiDevice?.Dispose();
}
_spiDevice = null!;
}
/// <summary>
/// The fault state of the sensor
/// </summary>
public FaultStatus Faults
{
get
{
SpanByte readBuffer = new byte[2];
WriteRead(Register.FaultStatus, readBuffer);
return new FaultStatus(
(readBuffer[1] & 0x04) == 0x04,
(readBuffer[1] & 0x08) == 0x08,
(readBuffer[1] & 0x20) == 0x20,
(readBuffer[1] & 0x10) == 0x10,
(readBuffer[1] & 0x40) == 0x40,
(readBuffer[1] & 0x80) == 0x80);
}
}
/// <summary>
/// Standard initialization routine.
/// </summary>
/// <remarks>
/// You can add new write lines if you want to alter the settings of the device. Settings can be found in the Technical Manual
/// </remarks>
private void Initialize()
{
SpanByte configurationSetting = new byte[]
{
(byte)Register.ConfigurationWrite,
(byte)((byte)(_rtdWires == ResistanceTemperatureDetectorWires.ThreeWire ? Configuration.ThreeWire : Configuration.TwoFourWire) | (byte)(_filterMode == ConversionFilterMode.Filter50Hz ? Configuration.Filter50HZ : Configuration.Filter60HZ))
};
Write(configurationSetting);
}
/// <summary>
/// Clears all the faults
/// </summary>
private void ClearFaults()
{
SpanByte configuration = new byte[2];
WriteRead(Register.ConfigurationRead, configuration);
// Page 14 (Fault Status Clear (D1)) of the technical documentation
configuration[1] = (byte)(configuration[1] & ~0x2C);
configuration[1] |= (byte)Configuration.FaultStatus;
configuration[0] = (byte)Register.ConfigurationWrite;
Write(configuration);
}
/// <summary>
/// Enable/Disable the bias voltage on the resistance temperature detector sensor
/// </summary>
private void EnableBias(bool enable)
{
SpanByte configuration = new byte[2];
WriteRead(Register.ConfigurationRead, configuration);
configuration[1] = enable ? (byte)(configuration[1] | (byte)Configuration.Bias) : (byte)(configuration[1] & ~(byte)Configuration.Bias);
configuration[0] = (byte)Register.ConfigurationWrite;
Write(configuration);
}
/// <summary>
/// Enable/Disable the one shot mode on the resistance temperature detector sensor
/// </summary>
private void EnableOneShot(bool enable)
{
SpanByte configuration = new byte[2];
WriteRead(Register.ConfigurationRead, configuration);
configuration[1] = enable ? (byte)(configuration[1] | (byte)Configuration.OneShot) : (byte)(configuration[1] & ~(byte)Configuration.OneShot);
configuration[0] = (byte)Register.ConfigurationWrite;
Write(configuration);
}
/// <summary>
/// Reads the raw resistance temperature detector value and converts it to a temperature using the Callender Van Dusen temperature conversion of 15 bit ADC resistance ratio data.
/// </summary>
/// <remarks>
/// This math originates from: http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf
/// </remarks>
/// <returns>Temperature in degrees celsius</returns>
private double GetTemperature()
{
short rtdNominal = (short)_prtType;
double z1, z2, z3, z4;
double temperature;
double resistance = GetResistance();
z1 = -TemperatureCoefficiantA;
z2 = TemperatureCoefficiantA * TemperatureCoefficiantA - (4 * TemperatureCoefficiantB);
z3 = (4 * TemperatureCoefficiantB) / rtdNominal;
z4 = 2 * TemperatureCoefficiantB;
temperature = z2 + (z3 * resistance);
temperature = (Math.Sqrt(temperature) + z1) / z4;
if (temperature < 0)
{
// For the following math to work, nominal resistance temperature detector resistance must be normalized to 100 ohms
resistance /= rtdNominal;
resistance *= 100;
double rpoly = resistance;
temperature = -242.02;
temperature += 2.2228 * rpoly;
rpoly *= resistance; // square
temperature += 2.5859e-3 * rpoly;
rpoly *= resistance; // ^3
temperature -= 4.8260e-6 * rpoly;
rpoly *= resistance; // ^4
temperature -= 2.8183e-8 * rpoly;
rpoly *= resistance; // ^5
temperature += 1.5243e-10 * rpoly;
}
return temperature;
}
/// <summary>
/// Read the resistance of the resistance temperature detector.
/// </summary>
/// <returns>Resistance in Ohms</returns>
private double GetResistance()
{
double rtd = ReadRawRTD();
rtd /= 32768;
rtd *= _referenceResistor;
return rtd;
}
/// <summary>
/// Read the raw 16-bit value from the resistance temperature detector reistance registers in one shot mode
/// </summary>
/// <returns>The raw 16-bit value, NOT temperature</returns>
private ushort ReadRawRTD()
{
ClearFaults();
EnableBias(true);
// Page 3 (Bias Voltage Startup Time) & 4 (see note 4) of the technical documentation
Thread.Sleep(10);
EnableOneShot(true);
// Page 3 (Temperature Conversion Time) of the technical documentation
Thread.Sleep((short)_filterMode);
SpanByte readBuffer = new byte[3];
WriteRead(Register.RTDMSB, readBuffer);
EnableBias(false); // Disable Bias current again to reduce selfheating.
return (ushort)(BinaryPrimitives.ReadUInt16BigEndian(readBuffer.Slice(1, 2)) >> 1);
}
/// <summary>
/// Writes the Data to the Spi Device
/// </summary>
/// <remarks>
/// Takes the data input byte and writes it to the spi device
/// </remarks>
/// <param name="data">Data to write to the device</param>
private void Write(SpanByte data) => _spiDevice.Write(data);
/// <summary>
/// Full Duplex Read of the Data on the Spi Device
/// </summary>
/// <remarks>
/// Writes the read address of the register and outputs a byte list of the length provided
/// </remarks>
/// <param name="register">Register location to write to which starts the device reading</param>
/// <param name="readBuffer">Number of bytes being read</param>
private void WriteRead(Register register, SpanByte readBuffer)
{
SpanByte regAddrBuf = new byte[readBuffer.Length];
regAddrBuf[0] = (byte)(register);
_spiDevice.TransferFullDuplex(regAddrBuf, readBuffer);
}
}
}