drivers: sensor: ccs811: provide access to sensor results

Expose the entire content of the ALG_RESULT_DATA block to the
application, primarily so the status and error flags can be seen.  With
those available the application has the ability to detect that a stale
result has been provided so sensor_fetch_sample() can return -EAGAIN in
this case instead of -EIO, and it doesn't need to block which is
annoying.

This should also make the sensor usable on older Nordic Thingy:52
devices with outdated CCS811 application firmware that doesn't properly
implement the DATA_READY bit.

Signed-off-by: Peter A. Bigot <pab@pabigot.com>
This commit is contained in:
Peter A. Bigot 2018-11-23 05:56:17 -06:00 committed by Maureen Helm
commit cb84836745
3 changed files with 92 additions and 63 deletions

View file

@ -70,6 +70,13 @@ static inline u8_t error_from_status(int status)
return status >> 8; return status >> 8;
} }
const struct ccs811_result_type *ccs811_result(struct device *dev)
{
struct ccs811_data *drv_data = dev->driver_data;
return &drv_data->result;
}
int ccs811_baseline_fetch(struct device *dev) int ccs811_baseline_fetch(struct device *dev)
{ {
const u8_t cmd = CCS811_REG_BASELINE; const u8_t cmd = CCS811_REG_BASELINE;
@ -176,55 +183,30 @@ int ccs811_envdata_update(struct device *dev,
static int ccs811_sample_fetch(struct device *dev, enum sensor_channel chan) static int ccs811_sample_fetch(struct device *dev, enum sensor_channel chan)
{ {
struct ccs811_data *drv_data = dev->driver_data; struct ccs811_data *drv_data = dev->driver_data;
struct ccs811_result_type *rp = &drv_data->result;
const u8_t cmd = CCS811_REG_ALG_RESULT_DATA;
int rc; int rc;
int tries;
u16_t buf[4]; u16_t buf[4];
int status; unsigned int status;
/* Check data ready flag for the measurement interval */ set_wake(drv_data, true);
#ifdef CONFIG_CCS811_DRIVE_MODE_1 rc = i2c_write_read(drv_data->i2c, DT_INST_0_AMS_CCS811_BASE_ADDRESS,
tries = 11; &cmd, sizeof(cmd),
#elif defined(CONFIG_CCS811_DRIVE_MODE_2) (u8_t *)buf, sizeof(buf));
tries = 101; set_wake(drv_data, false);
#elif defined(CONFIG_CCS811_DRIVE_MODE_3) if (rc < 0) {
tries = 601;
#endif
do {
const u8_t cmd = CCS811_REG_ALG_RESULT_DATA;
set_wake(drv_data, true);
rc = i2c_write_read(drv_data->i2c, DT_INST_0_AMS_CCS811_BASE_ADDRESS,
&cmd, sizeof(cmd),
(u8_t *)buf, sizeof(buf));
set_wake(drv_data, false);
if (rc < 0) {
return -EIO;
}
status = buf[2];
if (status & CCS811_STATUS_ERROR) {
LOG_ERR("CCS811 ERROR ID %02x",
error_from_status(status));
return -EIO;
}
if (status & CCS811_STATUS_DATA_READY) {
break;
}
k_sleep(K_MSEC(100));
} while (--tries > 0);
if (!(status & CCS811_STATUS_DATA_READY)) {
return -EIO; return -EIO;
} }
drv_data->co2 = sys_be16_to_cpu(buf[0]);
drv_data->voc = sys_be16_to_cpu(buf[1]); rp->co2 = sys_be16_to_cpu(buf[0]);
drv_data->status = status; rp->voc = sys_be16_to_cpu(buf[1]);
drv_data->error = error_from_status(status); status = sys_le16_to_cpu(buf[2]); /* sic */
drv_data->raw = sys_be16_to_cpu(buf[3]); rp->status = status;
return 0; rp->error = error_from_status(status);
rp->raw = sys_be16_to_cpu(buf[3]);
/* @todo APP FW 1.1 may not set DATA_READY. */
return (rp->status & CCS811_STATUS_DATA_READY) ? 0 : -EAGAIN;
} }
static int ccs811_channel_get(struct device *dev, static int ccs811_channel_get(struct device *dev,
@ -232,16 +214,17 @@ static int ccs811_channel_get(struct device *dev,
struct sensor_value *val) struct sensor_value *val)
{ {
struct ccs811_data *drv_data = dev->driver_data; struct ccs811_data *drv_data = dev->driver_data;
const struct ccs811_result_type *rp = &drv_data->result;
u32_t uval; u32_t uval;
switch (chan) { switch (chan) {
case SENSOR_CHAN_CO2: case SENSOR_CHAN_CO2:
val->val1 = drv_data->co2; val->val1 = rp->co2;
val->val2 = 0; val->val2 = 0;
break; break;
case SENSOR_CHAN_VOC: case SENSOR_CHAN_VOC:
val->val1 = drv_data->voc; val->val1 = rp->voc;
val->val2 = 0; val->val2 = 0;
break; break;
@ -249,7 +232,7 @@ static int ccs811_channel_get(struct device *dev,
/* /*
* Raw ADC readings are contained in least significant 10 bits * Raw ADC readings are contained in least significant 10 bits
*/ */
uval = ((drv_data->raw & CCS811_RAW_VOLTAGE_MSK) uval = ((rp->raw & CCS811_RAW_VOLTAGE_MSK)
>> CCS811_RAW_VOLTAGE_POS) * CCS811_RAW_VOLTAGE_SCALE; >> CCS811_RAW_VOLTAGE_POS) * CCS811_RAW_VOLTAGE_SCALE;
val->val1 = uval / 1000000U; val->val1 = uval / 1000000U;
val->val2 = uval % 1000000; val->val2 = uval % 1000000;
@ -260,7 +243,7 @@ static int ccs811_channel_get(struct device *dev,
* Current readings are contained in most * Current readings are contained in most
* significant 6 bits in microAmps * significant 6 bits in microAmps
*/ */
uval = ((drv_data->raw & CCS811_RAW_CURRENT_MSK) uval = ((rp->raw & CCS811_RAW_CURRENT_MSK)
>> CCS811_RAW_CURRENT_POS) * CCS811_RAW_CURRENT_SCALE; >> CCS811_RAW_CURRENT_POS) * CCS811_RAW_CURRENT_SCALE;
val->val1 = uval / 1000000U; val->val1 = uval / 1000000U;
val->val2 = uval % 1000000; val->val2 = uval % 1000000;

View file

@ -11,6 +11,7 @@
#include <device.h> #include <device.h>
#include <drivers/gpio.h> #include <drivers/gpio.h>
#include <sys/util.h> #include <sys/util.h>
#include <drivers/sensor/ccs811.h>
/* Registers */ /* Registers */
#define CCS811_REG_STATUS 0x00 #define CCS811_REG_STATUS 0x00
@ -29,17 +30,7 @@
#define CCS881_HW_ID 0x81 #define CCS881_HW_ID 0x81
#define CCS811_HW_VERSION 0x10 #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 */ /* 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_MODE_RAW_DATA 0x40
#define CCS811_MODE_DATARDY 0x08 #define CCS811_MODE_DATARDY 0x08
#define CCS811_MODE_THRESH 0x04 #define CCS811_MODE_THRESH 0x04
@ -85,11 +76,7 @@ struct ccs811_data {
#ifdef DT_INST_0_AMS_CCS811_WAKE_GPIOS_CONTROLLER #ifdef DT_INST_0_AMS_CCS811_WAKE_GPIOS_CONTROLLER
struct device *wake_gpio; struct device *wake_gpio;
#endif #endif
u16_t co2; struct ccs811_result_type result;
u16_t voc;
u16_t raw;
u8_t status;
u8_t error;
u8_t mode; u8_t mode;
}; };

View file

@ -22,6 +22,65 @@ extern "C" {
#include <device.h> #include <device.h>
#include <drivers/sensor.h> #include <drivers/sensor.h>
/* 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)
/* Error register fields */
#define CCS811_ERROR_WRITE_REG_INVALID BIT(0)
#define CCS811_ERROR_READ_REG_INVALID BIT(1)
#define CCS811_ERROR_MEASMODE_INVALID BIT(2)
#define CCS811_ERROR_MAX_RESISTANCE BIT(3)
#define CCS811_ERROR_HEATER_FAULT BIT(4)
#define CCS811_ERROR_HEATER_SUPPLY BIT(5)
/* Measurement mode constants */
#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_IAQ_RAW_250MSEC 0x40
/** @brief Information collected from the sensor on each fetch. */
struct ccs811_result_type {
/** Equivalent carbon dioxide in parts-per-million volume (ppmv). */
u16_t co2;
/**
* Equivalent total volatile organic compounts in
* parts-per-billion volume.
*/
u16_t voc;
/** Raw voltage and current measured by sensor. */
u16_t raw;
/** Sensor status at completion of most recent fetch. */
u8_t status;
/**
* Sensor error flags at completion of most recent fetch.
*
* Note that errors are cleared when read.
*/
u8_t error;
};
/**
* @brief Access storage for the most recent data read from the sensor.
*
* This content of the object referenced is updated by
* sensor_fetch_sample(), except for ccs811_result_type::mode which is
* set on driver initialization.
*
* @param dev Pointer to the sensor device
*
* @return a pointer to the result information.
*/
const struct ccs811_result_type *ccs811_result(struct device *dev);
/** /**
* @brief Fetch the current value of the BASELINE register. * @brief Fetch the current value of the BASELINE register.
* *