mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-09-10 14:51:57 +00:00
In order to bring consistency in-tree, migrate all drivers to the new prefix <zephyr/...>. Note that the conversion has been scripted, refer to #45388 for more details. Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
372 lines
9.4 KiB
C
372 lines
9.4 KiB
C
/* vl53l0x.c - Driver for ST VL53L0X time of flight sensor */
|
|
|
|
#define DT_DRV_COMPAT st_vl53l0x
|
|
|
|
/*
|
|
* Copyright (c) 2017 STMicroelectronics
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <errno.h>
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/drivers/i2c.h>
|
|
#include <zephyr/drivers/sensor.h>
|
|
#include <zephyr/init.h>
|
|
#include <zephyr/drivers/gpio.h>
|
|
#include <zephyr/sys/__assert.h>
|
|
#include <zephyr/types.h>
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/logging/log.h>
|
|
|
|
#include "vl53l0x_api.h"
|
|
#include "vl53l0x_platform.h"
|
|
|
|
LOG_MODULE_REGISTER(VL53L0X, CONFIG_SENSOR_LOG_LEVEL);
|
|
|
|
/* All the values used in this driver are coming from ST datasheet and examples.
|
|
* It can be found here:
|
|
* http://www.st.com/en/embedded-software/stsw-img005.html
|
|
* There are also examples of use in the L4 cube FW:
|
|
* http://www.st.com/en/embedded-software/stm32cubel4.html
|
|
*/
|
|
#define VL53L0X_INITIAL_ADDR 0x29
|
|
#define VL53L0X_REG_WHO_AM_I 0xC0
|
|
#define VL53L0X_CHIP_ID 0xEEAA
|
|
#define VL53L0X_SETUP_SIGNAL_LIMIT (0.1 * 65536)
|
|
#define VL53L0X_SETUP_SIGMA_LIMIT (60 * 65536)
|
|
#define VL53L0X_SETUP_MAX_TIME_FOR_RANGING 33000
|
|
#define VL53L0X_SETUP_PRE_RANGE_VCSEL_PERIOD 18
|
|
#define VL53L0X_SETUP_FINAL_RANGE_VCSEL_PERIOD 14
|
|
|
|
struct vl53l0x_config {
|
|
struct i2c_dt_spec i2c;
|
|
struct gpio_dt_spec xshut;
|
|
};
|
|
|
|
struct vl53l0x_data {
|
|
bool started;
|
|
VL53L0X_Dev_t vl53l0x;
|
|
VL53L0X_RangingMeasurementData_t RangingMeasurementData;
|
|
};
|
|
|
|
static int vl53l0x_setup_single_shot(const struct device *dev)
|
|
{
|
|
struct vl53l0x_data *drv_data = dev->data;
|
|
int ret;
|
|
uint8_t VhvSettings;
|
|
uint8_t PhaseCal;
|
|
uint32_t refSpadCount;
|
|
uint8_t isApertureSpads;
|
|
|
|
ret = VL53L0X_StaticInit(&drv_data->vl53l0x);
|
|
if (ret) {
|
|
LOG_ERR("[%s] VL53L0X_StaticInit failed",
|
|
dev->name);
|
|
goto exit;
|
|
}
|
|
|
|
ret = VL53L0X_PerformRefCalibration(&drv_data->vl53l0x,
|
|
&VhvSettings,
|
|
&PhaseCal);
|
|
if (ret) {
|
|
LOG_ERR("[%s] VL53L0X_PerformRefCalibration failed",
|
|
dev->name);
|
|
goto exit;
|
|
}
|
|
|
|
ret = VL53L0X_PerformRefSpadManagement(&drv_data->vl53l0x,
|
|
(uint32_t *)&refSpadCount,
|
|
&isApertureSpads);
|
|
if (ret) {
|
|
LOG_ERR("[%s] VL53L0X_PerformRefSpadManagement failed",
|
|
dev->name);
|
|
goto exit;
|
|
}
|
|
|
|
ret = VL53L0X_SetDeviceMode(&drv_data->vl53l0x,
|
|
VL53L0X_DEVICEMODE_SINGLE_RANGING);
|
|
if (ret) {
|
|
LOG_ERR("[%s] VL53L0X_SetDeviceMode failed",
|
|
dev->name);
|
|
goto exit;
|
|
}
|
|
|
|
ret = VL53L0X_SetLimitCheckEnable(&drv_data->vl53l0x,
|
|
VL53L0X_CHECKENABLE_SIGMA_FINAL_RANGE,
|
|
1);
|
|
if (ret) {
|
|
LOG_ERR("[%s] VL53L0X_SetLimitCheckEnable sigma failed",
|
|
dev->name);
|
|
goto exit;
|
|
}
|
|
|
|
ret = VL53L0X_SetLimitCheckEnable(&drv_data->vl53l0x,
|
|
VL53L0X_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE,
|
|
1);
|
|
if (ret) {
|
|
LOG_ERR("[%s] VL53L0X_SetLimitCheckEnable signal rate failed",
|
|
dev->name);
|
|
goto exit;
|
|
}
|
|
|
|
ret = VL53L0X_SetLimitCheckValue(&drv_data->vl53l0x,
|
|
VL53L0X_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE,
|
|
VL53L0X_SETUP_SIGNAL_LIMIT);
|
|
|
|
if (ret) {
|
|
LOG_ERR("[%s] VL53L0X_SetLimitCheckValue signal rate failed",
|
|
dev->name);
|
|
goto exit;
|
|
}
|
|
|
|
ret = VL53L0X_SetLimitCheckValue(&drv_data->vl53l0x,
|
|
VL53L0X_CHECKENABLE_SIGMA_FINAL_RANGE,
|
|
VL53L0X_SETUP_SIGMA_LIMIT);
|
|
if (ret) {
|
|
LOG_ERR("[%s] VL53L0X_SetLimitCheckValue sigma failed",
|
|
dev->name);
|
|
goto exit;
|
|
}
|
|
|
|
ret = VL53L0X_SetMeasurementTimingBudgetMicroSeconds(&drv_data->vl53l0x,
|
|
VL53L0X_SETUP_MAX_TIME_FOR_RANGING);
|
|
if (ret) {
|
|
LOG_ERR("[%s] VL53L0X_SetMeasurementTimingBudgetMicroSeconds failed",
|
|
dev->name);
|
|
goto exit;
|
|
}
|
|
|
|
ret = VL53L0X_SetVcselPulsePeriod(&drv_data->vl53l0x,
|
|
VL53L0X_VCSEL_PERIOD_PRE_RANGE,
|
|
VL53L0X_SETUP_PRE_RANGE_VCSEL_PERIOD);
|
|
if (ret) {
|
|
LOG_ERR("[%s] VL53L0X_SetVcselPulsePeriod pre range failed",
|
|
dev->name);
|
|
goto exit;
|
|
}
|
|
|
|
ret = VL53L0X_SetVcselPulsePeriod(&drv_data->vl53l0x,
|
|
VL53L0X_VCSEL_PERIOD_FINAL_RANGE,
|
|
VL53L0X_SETUP_FINAL_RANGE_VCSEL_PERIOD);
|
|
if (ret) {
|
|
LOG_ERR("[%s] VL53L0X_SetVcselPulsePeriod final range failed",
|
|
dev->name);
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
static int vl53l0x_start(const struct device *dev)
|
|
{
|
|
const struct vl53l0x_config *const config = dev->config;
|
|
struct vl53l0x_data *drv_data = dev->data;
|
|
int r;
|
|
VL53L0X_Error ret;
|
|
uint16_t vl53l0x_id = 0U;
|
|
VL53L0X_DeviceInfo_t vl53l0x_dev_info;
|
|
|
|
LOG_DBG("[%s] Starting", dev->name);
|
|
|
|
/* Pull XSHUT high to start the sensor */
|
|
if (config->xshut.port) {
|
|
r = gpio_pin_set_dt(&config->xshut, 1);
|
|
if (r < 0) {
|
|
LOG_ERR("[%s] Unable to set XSHUT gpio (error %d)",
|
|
dev->name, r);
|
|
return -EIO;
|
|
}
|
|
k_sleep(K_MSEC(2));
|
|
}
|
|
|
|
#ifdef CONFIG_VL53L0X_RECONFIGURE_ADDRESS
|
|
if (config->i2c.addr != VL53L0X_INITIAL_ADDR) {
|
|
ret = VL53L0X_SetDeviceAddress(&drv_data->vl53l0x, 2 * config->i2c.addr);
|
|
if (ret != 0) {
|
|
LOG_ERR("[%s] Unable to reconfigure I2C address",
|
|
dev->name);
|
|
return -EIO;
|
|
}
|
|
|
|
drv_data->vl53l0x.I2cDevAddr = config->i2c.addr;
|
|
LOG_DBG("[%s] I2C address reconfigured", dev->name);
|
|
k_sleep(K_MSEC(2));
|
|
}
|
|
#endif
|
|
|
|
/* Get info from sensor */
|
|
(void)memset(&vl53l0x_dev_info, 0, sizeof(VL53L0X_DeviceInfo_t));
|
|
|
|
ret = VL53L0X_GetDeviceInfo(&drv_data->vl53l0x, &vl53l0x_dev_info);
|
|
if (ret < 0) {
|
|
LOG_ERR("[%s] Could not get info from device.", dev->name);
|
|
return -ENODEV;
|
|
}
|
|
|
|
LOG_DBG("[%s] VL53L0X_GetDeviceInfo = %d", dev->name, ret);
|
|
LOG_DBG(" Device Name : %s", vl53l0x_dev_info.Name);
|
|
LOG_DBG(" Device Type : %s", vl53l0x_dev_info.Type);
|
|
LOG_DBG(" Device ID : %s", vl53l0x_dev_info.ProductId);
|
|
LOG_DBG(" ProductRevisionMajor : %d",
|
|
vl53l0x_dev_info.ProductRevisionMajor);
|
|
LOG_DBG(" ProductRevisionMinor : %d",
|
|
vl53l0x_dev_info.ProductRevisionMinor);
|
|
|
|
ret = VL53L0X_RdWord(&drv_data->vl53l0x,
|
|
VL53L0X_REG_WHO_AM_I,
|
|
(uint16_t *) &vl53l0x_id);
|
|
if ((ret < 0) || (vl53l0x_id != VL53L0X_CHIP_ID)) {
|
|
LOG_ERR("[%s] Issue on device identification", dev->name);
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
/* sensor init */
|
|
ret = VL53L0X_DataInit(&drv_data->vl53l0x);
|
|
if (ret < 0) {
|
|
LOG_ERR("[%s] VL53L0X_DataInit return error (%d)",
|
|
dev->name, ret);
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
ret = vl53l0x_setup_single_shot(dev);
|
|
if (ret < 0) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
drv_data->started = true;
|
|
LOG_DBG("[%s] Started", dev->name);
|
|
return 0;
|
|
}
|
|
|
|
static int vl53l0x_sample_fetch(const struct device *dev,
|
|
enum sensor_channel chan)
|
|
{
|
|
struct vl53l0x_data *drv_data = dev->data;
|
|
VL53L0X_Error ret;
|
|
int r;
|
|
|
|
__ASSERT_NO_MSG((chan == SENSOR_CHAN_ALL)
|
|
|| (chan == SENSOR_CHAN_DISTANCE)
|
|
|| (chan == SENSOR_CHAN_PROX));
|
|
|
|
if (!drv_data->started) {
|
|
r = vl53l0x_start(dev);
|
|
if (r < 0) {
|
|
return r;
|
|
}
|
|
}
|
|
|
|
ret = VL53L0X_PerformSingleRangingMeasurement(&drv_data->vl53l0x,
|
|
&drv_data->RangingMeasurementData);
|
|
if (ret < 0) {
|
|
LOG_ERR("[%s] Could not perform measurment (error=%d)",
|
|
dev->name, ret);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int vl53l0x_channel_get(const struct device *dev,
|
|
enum sensor_channel chan,
|
|
struct sensor_value *val)
|
|
{
|
|
struct vl53l0x_data *drv_data = dev->data;
|
|
|
|
if (chan == SENSOR_CHAN_PROX) {
|
|
if (drv_data->RangingMeasurementData.RangeMilliMeter <=
|
|
CONFIG_VL53L0X_PROXIMITY_THRESHOLD) {
|
|
val->val1 = 1;
|
|
} else {
|
|
val->val1 = 0;
|
|
}
|
|
val->val2 = 0;
|
|
} else if (chan == SENSOR_CHAN_DISTANCE) {
|
|
val->val1 = drv_data->RangingMeasurementData.RangeMilliMeter / 1000;
|
|
val->val2 = (drv_data->RangingMeasurementData.RangeMilliMeter % 1000) * 1000;
|
|
} else {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct sensor_driver_api vl53l0x_api_funcs = {
|
|
.sample_fetch = vl53l0x_sample_fetch,
|
|
.channel_get = vl53l0x_channel_get,
|
|
};
|
|
|
|
static int vl53l0x_init(const struct device *dev)
|
|
{
|
|
int r;
|
|
struct vl53l0x_data *drv_data = dev->data;
|
|
const struct vl53l0x_config *const config = dev->config;
|
|
|
|
/* Initialize the HAL peripheral with the default sensor address,
|
|
* ie. the address on power up
|
|
*/
|
|
drv_data->vl53l0x.I2cDevAddr = VL53L0X_INITIAL_ADDR;
|
|
drv_data->vl53l0x.i2c = config->i2c.bus;
|
|
|
|
#ifdef CONFIG_VL53L0X_RECONFIGURE_ADDRESS
|
|
if (!config->xshut.port) {
|
|
LOG_ERR("[%s] Missing XSHUT gpio spec", dev->name);
|
|
return -ENOTSUP;
|
|
}
|
|
#else
|
|
if (config->i2c.addr != VL53L0X_INITIAL_ADDR) {
|
|
LOG_ERR("[%s] Invalid device address (should be 0x%X or "
|
|
"CONFIG_VL53L0X_RECONFIGURE_ADDRESS should be enabled)",
|
|
dev->name, VL53L0X_INITIAL_ADDR);
|
|
return -ENOTSUP;
|
|
}
|
|
#endif
|
|
|
|
if (config->xshut.port) {
|
|
r = gpio_pin_configure_dt(&config->xshut, GPIO_OUTPUT);
|
|
if (r < 0) {
|
|
LOG_ERR("[%s] Unable to configure GPIO as output",
|
|
dev->name);
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_VL53L0X_RECONFIGURE_ADDRESS
|
|
/* Pull XSHUT low to shut down the sensor for now */
|
|
r = gpio_pin_set_dt(&config->xshut, 0);
|
|
if (r < 0) {
|
|
LOG_ERR("[%s] Unable to shutdown sensor", dev->name);
|
|
return -EIO;
|
|
}
|
|
LOG_DBG("[%s] Shutdown", dev->name);
|
|
#else
|
|
r = vl53l0x_start(dev);
|
|
if (r) {
|
|
return r;
|
|
}
|
|
#endif
|
|
|
|
LOG_DBG("[%s] Initialized", dev->name);
|
|
return 0;
|
|
}
|
|
|
|
#define VL53L0X_INIT(inst) \
|
|
static struct vl53l0x_config vl53l0x_##inst##_config = { \
|
|
.i2c = I2C_DT_SPEC_INST_GET(inst), \
|
|
.xshut = GPIO_DT_SPEC_INST_GET_OR(inst, xshut_gpios, {}) \
|
|
}; \
|
|
\
|
|
static struct vl53l0x_data vl53l0x_##inst##_driver; \
|
|
\
|
|
DEVICE_DT_INST_DEFINE(inst, vl53l0x_init, NULL, \
|
|
&vl53l0x_##inst##_driver, \
|
|
&vl53l0x_##inst##_config, \
|
|
POST_KERNEL, \
|
|
CONFIG_SENSOR_INIT_PRIORITY, \
|
|
&vl53l0x_api_funcs);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(VL53L0X_INIT)
|