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 = &reg,
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 = &reg, .len = 1};
29 	const struct spi_buf_set tx = {.buffers = &tx_buf, .count = 1};
30 	struct spi_buf rx_buf[] = {{
31 					   .buf = &reg,
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