1 /* vl53l0x.c - Driver for ST VL53L0X time of flight sensor */
2 
3 #define DT_DRV_COMPAT st_vl53l0x
4 
5 /*
6  * Copyright (c) 2017 STMicroelectronics
7  *
8  * SPDX-License-Identifier: Apache-2.0
9  */
10 
11 #include <errno.h>
12 
13 #include <zephyr/kernel.h>
14 #include <zephyr/drivers/i2c.h>
15 #include <zephyr/drivers/sensor.h>
16 #include <zephyr/init.h>
17 #include <zephyr/drivers/gpio.h>
18 #include <zephyr/pm/device.h>
19 #include <zephyr/sys/__assert.h>
20 #include <zephyr/types.h>
21 #include <zephyr/device.h>
22 #include <zephyr/logging/log.h>
23 #include <zephyr/drivers/sensor/vl53l0x.h>
24 
25 #include "vl53l0x_api.h"
26 #include "vl53l0x_platform.h"
27 
28 LOG_MODULE_REGISTER(VL53L0X, CONFIG_SENSOR_LOG_LEVEL);
29 
30 #define VL53L0X_FIXPOINT1616_SCALE_FACTOR (65536)
31 #define VL53L0X_SENSOR_CHANNEL_VAL2_FACTOR (1000000)
32 
33 /* All the values used in this driver are coming from ST datasheet and examples.
34  * It can be found here:
35  *   https://www.st.com/en/embedded-software/stsw-img005.html
36  * There are also examples of use in the L4 cube FW:
37  *   https://www.st.com/en/embedded-software/stm32cubel4.html
38  */
39 #define VL53L0X_INITIAL_ADDR                    0x29
40 #define VL53L0X_REG_WHO_AM_I                    0xC0
41 #define VL53L0X_CHIP_ID                         0xEEAA
42 #define VL53L0X_SETUP_SIGNAL_LIMIT              (0.1 * 65536)
43 #define VL53L0X_SETUP_SIGMA_LIMIT               (60 * 65536)
44 #define VL53L0X_SETUP_MAX_TIME_FOR_RANGING      33000
45 #define VL53L0X_SETUP_PRE_RANGE_VCSEL_PERIOD    18
46 #define VL53L0X_SETUP_FINAL_RANGE_VCSEL_PERIOD  14
47 
48 /* tBOOT (1.2ms max.) VL53L0X firmware boot period */
49 #define T_BOOT K_USEC(1200)
50 
51 struct vl53l0x_config {
52 	struct i2c_dt_spec i2c;
53 	struct gpio_dt_spec xshut;
54 };
55 
56 struct vl53l0x_data {
57 	bool started;
58 	VL53L0X_Dev_t vl53l0x;
59 	VL53L0X_RangingMeasurementData_t RangingMeasurementData;
60 };
61 
vl53l0x_setup_single_shot(const struct device * dev)62 static int vl53l0x_setup_single_shot(const struct device *dev)
63 {
64 	struct vl53l0x_data *drv_data = dev->data;
65 	int ret;
66 	uint8_t VhvSettings;
67 	uint8_t PhaseCal;
68 	uint32_t refSpadCount;
69 	uint8_t isApertureSpads;
70 
71 	ret = VL53L0X_StaticInit(&drv_data->vl53l0x);
72 	if (ret) {
73 		LOG_ERR("[%s] VL53L0X_StaticInit failed",
74 			dev->name);
75 		goto exit;
76 	}
77 
78 	ret = VL53L0X_PerformRefCalibration(&drv_data->vl53l0x,
79 					    &VhvSettings,
80 					    &PhaseCal);
81 	if (ret) {
82 		LOG_ERR("[%s] VL53L0X_PerformRefCalibration failed",
83 			dev->name);
84 		goto exit;
85 	}
86 
87 	ret = VL53L0X_PerformRefSpadManagement(&drv_data->vl53l0x,
88 					       &refSpadCount,
89 					       &isApertureSpads);
90 	if (ret) {
91 		LOG_ERR("[%s] VL53L0X_PerformRefSpadManagement failed",
92 			dev->name);
93 		goto exit;
94 	}
95 
96 	ret = VL53L0X_SetDeviceMode(&drv_data->vl53l0x,
97 				    VL53L0X_DEVICEMODE_SINGLE_RANGING);
98 	if (ret) {
99 		LOG_ERR("[%s] VL53L0X_SetDeviceMode failed",
100 			dev->name);
101 		goto exit;
102 	}
103 
104 	ret = VL53L0X_SetLimitCheckEnable(&drv_data->vl53l0x,
105 					  VL53L0X_CHECKENABLE_SIGMA_FINAL_RANGE,
106 					  1);
107 	if (ret) {
108 		LOG_ERR("[%s] VL53L0X_SetLimitCheckEnable sigma failed",
109 			dev->name);
110 		goto exit;
111 	}
112 
113 	ret = VL53L0X_SetLimitCheckEnable(&drv_data->vl53l0x,
114 					  VL53L0X_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE,
115 					  1);
116 	if (ret) {
117 		LOG_ERR("[%s] VL53L0X_SetLimitCheckEnable signal rate failed",
118 			dev->name);
119 		goto exit;
120 	}
121 
122 	ret = VL53L0X_SetLimitCheckValue(&drv_data->vl53l0x,
123 					 VL53L0X_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE,
124 					 VL53L0X_SETUP_SIGNAL_LIMIT);
125 
126 	if (ret) {
127 		LOG_ERR("[%s] VL53L0X_SetLimitCheckValue signal rate failed",
128 			dev->name);
129 		goto exit;
130 	}
131 
132 	ret = VL53L0X_SetLimitCheckValue(&drv_data->vl53l0x,
133 					 VL53L0X_CHECKENABLE_SIGMA_FINAL_RANGE,
134 					 VL53L0X_SETUP_SIGMA_LIMIT);
135 	if (ret) {
136 		LOG_ERR("[%s] VL53L0X_SetLimitCheckValue sigma failed",
137 			dev->name);
138 		goto exit;
139 	}
140 
141 	ret = VL53L0X_SetMeasurementTimingBudgetMicroSeconds(&drv_data->vl53l0x,
142 							     VL53L0X_SETUP_MAX_TIME_FOR_RANGING);
143 	if (ret) {
144 		LOG_ERR("[%s] VL53L0X_SetMeasurementTimingBudgetMicroSeconds failed",
145 			dev->name);
146 		goto exit;
147 	}
148 
149 	ret = VL53L0X_SetVcselPulsePeriod(&drv_data->vl53l0x,
150 					  VL53L0X_VCSEL_PERIOD_PRE_RANGE,
151 					  VL53L0X_SETUP_PRE_RANGE_VCSEL_PERIOD);
152 	if (ret) {
153 		LOG_ERR("[%s] VL53L0X_SetVcselPulsePeriod pre range failed",
154 			dev->name);
155 		goto exit;
156 	}
157 
158 	ret = VL53L0X_SetVcselPulsePeriod(&drv_data->vl53l0x,
159 					  VL53L0X_VCSEL_PERIOD_FINAL_RANGE,
160 					  VL53L0X_SETUP_FINAL_RANGE_VCSEL_PERIOD);
161 	if (ret) {
162 		LOG_ERR("[%s] VL53L0X_SetVcselPulsePeriod final range failed",
163 			dev->name);
164 		goto exit;
165 	}
166 
167 exit:
168 	return ret;
169 }
170 
vl53l0x_start(const struct device * dev)171 static int vl53l0x_start(const struct device *dev)
172 {
173 	const struct vl53l0x_config *const config = dev->config;
174 	struct vl53l0x_data *drv_data = dev->data;
175 	int r;
176 	VL53L0X_Error ret;
177 	uint16_t vl53l0x_id = 0U;
178 	VL53L0X_DeviceInfo_t vl53l0x_dev_info = { 0 };
179 
180 	LOG_DBG("[%s] Starting", dev->name);
181 
182 	if (config->xshut.port) {
183 		r = gpio_pin_configure_dt(&config->xshut, GPIO_OUTPUT_INACTIVE);
184 		if (r < 0) {
185 			LOG_ERR("[%s] Unable to inactivate XSHUT: %d",
186 				dev->name, r);
187 			return -EIO;
188 		}
189 		k_sleep(T_BOOT);
190 	}
191 
192 #ifdef CONFIG_VL53L0X_RECONFIGURE_ADDRESS
193 	if (config->i2c.addr != VL53L0X_INITIAL_ADDR) {
194 		ret = VL53L0X_SetDeviceAddress(&drv_data->vl53l0x, 2 * config->i2c.addr);
195 		if (ret != 0) {
196 			LOG_ERR("[%s] Unable to reconfigure I2C address",
197 				dev->name);
198 			return -EIO;
199 		}
200 
201 		drv_data->vl53l0x.I2cDevAddr = config->i2c.addr;
202 		LOG_DBG("[%s] I2C address reconfigured", dev->name);
203 		k_sleep(T_BOOT);
204 	}
205 #endif
206 
207 	ret = VL53L0X_GetDeviceInfo(&drv_data->vl53l0x, &vl53l0x_dev_info);
208 	if (ret < 0) {
209 		LOG_ERR("[%s] Could not get info from device.", dev->name);
210 		return -ENODEV;
211 	}
212 
213 	LOG_DBG("[%s] VL53L0X_GetDeviceInfo = %d", dev->name, ret);
214 	LOG_DBG("   Device Name : %s", vl53l0x_dev_info.Name);
215 	LOG_DBG("   Device Type : %s", vl53l0x_dev_info.Type);
216 	LOG_DBG("   Device ID : %s", vl53l0x_dev_info.ProductId);
217 	LOG_DBG("   ProductRevisionMajor : %d",
218 		vl53l0x_dev_info.ProductRevisionMajor);
219 	LOG_DBG("   ProductRevisionMinor : %d",
220 		vl53l0x_dev_info.ProductRevisionMinor);
221 
222 	ret = VL53L0X_RdWord(&drv_data->vl53l0x,
223 			     VL53L0X_REG_WHO_AM_I,
224 			     &vl53l0x_id);
225 	if ((ret < 0) || (vl53l0x_id != VL53L0X_CHIP_ID)) {
226 		LOG_ERR("[%s] Issue on device identification", dev->name);
227 		return -ENOTSUP;
228 	}
229 
230 	/* sensor init */
231 	ret = VL53L0X_DataInit(&drv_data->vl53l0x);
232 	if (ret < 0) {
233 		LOG_ERR("[%s] VL53L0X_DataInit return error (%d)",
234 			dev->name, ret);
235 		return -ENOTSUP;
236 	}
237 
238 	ret = vl53l0x_setup_single_shot(dev);
239 	if (ret < 0) {
240 		return -ENOTSUP;
241 	}
242 
243 	drv_data->started = true;
244 	LOG_DBG("[%s] Started", dev->name);
245 	return 0;
246 }
247 
vl53l0x_sample_fetch(const struct device * dev,enum sensor_channel chan)248 static int vl53l0x_sample_fetch(const struct device *dev,
249 				enum sensor_channel chan)
250 {
251 	struct vl53l0x_data *drv_data = dev->data;
252 	VL53L0X_Error ret;
253 	int r;
254 
255 	__ASSERT_NO_MSG((chan == SENSOR_CHAN_ALL)
256 			|| (chan == SENSOR_CHAN_DISTANCE)
257 			|| (chan == SENSOR_CHAN_PROX));
258 
259 	if (!drv_data->started) {
260 		r = vl53l0x_start(dev);
261 		if (r < 0) {
262 			return r;
263 		}
264 	}
265 
266 	ret = VL53L0X_PerformSingleRangingMeasurement(&drv_data->vl53l0x,
267 						      &drv_data->RangingMeasurementData);
268 	if (ret < 0) {
269 		LOG_ERR("[%s] Could not perform measurment (error=%d)",
270 			dev->name, ret);
271 		return -EINVAL;
272 	}
273 
274 	return 0;
275 }
276 
277 
vl53l0x_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)278 static int vl53l0x_channel_get(const struct device *dev,
279 			       enum sensor_channel chan,
280 			       struct sensor_value *val)
281 {
282 	struct vl53l0x_data *drv_data = dev->data;
283 
284 	if (chan == SENSOR_CHAN_PROX) {
285 		if (drv_data->RangingMeasurementData.RangeMilliMeter <=
286 		    CONFIG_VL53L0X_PROXIMITY_THRESHOLD) {
287 			val->val1 = 1;
288 		} else {
289 			val->val1 = 0;
290 		}
291 		val->val2 = 0;
292 	} else if (chan == SENSOR_CHAN_DISTANCE) {
293 		val->val1 = drv_data->RangingMeasurementData.RangeMilliMeter / 1000;
294 		val->val2 = (drv_data->RangingMeasurementData.RangeMilliMeter % 1000) * 1000;
295 	} else if ((enum sensor_channel_vl53l0x)chan ==
296 			SENSOR_CHAN_VL53L0X_EFFECTIVE_SPAD_RTN_COUNT) {
297 		val->val1 = drv_data->RangingMeasurementData.EffectiveSpadRtnCount / 256;
298 		val->val2 = 0;
299 	} else if ((enum sensor_channel_vl53l0x)chan == SENSOR_CHAN_VL53L0X_AMBIENT_RATE_RTN_CPS) {
300 		val->val1 = (drv_data->RangingMeasurementData.AmbientRateRtnMegaCps >> 16) +
301 			(((drv_data->RangingMeasurementData.AmbientRateRtnMegaCps & 0xFFFF) *
302 			  VL53L0X_SENSOR_CHANNEL_VAL2_FACTOR) / VL53L0X_FIXPOINT1616_SCALE_FACTOR);
303 		val->val2 = 0;
304 	} else if ((enum sensor_channel_vl53l0x)chan == SENSOR_CHAN_VL53L0X_SIGNAL_RATE_RTN_CPS) {
305 		val->val1 = (drv_data->RangingMeasurementData.SignalRateRtnMegaCps >> 16) +
306 			(((drv_data->RangingMeasurementData.SignalRateRtnMegaCps & 0xFFFF) *
307 			  VL53L0X_SENSOR_CHANNEL_VAL2_FACTOR) / VL53L0X_FIXPOINT1616_SCALE_FACTOR);
308 		val->val2 = 0;
309 	} else if ((enum sensor_channel_vl53l0x)chan == SENSOR_CHAN_VL53L0X_RANGE_DMAX) {
310 		val->val1 = drv_data->RangingMeasurementData.RangeDMaxMilliMeter / 1000;
311 		val->val2 = (drv_data->RangingMeasurementData.RangeDMaxMilliMeter % 1000) * 1000;
312 	} else if ((enum sensor_channel_vl53l0x)chan == SENSOR_CHAN_VL53L0X_RANGE_STATUS) {
313 		val->val1 = drv_data->RangingMeasurementData.RangeStatus;
314 		val->val2 = 0;
315 	} else {
316 		return -ENOTSUP;
317 	}
318 
319 	return 0;
320 }
321 
322 static DEVICE_API(sensor, vl53l0x_api_funcs) = {
323 	.sample_fetch = vl53l0x_sample_fetch,
324 	.channel_get = vl53l0x_channel_get,
325 };
326 
vl53l0x_init(const struct device * dev)327 static int vl53l0x_init(const struct device *dev)
328 {
329 	int r;
330 	struct vl53l0x_data *drv_data = dev->data;
331 	const struct vl53l0x_config *const config = dev->config;
332 
333 	/* Initialize the HAL peripheral with the default sensor address,
334 	 * ie. the address on power up
335 	 */
336 	drv_data->vl53l0x.I2cDevAddr = VL53L0X_INITIAL_ADDR;
337 	drv_data->vl53l0x.i2c = config->i2c.bus;
338 
339 #if defined(CONFIG_VL53L0X_RECONFIGURE_ADDRESS) || defined(CONFIG_PM_DEVICE)
340 	if (config->xshut.port == NULL) {
341 		LOG_ERR("[%s] Missing XSHUT gpio spec", dev->name);
342 		return -ENOTSUP;
343 	}
344 #endif
345 
346 #ifdef CONFIG_VL53L0X_RECONFIGURE_ADDRESS
347 	/*
348 	 * Shutdown all vl53l0x sensors so at each sensor's 1st fetch call
349 	 * they can be enabled one at a time and programmed with their address.
350 	 */
351 	r = gpio_pin_configure_dt(&config->xshut, GPIO_OUTPUT_ACTIVE);
352 	if (r < 0) {
353 		LOG_ERR("[%s] Unable to shutdown sensor", dev->name);
354 		return -EIO;
355 	}
356 	LOG_DBG("[%s] Shutdown", dev->name);
357 #else
358 	if (config->i2c.addr != VL53L0X_INITIAL_ADDR) {
359 		LOG_ERR("[%s] Invalid device address (should be 0x%X or "
360 			"CONFIG_VL53L0X_RECONFIGURE_ADDRESS should be enabled)",
361 			dev->name, VL53L0X_INITIAL_ADDR);
362 		return -ENOTSUP;
363 	}
364 
365 	r = vl53l0x_start(dev);
366 	if (r) {
367 		return r;
368 	}
369 #endif
370 
371 	LOG_DBG("[%s] Initialized", dev->name);
372 	return 0;
373 }
374 
375 #ifdef CONFIG_PM_DEVICE
vl53l0x_pm_action(const struct device * dev,enum pm_device_action action)376 static int vl53l0x_pm_action(const struct device *dev,
377 			     enum pm_device_action action)
378 {
379 	const struct vl53l0x_config *const config = dev->config;
380 	int ret;
381 
382 	switch (action) {
383 	case PM_DEVICE_ACTION_RESUME:
384 		ret = vl53l0x_init(dev);
385 		if (ret != 0) {
386 			LOG_ERR("resume init: %d", ret);
387 		}
388 		break;
389 	case PM_DEVICE_ACTION_SUSPEND:
390 		/* HW Standby */
391 		ret = gpio_pin_set_dt(&config->xshut, 1);
392 		if (ret < 0) {
393 			LOG_ERR("[%s] XSHUT pin active", dev->name);
394 		}
395 		break;
396 	default:
397 		ret = -ENOTSUP;
398 		break;
399 	}
400 
401 	return ret;
402 }
403 #endif
404 
405 #define VL53L0X_INIT(inst)						 \
406 	static struct vl53l0x_config vl53l0x_##inst##_config = {	 \
407 		.i2c = I2C_DT_SPEC_INST_GET(inst),			 \
408 		.xshut = GPIO_DT_SPEC_INST_GET_OR(inst, xshut_gpios, {}) \
409 	};								 \
410 									 \
411 	static struct vl53l0x_data vl53l0x_##inst##_driver;		 \
412 									 \
413 	PM_DEVICE_DT_INST_DEFINE(inst, vl53l0x_pm_action);		 \
414 									 \
415 	SENSOR_DEVICE_DT_INST_DEFINE(inst, vl53l0x_init,		 \
416 			      PM_DEVICE_DT_INST_GET(inst),		 \
417 			      &vl53l0x_##inst##_driver,			 \
418 			      &vl53l0x_##inst##_config,			 \
419 			      POST_KERNEL,				 \
420 			      CONFIG_SENSOR_INIT_PRIORITY,		 \
421 			      &vl53l0x_api_funcs);
422 
423 DT_INST_FOREACH_STATUS_OKAY(VL53L0X_INIT)
424