zephyr/drivers/sensor/bosch/bme280/bme280.c
Loic Domaigne a8eace10b3 drivers: sensor: bme280: fix "config" register intialization issue
A write to the "ctrl_meas" register can cause the sensor to transition
from sleep to normal mode (which is default Kconfig settings). As per
BME280 datasheet, writes to the "config" register in normal mode may be
ignored. This can lead to use BME280_STANDBY_05MS and BME280_FILTER_OFF
instead of the values set by the application. To fix this, write to the
"config" register before "ctrl_meas" in the sensor init function.

Signed-off-by: Loic Domaigne <tech@domaigne.com>
2025-05-29 12:06:39 +02:00

470 lines
13 KiB
C

/* bme280.c - Driver for Bosch BME280 temperature and pressure sensor */
/*
* Copyright (c) 2016, 2017 Intel Corporation
* Copyright (c) 2017 IpTronix S.r.l.
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/init.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/pm/device.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/logging/log.h>
#include "bme280.h"
LOG_MODULE_REGISTER(BME280, CONFIG_SENSOR_LOG_LEVEL);
#if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 0
#warning "BME280 driver enabled without any devices"
#endif
/* Maximum oversampling rate on each channel is 16x.
* Maximum measurement time is given by (Datasheet appendix B 9.1):
* 1.25 + [2.3 * T_over] + [2.3 * P_over + 0.575] + [2.3 * H_over + 0.575]
* = 112.8 ms
*/
#define BME280_MEASUREMENT_TIMEOUT_MS 150
/* Equation 9.1, with the fractional parts rounded down */
#define BME280_EXPECTED_SAMPLE_TIME_MS \
1 + BME280_TEMP_SAMPLE_TIME + BME280_PRESS_SAMPLE_TIME + BME280_HUMIDITY_SAMPLE_TIME
BUILD_ASSERT(BME280_EXPECTED_SAMPLE_TIME_MS < BME280_MEASUREMENT_TIMEOUT_MS,
"Expected duration over timeout duration");
struct bme280_config {
union bme280_bus bus;
const struct bme280_bus_io *bus_io;
};
static inline int bme280_bus_check(const struct device *dev)
{
const struct bme280_config *cfg = dev->config;
return cfg->bus_io->check(&cfg->bus);
}
static inline int bme280_reg_read(const struct device *dev,
uint8_t start, uint8_t *buf, int size)
{
const struct bme280_config *cfg = dev->config;
return cfg->bus_io->read(&cfg->bus, start, buf, size);
}
static inline int bme280_reg_write(const struct device *dev, uint8_t reg,
uint8_t val)
{
const struct bme280_config *cfg = dev->config;
return cfg->bus_io->write(&cfg->bus, reg, val);
}
/*
* Compensation code taken from BME280 datasheet, Section 4.2.3
* "Compensation formula".
*/
static int32_t bme280_compensate_temp(struct bme280_data *data, int32_t adc_temp)
{
int32_t var1, var2;
var1 = (((adc_temp >> 3) - ((int32_t)data->dig_t1 << 1)) *
((int32_t)data->dig_t2)) >> 11;
var2 = (((((adc_temp >> 4) - ((int32_t)data->dig_t1)) *
((adc_temp >> 4) - ((int32_t)data->dig_t1))) >> 12) *
((int32_t)data->dig_t3)) >> 14;
data->t_fine = var1 + var2;
return (data->t_fine * 5 + 128) >> 8;
}
static uint32_t bme280_compensate_press(struct bme280_data *data, int32_t adc_press)
{
int64_t var1, var2, p;
var1 = ((int64_t)data->t_fine) - 128000;
var2 = var1 * var1 * (int64_t)data->dig_p6;
var2 = var2 + ((var1 * (int64_t)data->dig_p5) << 17);
var2 = var2 + (((int64_t)data->dig_p4) << 35);
var1 = ((var1 * var1 * (int64_t)data->dig_p3) >> 8) +
((var1 * (int64_t)data->dig_p2) << 12);
var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)data->dig_p1) >> 33;
/* Avoid exception caused by division by zero. */
if (var1 == 0) {
return 0;
}
p = 1048576 - adc_press;
p = (((p << 31) - var2) * 3125) / var1;
var1 = (((int64_t)data->dig_p9) * (p >> 13) * (p >> 13)) >> 25;
var2 = (((int64_t)data->dig_p8) * p) >> 19;
p = ((p + var1 + var2) >> 8) + (((int64_t)data->dig_p7) << 4);
return (uint32_t)p;
}
static uint32_t bme280_compensate_humidity(struct bme280_data *data,
int32_t adc_humidity)
{
int32_t h;
h = (data->t_fine - ((int32_t)76800));
h = ((((adc_humidity << 14) - (((int32_t)data->dig_h4) << 20) -
(((int32_t)data->dig_h5) * h)) + ((int32_t)16384)) >> 15) *
(((((((h * ((int32_t)data->dig_h6)) >> 10) * (((h *
((int32_t)data->dig_h3)) >> 11) + ((int32_t)32768))) >> 10) +
((int32_t)2097152)) * ((int32_t)data->dig_h2) + 8192) >> 14);
h = (h - (((((h >> 15) * (h >> 15)) >> 7) *
((int32_t)data->dig_h1)) >> 4));
h = (h > 419430400 ? 419430400 : h);
h = (h < 0 ? 0 : h);
return (uint32_t)(h >> 12);
}
static int bme280_wait_until_ready(const struct device *dev, k_timeout_t timeout)
{
k_timepoint_t end = sys_timepoint_calc(timeout);
uint8_t status;
int ret;
/* Wait for relevant flags to clear */
while (1) {
ret = bme280_reg_read(dev, BME280_REG_STATUS, &status, 1);
if (ret < 0) {
return ret;
}
if (!(status & (BME280_STATUS_MEASURING | BME280_STATUS_IM_UPDATE))) {
break;
}
/* Check if waiting has timed out */
if (sys_timepoint_expired(end)) {
return -EAGAIN;
}
k_sleep(K_MSEC(3));
}
return 0;
}
int bme280_sample_fetch_helper(const struct device *dev,
enum sensor_channel chan, struct bme280_reading *reading)
{
struct bme280_data *dev_data = dev->data;
uint8_t buf[8];
int32_t adc_press, adc_temp, adc_humidity;
uint32_t poll_timeout;
int size = 6;
int ret;
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL);
#ifdef CONFIG_BME280_MODE_FORCED
ret = bme280_reg_write(dev, BME280_REG_CTRL_MEAS, BME280_CTRL_MEAS_VAL);
if (ret < 0) {
return ret;
}
/* Wait until the expected measurement time elapses */
k_sleep(K_MSEC(BME280_EXPECTED_SAMPLE_TIME_MS));
poll_timeout = BME280_MEASUREMENT_TIMEOUT_MS - BME280_EXPECTED_SAMPLE_TIME_MS;
#else
poll_timeout = BME280_MEASUREMENT_TIMEOUT_MS;
#endif
ret = bme280_wait_until_ready(dev, K_MSEC(poll_timeout));
if (ret < 0) {
return ret;
}
if (dev_data->chip_id == BME280_CHIP_ID) {
size = 8;
}
ret = bme280_reg_read(dev, BME280_REG_PRESS_MSB, buf, size);
if (ret < 0) {
return ret;
}
adc_press = (buf[0] << 12) | (buf[1] << 4) | (buf[2] >> 4);
adc_temp = (buf[3] << 12) | (buf[4] << 4) | (buf[5] >> 4);
reading->comp_temp = bme280_compensate_temp(dev_data, adc_temp);
reading->comp_press = bme280_compensate_press(dev_data, adc_press);
if (dev_data->chip_id == BME280_CHIP_ID) {
adc_humidity = (buf[6] << 8) | buf[7];
reading->comp_humidity = bme280_compensate_humidity(dev_data, adc_humidity);
}
return 0;
}
int bme280_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
struct bme280_data *data = dev->data;
return bme280_sample_fetch_helper(dev, chan, &data->reading);
}
static int bme280_channel_get(const struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
{
struct bme280_data *data = dev->data;
switch (chan) {
case SENSOR_CHAN_AMBIENT_TEMP:
/*
* comp_temp has a resolution of 0.01 degC. So
* 5123 equals 51.23 degC.
*/
val->val1 = data->reading.comp_temp / 100;
val->val2 = data->reading.comp_temp % 100 * 10000;
break;
case SENSOR_CHAN_PRESS:
/*
* comp_press has 24 integer bits and 8
* fractional. Output value of 24674867 represents
* 24674867/256 = 96386.2 Pa = 963.862 hPa
*/
val->val1 = (data->reading.comp_press >> 8) / 1000U;
val->val2 = (data->reading.comp_press >> 8) % 1000 * 1000U +
(((data->reading.comp_press & 0xff) * 1000U) >> 8);
break;
case SENSOR_CHAN_HUMIDITY:
/* The BMP280 doesn't have a humidity sensor */
if (data->chip_id != BME280_CHIP_ID) {
return -ENOTSUP;
}
/*
* comp_humidity has 22 integer bits and 10
* fractional. Output value of 47445 represents
* 47445/1024 = 46.333 %RH
*/
val->val1 = (data->reading.comp_humidity >> 10);
val->val2 = (((data->reading.comp_humidity & 0x3ff) * 1000U * 1000U) >> 10);
break;
default:
return -ENOTSUP;
}
return 0;
}
static DEVICE_API(sensor, bme280_api_funcs) = {
.sample_fetch = bme280_sample_fetch,
.channel_get = bme280_channel_get,
#ifdef CONFIG_SENSOR_ASYNC_API
.submit = bme280_submit,
.get_decoder = bme280_get_decoder,
#endif
};
static int bme280_read_compensation(const struct device *dev)
{
struct bme280_data *data = dev->data;
uint16_t buf[12];
uint8_t hbuf[7];
int err = 0;
err = bme280_reg_read(dev, BME280_REG_COMP_START,
(uint8_t *)buf, sizeof(buf));
if (err < 0) {
LOG_DBG("COMP_START read failed: %d", err);
return err;
}
data->dig_t1 = sys_le16_to_cpu(buf[0]);
data->dig_t2 = sys_le16_to_cpu(buf[1]);
data->dig_t3 = sys_le16_to_cpu(buf[2]);
data->dig_p1 = sys_le16_to_cpu(buf[3]);
data->dig_p2 = sys_le16_to_cpu(buf[4]);
data->dig_p3 = sys_le16_to_cpu(buf[5]);
data->dig_p4 = sys_le16_to_cpu(buf[6]);
data->dig_p5 = sys_le16_to_cpu(buf[7]);
data->dig_p6 = sys_le16_to_cpu(buf[8]);
data->dig_p7 = sys_le16_to_cpu(buf[9]);
data->dig_p8 = sys_le16_to_cpu(buf[10]);
data->dig_p9 = sys_le16_to_cpu(buf[11]);
if (data->chip_id == BME280_CHIP_ID) {
err = bme280_reg_read(dev, BME280_REG_HUM_COMP_PART1,
&data->dig_h1, 1);
if (err < 0) {
LOG_DBG("HUM_COMP_PART1 read failed: %d", err);
return err;
}
err = bme280_reg_read(dev, BME280_REG_HUM_COMP_PART2, hbuf, 7);
if (err < 0) {
LOG_DBG("HUM_COMP_PART2 read failed: %d", err);
return err;
}
data->dig_h2 = (hbuf[1] << 8) | hbuf[0];
data->dig_h3 = hbuf[2];
data->dig_h4 = (hbuf[3] << 4) | (hbuf[4] & 0x0F);
data->dig_h5 = ((hbuf[4] >> 4) & 0x0F) | (hbuf[5] << 4);
data->dig_h6 = hbuf[6];
}
return 0;
}
static int bme280_chip_init(const struct device *dev)
{
struct bme280_data *data = dev->data;
int err;
err = bme280_bus_check(dev);
if (err < 0) {
LOG_DBG("bus check failed: %d", err);
return err;
}
err = bme280_reg_read(dev, BME280_REG_ID, &data->chip_id, 1);
if (err < 0) {
LOG_DBG("ID read failed: %d", err);
return err;
}
if (data->chip_id == BME280_CHIP_ID) {
LOG_DBG("ID OK");
} else if (data->chip_id == BMP280_CHIP_ID_MP ||
data->chip_id == BMP280_CHIP_ID_SAMPLE_1) {
LOG_DBG("ID OK (BMP280)");
} else {
LOG_DBG("bad chip id 0x%x", data->chip_id);
return -ENOTSUP;
}
/* reset the sensor. This will put the sensor is sleep mode */
err = bme280_reg_write(dev, BME280_REG_RESET, BME280_CMD_SOFT_RESET);
if (err < 0) {
LOG_DBG("Soft-reset failed: %d", err);
}
/* The only mention of a soft reset duration is 2ms from the self test timeouts */
err = bme280_wait_until_ready(dev, K_MSEC(100));
if (err < 0) {
return err;
}
err = bme280_read_compensation(dev);
if (err < 0) {
return err;
}
if (data->chip_id == BME280_CHIP_ID) {
err = bme280_reg_write(dev, BME280_REG_CTRL_HUM,
BME280_HUMIDITY_OVER);
if (err < 0) {
LOG_DBG("CTRL_HUM write failed: %d", err);
return err;
}
}
/* Writes to "config" register may be ignored in normal
* mode, but never in sleep mode [datasheet 5.4.6].
*
* So perform "config" write before "ctrl_meas", as "ctrl_meas"
* could cause the sensor to transition from sleep to normal mode.
*/
err = bme280_reg_write(dev, BME280_REG_CONFIG, BME280_CONFIG_VAL);
if (err < 0) {
LOG_DBG("CONFIG write failed: %d", err);
return err;
}
err = bme280_reg_write(dev, BME280_REG_CTRL_MEAS, BME280_CTRL_MEAS_VAL);
if (err < 0) {
LOG_DBG("CTRL_MEAS write failed: %d", err);
return err;
}
/* Wait for the sensor to be ready */
k_sleep(K_MSEC(1));
LOG_DBG("\"%s\" OK", dev->name);
return 0;
}
#ifdef CONFIG_PM_DEVICE
static int bme280_pm_action(const struct device *dev,
enum pm_device_action action)
{
int ret = 0;
switch (action) {
#ifdef CONFIG_BME280_MODE_NORMAL
case PM_DEVICE_ACTION_RESUME:
/* Re-enable periodic measurement */
ret = bme280_reg_write(dev, BME280_REG_CTRL_MEAS, BME280_CTRL_MEAS_VAL);
break;
case PM_DEVICE_ACTION_SUSPEND:
/* Put the chip into sleep mode */
ret = bme280_reg_write(dev, BME280_REG_CTRL_MEAS, BME280_CTRL_MEAS_OFF_VAL);
break;
#else
case PM_DEVICE_ACTION_RESUME:
case PM_DEVICE_ACTION_SUSPEND:
/* Nothing to do in forced mode */
break;
#endif
default:
return -ENOTSUP;
}
return ret;
}
#endif /* CONFIG_PM_DEVICE */
/* Initializes a struct bme280_config for an instance on a SPI bus. */
#define BME280_CONFIG_SPI(inst) \
{ \
.bus.spi = SPI_DT_SPEC_INST_GET( \
inst, BME280_SPI_OPERATION, 0), \
.bus_io = &bme280_bus_io_spi, \
}
/* Initializes a struct bme280_config for an instance on an I2C bus. */
#define BME280_CONFIG_I2C(inst) \
{ \
.bus.i2c = I2C_DT_SPEC_INST_GET(inst), \
.bus_io = &bme280_bus_io_i2c, \
}
/*
* Main instantiation macro, which selects the correct bus-specific
* instantiation macros for the instance.
*/
#define BME280_DEFINE(inst) \
static struct bme280_data bme280_data_##inst; \
static const struct bme280_config bme280_config_##inst = \
COND_CODE_1(DT_INST_ON_BUS(inst, spi), \
(BME280_CONFIG_SPI(inst)), \
(BME280_CONFIG_I2C(inst))); \
\
PM_DEVICE_DT_INST_DEFINE(inst, bme280_pm_action); \
\
SENSOR_DEVICE_DT_INST_DEFINE(inst, \
bme280_chip_init, \
PM_DEVICE_DT_INST_GET(inst), \
&bme280_data_##inst, \
&bme280_config_##inst, \
POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, \
&bme280_api_funcs);
/* Create the struct device for every status "okay" node in the devicetree. */
DT_INST_FOREACH_STATUS_OKAY(BME280_DEFINE)