mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
660 lines
19 KiB
C
660 lines
19 KiB
C
/*
|
|
* Copyright (C) 2018 Gunar Schorcht
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @ingroup drivers_ccs811
|
|
* @brief Device Driver for AMS CCS811 digital gas sensor
|
|
* @author Gunar Schorcht <gunar@schorcht.net>
|
|
* @file
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "log.h"
|
|
#include "ztimer.h"
|
|
|
|
#include "ccs811_regs.h"
|
|
#include "ccs811.h"
|
|
|
|
#define ENABLE_DEBUG 0
|
|
#include "debug.h"
|
|
|
|
/**
|
|
* Internal macro definitions
|
|
*/
|
|
|
|
#define ASSERT_PARAM(cond) \
|
|
if (!(cond)) { \
|
|
DEBUG("[ccs811] %s: %s\n", \
|
|
__func__, "parameter condition (" # cond ") not fulfilled"); \
|
|
assert(cond); \
|
|
}
|
|
|
|
#define DEBUG_DEV(f, d, ...) \
|
|
DEBUG("[ccs811] %s dev=%d addr=%02x: " f "\n", \
|
|
__func__, d->params.i2c_dev, d->params.i2c_addr, ## __VA_ARGS__)
|
|
|
|
#define ERROR_DEV(f, d, ...) \
|
|
LOG_ERROR("[ccs811] dev=%d addr=%x: " f "\n", \
|
|
d->params.i2c_dev, d->params.i2c_addr, ## __VA_ARGS__)
|
|
|
|
/**
|
|
* Internal type declarations
|
|
*/
|
|
|
|
typedef struct {
|
|
uint8_t reserved_1 : 2;
|
|
uint8_t int_thresh : 1; /**< interrupt if new ALG_RESULT_DAT crosses on of the thresholds */
|
|
uint8_t int_datardy: 1; /**< interrupt if new sample is ready in ALG_RESULT_DAT */
|
|
uint8_t drive_mode : 3; /**< mode number binary coded */
|
|
} ccs811_meas_mode_reg_t;
|
|
|
|
/**
|
|
* forward declaration of functions for internal use only
|
|
*/
|
|
static int _reg_read(const ccs811_t *dev, uint8_t reg, uint8_t *data, uint32_t len);
|
|
static int _reg_write(const ccs811_t *dev, uint8_t reg, uint8_t *data, uint32_t len);
|
|
static int _check_error_status(const ccs811_t *dev);
|
|
static int _error_code(const ccs811_t *dev, uint8_t err_reg);
|
|
static int _is_available(const ccs811_t *dev);
|
|
|
|
int ccs811_init(ccs811_t *dev, const ccs811_params_t *params)
|
|
{
|
|
ASSERT_PARAM(dev != NULL);
|
|
ASSERT_PARAM(params != NULL);
|
|
|
|
/* init sensor data structure */
|
|
dev->params = *params;
|
|
|
|
int res = CCS811_OK;
|
|
|
|
if (gpio_is_valid(dev->params.reset_pin) &&
|
|
gpio_init(dev->params.reset_pin, GPIO_OUT) == 0) {
|
|
DEBUG_DEV("nRESET pin configured", dev);
|
|
/* enable low active reset signal */
|
|
gpio_clear(dev->params.reset_pin);
|
|
/* t_RESET (reset impuls) has to be at least 20 us, we wait 1 ms */
|
|
ztimer_sleep(ZTIMER_USEC, 1000);
|
|
/* disable low active reset signal */
|
|
gpio_set(dev->params.reset_pin);
|
|
/* t_START after reset is 1 ms, we wait 1 further ms */
|
|
ztimer_sleep(ZTIMER_USEC, 1000);
|
|
}
|
|
|
|
if (gpio_is_valid(dev->params.wake_pin) &&
|
|
gpio_init(dev->params.wake_pin, GPIO_OUT) == 0) {
|
|
gpio_clear(dev->params.wake_pin);
|
|
DEBUG_DEV("nWAKE pin configured", dev);
|
|
}
|
|
|
|
/* check whether sensor is available including the check of the hardware id */
|
|
if ((res = _is_available(dev)) != CCS811_OK) {
|
|
return res;
|
|
}
|
|
|
|
static const uint8_t sw_reset[4] = { 0x11, 0xe5, 0x72, 0x8a };
|
|
|
|
/* doing a software reset first */
|
|
if (_reg_write(dev, CCS811_REG_SW_RESET, (uint8_t *)sw_reset, 4) != CCS811_OK) {
|
|
DEBUG_DEV("could not write software reset command "
|
|
"to register CCS811_REG_SW_RESET", dev);
|
|
return -CCS811_ERROR_I2C;
|
|
}
|
|
|
|
uint8_t status;
|
|
|
|
/* wait 100 ms after the reset */
|
|
ztimer_sleep(ZTIMER_USEC, 100000);
|
|
|
|
/* get the status to check whether sensor is in bootloader mode */
|
|
if (_reg_read(dev, CCS811_REG_STATUS, &status, 1) != CCS811_OK) {
|
|
DEBUG_DEV("could not read register CCS811_REG_STATUS", dev);
|
|
return -CCS811_ERROR_I2C;
|
|
}
|
|
|
|
/*
|
|
* if sensor is in bootloader mode (FW_MODE == 0), it has to switch
|
|
* to the application mode first
|
|
*/
|
|
if (!(status & CCS811_STATUS_FW_MODE)) {
|
|
/* check whether valid application firmware is loaded */
|
|
if (!(status & CCS811_STATUS_APP_VALID)) {
|
|
DEBUG_DEV("sensor is in boot mode, but has no app", dev);
|
|
return -CCS811_ERROR_NO_APP;
|
|
}
|
|
|
|
/* switch to application mode */
|
|
if (_reg_write(dev, CCS811_REG_APP_START, 0, 0) != CCS811_OK) {
|
|
DEBUG_DEV("could not write app start command "
|
|
"to register CCS811_REG_APP_START", dev);
|
|
return -CCS811_ERROR_I2C;
|
|
}
|
|
|
|
/* wait 100 ms after starting the app */
|
|
ztimer_sleep(ZTIMER_USEC, 100000);
|
|
|
|
/* get the status to check whether sensor switched to application mode */
|
|
if (_reg_read(dev, CCS811_REG_STATUS, &status, 1) != CCS811_OK) {
|
|
DEBUG_DEV("could not read register CCS811_REG_STATUS", dev);
|
|
return -CCS811_ERROR_I2C;
|
|
}
|
|
if ((status & CCS811_STATUS_FW_MODE) == 0) {
|
|
DEBUG_DEV("could not start application", dev);
|
|
return -CCS811_ERROR_NO_APP;
|
|
}
|
|
}
|
|
|
|
#if MODULE_CCS811_FULL
|
|
/* try to set interrupt mode */
|
|
if (dev->params.int_mode != CCS811_INT_NONE &&
|
|
(res = ccs811_set_int_mode (dev, dev->params.int_mode)) != CCS811_OK) {
|
|
return res;
|
|
}
|
|
#endif /* MODULE_CCS811_FULL */
|
|
|
|
/* try to set default measurement mode */
|
|
return ccs811_set_mode(dev, dev->params.mode);
|
|
}
|
|
|
|
int ccs811_set_mode(ccs811_t *dev, ccs811_mode_t mode)
|
|
{
|
|
ASSERT_PARAM(dev != NULL);
|
|
|
|
ccs811_meas_mode_reg_t reg;
|
|
|
|
/* read measurement mode register value */
|
|
if (_reg_read(dev, CCS811_REG_MEAS_MODE, (uint8_t *)®, 1) != CCS811_OK) {
|
|
DEBUG_DEV("could not read current measurement mode "
|
|
"from register CCS811_REG_MEAS_MODE", dev);
|
|
return -CCS811_ERROR_I2C;
|
|
}
|
|
|
|
reg.drive_mode = mode;
|
|
|
|
/* write back measurement mode register */
|
|
if (_reg_write(dev, CCS811_REG_MEAS_MODE, (uint8_t *)®, 1) != CCS811_OK) {
|
|
DEBUG_DEV("could not write new measurement mode "
|
|
"to register CCS811_REG_MEAS_MODE", dev);
|
|
return -CCS811_ERROR_I2C;
|
|
}
|
|
|
|
/* check whether setting measurement mode were successful */
|
|
if (_reg_read(dev, CCS811_REG_MEAS_MODE, (uint8_t *)®, 1) != CCS811_OK) {
|
|
DEBUG_DEV("could not read new measurement mode "
|
|
"from register CCS811_REG_MEAS_MODE", dev);
|
|
return -CCS811_ERROR_I2C;
|
|
}
|
|
if (reg.drive_mode != mode) {
|
|
DEBUG_DEV("could not set measurement mode to %d", dev, mode);
|
|
return -CCS811_ERROR_MEASMODE_INV;
|
|
}
|
|
|
|
dev->params.mode = mode;
|
|
|
|
return CCS811_OK;
|
|
}
|
|
|
|
#if MODULE_CCS811_FULL
|
|
|
|
int ccs811_set_int_mode(ccs811_t *dev, ccs811_int_mode_t mode)
|
|
{
|
|
ASSERT_PARAM(dev != NULL);
|
|
|
|
if (!gpio_is_valid(dev->params.int_pin)) {
|
|
DEBUG_DEV("nINT pin not configured", dev);
|
|
return CCS811_ERROR_NO_INT_PIN;
|
|
}
|
|
|
|
ccs811_meas_mode_reg_t reg;
|
|
|
|
/* read measurement mode register value */
|
|
if (_reg_read(dev, CCS811_REG_MEAS_MODE, (uint8_t *)®, 1) != CCS811_OK) {
|
|
DEBUG_DEV("could not set interrupt mode, could not read register "
|
|
"CCS811_REG_MEAS_MODE", dev);
|
|
return CCS811_ERROR_I2C;
|
|
}
|
|
|
|
reg.int_datardy = mode != CCS811_INT_NONE;
|
|
reg.int_thresh = mode == CCS811_INT_THRESHOLD;
|
|
|
|
/* write back measurement mode register */
|
|
if (_reg_write(dev, CCS811_REG_MEAS_MODE, (uint8_t *)®, 1) != CCS811_OK) {
|
|
DEBUG_DEV("could not set interrupt mode, could not write register "
|
|
"CCS811_REG_MEAS_MODE", dev);
|
|
return CCS811_ERROR_I2C;
|
|
}
|
|
|
|
dev->params.int_mode = mode;
|
|
|
|
return CCS811_OK;
|
|
}
|
|
|
|
#endif /* MODULE_CCS811_FULL */
|
|
|
|
int ccs811_data_ready(const ccs811_t *dev)
|
|
{
|
|
uint8_t status;
|
|
|
|
/* check status register */
|
|
if (_reg_read(dev, CCS811_REG_STATUS, &status, 1) != CCS811_OK) {
|
|
DEBUG_DEV("could not read CCS811_REG_STATUS", dev);
|
|
return -CCS811_ERROR_I2C;
|
|
}
|
|
|
|
if ((status & CCS811_STATUS_DATA_RDY)) {
|
|
/* new data available */
|
|
return CCS811_OK;
|
|
}
|
|
|
|
return CCS811_ERROR_NO_NEW_DATA;
|
|
}
|
|
|
|
#define CCS811_ALG_DATA_ECO2_HB 0
|
|
#define CCS811_ALG_DATA_ECO2_LB 1
|
|
#define CCS811_ALG_DATA_TVOC_HB 2
|
|
#define CCS811_ALG_DATA_TVOC_LB 3
|
|
#define CCS811_ALG_DATA_STATUS 4
|
|
#define CCS811_ALG_DATA_ERROR_ID 5
|
|
#define CCS811_ALG_DATA_RAW_HB 6
|
|
#define CCS811_ALG_DATA_RAW_LB 7
|
|
|
|
int ccs811_read_iaq(const ccs811_t *dev,
|
|
uint16_t *iaq_tvoc, uint16_t *iaq_eco2,
|
|
uint16_t *raw_i, uint16_t *raw_v)
|
|
{
|
|
ASSERT_PARAM(dev != NULL);
|
|
|
|
int res = CCS811_OK;
|
|
|
|
if (dev->params.mode == CCS811_MODE_IDLE) {
|
|
DEBUG_DEV("sensor is in idle mode and not performing "
|
|
"measurements", dev);
|
|
return -CCS811_ERROR_MEASMODE_INV;
|
|
}
|
|
|
|
if (dev->params.mode == CCS811_MODE_250MS && (iaq_tvoc || iaq_eco2)) {
|
|
DEBUG_DEV("sensor is in constant power mode, only raw data "
|
|
"are available every 250ms", dev);
|
|
return -CCS811_ERROR_NO_IAQ_DATA;
|
|
}
|
|
|
|
uint8_t data[8];
|
|
|
|
/* read IAQ sensor values and RAW sensor data including status and error id */
|
|
if (_reg_read(dev, CCS811_REG_ALG_RESULT_DATA, data, 8) != CCS811_OK) {
|
|
DEBUG_DEV("could not read sensor data from "
|
|
"register CCS811_REG_ALG_RESULT_DATA", dev);
|
|
return -CCS811_ERROR_I2C;
|
|
}
|
|
|
|
/* check for errors */
|
|
if (data[CCS811_ALG_DATA_STATUS] & CCS811_STATUS_ERROR) {
|
|
return _error_code(dev, data[CCS811_ALG_DATA_ERROR_ID]);
|
|
}
|
|
|
|
/* if *iaq* is not NULL return IAQ sensor values */
|
|
if (iaq_tvoc) {
|
|
*iaq_tvoc = data[CCS811_ALG_DATA_TVOC_HB] << 8;
|
|
*iaq_tvoc |= data[CCS811_ALG_DATA_TVOC_LB];
|
|
}
|
|
if (iaq_eco2) {
|
|
*iaq_eco2 = data[CCS811_ALG_DATA_ECO2_HB] << 8;
|
|
*iaq_eco2 |= data[CCS811_ALG_DATA_ECO2_LB];
|
|
}
|
|
|
|
/* if *raw* is not NULL return RAW sensor data */
|
|
if (raw_i) {
|
|
*raw_i = data[CCS811_ALG_DATA_RAW_HB] >> 2;
|
|
}
|
|
if (raw_v) {
|
|
*raw_v = (data[CCS811_ALG_DATA_RAW_HB] & 0x03) << 8;
|
|
*raw_v |= data[CCS811_ALG_DATA_RAW_LB];
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
#if MODULE_CCS811_FULL
|
|
|
|
int ccs811_read_ntc(const ccs811_t *dev, uint32_t r_ref, uint32_t *r_ntc)
|
|
{
|
|
ASSERT_PARAM(dev != NULL);
|
|
ASSERT_PARAM(r_ntc != NULL);
|
|
|
|
uint8_t data[4];
|
|
|
|
/* read baseline register */
|
|
if (_reg_read(dev, CCS811_REG_NTC, data, 4) != CCS811_OK) {
|
|
DEBUG_DEV("could not read the V_REF and V_NTC "
|
|
"from register CCS811_REG_NTC", dev);
|
|
return -CCS811_ERROR_I2C;
|
|
}
|
|
|
|
/* calculation from application note ams AN000372 */
|
|
uint32_t v_ref = (uint16_t)(data[0]) << 8 | data[1];
|
|
uint32_t v_ntc = (uint16_t)(data[2]) << 8 | data[3];
|
|
|
|
*r_ntc = v_ntc * r_ref / v_ref;
|
|
return CCS811_OK;
|
|
}
|
|
|
|
#endif /* MODULE_CCS811_FULL */
|
|
|
|
int ccs811_power_down (ccs811_t *dev)
|
|
{
|
|
ASSERT_PARAM(dev != NULL);
|
|
|
|
ccs811_mode_t tmp_mode = dev->params.mode;
|
|
int res = ccs811_set_mode(dev, CCS811_MODE_IDLE);
|
|
dev->params.mode = tmp_mode;
|
|
|
|
if (gpio_is_valid(dev->params.wake_pin)) {
|
|
DEBUG_DEV("Setting nWAKE pin high", dev);
|
|
gpio_set(dev->params.wake_pin);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
int ccs811_power_up (ccs811_t *dev)
|
|
{
|
|
ASSERT_PARAM(dev != NULL);
|
|
|
|
if (gpio_is_valid(dev->params.wake_pin)) {
|
|
DEBUG_DEV("Setting nWAKE pin low", dev);
|
|
gpio_clear(dev->params.wake_pin);
|
|
}
|
|
|
|
return ccs811_set_mode(dev, dev->params.mode);
|
|
}
|
|
|
|
#if MODULE_CCS811_FULL
|
|
|
|
int ccs811_set_environmental_data(const ccs811_t *dev,
|
|
int16_t temp, int16_t hum)
|
|
{
|
|
ASSERT_PARAM(dev != NULL);
|
|
|
|
temp = (((uint32_t)temp + 2500) << 9) / 100; /* -25 °C maps to 0 */
|
|
hum = ((uint32_t)hum << 9) / 100;
|
|
|
|
/* fill environmental data */
|
|
uint8_t data[4] = { temp >> 8, temp & 0xff,
|
|
hum >> 8, hum & 0xff };
|
|
|
|
/* send environmental data to the sensor */
|
|
if (_reg_write(dev, CCS811_REG_ENV_DATA, data, 4) != CCS811_OK) {
|
|
DEBUG_DEV("could not write environmental data "
|
|
"to register CCS811_REG_ENV_DATA", dev);
|
|
return CCS811_ERROR_I2C;
|
|
}
|
|
|
|
return CCS811_OK;
|
|
}
|
|
|
|
int ccs811_set_eco2_thresholds(const ccs811_t *dev,
|
|
uint16_t low, uint16_t high, uint8_t hyst)
|
|
{
|
|
ASSERT_PARAM(dev != NULL);
|
|
|
|
/* check parameters */
|
|
if (low < CCS811_ECO2_RANGE_MIN ||
|
|
high > CCS811_ECO2_RANGE_MAX || low > high || !hyst) {
|
|
DEBUG_DEV("wrong threshold parameters", dev);
|
|
return CCS811_ERROR_THRESH_INV;
|
|
}
|
|
|
|
/* fill the threshold data */
|
|
uint8_t data[5] = { low >> 8, low & 0xff,
|
|
high >> 8, high & 0xff,
|
|
hyst };
|
|
|
|
/* write threshold data to the sensor */
|
|
if (_reg_write(dev, CCS811_REG_THRESHOLDS, data, 5) != CCS811_OK) {
|
|
DEBUG_DEV("could not set threshold interrupt parameters, "
|
|
"could not write register CCS811_REG_THRESHOLDS", dev);
|
|
return CCS811_ERROR_I2C;
|
|
}
|
|
|
|
return CCS811_OK;
|
|
}
|
|
|
|
int ccs811_get_baseline(const ccs811_t *dev, uint16_t *base)
|
|
{
|
|
ASSERT_PARAM(dev != NULL);
|
|
ASSERT_PARAM(base != NULL);
|
|
|
|
uint8_t data[2];
|
|
|
|
/* read baseline register */
|
|
if (_reg_read(dev, CCS811_REG_BASELINE, data, 2) != CCS811_OK) {
|
|
DEBUG_DEV("could not get current baseline value, "
|
|
"could not read register CCS811_REG_BASELINE", dev);
|
|
return CCS811_ERROR_I2C;
|
|
}
|
|
|
|
*base = (uint16_t)(data[0]) << 8 | data[1];
|
|
|
|
return CCS811_OK;
|
|
}
|
|
|
|
int ccs811_set_baseline(const ccs811_t *dev, uint16_t baseline)
|
|
{
|
|
ASSERT_PARAM(dev != NULL);
|
|
|
|
uint8_t data[2] = { baseline >> 8, baseline & 0xff };
|
|
|
|
/* write baseline register */
|
|
if (_reg_write(dev, CCS811_REG_THRESHOLDS, data, 5) != CCS811_OK) {
|
|
DEBUG_DEV("could not set baseline value, "
|
|
"could not write register CCS811_REG_BASELINE", dev);
|
|
return CCS811_ERROR_I2C;
|
|
}
|
|
|
|
return CCS811_OK;
|
|
}
|
|
|
|
#endif /* MODULE_CCS811_FULL */
|
|
|
|
/**
|
|
* function for internal use only
|
|
*/
|
|
|
|
static int _reg_read(const ccs811_t *dev, uint8_t reg, uint8_t *data, uint32_t len)
|
|
{
|
|
DEBUG_DEV("read %"PRIu32" bytes from sensor registers starting at addr %02x",
|
|
dev, len, reg);
|
|
|
|
int res;
|
|
|
|
i2c_acquire(dev->params.i2c_dev);
|
|
|
|
#if MODULE_CCS811_FULL
|
|
if (gpio_is_valid(dev->params.wake_pin)) {
|
|
/* wake the sensor with low active WAKE signal */
|
|
gpio_clear(dev->params.wake_pin);
|
|
/* t_WAKE is 50 us */
|
|
ztimer_sleep(ZTIMER_USEC, 50);
|
|
}
|
|
#endif
|
|
|
|
res = i2c_read_regs(dev->params.i2c_dev, dev->params.i2c_addr, reg, data, len, 0);
|
|
i2c_release(dev->params.i2c_dev);
|
|
|
|
#if MODULE_CCS811_FULL
|
|
if (gpio_is_valid(dev->params.wake_pin)) {
|
|
/* let the sensor enter to sleep mode */
|
|
gpio_set(dev->params.wake_pin);
|
|
/* minimum t_DWAKE is 20 us */
|
|
ztimer_sleep(ZTIMER_USEC, 20);
|
|
}
|
|
#endif
|
|
|
|
if (res == CCS811_OK) {
|
|
if (IS_ACTIVE(ENABLE_DEBUG)) {
|
|
printf("[ccs811] %s dev=%d addr=%02x: read following bytes: ",
|
|
__func__, dev->params.i2c_dev, dev->params.i2c_addr);
|
|
for (unsigned i = 0; i < len; i++) {
|
|
printf("%02x ", data[i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
else {
|
|
DEBUG_DEV("could not read %"PRIu32" bytes from sensor registers "
|
|
"starting at addr %02x, reason %i", dev, len, reg, res);
|
|
return -CCS811_ERROR_I2C;
|
|
}
|
|
|
|
return CCS811_OK;
|
|
}
|
|
|
|
static int _reg_write(const ccs811_t *dev, uint8_t reg, uint8_t *data, uint32_t len)
|
|
{
|
|
DEBUG_DEV("write %"PRIu32" bytes to sensor registers starting at addr %02x",
|
|
dev, len, reg);
|
|
|
|
int res = CCS811_OK;
|
|
|
|
if (IS_ACTIVE(ENABLE_DEBUG) && data && len) {
|
|
printf("[css811] %s dev=%d addr=%02x: write following bytes: ",
|
|
__func__, dev->params.i2c_dev, dev->params.i2c_addr);
|
|
for (unsigned i = 0; i < len; i++) {
|
|
printf("%02x ", data[i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
i2c_acquire(dev->params.i2c_dev);
|
|
|
|
#if MODULE_CCS811_FULL
|
|
if (gpio_is_valid(dev->params.wake_pin)) {
|
|
/* wake the sensor with low active WAKE signal */
|
|
gpio_clear(dev->params.wake_pin);
|
|
/* t_WAKE is 50 us */
|
|
ztimer_sleep(ZTIMER_USEC, 50);
|
|
}
|
|
#endif
|
|
|
|
if (!data || !len) {
|
|
res = i2c_write_byte(dev->params.i2c_dev, dev->params.i2c_addr, reg, 0);
|
|
}
|
|
else {
|
|
res = i2c_write_regs(dev->params.i2c_dev, dev->params.i2c_addr, reg, data, len, 0);
|
|
}
|
|
i2c_release(dev->params.i2c_dev);
|
|
|
|
#if MODULE_CCS811_FULL
|
|
if (gpio_is_valid(dev->params.wake_pin)) {
|
|
/* let the sensor enter to sleep mode */
|
|
gpio_set(dev->params.wake_pin);
|
|
/* minimum t_DWAKE is 20 us */
|
|
ztimer_sleep(ZTIMER_USEC, 20);
|
|
}
|
|
#endif
|
|
|
|
if (res != CCS811_OK) {
|
|
DEBUG_DEV("could not write %"PRIu32" bytes to sensor registers "
|
|
"starting at addr %02x, reason %i", dev, len, reg, res);
|
|
return -CCS811_ERROR_I2C;
|
|
}
|
|
|
|
return CCS811_OK;
|
|
}
|
|
|
|
static int _error_code(const ccs811_t *dev, uint8_t err_reg)
|
|
{
|
|
if (err_reg & CCS811_ERR_WRITE_REG_INV) {
|
|
DEBUG_DEV("invalid register address on write", dev);
|
|
return -CCS811_ERROR_WRITE_REG_INV;
|
|
}
|
|
|
|
if (err_reg & CCS811_ERR_READ_REG_INV) {
|
|
DEBUG_DEV("invalid register address on read", dev);
|
|
return -CCS811_ERROR_READ_REG_INV;
|
|
}
|
|
|
|
if (err_reg & CCS811_ERR_MEASMODE_INV) {
|
|
DEBUG_DEV("invalid requested measurement mode", dev);
|
|
return -CCS811_ERROR_MEASMODE_INV;
|
|
}
|
|
|
|
if (err_reg & CCS811_ERR_MAX_RESISTANCE) {
|
|
DEBUG_DEV("sensor resistance measurement has reached "
|
|
"or exceeded the maximum range", dev);
|
|
return -CCS811_ERROR_MAX_RESISTANCE;
|
|
}
|
|
|
|
if (err_reg & CCS811_ERR_HEATER_FAULT) {
|
|
DEBUG_DEV("heater current not in range", dev);
|
|
return -CCS811_ERROR_HEATER_FAULT;
|
|
}
|
|
|
|
if (err_reg & CCS811_ERR_HEATER_SUPPLY) {
|
|
DEBUG_DEV("heater voltage is not being applied correctly", dev);
|
|
return -CCS811_ERROR_HEATER_SUPPLY;
|
|
}
|
|
|
|
return CCS811_OK;
|
|
}
|
|
|
|
static int _check_error_status(const ccs811_t *dev)
|
|
{
|
|
uint8_t status;
|
|
uint8_t err_reg;
|
|
|
|
/* check status register */
|
|
if (_reg_read(dev, CCS811_REG_STATUS, &status, 1) != CCS811_OK) {
|
|
DEBUG_DEV("could not read CCS811_REG_STATUS", dev);
|
|
return -CCS811_ERROR_I2C;
|
|
}
|
|
|
|
if (!(status & CCS811_STATUS_ERROR)) {
|
|
/* everything is OK */
|
|
return CCS811_OK;
|
|
}
|
|
|
|
/* Check the error id register */
|
|
if (_reg_read(dev, CCS811_REG_ERROR_ID, &err_reg, 1) != CCS811_OK) {
|
|
DEBUG_DEV("could not read CCS811_REG_ERROR_ID", dev);
|
|
return -CCS811_ERROR_I2C;
|
|
}
|
|
|
|
if (err_reg != 0) {
|
|
return _error_code(dev, err_reg);
|
|
}
|
|
|
|
return CCS811_OK;
|
|
}
|
|
|
|
static int _is_available(const ccs811_t *dev)
|
|
{
|
|
uint8_t reg_data[5];
|
|
|
|
/* check hardware id (register 0x20) and hardware version (register 0x21) */
|
|
if (_reg_read(dev, CCS811_REG_HW_ID, reg_data, 5) != CCS811_OK) {
|
|
DEBUG_DEV("could not read CCS811_REG_HW_ID", dev);
|
|
return -CCS811_ERROR_I2C;
|
|
}
|
|
|
|
if (reg_data[0] != CCS811_HW_ID) {
|
|
DEBUG_DEV("wrong hardware ID %02x, should be %02x",
|
|
dev, reg_data[0], CCS811_HW_ID);
|
|
return -CCS811_ERROR_NO_DEV;
|
|
}
|
|
|
|
DEBUG_DEV("hardware version: %02x", dev, reg_data[1]);
|
|
DEBUG_DEV("firmware boot version: %02x", dev, reg_data[3]);
|
|
DEBUG_DEV("firmware app version: %02x", dev, reg_data[4]);
|
|
|
|
return _check_error_status(dev);
|
|
}
|