Commit 93d06330 authored by Titouan Christophe's avatar Titouan Christophe Committed by Maureen Helm
Browse files

drivers: sensor: vl53l0x: add support to reprogram I2C address



The I2C address of the VL53L0X distance sensor can only be programmed
over the I2C bus. To do this:

1. The sensor is powered off or in standby mode
2. Power up the sensor, it boots with a default I2C address (0x29)
3. The I2C master sends a configuration command to set the new address
4. The sensor now communicates at the new address

In case there are more than one such sensor on the bus, they all have
the same address when starting up. We therefore need to first apply
step 1. on all of them. Then, sensor by sensor apply steps 2. to 4.

Because simple designs may not need to reprogram the address, we
introduce a new configuration option CONFIG_VL53L0X_RECONFIGURE_ADDRESS

If this setting is disabled, then the driver behaves as before.

If CONFIG_VL53L0X_RECONFIGURE_ADDRESS is enabled, then the driver does
the following:

- In vl53l0x_init(), apply step 1. This is done when the driver is
  brought up when starting the system
- in vl53l0x_start(), apply steps 2. to 4. This is done when fetching
  a sample, if the sensor has not been started yet.

Also, as cosmetic changes:
- add parenthesis around sub conditions in call to __ASSERT_NO_MSG
- gracefully handle unknown sensor channels in vl53l0x_channel_get

Signed-off-by: default avatarTitouan Christophe <moiandme@gmail.com>
parent b2172b42
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -16,3 +16,16 @@ config VL53L0X_PROXIMITY_THRESHOLD
	depends on VL53L0X
	help
	  Threshold used for proximity detection when sensor is used with SENSOR_CHAN_PROX.

config VL53L0X_RECONFIGURE_ADDRESS
	bool "Support reconfigurable sensor address"
	depends on VL53L0X
	help
	  Enable support for reconfiguring the sensor address at runtime.
	  When this option is enabled, all sensors declared in the device tree
	  must have an xshut-gpio property.

	  All sensors are disabled during initialization. When reading the first
	  value from a sensor, it is powered up and its I2C address is reconfigured
	  from the manufacturer default (0x29) to the address specified in the
	  device tree.
+138 −68
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ LOG_MODULE_REGISTER(VL53L0X, CONFIG_SENSOR_LOG_LEVEL);
 * 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)
@@ -45,62 +46,11 @@ struct vl53l0x_config {
};

struct vl53l0x_data {
	bool started;
	VL53L0X_Dev_t vl53l0x;
	VL53L0X_RangingMeasurementData_t RangingMeasurementData;
};

static int vl53l0x_sample_fetch(const struct device *dev,
				enum sensor_channel chan)
{
	struct vl53l0x_data *drv_data = dev->data;
	VL53L0X_Error ret;

	__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL
			|| chan == SENSOR_CHAN_DISTANCE
			|| chan == SENSOR_CHAN_PROX);

	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;

	__ASSERT_NO_MSG(chan == SENSOR_CHAN_DISTANCE
			|| chan == SENSOR_CHAN_PROX);

	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 {
		val->val1 = drv_data->RangingMeasurementData.RangeMilliMeter / 1000;
		val->val2 = (drv_data->RangingMeasurementData.RangeMilliMeter % 1000) * 1000;
	}

	return 0;
}

static const struct sensor_driver_api vl53l0x_api_funcs = {
	.sample_fetch = vl53l0x_sample_fetch,
	.channel_get = vl53l0x_channel_get,
};

static int vl53l0x_setup_single_shot(const struct device *dev)
{
	struct vl53l0x_data *drv_data = dev->data;
@@ -210,8 +160,7 @@ exit:
	return ret;
}


static int vl53l0x_init(const struct device *dev)
static int vl53l0x_start(const struct device *dev)
{
	const struct vl53l0x_config *const config = dev->config;
	struct vl53l0x_data *drv_data = dev->data;
@@ -220,26 +169,33 @@ static int vl53l0x_init(const struct device *dev)
	uint16_t vl53l0x_id = 0U;
	VL53L0X_DeviceInfo_t vl53l0x_dev_info;

	LOG_DBG("enter in %s", __func__);
	LOG_DBG("[%s] Starting", dev->name);

	/* Pull XSHUT high to start the sensor */
	if (config->xshut.port) {
		r = gpio_pin_configure_dt(&config->xshut,
					  GPIO_OUTPUT | GPIO_PULL_UP);
		if (r) {
			LOG_ERR("[%s] Could not configure XSHUT gpio %s %d (error %d)",
				dev->name,
				config->xshut.port->name,
				config->xshut.pin,
				r);
			return -EINVAL;
		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;
		}

		gpio_pin_set_dt(&config->xshut, 1);
		k_sleep(K_MSEC(2));
	}

	drv_data->vl53l0x.i2c = config->i2c.bus;
#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));
@@ -280,6 +236,120 @@ static int vl53l0x_init(const struct device *dev)
		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;
}