drivers: sensor: Add support for CCS811 Digital Gas Sensor
Add support for AMS CCS811 Digital Gas Sensor for monitoring indoor air quality. This sensor reports the following parameters: 1. Co2 concentration 2. VOC concentration 3. Sensor voltage 4. Sensor current This driver only supports polling mode as of now. Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
This commit is contained in:
parent
75103ad683
commit
9e88120fdf
7 changed files with 388 additions and 0 deletions
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
1
drivers/sensor/ccs811/CMakeLists.txt
Normal file
1
drivers/sensor/ccs811/CMakeLists.txt
Normal file
|
@ -0,0 +1 @@
|
|||
zephyr_sources_ifdef(CONFIG_CCS811 ccs811.c)
|
69
drivers/sensor/ccs811/Kconfig
Normal file
69
drivers/sensor/ccs811/Kconfig
Normal file
|
@ -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
|
232
drivers/sensor/ccs811/ccs811.c
Normal file
232
drivers/sensor/ccs811/ccs811.c
Normal file
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Linaro Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <device.h>
|
||||
#include <gpio.h>
|
||||
#include <i2c.h>
|
||||
#include <kernel.h>
|
||||
#include <misc/byteorder.h>
|
||||
#include <misc/util.h>
|
||||
#include <sensor.h>
|
||||
#include <misc/__assert.h>
|
||||
|
||||
#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);
|
61
drivers/sensor/ccs811/ccs811.h
Normal file
61
drivers/sensor/ccs811/ccs811.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Linaro Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _SENSOR_CCS811
|
||||
#define _SENSOR_CCS811
|
||||
|
||||
#include <device.h>
|
||||
#include <gpio.h>
|
||||
#include <misc/util.h>
|
||||
|
||||
/* 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 <logging/sys_log.h>
|
||||
|
||||
#endif /* _SENSOR_CCS811_ */
|
22
dts/bindings/sensor/ams,ccs811.yaml
Normal file
22
dts/bindings/sensor/ams,ccs811.yaml
Normal file
|
@ -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"
|
||||
|
||||
...
|
Loading…
Add table
Add a link
Reference in a new issue