mirror of
https://github.com/apache/nuttx.git
synced 2026-09-06 17:06:31 +00:00
Add support for BME688 sensor based on BME680 implementation. The BME688 is nearly identical to the BME680, and this implementation reuses much of the existing BME680 code. However, there are some differences related to gas measurements that make it impractical to use the same driver without significantly complicating it. BME688 also supports parallel mode which isn't yet supported. Signed-off-by: raiden00pl <raiden00@railab.me>
1783 lines
46 KiB
C
1783 lines
46 KiB
C
/****************************************************************************
|
|
* drivers/sensors/bme688_uorb.c
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
#include <nuttx/nuttx.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <fixedmath.h>
|
|
#include <errno.h>
|
|
#include <debug.h>
|
|
#include <string.h>
|
|
|
|
#include <nuttx/arch.h>
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/kthread.h>
|
|
#include <nuttx/signal.h>
|
|
#include <nuttx/fs/fs.h>
|
|
#include <nuttx/i2c/i2c_master.h>
|
|
#include <nuttx/sensors/bme688.h>
|
|
#include <nuttx/sensors/sensor.h>
|
|
|
|
#if defined(CONFIG_I2C) && defined(CONFIG_SENSORS_BME688)
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#define BME688_ADDR 0x76 /* I2C Slave Address */
|
|
#define BME688_FREQ CONFIG_BME688_I2C_FREQUENCY
|
|
#define BME688_DEVID 0x61
|
|
|
|
/* Sub-sensor definitions */
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_TEMP_MEAS
|
|
# define BME688_TEMP_IDX 0
|
|
#else
|
|
# define BME688_TEMP_IDX (-1)
|
|
#endif
|
|
|
|
#define BME688_TEMP_IDX_OFF BME688_TEMP_IDX
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_PRESS_MEAS
|
|
# define BME688_PRESS_IDX (BME688_TEMP_IDX_OFF + 1)
|
|
# define BME688_PRESS_IDX_OFF BME688_PRESS_IDX
|
|
#else
|
|
# define BME688_PRESS_IDX (-1)
|
|
# define BME688_PRESS_IDX_OFF BME688_TEMP_IDX_OFF
|
|
#endif
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_HUM_MEAS
|
|
# define BME688_HUM_IDX (BME688_PRESS_IDX_OFF + 1)
|
|
# define BME688_HUM_IDX_OFF BME688_HUM_IDX
|
|
#else
|
|
# define BME688_HUM_IDX (-1)
|
|
# define BME688_HUM_IDX_OFF BME688_PRESS_IDX_OFF
|
|
#endif
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_GAS_MEAS
|
|
# define BME688_GAS_IDX (BME688_HUM_IDX_OFF + 1)
|
|
# define BME688_GAS_IDX_OFF BME688_GAS_IDX
|
|
#else
|
|
# define BME688_GAS_IDX (-1)
|
|
# define BME688_GAS_IDX_OFF BME688_HUM_IDX_OFF
|
|
#endif
|
|
|
|
#define BME688_SENSORS_COUNT (BME688_GAS_IDX_OFF + 1)
|
|
|
|
/* Register addresses */
|
|
|
|
#define BME688_STATUS_REG_ADDR 0x73
|
|
#define BME688_RESET_REG_ADDR 0xe0
|
|
#define BME688_ID_REG_ADDR 0xd0
|
|
#define BME688_CTRL_HUM_ADDR 0x72
|
|
#define BME688_CTRL_MEAS_ADDR 0x74
|
|
#define BME688_CONFIG_REG_ADDR 0x75
|
|
#define BME688_CTRL_GAS0 0x70
|
|
#define BME688_CTRL_GAS1 0x71
|
|
#define BME688_PRESS_MSB 0x1f
|
|
#define BME688_PRESS_LSB 0x20
|
|
#define BME688_PRESS_XLSB 0x21
|
|
#define BME688_TEMP_MSB 0x22
|
|
#define BME688_TEMP_LSB 0x23
|
|
#define BME688_TEMP_XLSB 0x24
|
|
#define BME688_HUM_MSB 0x25
|
|
#define BME688_HUM_LSB 0x26
|
|
#define BME688_GAS_R_MSB 0x2a
|
|
#define BME688_GAS_R_LSB 0x2b
|
|
#define BME688_IDAC_HEAT_ADDR 0x50
|
|
#define BME688_RES_HEAT_ADDR 0x5a
|
|
#define BME688_GAS_WAIT_ADDR 0x64
|
|
#define BME688_MEAS_STAT0 0x1d
|
|
|
|
/* nbconv boundaries */
|
|
|
|
#define BME688_NBCONV_MIN 0
|
|
#define BME688_NBCONV_MAX 9
|
|
|
|
/* Power modes */
|
|
|
|
#define BME688_SLEEP_MODE 0x00
|
|
#define BME688_FORCED_MODE 0x01
|
|
#define BME688_PARALLEL_MODE 0x02
|
|
|
|
/* Soft reset, same effect as a power-on reset */
|
|
|
|
#define BME688_SOFT_RESET 0xb6
|
|
|
|
/* Start addresses for coefficient arrays */
|
|
|
|
#define BME688_COEFF_ADDR1 0x89
|
|
#define BME688_COEFF_ADDR2 0xe1
|
|
|
|
#define BME688_COEFF_SIZE 41
|
|
#define BME688_COEFF_ADDR1_LEN 25
|
|
#define BME688_COEFF_ADDR2_LEN 16
|
|
|
|
/* Start address for measurements and status regs */
|
|
|
|
#define BME688_DATA_ADDR 0x1d
|
|
#define BME688_DATA_LEN 17
|
|
|
|
/* Gas coefficients */
|
|
|
|
#define BME688_RES_HEAT_RANGE_ADDR 0x02
|
|
#define BME688_RES_HEAT_VAL_ADDR 0x00
|
|
#define BME688_RANGE_SW_ERR_ADDR 0x04
|
|
|
|
/* Array index for mapping calibration data */
|
|
|
|
#define BME688_T2_LSB_REG 1
|
|
#define BME688_T2_MSB_REG 2
|
|
#define BME688_T3_REG 3
|
|
#define BME688_P1_LSB_REG 5
|
|
#define BME688_P1_MSB_REG 6
|
|
#define BME688_P2_LSB_REG 7
|
|
#define BME688_P2_MSB_REG 8
|
|
#define BME688_P3_REG 9
|
|
#define BME688_P4_LSB_REG 11
|
|
#define BME688_P4_MSB_REG 12
|
|
#define BME688_P5_LSB_REG 13
|
|
#define BME688_P5_MSB_REG 14
|
|
#define BME688_P7_REG 15
|
|
#define BME688_P6_REG 16
|
|
#define BME688_P8_LSB_REG 19
|
|
#define BME688_P8_MSB_REG 20
|
|
#define BME688_P9_LSB_REG 21
|
|
#define BME688_P9_MSB_REG 22
|
|
#define BME688_P10_REG 23
|
|
#define BME688_H2_MSB_REG 25
|
|
#define BME688_H2_LSB_REG 26
|
|
#define BME688_H1_LSB_REG 26
|
|
#define BME688_H1_MSB_REG 27
|
|
#define BME688_H3_REG 28
|
|
#define BME688_H4_REG 29
|
|
#define BME688_H5_REG 30
|
|
#define BME688_H6_REG 31
|
|
#define BME688_H7_REG 32
|
|
#define BME688_T1_LSB_REG 33
|
|
#define BME688_T1_MSB_REG 34
|
|
#define BME688_GH2_LSB_REG 35
|
|
#define BME688_GH2_MSB_REG 36
|
|
#define BME688_GH1_REG 37
|
|
#define BME688_GH3_REG 38
|
|
|
|
/* Masks for register values */
|
|
|
|
#define BME688_GAS_MEAS_MSK 0x30
|
|
#define BME688_NBCONV_MSK 0x0f
|
|
#define BME688_FILTER_MSK 0x1c
|
|
#define BME688_OST_MSK 0xe0
|
|
#define BME688_OSP_MSK 0x1c
|
|
#define BME688_OSH_MSK 0x07
|
|
#define BME688_HCTRL_MSK 0x08
|
|
#define BME688_RUN_GAS_MSK 0x10
|
|
#define BME688_MODE_MSK 0x03
|
|
#define BME688_RHRANGE_MSK 0x30
|
|
#define BME688_RSERROR_MSK 0xf0
|
|
#define BME688_NEW_DATA_MSK 0x80
|
|
#define BME688_GAS_INDEX_MSK 0x0f
|
|
#define BME688_GAS_RANGE_MSK 0x0f
|
|
#define BME688_GASM_VALID_MSK 0x20
|
|
#define BME688_HEAT_STAB_MSK 0x10
|
|
#define BME688_MEM_PAGE_MSK 0x10
|
|
#define BME688_BIT_H1_DATA_MSK 0x0f
|
|
|
|
/* Bounds for tpg */
|
|
|
|
#define MIN_HOT_PLATE_TEMP 200 /* Celsius */
|
|
#define MAX_HOT_PLATE_TEMP 400 /* Celsius */
|
|
|
|
#define BME688_MAX_OVERFLOW_VAL 0x40000000ull
|
|
|
|
#define CHECK_OS_BOUNDS(type) \
|
|
((type) >= BME688_OS_SKIPPED && (type) <= BME688_OS_16X)
|
|
|
|
/****************************************************************************
|
|
* Private Type Definitions
|
|
****************************************************************************/
|
|
|
|
struct bme688_data_s
|
|
{
|
|
uint64_t timestamp; /* Units is microseconds */
|
|
float temperature; /* Temperature in degrees Celsius */
|
|
float pressure; /* Pressure in millibar or hPa */
|
|
float humidity; /* Relative humidity in rH */
|
|
float gas_resistance; /* Gas resistance in Ohm */
|
|
};
|
|
|
|
struct bme688_calib_s
|
|
{
|
|
/* Temperature coefficients */
|
|
|
|
uint16_t t1;
|
|
int16_t t2;
|
|
int8_t t3;
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_PRESS_MEAS
|
|
/* Pressure coefficients */
|
|
|
|
uint16_t p1;
|
|
int16_t p2;
|
|
int8_t p3;
|
|
int16_t p4;
|
|
int16_t p5;
|
|
int8_t p6;
|
|
int8_t p7;
|
|
int16_t p8;
|
|
int16_t p9;
|
|
uint8_t p10;
|
|
#endif /* !CONFIG_BME688_DISABLE_PRESS_MEAS */
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_HUM_MEAS
|
|
|
|
/* Humidity coefficients */
|
|
|
|
uint16_t h1;
|
|
uint16_t h2;
|
|
int8_t h3;
|
|
int8_t h4;
|
|
int8_t h5;
|
|
uint8_t h6;
|
|
int8_t h7;
|
|
#endif /* !CONFIG_BME688_DISABLE_PRESS_MEAS */
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_GAS_MEAS
|
|
/* Gas heater coefficients */
|
|
|
|
int8_t gh1;
|
|
int16_t gh2;
|
|
int8_t gh3;
|
|
|
|
uint8_t res_heat_range; /* Heater resistance range */
|
|
int8_t res_heat_val; /* Heater resistance value */
|
|
int8_t range_sw_err; /* Error switching range */
|
|
|
|
#endif /* !CONFIG_BME688_DISABLE_GAS_MEAS */
|
|
|
|
int32_t t_fine;
|
|
};
|
|
|
|
struct bme688_sensor_s
|
|
{
|
|
/* Lowerhalfs for every sub-sensor */
|
|
|
|
struct sensor_lowerhalf_s lower[BME688_SENSORS_COUNT];
|
|
struct bme688_calib_s calib; /* Calibration data */
|
|
struct bme688_config_s config; /* Configuration data */
|
|
bool calibrated; /* Is the device set up? */
|
|
};
|
|
|
|
struct bme688_dev_s
|
|
{
|
|
struct bme688_sensor_s dev; /* Sensor private data */
|
|
FAR struct i2c_master_s *i2c; /* I2C interface */
|
|
mutex_t dev_lock; /* Manages exclusive access to the device */
|
|
sem_t run; /* Locks sensor thread */
|
|
bool enabled; /* Enable/Disable BME688 */
|
|
};
|
|
|
|
typedef int (*push_data_func)(FAR struct bme688_dev_s *priv,
|
|
FAR struct bme688_data_s *data);
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static uint8_t bme688_getreg8(FAR struct bme688_dev_s *priv,
|
|
uint8_t regaddr);
|
|
static int bme688_putreg8(FAR struct bme688_dev_s *priv, uint8_t regaddr,
|
|
uint8_t regval);
|
|
static int bme688_getregs(FAR struct bme688_dev_s *priv, uint8_t regaddr,
|
|
uint8_t *rxbuffer, uint8_t length);
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_TEMP_MEAS
|
|
static int bme688_push_temp_data(FAR struct bme688_dev_s *priv,
|
|
FAR struct bme688_data_s *data);
|
|
#endif
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_PRESS_MEAS
|
|
static int bme688_push_press_data(FAR struct bme688_dev_s *priv,
|
|
FAR struct bme688_data_s *data);
|
|
#endif
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_HUM_MEAS
|
|
static int bme688_push_hum_data(FAR struct bme688_dev_s *priv,
|
|
FAR struct bme688_data_s *data);
|
|
#endif
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_GAS_MEAS
|
|
static int bme688_push_gas_data(FAR struct bme688_dev_s *priv,
|
|
FAR struct bme688_data_s *data);
|
|
#endif
|
|
|
|
/* Sensor methods */
|
|
|
|
static int bme688_activate(FAR struct sensor_lowerhalf_s *lower,
|
|
FAR struct file *filep,
|
|
bool enable);
|
|
static int bme688_calibrate(FAR struct sensor_lowerhalf_s *lower,
|
|
FAR struct file *filep,
|
|
unsigned long arg);
|
|
static int bme688_control(FAR struct sensor_lowerhalf_s *lower,
|
|
FAR struct file *filep,
|
|
int cmd, unsigned long arg);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const push_data_func deliver_data[BME688_SENSORS_COUNT] =
|
|
{
|
|
#ifndef CONFIG_BME688_DISABLE_TEMP_MEAS
|
|
bme688_push_temp_data,
|
|
#endif
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_PRESS_MEAS
|
|
bme688_push_press_data,
|
|
#endif
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_HUM_MEAS
|
|
bme688_push_hum_data,
|
|
#endif
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_GAS_MEAS
|
|
bme688_push_gas_data,
|
|
#endif
|
|
};
|
|
|
|
static const struct sensor_ops_s g_sensor_ops =
|
|
{
|
|
NULL, /* open */
|
|
NULL, /* close */
|
|
bme688_activate, /* activate */
|
|
NULL, /* set_interval */
|
|
NULL, /* batch */
|
|
NULL, /* fetch */
|
|
NULL, /* flush */
|
|
NULL, /* selftest */
|
|
NULL, /* set_calibvalue */
|
|
bme688_calibrate, /* calibrate */
|
|
NULL, /* get_info */
|
|
bme688_control /* control */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: bme688_getreg8
|
|
*
|
|
* Description:
|
|
* Read from an 8-bit BME688 register
|
|
*
|
|
****************************************************************************/
|
|
|
|
static uint8_t bme688_getreg8(FAR struct bme688_dev_s *priv, uint8_t regaddr)
|
|
{
|
|
struct i2c_msg_s msg[2];
|
|
uint8_t regval = 0;
|
|
int ret;
|
|
|
|
msg[0].frequency = BME688_FREQ;
|
|
msg[0].addr = BME688_ADDR;
|
|
msg[0].flags = 0;
|
|
msg[0].buffer = ®addr;
|
|
msg[0].length = 1;
|
|
|
|
msg[1].frequency = BME688_FREQ;
|
|
msg[1].addr = BME688_ADDR;
|
|
msg[1].flags = I2C_M_READ;
|
|
msg[1].buffer = ®val;
|
|
msg[1].length = 1;
|
|
|
|
ret = I2C_TRANSFER(priv->i2c, msg, 2);
|
|
if (ret < 0)
|
|
{
|
|
snerr("I2C_TRANSFER failed: %d\n", ret);
|
|
return 0;
|
|
}
|
|
|
|
return regval;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bme688_getregs
|
|
*
|
|
* Description:
|
|
* Read <length> bytes starting from a BME688 register addr
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int bme688_getregs(FAR struct bme688_dev_s *priv, uint8_t regaddr,
|
|
uint8_t *rxbuffer, uint8_t length)
|
|
{
|
|
struct i2c_msg_s msg[2];
|
|
int ret;
|
|
|
|
msg[0].frequency = BME688_FREQ;
|
|
msg[0].addr = BME688_ADDR;
|
|
msg[0].flags = 0;
|
|
msg[0].buffer = ®addr;
|
|
msg[0].length = 1;
|
|
|
|
msg[1].frequency = BME688_FREQ;
|
|
msg[1].addr = BME688_ADDR;
|
|
msg[1].flags = I2C_M_READ;
|
|
msg[1].buffer = rxbuffer;
|
|
msg[1].length = length;
|
|
|
|
ret = I2C_TRANSFER(priv->i2c, msg, 2);
|
|
if (ret < 0)
|
|
{
|
|
snerr("I2C_TRANSFER failed: %d\n", ret);
|
|
return -1;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bme688_putreg8
|
|
*
|
|
* Description:
|
|
* Write to an 8-bit BME688 register
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int bme688_putreg8(FAR struct bme688_dev_s *priv, uint8_t regaddr,
|
|
uint8_t regval)
|
|
{
|
|
struct i2c_msg_s msg[2];
|
|
uint8_t txbuffer[2];
|
|
int ret;
|
|
|
|
txbuffer[0] = regaddr;
|
|
txbuffer[1] = regval;
|
|
|
|
msg[0].frequency = BME688_FREQ;
|
|
msg[0].addr = BME688_ADDR;
|
|
msg[0].flags = 0;
|
|
msg[0].buffer = txbuffer;
|
|
msg[0].length = 2;
|
|
|
|
ret = I2C_TRANSFER(priv->i2c, msg, 1);
|
|
if (ret < 0)
|
|
{
|
|
snerr("I2C_TRANSFER failed: %d\n", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bme688_checkid
|
|
*
|
|
* Description:
|
|
* Read and verify the BME688 chip ID
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int bme688_checkid(FAR struct bme688_dev_s *priv)
|
|
{
|
|
uint8_t devid = 0;
|
|
|
|
/* Read device ID */
|
|
|
|
devid = bme688_getreg8(priv, BME688_ID_REG_ADDR);
|
|
up_mdelay(1);
|
|
sninfo("devid: 0x%02x\n", devid);
|
|
|
|
if (devid != (uint8_t)BME688_DEVID)
|
|
{
|
|
/* ID is not Correct */
|
|
|
|
snerr("Wrong Device ID! %02x\n", devid);
|
|
return -ENODEV;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bme688_get_calib_data
|
|
*
|
|
* Description:
|
|
* Read sensor-specific parameters and store them for later
|
|
* use in computing the compensated values.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int bme688_get_calib_data(FAR struct bme688_dev_s *priv)
|
|
{
|
|
uint8_t coeff[BME688_COEFF_SIZE];
|
|
uint8_t temp_val;
|
|
int ret;
|
|
|
|
/* Get first part of the calibration data. */
|
|
|
|
ret = bme688_getregs(priv, BME688_COEFF_ADDR1, coeff,
|
|
BME688_COEFF_ADDR1_LEN);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Concatenate the second part of the data to coeff */
|
|
|
|
ret = bme688_getregs(priv, BME688_COEFF_ADDR2,
|
|
&coeff[BME688_COEFF_ADDR1_LEN],
|
|
BME688_COEFF_ADDR2_LEN);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Get data */
|
|
|
|
priv->dev.calib.t1 = coeff[BME688_T1_MSB_REG] << 8
|
|
| coeff[BME688_T1_LSB_REG];
|
|
priv->dev.calib.t2 = coeff[BME688_T2_MSB_REG] << 8
|
|
| coeff[BME688_T2_LSB_REG];
|
|
priv->dev.calib.t3 = coeff[BME688_T3_REG];
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_PRESS_MEAS
|
|
priv->dev.calib.p1 = coeff[BME688_P1_MSB_REG] << 8
|
|
| coeff[BME688_P1_LSB_REG];
|
|
priv->dev.calib.p2 = coeff[BME688_P2_MSB_REG] << 8
|
|
| coeff[BME688_P2_LSB_REG];
|
|
priv->dev.calib.p3 = coeff[BME688_P3_REG];
|
|
priv->dev.calib.p4 = coeff[BME688_P4_MSB_REG] << 8
|
|
| coeff[BME688_P4_LSB_REG];
|
|
priv->dev.calib.p5 = coeff[BME688_P5_MSB_REG] << 8
|
|
| coeff[BME688_P5_LSB_REG];
|
|
priv->dev.calib.p6 = coeff[BME688_P6_REG];
|
|
priv->dev.calib.p7 = coeff[BME688_P7_REG];
|
|
priv->dev.calib.p8 = coeff[BME688_P8_MSB_REG] << 8
|
|
| coeff[BME688_P8_LSB_REG];
|
|
priv->dev.calib.p9 = coeff[BME688_P9_MSB_REG] << 8
|
|
| coeff[BME688_P9_LSB_REG];
|
|
priv->dev.calib.p10 = coeff[BME688_P10_REG];
|
|
#endif /* !CONFIG_BME688_DISABLE_PRESS_MEAS */
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_HUM_MEAS
|
|
priv->dev.calib.h1 = (uint16_t)(((uint16_t)coeff[BME688_H1_MSB_REG] << 4)
|
|
| (coeff[BME688_H1_LSB_REG] & BME688_BIT_H1_DATA_MSK));
|
|
priv->dev.calib.h2 = (uint16_t)(((uint16_t)coeff[BME688_H2_MSB_REG] << 4)
|
|
| ((coeff[BME688_H2_LSB_REG]) >> 4));
|
|
priv->dev.calib.h3 = coeff[BME688_H3_REG];
|
|
priv->dev.calib.h4 = coeff[BME688_H4_REG];
|
|
priv->dev.calib.h5 = coeff[BME688_H5_REG];
|
|
priv->dev.calib.h6 = coeff[BME688_H6_REG];
|
|
priv->dev.calib.h7 = coeff[BME688_H7_REG];
|
|
#endif /* !CONFIG_BME688_DISABLE_HUM_MEAS */
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_GAS_MEAS
|
|
|
|
/* Gas-related coefficients */
|
|
|
|
priv->dev.calib.gh1 = coeff[BME688_GH1_REG];
|
|
priv->dev.calib.gh2 = coeff[BME688_GH2_MSB_REG] << 8
|
|
| coeff[BME688_GH2_LSB_REG];
|
|
priv->dev.calib.gh3 = coeff[BME688_GH3_REG];
|
|
|
|
ret = bme688_getregs(priv, BME688_RES_HEAT_RANGE_ADDR, &temp_val, 1);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
priv->dev.calib.res_heat_range = ((temp_val & BME688_RHRANGE_MSK)) / 16;
|
|
|
|
ret = bme688_getregs(priv, BME688_RES_HEAT_VAL_ADDR, &temp_val, 1);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
priv->dev.calib.res_heat_val = (int8_t)temp_val;
|
|
|
|
ret = bme688_getregs(priv, BME688_RANGE_SW_ERR_ADDR, &temp_val, 1);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
priv->dev.calib.range_sw_err = ((int8_t)temp_val
|
|
& (int8_t)BME688_RSERROR_MSK) / 16;
|
|
|
|
#endif /* !CONFIG_BME688_DISABLE_GAS_MEAS */
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bme688_set_mode
|
|
*
|
|
* Description:
|
|
* Set sensor mode and wait for it to change accordingly.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int bme688_set_mode(FAR struct bme688_dev_s *priv, uint8_t mode)
|
|
{
|
|
int ret;
|
|
uint8_t power_mode;
|
|
uint8_t regval;
|
|
|
|
/* Get current sensor mode */
|
|
|
|
ret = bme688_getregs(priv, BME688_CTRL_MEAS_ADDR, ®val, 1);
|
|
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
power_mode = regval & BME688_MODE_MSK;
|
|
|
|
if (power_mode != mode)
|
|
{
|
|
regval &= (uint8_t)(~BME688_MODE_MSK);
|
|
regval |= (mode & BME688_MODE_MSK);
|
|
|
|
ret = bme688_putreg8(priv, BME688_CTRL_MEAS_ADDR, regval);
|
|
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
ret = bme688_getregs(priv, BME688_CTRL_MEAS_ADDR, ®val, 1);
|
|
|
|
/* Check if the mode has changed and wait if it hasn't */
|
|
|
|
while ((regval & BME688_MODE_MSK) != mode)
|
|
{
|
|
up_mdelay(100);
|
|
ret = bme688_getregs(priv, BME688_CTRL_MEAS_ADDR, ®val, 1);
|
|
}
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bme688_set_oversamp
|
|
*
|
|
* Description:
|
|
* Set temperature, pressure and humidity oversampling.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int bme688_set_oversamp(FAR struct bme688_dev_s *priv)
|
|
{
|
|
struct bme688_config_s config = priv->dev.config;
|
|
|
|
int ret;
|
|
uint8_t regval;
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_HUM_MEAS
|
|
/* Set humidity oversampling */
|
|
|
|
regval = config.hum_os & BME688_OSH_MSK;
|
|
ret = bme688_putreg8(priv, BME688_CTRL_HUM_ADDR, regval);
|
|
#endif
|
|
|
|
/* Set temperature and pressure oversampling */
|
|
|
|
regval = 0;
|
|
ret = bme688_getregs(priv, BME688_CTRL_MEAS_ADDR, ®val, 1);
|
|
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
regval &= BME688_MODE_MSK;
|
|
regval |= ((config.temp_os << 5) & BME688_OST_MSK);
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_PRESS_MEAS
|
|
regval |= ((config.press_os << 2) & BME688_OSP_MSK);
|
|
#endif
|
|
|
|
ret = bme688_putreg8(priv, BME688_CTRL_MEAS_ADDR, regval);
|
|
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_TEMP_MEAS
|
|
/****************************************************************************
|
|
* Name: bme688_push_temp_data
|
|
****************************************************************************/
|
|
|
|
static int bme688_push_temp_data(FAR struct bme688_dev_s *priv,
|
|
FAR struct bme688_data_s *data)
|
|
{
|
|
struct sensor_temp temp_data;
|
|
int ret;
|
|
|
|
struct sensor_lowerhalf_s lower = priv->dev.lower[BME688_TEMP_IDX];
|
|
|
|
temp_data.timestamp = data->timestamp;
|
|
temp_data.temperature = data->temperature;
|
|
|
|
ret = lower.push_event(lower.priv, &temp_data, sizeof(struct sensor_temp));
|
|
|
|
if (ret < 0)
|
|
{
|
|
snerr("Pushing temperature data failed\n");
|
|
return ret;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
#endif
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_PRESS_MEAS
|
|
/****************************************************************************
|
|
* Name: bme688_push_press_data
|
|
****************************************************************************/
|
|
|
|
static int bme688_push_press_data(FAR struct bme688_dev_s *priv,
|
|
FAR struct bme688_data_s *data)
|
|
{
|
|
struct sensor_baro press_data;
|
|
int ret;
|
|
|
|
struct sensor_lowerhalf_s lower = priv->dev.lower[BME688_PRESS_IDX];
|
|
|
|
press_data.timestamp = data->timestamp;
|
|
press_data.temperature = data->temperature;
|
|
press_data.pressure = data->pressure / 100.f;
|
|
|
|
ret = lower.push_event(lower.priv, &press_data,
|
|
sizeof(struct sensor_baro));
|
|
|
|
if (ret < 0)
|
|
{
|
|
snerr("Pushing baro data failed\n");
|
|
return ret;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
#endif
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_HUM_MEAS
|
|
/****************************************************************************
|
|
* Name: bme688_push_hum_data
|
|
****************************************************************************/
|
|
|
|
static int bme688_push_hum_data(FAR struct bme688_dev_s *priv,
|
|
FAR struct bme688_data_s *data)
|
|
{
|
|
struct sensor_humi hum_data;
|
|
int ret;
|
|
|
|
struct sensor_lowerhalf_s lower = priv->dev.lower[BME688_HUM_IDX];
|
|
|
|
hum_data.timestamp = data->timestamp;
|
|
hum_data.humidity = data->humidity;
|
|
|
|
ret = lower.push_event(lower.priv, &hum_data, sizeof(struct sensor_humi));
|
|
|
|
if (ret < 0)
|
|
{
|
|
snerr("Pushing humidity data failed\n");
|
|
return ret;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
#endif /* !CONFIG_BME688_DISABLE_HUM_MEAS */
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_GAS_MEAS
|
|
/****************************************************************************
|
|
* Name: bme688_push_gas_data
|
|
****************************************************************************/
|
|
|
|
static int bme688_push_gas_data(FAR struct bme688_dev_s *priv,
|
|
FAR struct bme688_data_s *data)
|
|
{
|
|
struct sensor_gas gas_data;
|
|
int ret;
|
|
|
|
struct sensor_lowerhalf_s lower = priv->dev.lower[BME688_GAS_IDX];
|
|
|
|
gas_data.timestamp = data->timestamp;
|
|
gas_data.gas_resistance = data->gas_resistance / 1000.f;
|
|
|
|
ret = lower.push_event(lower.priv, &gas_data, sizeof(struct sensor_gas));
|
|
|
|
if (ret < 0)
|
|
{
|
|
snerr("Pushing gas data failed\n");
|
|
return ret;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: calc_heater_res
|
|
*
|
|
* Description:
|
|
* Compute the heater resistance using the target temperature.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static uint8_t calc_heater_res(FAR const struct bme688_dev_s *priv)
|
|
{
|
|
uint8_t res_heat;
|
|
int32_t var1;
|
|
int32_t var2;
|
|
int32_t var3;
|
|
int32_t var4;
|
|
int32_t var5;
|
|
int32_t res_heat_x100;
|
|
|
|
int16_t temp;
|
|
int16_t amb_temp;
|
|
|
|
struct bme688_sensor_s dev = priv->dev;
|
|
|
|
temp = dev.config.target_temp;
|
|
|
|
if (temp > 400)
|
|
{
|
|
temp = 400;
|
|
}
|
|
|
|
amb_temp = dev.config.amb_temp;
|
|
|
|
var1 = (((int32_t)amb_temp * dev.calib.gh3) / 10) * 256;
|
|
var2 = (dev.calib.gh1 + 784) * (((((dev.calib.gh2 + 154009)
|
|
* temp * 5) / 100) + 3276880) / 10);
|
|
var3 = var1 + (var2 / 2);
|
|
var4 = (var3 / (dev.calib.res_heat_range + 4));
|
|
var5 = (131 * dev.calib.res_heat_val) + 65536;
|
|
res_heat_x100 = (int32_t)(((var4 / var5) - 250) * 34);
|
|
res_heat = (uint8_t)((res_heat_x100 + 50) / 100);
|
|
|
|
return res_heat;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: calc_heater_dur
|
|
*
|
|
* Description:
|
|
* Compute the heater duration to be written to heat_dur register.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static uint8_t calc_heater_dur(FAR const struct bme688_dev_s *priv)
|
|
{
|
|
uint16_t heat_dur = priv->dev.config.heater_duration;
|
|
uint8_t gas_wait_val;
|
|
uint8_t factor;
|
|
|
|
/* Max value of duration is 4032 ms */
|
|
|
|
if (heat_dur > 0xfc0)
|
|
{
|
|
heat_dur = 0xfc0;
|
|
}
|
|
|
|
/* Compute multiplication factor */
|
|
|
|
factor = 0;
|
|
|
|
while (heat_dur > 0x3f)
|
|
{
|
|
heat_dur = heat_dur / 4;
|
|
factor++;
|
|
}
|
|
|
|
gas_wait_val = (factor << 6) | heat_dur;
|
|
|
|
return gas_wait_val;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bme688_set_gas_config
|
|
****************************************************************************/
|
|
|
|
static int bme688_set_gas_config(FAR struct bme688_dev_s *priv)
|
|
{
|
|
int ret;
|
|
uint8_t heat_res;
|
|
uint8_t heat_dur;
|
|
uint8_t run_gas;
|
|
uint8_t regval;
|
|
uint8_t nb_conv = priv->dev.config.nb_conv;
|
|
|
|
/* Set heater resistance */
|
|
|
|
heat_res = calc_heater_res(priv);
|
|
|
|
ret = bme688_putreg8(priv, (BME688_RES_HEAT_ADDR + nb_conv), heat_res);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Set heater duration */
|
|
|
|
heat_dur = calc_heater_dur(priv);
|
|
|
|
ret = bme688_putreg8(priv, (BME688_GAS_WAIT_ADDR + nb_conv), heat_dur);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Set nbconv and run_gas */
|
|
|
|
run_gas = priv->dev.config.target_temp ? 1 : 0;
|
|
regval = (run_gas << 5) | nb_conv;
|
|
|
|
ret = bme688_putreg8(priv, BME688_CTRL_GAS1, regval);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
#endif /* !CONFIG_BME688_DISABLE_GAS_MEAS */
|
|
|
|
/****************************************************************************
|
|
* Name: bme688_write_config
|
|
*
|
|
* Description:
|
|
* Write the configuration of the sensor into its registers
|
|
* (oversampling, heater temp, heater duration, etc).
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int bme688_write_config(FAR struct bme688_dev_s *priv)
|
|
{
|
|
int ret;
|
|
|
|
#ifdef CONFIG_BME688_ENABLE_IIR_FILTER
|
|
uint8_t regval;
|
|
#endif /* CONFIG_BME688_ENABLE_IIR_FILTER */
|
|
|
|
nxmutex_lock(&priv->dev_lock);
|
|
|
|
/* Before anything is written, make sure it is in sleep mode */
|
|
|
|
ret = bme688_set_mode(priv, BME688_SLEEP_MODE);
|
|
if (ret < 0)
|
|
{
|
|
goto err_out;
|
|
}
|
|
|
|
/* Set oversampling */
|
|
|
|
ret = bme688_set_oversamp(priv);
|
|
if (ret < 0)
|
|
{
|
|
goto err_out;
|
|
}
|
|
|
|
#ifdef CONFIG_BME688_ENABLE_IIR_FILTER
|
|
/* Set filter */
|
|
|
|
regval = priv->dev.config.filter_coef << 2;
|
|
ret = bme688_putreg8(priv, BME688_CONFIG_REG_ADDR, regval);
|
|
if (ret < 0)
|
|
{
|
|
goto err_out;
|
|
}
|
|
#endif /* CONFIG_ENABLE_IIR_FILTER */
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_GAS_MEAS
|
|
/* Set gas configs */
|
|
|
|
ret = bme688_set_gas_config(priv);
|
|
if (ret < 0)
|
|
{
|
|
goto err_out;
|
|
}
|
|
#endif /* !CONFIG_BME688_DISABLE_GAS_MEAS */
|
|
|
|
nxmutex_unlock(&priv->dev_lock);
|
|
return OK;
|
|
|
|
err_out:
|
|
snerr("Failed to calibrate sensor.\n");
|
|
nxmutex_unlock(&priv->dev_lock);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bme688_configure
|
|
****************************************************************************/
|
|
|
|
static int bme688_configure(FAR struct bme688_dev_s *priv,
|
|
FAR struct bme688_config_s *config)
|
|
{
|
|
int ret;
|
|
|
|
/* Sanity checks */
|
|
|
|
if (!CHECK_OS_BOUNDS(config->temp_os))
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_PRESS_MEAS
|
|
if (!CHECK_OS_BOUNDS(config->press_os))
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
#endif
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_HUM_MEAS
|
|
if (!CHECK_OS_BOUNDS(config->hum_os))
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_BME688_ENABLE_IIR_FILTER
|
|
if (config->filter_coef < BME688_FILTER_COEF0 ||
|
|
config->filter_coef > BME688_FILTER_COEF127)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
#endif
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_GAS_MEAS
|
|
if (config->target_temp < MIN_HOT_PLATE_TEMP ||
|
|
config->target_temp > MAX_HOT_PLATE_TEMP)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (config->nb_conv > 9)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
#endif
|
|
|
|
/* Update config in priv */
|
|
|
|
memcpy(&priv->dev.config, config, sizeof(struct bme688_config_s));
|
|
|
|
ret = bme688_write_config(priv);
|
|
if (ret < 0)
|
|
{
|
|
snerr("Failed to calibrate sensor.\n");
|
|
return ret;
|
|
}
|
|
|
|
priv->dev.calibrated = true;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bme688_comp_temp
|
|
****************************************************************************/
|
|
|
|
static float bme688_comp_temp(FAR struct bme688_dev_s *priv,
|
|
uint32_t adc_temp)
|
|
{
|
|
float var1 = 0.0f;
|
|
float var2 = 0.0f;
|
|
float calc_temp = 0.0f;
|
|
|
|
struct bme688_sensor_s dev = priv->dev;
|
|
|
|
var1 = ((((float)adc_temp / 16384.0f) - ((float)dev.calib.t1 / 1024.0f))
|
|
* ((float)dev.calib.t2));
|
|
|
|
var2 = (((((float)adc_temp / 131072.0f) - ((float)dev.calib.t1 / 8192.0f))
|
|
* (((float)adc_temp / 131072.0f) - ((float)dev.calib.t1 / 8192.0f)))
|
|
* ((float)dev.calib.t3 * 16.0f));
|
|
|
|
priv->dev.calib.t_fine = (var1 + var2);
|
|
|
|
/* Compensated temperature data */
|
|
|
|
calc_temp = (priv->dev.calib.t_fine) / 5120.0f;
|
|
|
|
return calc_temp;
|
|
}
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_PRESS_MEAS
|
|
/****************************************************************************
|
|
* Name: bme688_comp_press
|
|
****************************************************************************/
|
|
|
|
static float bme688_comp_press(FAR struct bme688_dev_s *priv,
|
|
uint32_t adc_press)
|
|
{
|
|
float var1 = 0.0f;
|
|
float var2 = 0.0f;
|
|
float var3 = 0.0f;
|
|
float calc_pres = 0.0f;
|
|
|
|
struct bme688_sensor_s dev = priv->dev;
|
|
|
|
var1 = (((float)dev.calib.t_fine / 2.0f) - 64000.0f);
|
|
var2 = var1 * var1 * (((float)dev.calib.p6) / (131072.0f));
|
|
var2 = var2 + (var1 * ((float)dev.calib.p5) * 2.0f);
|
|
var2 = (var2 / 4.0f) + (((float)dev.calib.p4) * 65536.0f);
|
|
var1 = (((((float)dev.calib.p3 * var1 * var1) / 16384.0f)
|
|
+ ((float)dev.calib.p2 * var1)) / 524288.0f);
|
|
var1 = ((1.0f + (var1 / 32768.0f)) * ((float)dev.calib.p1));
|
|
calc_pres = (1048576.0f - ((float)adc_press));
|
|
|
|
/* Avoid exception caused by division by zero */
|
|
|
|
if ((int)var1 != 0)
|
|
{
|
|
calc_pres = (((calc_pres - (var2 / 4096.0f)) * 6250.0f) / var1);
|
|
var1 = (((float)dev.calib.p9) * calc_pres * calc_pres) / 2147483648.0f;
|
|
var2 = calc_pres * (((float)dev.calib.p8) / 32768.0f);
|
|
var3 = ((calc_pres / 256.0f) * (calc_pres / 256.0f)
|
|
* (calc_pres / 256.0f) * (dev.calib.p10 / 131072.0f));
|
|
calc_pres = (calc_pres + (var1 + var2 + var3
|
|
+ ((float)dev.calib.p7 * 128.0f)) / 16.0f);
|
|
}
|
|
|
|
return calc_pres;
|
|
}
|
|
#endif /* !CONFIG_BME688_DISABLE_PRESS_MEAS */
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_HUM_MEAS
|
|
/****************************************************************************
|
|
* Name: bme688_comp_hum
|
|
****************************************************************************/
|
|
|
|
static float bme688_comp_hum(FAR struct bme688_dev_s *priv,
|
|
uint16_t adc_hum)
|
|
{
|
|
float calc_hum = 0.0f;
|
|
float var1 = 0.0f;
|
|
float var2 = 0.0f;
|
|
float var3 = 0.0f;
|
|
float var4 = 0.0f;
|
|
float temp_comp;
|
|
|
|
struct bme688_sensor_s dev = priv->dev;
|
|
|
|
/* Compensated temperature data */
|
|
|
|
temp_comp = ((dev.calib.t_fine) / 5120.0f);
|
|
|
|
var1 = (float)((float)adc_hum) - (((float)dev.calib.h1 * 16.0f)
|
|
+ (((float)dev.calib.h3 / 2.0f) * temp_comp));
|
|
|
|
var2 = var1 * ((float)(((float)dev.calib.h2 / 262144.0f)
|
|
* (1.0f + (((float)dev.calib.h4 / 16384.0f) * temp_comp)
|
|
+ (((float)dev.calib.h5 / 1048576.0f) * temp_comp * temp_comp))));
|
|
|
|
var3 = (float)dev.calib.h6 / 16384.0f;
|
|
|
|
var4 = (float)dev.calib.h7 / 2097152.0f;
|
|
|
|
calc_hum = var2 + ((var3 + (var4 * temp_comp)) * var2 * var2);
|
|
|
|
if (calc_hum > 100.0f)
|
|
{
|
|
calc_hum = 100.0f;
|
|
}
|
|
else if (calc_hum < 0.0f)
|
|
{
|
|
calc_hum = 0.0f;
|
|
}
|
|
|
|
return calc_hum;
|
|
}
|
|
#endif /* !CONFIG_BME688_DISABLE_HUM_MEAS */
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_GAS_MEAS
|
|
/****************************************************************************
|
|
* Name: bme688_calc_gas_res
|
|
****************************************************************************/
|
|
|
|
static float bme688_calc_gas_res(FAR struct bme688_dev_s *priv,
|
|
uint16_t adc_gas_res, uint8_t gas_range)
|
|
{
|
|
uint32_t var1 = 262144 >> gas_range;
|
|
int32_t var2 = (int32_t)adc_gas_res - 512;
|
|
|
|
var2 *= 3;
|
|
var2 = 4096 + var2;
|
|
return 1000000.0f * (float)var1 / (float)var2;
|
|
}
|
|
#endif /* !CONFIG_BME688_DISABLE_GAS_MEAS */
|
|
|
|
/****************************************************************************
|
|
* Name: bme688_read_measurements
|
|
*
|
|
* Description:
|
|
* Reads the raw data from the sensor and computes the compensated
|
|
* values, storing them in the data struct.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int bme688_read_measurements(FAR struct bme688_dev_s *priv,
|
|
FAR struct bme688_data_s *data)
|
|
{
|
|
uint8_t status;
|
|
uint32_t adc_temp;
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_PRESS_MEAS
|
|
uint32_t adc_press;
|
|
#endif /* !CONFIG_BME688_DISABLE_PRESS_MEAS */
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_HUM_MEAS
|
|
uint16_t adc_hum;
|
|
#endif /* !CONFIG_BME688_DISABLE_HUM_MEAS */
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_GAS_MEAS
|
|
uint16_t adc_gas_res;
|
|
uint8_t gas_range;
|
|
uint8_t gas_valid;
|
|
uint8_t heat_stab;
|
|
#endif /* !CONFIG_BME688_DISABLE_GAS_MEAS */
|
|
|
|
int ret;
|
|
|
|
uint8_t data_regs[BME688_DATA_LEN];
|
|
|
|
ret = bme688_getregs(priv, BME688_DATA_ADDR, data_regs, BME688_DATA_LEN);
|
|
if (ret < 0)
|
|
{
|
|
snerr("Failed to read data registers.\n");
|
|
return ret;
|
|
}
|
|
|
|
status = data_regs[0] & BME688_NEW_DATA_MSK;
|
|
|
|
/* No new data, return */
|
|
|
|
if (!status)
|
|
{
|
|
sninfo("No new data\n");
|
|
return -ENODATA;
|
|
}
|
|
|
|
adc_temp = (uint32_t)(((uint32_t)data_regs[5] << 12)
|
|
| ((uint32_t)data_regs[6] << 4)
|
|
| ((uint32_t)data_regs[7] >> 4));
|
|
|
|
data->temperature = bme688_comp_temp(priv, adc_temp);
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_PRESS_MEAS
|
|
adc_press = (uint32_t)(((uint32_t)data_regs[2] << 12)
|
|
| ((uint32_t)data_regs[3] << 4)
|
|
| ((uint32_t)data_regs[4] >> 4));
|
|
|
|
data->pressure = bme688_comp_press(priv, adc_press);
|
|
#endif /* !CONFIG_BME688_DISABLE_PRESS_MEAS */
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_HUM_MEAS
|
|
adc_hum = (uint16_t)(((uint32_t)data_regs[8] << 8)
|
|
| (uint32_t)data_regs[9]);
|
|
|
|
data->humidity = bme688_comp_hum(priv, adc_hum);
|
|
#endif /* !CONFIG_BME688_DISABLE_HUM_MEAS */
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_GAS_MEAS
|
|
adc_gas_res = (uint16_t)((uint32_t)data_regs[15] << 2
|
|
| (((uint32_t)data_regs[16]) >> 6));
|
|
gas_range = data_regs[16] & BME688_GAS_RANGE_MSK;
|
|
|
|
/* Is measured gas valid? */
|
|
|
|
gas_valid = data_regs[16] & BME688_GASM_VALID_MSK;
|
|
if (!gas_valid)
|
|
{
|
|
sninfo("Invalid gas measurement.\n");
|
|
return -1;
|
|
}
|
|
|
|
heat_stab = data_regs[16] & BME688_HEAT_STAB_MSK;
|
|
if (!heat_stab)
|
|
{
|
|
sninfo("The heater did not stabilize.\n");
|
|
return -1;
|
|
}
|
|
|
|
priv->dev.config.amb_temp = data->temperature; /* Update ambient temp */
|
|
|
|
data->gas_resistance = bme688_calc_gas_res(priv, adc_gas_res, gas_range);
|
|
#endif /* !CONFIG_BME688_DISABLE_GAS_MEAS */
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bme688_get_tphg_dur
|
|
*
|
|
* Description:
|
|
* Compute the duration of a tphg cycle in us, taking into consideration
|
|
* the settings of the sensor.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static uint16_t bme688_get_tphg_dur(FAR struct bme688_dev_s *priv)
|
|
{
|
|
uint32_t tph_dur; /* Calculate in us */
|
|
uint32_t meas_cycles;
|
|
uint16_t duration;
|
|
uint8_t os_to_meas_cycles[6] =
|
|
{
|
|
0, 1, 2, 4, 8, 16
|
|
};
|
|
|
|
meas_cycles = os_to_meas_cycles[priv->dev.config.temp_os];
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_PRESS_MEAS
|
|
meas_cycles += os_to_meas_cycles[priv->dev.config.press_os];
|
|
#endif
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_HUM_MEAS
|
|
meas_cycles += os_to_meas_cycles[priv->dev.config.hum_os];
|
|
#endif
|
|
|
|
/* TPH measurement duration */
|
|
|
|
tph_dur = meas_cycles * 1963;
|
|
tph_dur += 477 * 4; /* TPH switching duration */
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_GAS_MEAS
|
|
tph_dur += 477 * 5; /* Gas measurement duration */
|
|
#endif
|
|
|
|
tph_dur += 500;
|
|
tph_dur /= 1000; /* Convert to ms */
|
|
|
|
tph_dur += 1; /* Wake up duration of 1ms */
|
|
|
|
duration = (uint16_t)tph_dur;
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_GAS_MEAS
|
|
/* The remaining time should be used for heating */
|
|
|
|
duration += priv->dev.config.heater_duration;
|
|
#endif
|
|
|
|
return duration;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bme688_activate
|
|
****************************************************************************/
|
|
|
|
static int bme688_activate(FAR struct sensor_lowerhalf_s *lower,
|
|
FAR struct file *filep, bool enable)
|
|
{
|
|
int offset;
|
|
FAR struct bme688_sensor_s *dev;
|
|
FAR struct bme688_dev_s *priv;
|
|
|
|
/* Get offset inside array of lowerhalfs */
|
|
|
|
switch (lower->type)
|
|
{
|
|
case SENSOR_TYPE_AMBIENT_TEMPERATURE:
|
|
offset = BME688_TEMP_IDX;
|
|
break;
|
|
|
|
case SENSOR_TYPE_BAROMETER:
|
|
offset = BME688_PRESS_IDX;
|
|
break;
|
|
|
|
case SENSOR_TYPE_RELATIVE_HUMIDITY:
|
|
offset = BME688_HUM_IDX;
|
|
break;
|
|
|
|
case SENSOR_TYPE_GAS:
|
|
offset = BME688_GAS_IDX;
|
|
break;
|
|
|
|
default:
|
|
offset = 0;
|
|
break;
|
|
}
|
|
|
|
dev = (FAR struct bme688_sensor_s *)
|
|
((uintptr_t)lower - offset * sizeof(*lower));
|
|
|
|
priv = container_of(dev, FAR struct bme688_dev_s, dev);
|
|
|
|
/* Wake the thread only once (the activate method will be called
|
|
* multiple times for the bme688 sub-sensors)
|
|
*/
|
|
|
|
if (!priv->enabled && enable)
|
|
{
|
|
priv->enabled = enable;
|
|
|
|
/* Wake up the polling thread */
|
|
|
|
nxsem_post(&priv->run);
|
|
|
|
return OK;
|
|
}
|
|
|
|
priv->enabled = enable;
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bme688_calibrate
|
|
****************************************************************************/
|
|
|
|
static int bme688_calibrate(FAR struct sensor_lowerhalf_s *lower,
|
|
FAR struct file *filep, unsigned long arg)
|
|
{
|
|
int offset;
|
|
FAR struct bme688_sensor_s *dev;
|
|
FAR struct bme688_dev_s *priv;
|
|
FAR struct bme688_config_s *calibval = (FAR struct bme688_config_s *)arg;
|
|
|
|
/* Get offset inside array of lowerhalfs */
|
|
|
|
switch (lower->type)
|
|
{
|
|
case SENSOR_TYPE_AMBIENT_TEMPERATURE:
|
|
offset = BME688_TEMP_IDX;
|
|
break;
|
|
|
|
case SENSOR_TYPE_BAROMETER:
|
|
offset = BME688_PRESS_IDX;
|
|
break;
|
|
|
|
case SENSOR_TYPE_RELATIVE_HUMIDITY:
|
|
offset = BME688_HUM_IDX;
|
|
break;
|
|
|
|
case SENSOR_TYPE_GAS:
|
|
offset = BME688_GAS_IDX;
|
|
break;
|
|
|
|
default:
|
|
offset = 0;
|
|
break;
|
|
}
|
|
|
|
dev = (FAR struct bme688_sensor_s *)
|
|
((uintptr_t)lower - offset * sizeof(*lower));
|
|
|
|
priv = container_of(dev, FAR struct bme688_dev_s, dev);
|
|
|
|
return bme688_configure(priv, calibval);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bme688_control
|
|
****************************************************************************/
|
|
|
|
static int bme688_control(FAR struct sensor_lowerhalf_s *lower,
|
|
FAR struct file *filep,
|
|
int cmd, unsigned long arg)
|
|
{
|
|
FAR struct bme688_sensor_s *dev = container_of(lower,
|
|
FAR struct bme688_sensor_s,
|
|
lower);
|
|
FAR struct bme688_dev_s *priv = container_of(dev,
|
|
FAR struct bme688_dev_s,
|
|
dev);
|
|
int ret;
|
|
|
|
switch (cmd)
|
|
{
|
|
case SNIOC_RESET:
|
|
{
|
|
/* Perform Soft Reset */
|
|
|
|
uint8_t regval = 0xb6;
|
|
ret = bme688_putreg8(priv, BME688_RESET_REG_ADDR, regval);
|
|
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Wait for the device to reset */
|
|
|
|
up_mdelay(100);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bme688_thread
|
|
****************************************************************************/
|
|
|
|
static int bme688_thread(int argc, char **argv)
|
|
{
|
|
FAR struct bme688_dev_s *priv =
|
|
(FAR struct bme688_dev_s *)((uintptr_t)strtoul(argv[1], NULL, 16));
|
|
struct bme688_data_s data;
|
|
int ret;
|
|
|
|
while (true)
|
|
{
|
|
int sensor;
|
|
|
|
if (!priv->enabled)
|
|
{
|
|
/* Wait for the sensor to be enabled */
|
|
|
|
nxsem_wait(&priv->run);
|
|
}
|
|
|
|
/* No measurements are done unless the sensor is calibrated */
|
|
|
|
if (!priv->dev.calibrated)
|
|
{
|
|
sninfo("The sensor is not calibrated!\n");
|
|
goto thread_sleep;
|
|
}
|
|
|
|
/* Trigger a measurement */
|
|
|
|
ret = bme688_set_mode(priv, BME688_FORCED_MODE);
|
|
|
|
/* Wait for the TPHG cycle to complete */
|
|
|
|
uint16_t cycle_duration = bme688_get_tphg_dur(priv);
|
|
|
|
up_mdelay(cycle_duration);
|
|
|
|
ret = bme688_read_measurements(priv, &data);
|
|
if (ret < 0)
|
|
{
|
|
goto thread_sleep;
|
|
}
|
|
|
|
data.timestamp = sensor_get_timestamp();
|
|
|
|
for (sensor = 0; sensor < BME688_SENSORS_COUNT; sensor++)
|
|
{
|
|
deliver_data[sensor](priv, &data);
|
|
}
|
|
|
|
thread_sleep:
|
|
nxsig_usleep(CONFIG_SENSORS_BME688_POLL_INTERVAL);
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: bme688_register
|
|
*
|
|
* Description:
|
|
* Register the BME688 character device
|
|
*
|
|
* Input Parameters:
|
|
* devno - The user-specified device number, starting from 0
|
|
* i2c - An instance of the I2C interface to use to communicate with
|
|
* BME688
|
|
* config - optional sensor initial configuration
|
|
*
|
|
* Return value:
|
|
* Zero (OK) on success; a negated errno value on failure
|
|
*
|
|
****************************************************************************/
|
|
|
|
int bme688_register(int devno, FAR struct i2c_master_s *i2c,
|
|
FAR struct bme688_config_s *config)
|
|
{
|
|
FAR struct sensor_lowerhalf_s *lower;
|
|
FAR struct bme688_dev_s *priv;
|
|
FAR char *argv[2];
|
|
char arg1[32];
|
|
int ret = OK;
|
|
int i;
|
|
|
|
DEBUGASSERT(i2c != NULL);
|
|
|
|
/* Initialize the BME688 device structure */
|
|
|
|
priv = kmm_zalloc(sizeof(struct bme688_dev_s));
|
|
if (priv == NULL)
|
|
{
|
|
snerr("ERROR: Failed to allocate instance (err = %d)\n", ret);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
priv->i2c = i2c;
|
|
priv->enabled = false;
|
|
nxmutex_init(&priv->dev_lock);
|
|
nxsem_init(&priv->run, 0, 0);
|
|
|
|
/* Check Device ID */
|
|
|
|
ret = bme688_checkid(priv);
|
|
if (ret < 0)
|
|
{
|
|
snerr("ERROR: Wrong device ID!\n");
|
|
goto err_init;
|
|
}
|
|
|
|
/* Get Calibration Data */
|
|
|
|
ret = bme688_get_calib_data(priv);
|
|
if (ret < 0)
|
|
{
|
|
snerr("Failed to read calib data from bme688:%d\n", ret);
|
|
goto err_init;
|
|
}
|
|
|
|
/* Sensor configuration */
|
|
|
|
priv->dev.calibrated = false;
|
|
|
|
if (config)
|
|
{
|
|
ret = bme688_configure(priv, config);
|
|
if (ret < 0)
|
|
{
|
|
snerr("Failed to configure bme688:%d\n", ret);
|
|
goto err_init;
|
|
}
|
|
}
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_TEMP_MEAS
|
|
/* Register the temperature driver */
|
|
|
|
lower = &priv->dev.lower[BME688_TEMP_IDX];
|
|
lower->ops = &g_sensor_ops;
|
|
lower->type = SENSOR_TYPE_AMBIENT_TEMPERATURE;
|
|
|
|
ret = sensor_register(lower, devno);
|
|
if (ret < 0)
|
|
{
|
|
snerr("ERROR: Failed to register temperature driver (err = %d)\n",
|
|
ret);
|
|
goto err_init;
|
|
}
|
|
#endif
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_PRESS_MEAS
|
|
/* Register the barometer driver */
|
|
|
|
lower = &priv->dev.lower[BME688_PRESS_IDX];
|
|
lower->ops = &g_sensor_ops;
|
|
lower->type = SENSOR_TYPE_BAROMETER;
|
|
|
|
ret = sensor_register(lower, devno);
|
|
if (ret < 0)
|
|
{
|
|
snerr("ERROR: Failed to register barometer driver (err = %d)\n",
|
|
ret);
|
|
goto err_init;
|
|
}
|
|
#endif
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_HUM_MEAS
|
|
/* Register the humidity driver */
|
|
|
|
lower = &priv->dev.lower[BME688_HUM_IDX];
|
|
lower->ops = &g_sensor_ops;
|
|
lower->type = SENSOR_TYPE_RELATIVE_HUMIDITY;
|
|
|
|
ret = sensor_register(lower, devno);
|
|
if (ret < 0)
|
|
{
|
|
snerr("ERROR: Failed to register humidity driver (err = %d)\n",
|
|
ret);
|
|
goto err_init;
|
|
}
|
|
#endif
|
|
|
|
#ifndef CONFIG_BME688_DISABLE_GAS_MEAS
|
|
/* Register the gas driver */
|
|
|
|
lower = &priv->dev.lower[BME688_GAS_IDX];
|
|
lower->ops = &g_sensor_ops;
|
|
lower->type = SENSOR_TYPE_GAS;
|
|
|
|
ret = sensor_register(lower, devno);
|
|
if (ret < 0)
|
|
{
|
|
snerr("ERROR: Failed to register gas driver (err = %d)\n",
|
|
ret);
|
|
goto err_init;
|
|
}
|
|
#endif
|
|
|
|
/* Create thread for polling sensor data */
|
|
|
|
snprintf(arg1, 16, "%p", priv);
|
|
argv[0] = arg1;
|
|
argv[1] = NULL;
|
|
ret = kthread_create("bme688_thread", SCHED_PRIORITY_DEFAULT,
|
|
CONFIG_SENSORS_BME688_THREAD_STACKSIZE,
|
|
bme688_thread, argv);
|
|
if (ret < 0)
|
|
{
|
|
snerr("ERROR: Failed to create poll thread (err = %d)\n", ret);
|
|
goto err_register;
|
|
}
|
|
|
|
sninfo("BME688 driver loaded successfully!\n");
|
|
return OK;
|
|
|
|
err_register:
|
|
for (i = 0; i < BME688_SENSORS_COUNT; i++)
|
|
{
|
|
sensor_unregister(&priv->dev.lower[i], devno);
|
|
}
|
|
|
|
err_init:
|
|
nxsem_destroy(&priv->run);
|
|
nxmutex_destroy(&priv->dev_lock);
|
|
kmm_free(priv);
|
|
return ret;
|
|
}
|
|
|
|
#endif /* CONFIG_I2C && CONFIG_SENSORS_BME688 */
|