drivers: sensor: ccs811: support triggers

Add the standard suite of trigger configuration options.  Add driver
support for the DATA_READY and THRESHOLD triggers.  Note cross-module
dependency as configuring the trigger requires a controlled modification
of the sensor registers and the sensor driver API doesn't support this.

Signed-off-by: Peter A. Bigot <pab@pabigot.com>
This commit is contained in:
Peter A. Bigot 2018-11-21 14:35:01 -06:00 committed by Maureen Helm
commit 403a7ab2c5
5 changed files with 373 additions and 41 deletions

View file

@ -23,7 +23,7 @@ LOG_MODULE_REGISTER(CCS811, CONFIG_SENSOR_LOG_LEVEL);
static void set_wake(struct ccs811_data *drv_data, bool enable)
{
/* Always active-low */
gpio_pin_write(drv_data->gpio, DT_INST_0_AMS_CCS811_WAKE_GPIOS_PIN, !enable);
gpio_pin_write(drv_data->wake_gpio, DT_INST_0_AMS_CCS811_WAKE_GPIOS_PIN, !enable);
if (enable) {
k_busy_wait(50); /* t_WAKE = 50 us */
} else {
@ -170,6 +170,10 @@ static int ccs811_channel_get(struct device *dev,
}
static const struct sensor_driver_api ccs811_driver_api = {
#ifdef CONFIG_CCS811_TRIGGER
.attr_set = ccs811_attr_set,
.trigger_set = ccs811_trigger_set,
#endif
.sample_fetch = ccs811_sample_fetch,
.channel_get = ccs811_channel_get,
};
@ -222,6 +226,65 @@ static int switch_to_app_mode(struct device *i2c)
return 0;
}
#ifdef CONFIG_CCS811_TRIGGER
int ccs811_mutate_meas_mode(struct device *dev,
u8_t set,
u8_t clear)
{
struct ccs811_data *drv_data = dev->driver_data;
int rc = 0;
u8_t mode = set | (drv_data->mode & ~clear);
/*
* Changing drive mode of a running system has preconditions.
* Only allow changing the interrupt generation.
*/
if ((set | clear) & ~(CCS811_MODE_DATARDY | CCS811_MODE_THRESH)) {
return -EINVAL;
}
if (mode != drv_data->mode) {
set_wake(drv_data, true);
rc = i2c_reg_write_byte(drv_data->i2c, DT_INST_0_AMS_CCS811_BASE_ADDRESS,
CCS811_REG_MEAS_MODE,
mode);
LOG_DBG("CCS811 meas mode change %02x to %02x got %d",
drv_data->mode, mode, rc);
if (rc < 0) {
LOG_ERR("Failed to set mode");
rc = -EIO;
} else {
drv_data->mode = mode;
rc = 0;
}
set_wake(drv_data, false);
}
return rc;
}
int ccs811_set_thresholds(struct device *dev)
{
struct ccs811_data *drv_data = dev->driver_data;
const u8_t buf[5] = {
CCS811_REG_THRESHOLDS,
drv_data->co2_l2m >> 8,
drv_data->co2_l2m,
drv_data->co2_m2h >> 8,
drv_data->co2_m2h,
};
int rc;
set_wake(drv_data, true);
rc = i2c_write(drv_data->i2c, buf, sizeof(buf), DT_INST_0_AMS_CCS811_BASE_ADDRESS);
set_wake(drv_data, false);
return rc;
}
#endif /* CONFIG_CCS811_TRIGGER */
int ccs811_init(struct device *dev)
{
struct ccs811_data *drv_data = dev->driver_data;
@ -229,6 +292,7 @@ int ccs811_init(struct device *dev)
u8_t hw_id;
int status;
*drv_data = (struct ccs811_data){ 0 };
drv_data->i2c = device_get_binding(DT_INST_0_AMS_CCS811_BUS_NAME);
if (drv_data->i2c == NULL) {
LOG_ERR("Failed to get pointer to %s device!",
@ -236,65 +300,56 @@ int ccs811_init(struct device *dev)
return -EINVAL;
}
struct device *gpio = NULL;
(void)gpio;
#ifdef DT_INST_0_AMS_CCS811_WAKE_GPIOS_CONTROLLER
gpio = device_get_binding(DT_INST_0_AMS_CCS811_WAKE_GPIOS_CONTROLLER);
if (gpio == NULL) {
drv_data->wake_gpio = device_get_binding(DT_INST_0_AMS_CCS811_WAKE_GPIOS_CONTROLLER);
if (drv_data->wake_gpio == NULL) {
LOG_ERR("Failed to get pointer to WAKE device: %s",
DT_INST_0_AMS_CCS811_WAKE_GPIOS_CONTROLLER);
return -EINVAL;
}
drv_data->gpio = gpio;
/*
* Wakeup pin should be pulled low before initiating
* any I2C transfer. If it has been tied to GND by
* default, skip this part.
*/
gpio_pin_configure(drv_data->wake_gpio, DT_INST_0_AMS_CCS811_WAKE_GPIOS_PIN,
GPIO_DIR_OUT);
set_wake(drv_data, true);
k_sleep(1);
#endif
#ifdef DT_INST_0_AMS_CCS811_RESET_GPIOS_CONTROLLER
gpio = device_get_binding(DT_INST_0_AMS_CCS811_RESET_GPIOS_CONTROLLER);
if (gpio == NULL) {
drv_data->reset_gpio = device_get_binding(DT_INST_0_AMS_CCS811_RESET_GPIOS_CONTROLLER);
if (drv_data->reset_gpio == NULL) {
LOG_ERR("Failed to get pointer to RESET device: %s",
DT_INST_0_AMS_CCS811_RESET_GPIOS_CONTROLLER);
return -EINVAL;
}
gpio_pin_configure(drv_data->reset_gpio, DT_INST_0_AMS_CCS811_RESET_GPIOS_PIN,
GPIO_DIR_OUT);
gpio_pin_write(drv_data->reset_gpio, DT_INST_0_AMS_CCS811_RESET_GPIOS_PIN, 1);
if (drv_data->gpio == NULL) {
drv_data->gpio = gpio;
} else if (drv_data->gpio != gpio) {
LOG_ERR("Crossing GPIO devices not supported");
k_sleep(1);
#endif
#ifdef DT_INST_0_AMS_CCS811_IRQ_GPIOS_CONTROLLER
drv_data->int_gpio = device_get_binding(DT_INST_0_AMS_CCS811_IRQ_GPIOS_CONTROLLER);
if (drv_data->int_gpio == NULL) {
LOG_ERR("Failed to get pointer to INT device: %s",
DT_INST_0_AMS_CCS811_IRQ_GPIOS_CONTROLLER);
return -EINVAL;
}
#endif
if (drv_data->gpio) {
#ifdef DT_INST_0_AMS_CCS811_RESET_GPIOS_PIN
gpio_pin_configure(drv_data->gpio, DT_INST_0_AMS_CCS811_RESET_GPIOS_PIN,
GPIO_DIR_OUT);
gpio_pin_write(drv_data->gpio, DT_INST_0_AMS_CCS811_RESET_GPIOS_PIN, 1);
k_sleep(K_MSEC(1));
#endif
/*
* Wakeup pin should be pulled low before initiating
* any I2C transfer. If it has been tied to GND by
* default, skip this part.
*/
#ifdef DT_INST_0_AMS_CCS811_WAKE_GPIOS_PIN
gpio_pin_configure(drv_data->gpio, DT_INST_0_AMS_CCS811_WAKE_GPIOS_PIN,
GPIO_DIR_OUT);
set_wake(drv_data, true);
k_sleep(K_MSEC(1));
#endif
}
/* Reset the device. This saves having to deal with detecting
* and validating any errors or configuration inconsistencies
* after a reset that left the device running.
*/
#ifdef DT_INST_0_AMS_CCS811_RESET_GPIOS_PIN
gpio_pin_write(drv_data->gpio, DT_INST_0_AMS_CCS811_RESET_GPIOS_PIN, 0);
k_busy_wait(15); /* t_RESET */
gpio_pin_write(drv_data->gpio, DT_INST_0_AMS_CCS811_RESET_GPIOS_PIN, 1);
gpio_pin_write(drv_data->reset_gpio, DT_INST_0_AMS_CCS811_RESET_GPIOS_PIN, 0);
k_busy_wait(15); /* t_RESET */
gpio_pin_write(drv_data->reset_gpio, DT_INST_0_AMS_CCS811_RESET_GPIOS_PIN, 1);
#else
{
static u8_t const reset_seq[] = {
@ -347,6 +402,7 @@ int ccs811_init(struct device *dev)
ret = -EIO;
goto out;
}
drv_data->mode = meas_mode;
/* Check for error */
status = fetch_status(drv_data->i2c);
@ -362,6 +418,11 @@ int ccs811_init(struct device *dev)
goto out;
}
#ifdef CONFIG_CCS811_TRIGGER
ret = ccs811_init_interrupt(dev);
LOG_DBG("CCS811 interrupt init got %d", ret);
#endif
out:
set_wake(drv_data, false);
return ret;