1 /*
2 * Copyright (c) 2022 HAW Hamburg FTZ-DIWIP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include "max31865.h"
7
max31865_spi_write(const struct device * dev,uint8_t reg,uint8_t * data,size_t len)8 static int max31865_spi_write(const struct device *dev, uint8_t reg, uint8_t *data, size_t len)
9 {
10 const struct max31865_config *cfg = dev->config;
11
12 const struct spi_buf bufs[] = {{
13 .buf = ®,
14 .len = 1,
15 },
16 {.buf = data, .len = len}};
17
18 const struct spi_buf_set tx = {.buffers = bufs, .count = 2};
19
20 return spi_write_dt(&cfg->spi, &tx);
21 }
22
max31865_spi_read(const struct device * dev,uint8_t reg,uint8_t * data,size_t len)23 static int max31865_spi_read(const struct device *dev, uint8_t reg, uint8_t *data, size_t len)
24 {
25 const struct max31865_config *cfg = dev->config;
26
27 reg &= 0x7F;
28 const struct spi_buf tx_buf = {.buf = ®, .len = 1};
29 const struct spi_buf_set tx = {.buffers = &tx_buf, .count = 1};
30 struct spi_buf rx_buf[] = {{
31 .buf = ®,
32 .len = 1,
33 },
34 {.buf = data, .len = len}};
35 const struct spi_buf_set rx = {.buffers = rx_buf, .count = 2};
36
37 return spi_transceive_dt(&cfg->spi, &tx, &rx);
38 }
39
40 /**
41 * @brief Set device configuration register
42 *
43 * @param device device instance
44 * @return 0 if successful, or negative error code from SPI API
45 */
configure_device(const struct device * dev)46 static int configure_device(const struct device *dev)
47 {
48 struct max31865_data *data = dev->data;
49 uint8_t cmd[] = {data->config_control_bits};
50 int err = max31865_spi_write(dev, WR(REG_CONFIG), cmd, 1);
51
52 if (err < 0) {
53 LOG_ERR("Error write SPI%d\n", err);
54 }
55 return err;
56 }
57
58 /**
59 * @brief Set device fail threshold registers
60 *
61 * @param device device instance
62 * @return 0 if successful, or negative error code from SPI API
63 */
set_threshold_values(const struct device * dev)64 static int set_threshold_values(const struct device *dev)
65 {
66 const struct max31865_config *config = dev->config;
67 uint8_t cmd[] = {
68 (config->high_threshold >> 7) & 0x00ff, (config->high_threshold << 1) & 0x00ff,
69 (config->low_threshold >> 7) & 0x00ff, (config->low_threshold << 1) & 0x00ff};
70 int err = max31865_spi_write(dev, WR(REG_HIGH_FAULT_THR_MSB), cmd, 4);
71
72 if (err < 0) {
73 LOG_ERR("Error write SPI%d\n", err);
74 }
75 return err;
76 }
77
78 #ifdef CONFIG_NEWLIB_LIBC
79
80 /**
81 * Apply the Callendar-Van Dusen equation to convert the RTD resistance
82 * to temperature:
83 * Tr = (-A + SQRT(delta) ) / 2*B
84 * delta = A^2 - 4B*(1-Rt/Ro)
85 * For under zero, taken from
86 * https://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf
87 * @param resistance measured resistance
88 * @param resistance_0 constant resistance at 0oC
89 * @return calculated temperature
90 */
calculate_temperature(double resistance,double resistance_0)91 static double calculate_temperature(double resistance, double resistance_0)
92 {
93 double temperature;
94 double delta = (RTD_A * RTD_A) - 4 * RTD_B * (1.0 - resistance / resistance_0);
95
96 temperature = (-RTD_A + sqrt(delta)) / (2 * RTD_B);
97 if (temperature > 0.0) {
98 return temperature;
99 }
100 resistance /= resistance_0;
101 resistance *= 100.0;
102 temperature = RTD_C[0] + RTD_C[1] * resistance + RTD_C[2] * pow(resistance, 2) -
103 RTD_C[3] * pow(resistance, 3) - RTD_C[4] * pow(resistance, 4) +
104 RTD_C[5] * pow(resistance, 5);
105 return temperature;
106 }
107
108 #else
109
110 /**
111 * Apply a very good linear approximation of the Callendar-Van Dusen equation to convert the RTD
112 * resistance to temperature:
113 * @param resistance measured resistance
114 * @param resistance_0 constant resistance at 0oC
115 * @return calculated temperature
116 */
calculate_temperature(double resistance,double resistance_0)117 static double calculate_temperature(double resistance, double resistance_0)
118 {
119 double temperature;
120
121 temperature = (resistance - resistance_0) / (resistance_0 * RTD_A);
122 return temperature;
123 }
124
125 #endif
126
127 /**
128 * @brief Enable/Disable Vbias for MAX31865
129 *
130 * @param device device instance
131 * @param enable true, turn on vbias, false, turn off vbias
132 * @return 0 if successful, or negative error code from SPI API
133 */
max31865_set_vbias(const struct device * dev,bool enable)134 static int max31865_set_vbias(const struct device *dev, bool enable)
135 {
136 struct max31865_data *data = dev->data;
137
138 WRITE_BIT(data->config_control_bits, 7, enable);
139 return configure_device(dev);
140 }
141
max31865_set_three_wire(const struct device * dev,bool enable)142 static int max31865_set_three_wire(const struct device *dev, bool enable)
143 {
144 struct max31865_data *data = dev->data;
145
146 WRITE_BIT(data->config_control_bits, 4, enable);
147 return configure_device(dev);
148 }
149
max31865_error_to_string(uint8_t fault_register)150 static char *max31865_error_to_string(uint8_t fault_register)
151 {
152 switch (fault_register) {
153 case 0:
154 return "No error";
155
156 case MAX31865_FAULT_VOLTAGE:
157 return "Over/under voltage fault";
158
159 case MAX31865_FAULT_RTDIN_FORCE:
160 return "RTDIN- < 0.85*VBIAS (FORCE- open)";
161
162 case MAX31865_FAULT_REFIN_FORCE:
163 return "REFIN- < 0.85*VBIAS (FORCE- open)";
164
165 case MAX31865_FAULT_REFIN:
166 return "REFIN- > 0.85*VBIAS";
167
168 case MAX31865_FAULT_LOW_THRESHOLD:
169 return "RTD below low threshold";
170
171 case MAX31865_FAULT_HIGH_THRESHOLD:
172 return "RTD above high threshold";
173 }
174 return "";
175 }
176
max31865_fault_register(const struct device * dev)177 static int max31865_fault_register(const struct device *dev)
178 {
179 uint8_t fault_register;
180 uint8_t saved_fault_bits;
181
182 max31865_spi_read(dev, (REG_FAULT_STATUS), &fault_register, 1);
183 struct max31865_data *data = dev->data;
184 saved_fault_bits = data->config_control_bits & FAULT_BITS_CLEAR_MASK;
185 /*Clear fault register */
186 WRITE_BIT(data->config_control_bits, 1, 1);
187 data->config_control_bits &= ~FAULT_BITS_CLEAR_MASK;
188 configure_device(dev);
189 LOG_ERR("Fault Register: 0x%02x, %s", fault_register,
190 max31865_error_to_string(fault_register));
191 WRITE_BIT(data->config_control_bits, 1, 0);
192 data->config_control_bits |= saved_fault_bits;
193
194 return 0;
195 }
196
197 /**
198 * @brief Get temperature value in oC for device
199 *
200 * @param device device instance
201 * @param temperature measured temperature
202 * @return 0 if successful, or negative error code
203 */
max31865_get_temperature(const struct device * dev)204 static int max31865_get_temperature(const struct device *dev)
205 {
206 max31865_set_vbias(dev, true);
207 union read_reg_u {
208 uint8_t u8[2];
209 uint16_t u16;
210 } read_reg;
211
212 read_reg.u16 = 0;
213 /* Waiting Time for Temerature Conversion (Page 3 of the datasheet)*/
214 k_sleep(K_MSEC(66));
215 /* Read resistance measured value */
216 int err = max31865_spi_read(dev, (REG_RTD_MSB), read_reg.u8, 2);
217
218 max31865_set_vbias(dev, false);
219
220 if (err < 0) {
221 LOG_ERR("SPI read %d\n", err);
222 return -EIO;
223 }
224 read_reg.u16 = sys_be16_to_cpu(read_reg.u16);
225
226 LOG_DBG("RAW: %02X %02X , %04X", read_reg.u8[0], read_reg.u8[1], read_reg.u16);
227 if (TESTBIT(read_reg.u16, 0)) {
228 max31865_fault_register(dev);
229 return -EIO;
230 }
231
232 const struct max31865_config *config = dev->config;
233 struct max31865_data *data = dev->data;
234
235 read_reg.u16 = read_reg.u16 >> 1;
236 double resistance = (double)read_reg.u16;
237
238 resistance /= 32768;
239 resistance *= config->resistance_reference;
240 data->temperature = calculate_temperature(resistance, config->resistance_at_zero);
241 return 0;
242 }
243
max31865_init(const struct device * dev)244 static int max31865_init(const struct device *dev)
245 {
246 const struct max31865_config *config = dev->config;
247
248 if (!spi_is_ready_dt(&config->spi)) {
249 return -ENODEV;
250 }
251 struct max31865_data *data = dev->data;
252 /* Set the confgiuration register */
253 data->config_control_bits = 0;
254
255 WRITE_BIT(data->config_control_bits, 6, config->conversion_mode);
256 WRITE_BIT(data->config_control_bits, 5, config->one_shot);
257 data->config_control_bits |= config->fault_cycle & 0b00001100;
258 WRITE_BIT(data->config_control_bits, 0, config->filter_50hz);
259
260 configure_device(dev);
261 set_threshold_values(dev);
262 max31865_set_vbias(dev, false);
263 max31865_set_three_wire(dev, config->three_wire);
264 return 0;
265 }
266
max31865_sample_fetch(const struct device * dev,enum sensor_channel chan)267 static int max31865_sample_fetch(const struct device *dev, enum sensor_channel chan)
268 {
269 if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP) {
270 LOG_ERR("Invalid channel provided");
271 return -ENOTSUP;
272 }
273 return max31865_get_temperature(dev);
274 }
275
max31865_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)276 static int max31865_channel_get(const struct device *dev, enum sensor_channel chan,
277 struct sensor_value *val)
278 {
279 struct max31865_data *data = dev->data;
280
281 switch (chan) {
282 case SENSOR_CHAN_AMBIENT_TEMP:
283 return sensor_value_from_double(val, data->temperature);
284 default:
285 return -ENOTSUP;
286 }
287 }
288
max31865_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)289 static int max31865_attr_set(const struct device *dev, enum sensor_channel chan,
290 enum sensor_attribute attr, const struct sensor_value *val)
291 {
292 if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP) {
293 LOG_ERR("Invalid channel provided");
294 return -ENOTSUP;
295 }
296
297 switch (attr) {
298 case SENSOR_ATTR_MAX31865_THREE_WIRE:
299 return max31865_set_three_wire(dev, val->val1);
300 default:
301 return -ENOTSUP;
302 }
303 }
304
305 static DEVICE_API(sensor, max31865_api_funcs) = {
306 .sample_fetch = max31865_sample_fetch,
307 .channel_get = max31865_channel_get,
308 .attr_set = max31865_attr_set,
309 };
310
311 #define MAX31865_DEFINE(inst) \
312 \
313 static struct max31865_data max31865_data_##inst; \
314 \
315 static const struct max31865_config max31865_config_##inst = { \
316 .spi = SPI_DT_SPEC_INST_GET(inst, SPI_MODE_CPHA | SPI_WORD_SET(8), 0), \
317 .resistance_at_zero = DT_INST_PROP(inst, resistance_at_zero), \
318 .resistance_reference = DT_INST_PROP(inst, resistance_reference), \
319 .conversion_mode = false, \
320 .one_shot = true, \
321 .three_wire = DT_INST_PROP(inst, maxim_3_wire), \
322 .fault_cycle = MAX31865_FAULT_DETECTION_NONE, \
323 .filter_50hz = DT_INST_PROP(inst, filter_50hz), \
324 .low_threshold = DT_INST_PROP(inst, low_threshold), \
325 .high_threshold = DT_INST_PROP(inst, high_threshold), \
326 }; \
327 \
328 SENSOR_DEVICE_DT_INST_DEFINE(inst, max31865_init, NULL, &max31865_data_##inst, \
329 &max31865_config_##inst, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
330 &max31865_api_funcs);
331
332 /* Create the struct device for every status "okay" node in the devicetree. */
333 DT_INST_FOREACH_STATUS_OKAY(MAX31865_DEFINE)
334