1 /* Driver for MS5837 pressure sensor
2  *
3  * Copyright (c) 2018 Jan Van Winkel <jan.van_winkel@dxplore.eu>
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define DT_DRV_COMPAT meas_ms5837
9 
10 #include <zephyr/init.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/sys/byteorder.h>
13 #include <zephyr/drivers/sensor.h>
14 #include <zephyr/sys/__assert.h>
15 #include <zephyr/logging/log.h>
16 
17 #include "ms5837.h"
18 
19 LOG_MODULE_REGISTER(MS5837, CONFIG_SENSOR_LOG_LEVEL);
20 
ms5837_get_measurement(const struct device * dev,uint32_t * val,uint8_t cmd,const uint8_t delay)21 static int ms5837_get_measurement(const struct device *dev, uint32_t *val,
22 				  uint8_t cmd, const uint8_t delay)
23 {
24 	const struct ms5837_config *cfg = dev->config;
25 	uint8_t adc_read_cmd = MS5837_CMD_CONV_READ_ADC;
26 	int err;
27 
28 	*val = 0U;
29 
30 	err = i2c_write_dt(&cfg->i2c, &cmd, 1);
31 	if (err < 0) {
32 		return err;
33 	}
34 
35 	k_msleep(delay);
36 
37 	err = i2c_burst_read_dt(&cfg->i2c, adc_read_cmd, ((uint8_t *)val) + 1,
38 				3);
39 	if (err < 0) {
40 		return err;
41 	}
42 
43 	*val = sys_be32_to_cpu(*val);
44 
45 	return 0;
46 }
47 
ms5837_compensate_30(const struct device * dev,const int32_t adc_temperature,const int32_t adc_pressure)48 static void ms5837_compensate_30(const struct device *dev,
49 				 const int32_t adc_temperature,
50 				 const int32_t adc_pressure)
51 {
52 	struct ms5837_data *data = dev->data;
53 	int64_t dT;
54 	int64_t OFF;
55 	int64_t SENS;
56 	int64_t temp_sq;
57 	int32_t Ti;
58 	int32_t OFFi;
59 	int32_t SENSi;
60 
61 	/* first order compensation as per datasheet
62 	 * (https://www.te.com/usa-en/product-CAT-BLPS0017.html) section
63 	 * PRESSURE AND TEMPERATURE CALCULATION
64 	 */
65 
66 	dT = adc_temperature - ((int32_t)(data->t_ref) << 8);
67 	data->temperature = 2000 + (dT * data->tempsens) / (1ll << 23);
68 	OFF = ((int64_t)(data->off_t1) << 16) + (dT * data->tco) / (1ll << 7);
69 	SENS = ((int64_t)(data->sens_t1) << 15) + (dT * data->tcs) / (1ll << 8);
70 
71 	/* Second order compensation as per datasheet
72 	 * (https://www.te.com/usa-en/product-CAT-BLPS0017.html) section
73 	 * SECOND ORDER TEMPERATURE COMPENSATION
74 	 */
75 
76 	temp_sq = (int64_t)(data->temperature - 2000) * (data->temperature - 2000);
77 	if (data->temperature < 2000) {
78 		Ti = (3ll * dT * dT) / (1ll << 33);
79 		OFFi = (3ll * temp_sq) / (1ll << 1);
80 		SENSi = (5ll * temp_sq) / (1ll << 3);
81 		if (data->temperature < -1500) {
82 			temp_sq = (data->temperature + 1500) *
83 				  (data->temperature + 1500);
84 			OFFi += 7ll * temp_sq;
85 			SENSi += 4ll * temp_sq;
86 		}
87 	} else {
88 		Ti = (2ll * dT * dT) / (1ll << 37);
89 		OFFi = temp_sq / (1ll << 4);
90 		SENSi = 0;
91 	}
92 
93 	OFF -= OFFi;
94 	SENS -= SENSi;
95 
96 	data->temperature -= Ti;
97 	data->pressure =
98 	    (((SENS * adc_pressure) / (1ll << 21)) - OFF) / (1ll << 13);
99 }
100 
101 /*
102  * First and second order pressure and temperature calculations, as per the flowchart in the
103  * MS5837-02B datasheet. (see "Pressure and Temperature Calculation", pages 6 and 7, REV a8 12/2019)
104  */
ms5837_compensate_02(const struct device * dev,const int32_t adc_temperature,const int32_t adc_pressure)105 static void ms5837_compensate_02(const struct device *dev,
106 				 const int32_t adc_temperature,
107 				 const int32_t adc_pressure)
108 {
109 	struct ms5837_data *data = dev->data;
110 	int64_t dT;
111 	int64_t OFF;
112 	int64_t SENS;
113 	int64_t temp_sq;
114 	int32_t Ti;
115 	int32_t OFFi;
116 	int32_t SENSi;
117 
118 	dT = adc_temperature - ((int32_t)(data->t_ref) << 8);
119 	data->temperature = 2000 + (dT * data->tempsens) / (1ll << 23);
120 	OFF = ((int64_t)(data->off_t1) << 17) + (dT * data->tco) / (1ll << 6);
121 	SENS = ((int64_t)(data->sens_t1) << 16) + (dT * data->tcs) / (1ll << 7);
122 
123 	temp_sq = (int64_t)(data->temperature - 2000) * (data->temperature - 2000);
124 	if (data->temperature < 2000) {
125 		Ti = (11ll * dT * dT) / (1ll << 35);
126 		OFFi = (31ll * temp_sq) / (1ll << 3);
127 		SENSi = (63ll * temp_sq) / (1ll << 5);
128 	} else {
129 		Ti = 0;
130 		OFFi = 0;
131 		SENSi = 0;
132 	}
133 
134 	OFF -= OFFi;
135 	SENS -= SENSi;
136 
137 	data->temperature -= Ti;
138 	data->pressure = (((SENS * adc_pressure) / (1ll << 21)) - OFF) / (1ll << 15);
139 }
140 
ms5837_sample_fetch(const struct device * dev,enum sensor_channel channel)141 static int ms5837_sample_fetch(const struct device *dev,
142 			       enum sensor_channel channel)
143 {
144 	struct ms5837_data *data = dev->data;
145 	int err;
146 	uint32_t adc_pressure;
147 	uint32_t adc_temperature;
148 
149 	__ASSERT_NO_MSG(channel == SENSOR_CHAN_ALL);
150 
151 	err = ms5837_get_measurement(dev, &adc_pressure, data->presure_conv_cmd,
152 				     data->presure_conv_delay);
153 	if (err < 0) {
154 		return err;
155 	}
156 
157 	err = ms5837_get_measurement(dev, &adc_temperature,
158 				     data->temperature_conv_cmd,
159 				     data->temperature_conv_delay);
160 	if (err < 0) {
161 		return err;
162 	}
163 
164 	data->comp_func(dev, adc_temperature, adc_pressure);
165 
166 	return 0;
167 }
168 
ms5837_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)169 static int ms5837_channel_get(const struct device *dev,
170 			      enum sensor_channel chan,
171 			      struct sensor_value *val)
172 {
173 	struct ms5837_data *data = dev->data;
174 
175 	switch (chan) {
176 	case SENSOR_CHAN_AMBIENT_TEMP:
177 		/* Internal temperature is in 100ths of deg C */
178 		val->val1 = data->temperature / 100;
179 		val->val2 = data->temperature % 100 * 10000;
180 		break;
181 	case SENSOR_CHAN_PRESS:
182 		/* Internal value is (mbar * 100), so factor to kPa is 1000 */
183 		val->val1 = data->pressure / 1000;
184 		val->val2 = data->pressure % 1000 * 1000;
185 		break;
186 	default:
187 		return -ENOTSUP;
188 	}
189 
190 	return 0;
191 }
192 
ms5837_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)193 static int ms5837_attr_set(const struct device *dev, enum sensor_channel chan,
194 			   enum sensor_attribute attr,
195 			   const struct sensor_value *val)
196 {
197 	struct ms5837_data *data = dev->data;
198 	uint8_t p_conv_cmd;
199 	uint8_t t_conv_cmd;
200 	uint8_t conv_delay;
201 
202 	if (attr == SENSOR_ATTR_OVERSAMPLING) {
203 
204 		switch (val->val1) {
205 		case 8192:
206 			p_conv_cmd = MS5837_CMD_CONV_P_8192;
207 			t_conv_cmd = MS5837_CMD_CONV_T_8192;
208 			conv_delay = 19U;
209 			break;
210 		case 4096:
211 			p_conv_cmd = MS5837_CMD_CONV_P_4096;
212 			t_conv_cmd = MS5837_CMD_CONV_T_4096;
213 			conv_delay = 10U;
214 			break;
215 		case 2048:
216 			p_conv_cmd = MS5837_CMD_CONV_P_2048;
217 			t_conv_cmd = MS5837_CMD_CONV_T_2048;
218 			conv_delay = 5U;
219 			break;
220 		case 1024:
221 			p_conv_cmd = MS5837_CMD_CONV_P_1024;
222 			t_conv_cmd = MS5837_CMD_CONV_T_1024;
223 			conv_delay = 3U;
224 			break;
225 		case 512:
226 			p_conv_cmd = MS5837_CMD_CONV_P_512;
227 			t_conv_cmd = MS5837_CMD_CONV_T_512;
228 			conv_delay = 2U;
229 			break;
230 		case 256:
231 			p_conv_cmd = MS5837_CMD_CONV_P_256;
232 			t_conv_cmd = MS5837_CMD_CONV_T_256;
233 			conv_delay = 1U;
234 			break;
235 		default:
236 			LOG_ERR("invalid oversampling rate %d", val->val1);
237 			return -EINVAL;
238 		}
239 
240 		if (chan == SENSOR_CHAN_ALL) {
241 			data->presure_conv_cmd = p_conv_cmd;
242 			data->presure_conv_delay = conv_delay;
243 			data->temperature_conv_cmd = t_conv_cmd;
244 			data->temperature_conv_delay = conv_delay;
245 			return 0;
246 		}
247 
248 		if (chan == SENSOR_CHAN_PRESS) {
249 			data->presure_conv_cmd = p_conv_cmd;
250 			data->presure_conv_delay = conv_delay;
251 			return 0;
252 		}
253 
254 		if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
255 			data->temperature_conv_cmd = t_conv_cmd;
256 			data->temperature_conv_delay = conv_delay;
257 			return 0;
258 		}
259 
260 		return -ENOTSUP;
261 	}
262 
263 	return -ENOTSUP;
264 }
265 
266 static DEVICE_API(sensor, ms5837_api_funcs) = {
267 	.attr_set = ms5837_attr_set,
268 	.sample_fetch = ms5837_sample_fetch,
269 	.channel_get = ms5837_channel_get,
270 };
271 
ms5837_read_prom(const struct device * dev,const uint8_t cmd,uint16_t * val)272 static int ms5837_read_prom(const struct device *dev, const uint8_t cmd,
273 			    uint16_t *val)
274 {
275 	const struct ms5837_config *cfg = dev->config;
276 	int err;
277 
278 	err = i2c_burst_read_dt(&cfg->i2c, cmd, (uint8_t *)val, 2);
279 	if (err < 0) {
280 		return err;
281 	}
282 
283 	*val = sys_be16_to_cpu(*val);
284 
285 	return 0;
286 }
287 
ms5837_init(const struct device * dev)288 static int ms5837_init(const struct device *dev)
289 {
290 	struct ms5837_data *data = dev->data;
291 	const struct ms5837_config *cfg = dev->config;
292 	int err;
293 	uint8_t cmd;
294 
295 	data->pressure = 0;
296 	data->temperature = 0;
297 
298 	data->presure_conv_cmd = MS5837_CMD_CONV_P_256;
299 	data->presure_conv_delay = 1U;
300 	data->temperature_conv_cmd = MS5837_CMD_CONV_T_256;
301 	data->temperature_conv_delay = 1U;
302 
303 	if (!device_is_ready(cfg->i2c.bus)) {
304 		LOG_ERR("Bus device is not ready");
305 		return -ENODEV;
306 	}
307 
308 	cmd = MS5837_CMD_RESET;
309 	err = i2c_write_dt(&cfg->i2c, &cmd, 1);
310 	if (err < 0) {
311 		return err;
312 	}
313 
314 	err = ms5837_read_prom(dev, MS5837_CMD_CONV_READ_CRC, &data->factory);
315 	if (err < 0) {
316 		LOG_ERR("couldn't read device info");
317 		return err;
318 	}
319 
320 	err = ms5837_read_prom(dev, MS5837_CMD_CONV_READ_SENS_T1, &data->sens_t1);
321 	if (err < 0) {
322 		return err;
323 	}
324 
325 	err = ms5837_read_prom(dev, MS5837_CMD_CONV_READ_OFF_T1, &data->off_t1);
326 	if (err < 0) {
327 		return err;
328 	}
329 
330 	err = ms5837_read_prom(dev, MS5837_CMD_CONV_READ_TCS, &data->tcs);
331 	if (err < 0) {
332 		return err;
333 	}
334 
335 	err = ms5837_read_prom(dev, MS5837_CMD_CONV_READ_TCO, &data->tco);
336 	if (err < 0) {
337 		return err;
338 	}
339 
340 	err = ms5837_read_prom(dev, MS5837_CMD_CONV_READ_T_REF, &data->t_ref);
341 	if (err < 0) {
342 		return err;
343 	}
344 
345 	err = ms5837_read_prom(dev, MS5837_CMD_CONV_READ_TEMPSENS,
346 			       &data->tempsens);
347 	if (err < 0) {
348 		return err;
349 	}
350 
351 	const int type_id = (data->factory >> 5) & 0x7f;
352 
353 	switch (type_id) {
354 	case  MS5837_02BA01:
355 	case MS5837_02BA21:
356 		data->comp_func = ms5837_compensate_02;
357 		break;
358 	case MS5837_30BA26:
359 		data->comp_func = ms5837_compensate_30;
360 		break;
361 	default:
362 		LOG_WRN(" unrecognized type: '%2x', defaulting to MS5837-30", type_id);
363 		data->comp_func = ms5837_compensate_30;
364 		break;
365 	}
366 
367 	return 0;
368 }
369 
370 #define MS5837_DEFINE(inst)								\
371 	static struct ms5837_data ms5837_data_##inst;					\
372 											\
373 	static const struct ms5837_config ms5837_config_##inst = {			\
374 		.i2c = I2C_DT_SPEC_INST_GET(inst),					\
375 	};										\
376 											\
377 	SENSOR_DEVICE_DT_INST_DEFINE(inst, ms5837_init, NULL,				\
378 			      &ms5837_data_##inst, &ms5837_config_##inst, POST_KERNEL,	\
379 			      CONFIG_SENSOR_INIT_PRIORITY, &ms5837_api_funcs);		\
380 
381 DT_INST_FOREACH_STATUS_OKAY(MS5837_DEFINE)
382