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