diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt index acba07608d6..0354430e37b 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -7,6 +7,7 @@ add_subdirectory_ifdef(CONFIG_BMC150_MAGN bmc150_magn) add_subdirectory_ifdef(CONFIG_BME280 bme280) add_subdirectory_ifdef(CONFIG_BMG160 bmg160) add_subdirectory_ifdef(CONFIG_BMI160 bmi160) +add_subdirectory_ifdef(CONFIG_CCS811 ccs811) add_subdirectory_ifdef(CONFIG_DHT dht) add_subdirectory_ifdef(CONFIG_FXAS21002 fxas21002) add_subdirectory_ifdef(CONFIG_FXOS8700 fxos8700) diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index ae831d9ddf6..76326c1cc1e 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -58,6 +58,8 @@ source "drivers/sensor/bmi160/Kconfig" source "drivers/sensor/bmm150/Kconfig" +source "drivers/sensor/ccs811/Kconfig" + source "drivers/sensor/dht/Kconfig" source "drivers/sensor/fxas21002/Kconfig" diff --git a/drivers/sensor/ccs811/CMakeLists.txt b/drivers/sensor/ccs811/CMakeLists.txt new file mode 100644 index 00000000000..0c521e8a8c9 --- /dev/null +++ b/drivers/sensor/ccs811/CMakeLists.txt @@ -0,0 +1 @@ +zephyr_sources_ifdef(CONFIG_CCS811 ccs811.c) diff --git a/drivers/sensor/ccs811/Kconfig b/drivers/sensor/ccs811/Kconfig new file mode 100644 index 00000000000..1c0a32b6210 --- /dev/null +++ b/drivers/sensor/ccs811/Kconfig @@ -0,0 +1,69 @@ +# Kconfig - CCS811 Digital Gas sensor configuration options + +# +# Copyright (c) 2018 Linaro Ltd. +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig CCS811 + bool + prompt "CCS811 Digital Gas Sensor" + depends on I2C + default n + help + Enable driver for CCS811 Gas sensors. + +config CCS811_NAME + string + prompt "Driver name" + default "CCS811" + depends on CCS811 + help + Device name with which the CCS811 sensor is identified. + +config CCS811_I2C_ADDR + hex + prompt "I2C address for CCS811 Sensor" + depends on CCS811 + range 0x5a 0x5b + default "0x5b" + help + I2C address of the CCS811 sensor. + 0x5a: I2C_ADDR connected to GND. + 0x5b: I2C_ADDR connected to VDD. + +config CCS811_I2C_MASTER_DEV_NAME + string + prompt "I2C master where CCS811 is connected" + depends on CCS811 + default "I2C_1" + help + Specify the device name of the I2C master device to which the + CCS811 chip is connected. + +config CCS811_GPIO_DEV_NAME + string + prompt "GPIO device" + default "GPIOC" + depends on CCS811 + help + The device name of the GPIO device to which the CCS811 WAKE + pin is connected. + +config CCS811_GPIO_WAKEUP + bool + prompt "Enable GPIO Wakeup for CCS811" + default n + depends on CCS811 + help + Enable GPIO Wakeup support for CCS811 + +config CCS811_GPIO_WAKEUP_PIN_NUM + int + prompt "GPIO Wakeup pin number" + default 0 + depends on CCS811 && CCS811_GPIO_WAKEUP + help + The number of the GPIO pin on which the WAKE pin of CCS811 + is connected diff --git a/drivers/sensor/ccs811/ccs811.c b/drivers/sensor/ccs811/ccs811.c new file mode 100644 index 00000000000..8df42b2a984 --- /dev/null +++ b/drivers/sensor/ccs811/ccs811.c @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2018 Linaro Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ccs811.h" + +static int ccs811_sample_fetch(struct device *dev, enum sensor_channel chan) +{ + struct ccs811_data *drv_data = dev->driver_data; + int tries = 11; + u16_t buf[4]; + u8_t status; + + /* Check data ready flag for the measurement interval of 1 seconds */ + while (tries-- > 0) { + if (i2c_reg_read_byte(drv_data->i2c, CONFIG_CCS811_I2C_ADDR, + CCS811_REG_STATUS, &status) < 0) { + SYS_LOG_ERR("Failed to read Status register"); + return -EIO; + } + + if ((status & CCS811_STATUS_DATA_READY) || tries == 0) { + break; + } + + k_sleep(100); + } + + if (!(status & CCS811_STATUS_DATA_READY)) { + SYS_LOG_ERR("Sensor data not available"); + return -EIO; + } + + if (i2c_burst_read(drv_data->i2c, CONFIG_CCS811_I2C_ADDR, + CCS811_REG_ALG_RESULT_DATA, (u8_t *)buf, 8) < 0) { + SYS_LOG_ERR("Failed to read conversion data."); + return -EIO; + } + + drv_data->co2 = sys_be16_to_cpu(buf[0]); + drv_data->voc = sys_be16_to_cpu(buf[1]); + drv_data->status = buf[2] & 0xff; + drv_data->error = buf[2] >> 8; + drv_data->resistance = sys_be16_to_cpu(buf[3]); + + return 0; +} + +static int ccs811_channel_get(struct device *dev, + enum sensor_channel chan, + struct sensor_value *val) +{ + struct ccs811_data *drv_data = dev->driver_data; + u32_t uval; + + switch (chan) { + case SENSOR_CHAN_CO2: + uval = drv_data->co2 * 1000000; + val->val1 = uval / 1000000; + val->val2 = uval % 1000000; + + break; + case SENSOR_CHAN_VOC: + uval = drv_data->voc * 1000000; + val->val1 = uval / 1000000; + val->val2 = uval % 1000000; + + break; + case SENSOR_CHAN_VOLTAGE: + /* + * Voltage readings are contained in least + * significant 10 bits in volts + */ + uval = (drv_data->resistance & CCS811_VOLTAGE_MASK) + * CCS811_VOLTAGE_SCALE; + val->val1 = uval / 1000000; + val->val2 = uval % 1000000; + + break; + case SENSOR_CHAN_CURRENT: + /* + * Current readings are contained in most + * significant 6 bits in microAmps + */ + uval = drv_data->resistance >> 10; + val->val1 = uval / 1000000; + val->val2 = uval % 1000000; + + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static const struct sensor_driver_api ccs811_driver_api = { + .sample_fetch = ccs811_sample_fetch, + .channel_get = ccs811_channel_get, +}; + +static int switch_to_app_mode(struct device *i2c) +{ + u8_t status, buf; + + SYS_LOG_DBG("Switching to Application mode..."); + + if (i2c_reg_read_byte(i2c, CONFIG_CCS811_I2C_ADDR, + CCS811_REG_STATUS, &status) < 0) { + SYS_LOG_ERR("Failed to read Status register"); + return -EIO; + } + + /* Check for the application firmware */ + if (!(status & CCS811_STATUS_APP_VALID)) { + SYS_LOG_ERR("No Application firmware loaded"); + return -EINVAL; + } + + buf = CCS811_REG_APP_START; + /* Set the device to application mode */ + if (i2c_write(i2c, &buf, 1, CONFIG_CCS811_I2C_ADDR) < 0) { + SYS_LOG_ERR("Failed to set Application mode"); + return -EIO; + } + + if (i2c_reg_read_byte(i2c, CONFIG_CCS811_I2C_ADDR, + CCS811_REG_STATUS, &status) < 0) { + SYS_LOG_ERR("Failed to read Status register"); + return -EIO; + } + + /* Check for application mode */ + if (!(status & CCS811_STATUS_FW_MODE)) { + SYS_LOG_ERR("Failed to start Application firmware"); + return -EINVAL; + } + + SYS_LOG_DBG("CCS811 Application firmware started!"); + + return 0; +} + +int ccs811_init(struct device *dev) +{ + struct ccs811_data *drv_data = dev->driver_data; + int ret; + u8_t hw_id, status; + + drv_data->i2c = device_get_binding(CONFIG_CCS811_I2C_MASTER_DEV_NAME); + if (drv_data->i2c == NULL) { + SYS_LOG_ERR("Failed to get pointer to %s device!", + CONFIG_CCS811_I2C_MASTER_DEV_NAME); + return -EINVAL; + } + + /* + * Wakeup pin should be pulled low before initiating any I2C transfer. + * If it has been tied to GND by default, skip this part. + */ +#ifdef CONFIG_CCS811_GPIO_WAKEUP + drv_data->gpio = device_get_binding(CONFIG_CCS811_GPIO_DEV_NAME); + if (drv_data->gpio == NULL) { + SYS_LOG_ERR("Failed to get pointer to %s device!", + CONFIG_CCS811_GPIO_DEV_NAME); + return -EINVAL; + } + + gpio_pin_configure(drv_data->gpio, CONFIG_CCS811_GPIO_WAKEUP_PIN_NUM, + GPIO_DIR_OUT); + gpio_pin_write(drv_data->gpio, CONFIG_CCS811_GPIO_WAKEUP_PIN_NUM, 0); + + k_sleep(1); +#endif + + /* Switch device to application mode */ + ret = switch_to_app_mode(drv_data->i2c); + if (ret) { + return ret; + } + + /* Check Hardware ID */ + if (i2c_reg_read_byte(drv_data->i2c, CONFIG_CCS811_I2C_ADDR, + CCS811_REG_HW_ID, &hw_id) < 0) { + SYS_LOG_ERR("Failed to read Hardware ID register"); + return -EIO; + } + + if (hw_id != CCS881_HW_ID) { + SYS_LOG_ERR("Hardware ID mismatch!"); + return -EINVAL; + } + + /* Set Measurement mode for 1 second */ + if (i2c_reg_write_byte(drv_data->i2c, CONFIG_CCS811_I2C_ADDR, + CCS811_REG_MEAS_MODE, + CCS811_MODE_IAQ_1SEC) < 0) { + SYS_LOG_ERR("Failed to set Measurement mode"); + return -EIO; + } + + /* Check for error */ + if (i2c_reg_read_byte(drv_data->i2c, CONFIG_CCS811_I2C_ADDR, + CCS811_REG_STATUS, &status) < 0) { + SYS_LOG_ERR("Failed to read Status register"); + return -EIO; + } + + if (status & CCS811_STATUS_ERROR) { + SYS_LOG_ERR("Error occurred during sensor configuration"); + return -EINVAL; + } + + return 0; +} + +static struct ccs811_data ccs811_driver; + +DEVICE_AND_API_INIT(ccs811, CONFIG_CCS811_NAME, ccs811_init, &ccs811_driver, + NULL, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, + &ccs811_driver_api); diff --git a/drivers/sensor/ccs811/ccs811.h b/drivers/sensor/ccs811/ccs811.h new file mode 100644 index 00000000000..09edbe365cf --- /dev/null +++ b/drivers/sensor/ccs811/ccs811.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018 Linaro Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _SENSOR_CCS811 +#define _SENSOR_CCS811 + +#include +#include +#include + +/* Registers */ +#define CCS811_REG_STATUS 0x00 +#define CCS811_REG_MEAS_MODE 0x01 +#define CCS811_REG_ALG_RESULT_DATA 0x02 +#define CCS811_REG_RAW_DATA 0x03 +#define CCS811_REG_HW_ID 0x20 +#define CCS811_REG_HW_VERSION 0x21 +#define CCS811_REG_HW_VERSION_MASK 0xF0 +#define CCS811_REG_ERR 0xE0 +#define CCS811_REG_APP_START 0xF4 + +#define CCS881_HW_ID 0x81 +#define CCS811_HW_VERSION 0x10 + +/* Status register fields */ +#define CCS811_STATUS_ERROR BIT(0) +#define CCS811_STATUS_DATA_READY BIT(3) +#define CCS811_STATUS_APP_VALID BIT(4) +#define CCS811_STATUS_FW_MODE BIT(7) + +/* Measurement modes */ +#define CCS811_MODE_IDLE 0x00 +#define CCS811_MODE_IAQ_1SEC 0x10 +#define CCS811_MODE_IAQ_10SEC 0x20 +#define CCS811_MODE_IAQ_60SEC 0x30 +#define CCS811_MODE_RAW_DATA 0x40 + +#define CCS811_VOLTAGE_SCALE 1613 + +#define CCS811_VOLTAGE_MASK 0x3FF + +struct ccs811_data { + struct device *i2c; +#ifdef CONFIG_CCS811_GPIO_WAKEUP + struct device *gpio; +#endif + u16_t co2; + u16_t voc; + u8_t status; + u8_t error; + u16_t resistance; +}; + +#define SYS_LOG_DOMAIN "CCS811" +#define SYS_LOG_LEVEL CONFIG_SYS_LOG_SENSOR_LEVEL +#include + +#endif /* _SENSOR_CCS811_ */ diff --git a/dts/bindings/sensor/ams,ccs811.yaml b/dts/bindings/sensor/ams,ccs811.yaml new file mode 100644 index 00000000000..14a4fe87f25 --- /dev/null +++ b/dts/bindings/sensor/ams,ccs811.yaml @@ -0,0 +1,22 @@ +# +# Copyright (c) 2018, Linaro Limited +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: AMS (Austria Mikro Systeme) Digital Air Quality Sensor CCS811 +id: ams,ccs811 +version: 0.1 + +description: > + This binding gives a base representation of CCS811 digital air quality + sensor + +inherits: + !include i2c-device.yaml + +properties: + compatible: + constraint: "ams,ccs811" + +...