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