diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt index e38ed869277..a061157b539 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -18,6 +18,7 @@ add_subdirectory_ifdef(CONFIG_BMM150 bmm150) add_subdirectory_ifdef(CONFIG_BQ274XX bq274xx) add_subdirectory_ifdef(CONFIG_CCS811 ccs811) add_subdirectory_ifdef(CONFIG_DHT dht) +add_subdirectory_ifdef(CONFIG_DPS310 dps310) add_subdirectory_ifdef(CONFIG_ENS210 ens210) add_subdirectory_ifdef(CONFIG_FXAS21002 fxas21002) add_subdirectory_ifdef(CONFIG_FXOS8700 fxos8700) diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index 14d6020d4aa..fdc4f51430a 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -65,6 +65,8 @@ source "drivers/sensor/ccs811/Kconfig" source "drivers/sensor/dht/Kconfig" +source "drivers/sensor/dps310/Kconfig" + source "drivers/sensor/ens210/Kconfig" source "drivers/sensor/fxas21002/Kconfig" diff --git a/drivers/sensor/dps310/CMakeLists.txt b/drivers/sensor/dps310/CMakeLists.txt new file mode 100644 index 00000000000..62e354118a1 --- /dev/null +++ b/drivers/sensor/dps310/CMakeLists.txt @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources_ifdef(CONFIG_DPS310 dps310.c) diff --git a/drivers/sensor/dps310/Kconfig b/drivers/sensor/dps310/Kconfig new file mode 100644 index 00000000000..d402617ce16 --- /dev/null +++ b/drivers/sensor/dps310/Kconfig @@ -0,0 +1,66 @@ +# DPS310 temperature and pressure sensor configuration options + +# Copyright (c) 2019 Infineon Technologies AG +# SPDX-License-Identifier: Apache-2.0 + +menuconfig DPS310 + bool "DPS310 sensor" + depends on I2C + help + Enable driver for DPS310 I2C-based temperature and pressure sensor. + +if DPS310 + +menu "Attributes" + +choice + prompt "DPS310 temperature oversampling" + default DPS310_TEMP_OSR_8X + help + Select temperature oversampling for the DPS310 sensor. Higher values + lead to more accurate readings, but higher power consumption. +config DPS310_TEMP_OSR_1X + bool "x1" +config DPS310_TEMP_OSR_2X + bool "x2" +config DPS310_TEMP_OSR_4X + bool "x4" +config DPS310_TEMP_OSR_8X + bool "x8" +config DPS310_TEMP_OSR_16X + bool "x16" +config DPS310_TEMP_OSR_32X + bool "x32" +config DPS310_TEMP_OSR_64X + bool "x64" +config DPS310_TEMP_OSR_128X + bool "x128" +endchoice + +choice + prompt "DPS310 pressure oversampling" + default DPS310_PRESS_OSR_8X + help + Select pressure oversampling for the DPS310 sensor. Higher values + lead to more accurate readings, but higher power consumption. +config DPS310_PRESS_OSR_1X + bool "x1" +config DPS310_PRESS_OSR_2X + bool "x2" +config DPS310_PRESS_OSR_4X + bool "x4" +config DPS310_PRESS_OSR_8X + bool "x8" +config DPS310_PRESS_OSR_16X + bool "x16" +config DPS310_PRESS_OSR_32X + bool "x32" +config DPS310_PRESS_OSR_64X + bool "x64" +config DPS310_PRESS_OSR_128X + bool "x128" +endchoice + +endmenu + +endif # DPS310 diff --git a/drivers/sensor/dps310/dps310.c b/drivers/sensor/dps310/dps310.c new file mode 100644 index 00000000000..65c6d3a344a --- /dev/null +++ b/drivers/sensor/dps310/dps310.c @@ -0,0 +1,748 @@ +/* Driver for Infineon DPS310 temperature and pressure sensor */ + +/* + * Copyright (c) 2019 Infineon Technologies AG + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT infineon_dps310 + +#include +#include +#include +#include +#include + +#include +#include + +LOG_MODULE_REGISTER(DPS310, CONFIG_SENSOR_LOG_LEVEL); + +/* Register addresses as in the datasheet */ +#define IFX_DPS310_REG_ADDR_PSR_B2 (0x00) +#define IFX_DPS310_REG_ADDR_TMP_B2 (0x03) +#define IFX_DPS310_REG_ADDR_PRS_CFG (0x06) +#define IFX_DPS310_REG_ADDR_TMP_CFG (0x07) +#define IFX_DPS310_REG_ADDR_MEAS_CFG (0x08) +#define IFX_DPS310_REG_ADDR_CFG_REG (0x09) +#define IFX_DPS310_REG_ADDR_INT_STS (0x0A) +#define IFX_DPS310_REG_ADDR_FIFO_STS (0x0B) +#define IFX_DPS310_REG_ADDR_RESET (0x0C) +#define IFX_DPS310_REG_ADDR_PRODUCT_ID (0x0D) +#define IFX_DPS310_REG_ADDR_COEF_0 (0x10) +#define IFX_DPS310_REG_ADDR_COEF_SRCE (0x28) + +enum { + IFX_DPS310_MODE_IDLE = 0x00, + IFX_DPS310_MODE_COMMAND_PRESSURE = 0x01, + IFX_DPS310_MODE_COMMAND_TEMPERATURE = 0x02, + IFX_DPS310_MODE_BACKGROUND_PRESSURE = 0x05, + IFX_DPS310_MODE_BACKGROUND_TEMPERATURE = 0x06, + IFX_DPS310_MODE_BACKGROUND_ALL = 0x07 +}; + +/* Bits in registers as in the datasheet */ +#define IFX_DPS310_REG_ADDR_MEAS_CFG_PRS_RDY (0x10) +#define IFX_DPS310_REG_ADDR_MEAS_CFG_TMP_RDY (0x20) +/* + * If sensor is ready after self initialization + * bits 6 and 7 in register MEAS_CFG (0x08) should be "1" + */ +#define IFX_DPS310_REG_ADDR_MEAS_CFG_SELF_INIT_OK (0xC0) +#define IFX_DPS310_COEF_SRCE_MASK (0x80) +#define IFX_DPS310_PRODUCT_ID (0x10) + +/* Polling time in ms*/ +#define POLL_TIME_MS (K_MSEC(10)) +/* Number of times to poll before timeout */ +#define POLL_TRIES 3 + +/* + * Measurement times in ms for different oversampling rates + * From Table 16 in the datasheet, rounded up for safety margin + */ +enum { + IFX_DPS310_MEAS_TIME_OSR_1 = 4, + IFX_DPS310_MEAS_TIME_OSR_2 = 6, + IFX_DPS310_MEAS_TIME_OSR_4 = 9, + IFX_DPS310_MEAS_TIME_OSR_8 = 15, + IFX_DPS310_MEAS_TIME_OSR_16 = 28, + IFX_DPS310_MEAS_TIME_OSR_32 = 54, + IFX_DPS310_MEAS_TIME_OSR_64 = 105, + IFX_DPS310_MEAS_TIME_OSR_128 = 207 +}; + +/* Compensation scale factors from Table 9 in the datasheet */ +enum { + IFX_DPS310_SF_OSR_1 = 524288, + IFX_DPS310_SF_OSR_2 = 1572864, + IFX_DPS310_SF_OSR_4 = 3670016, + IFX_DPS310_SF_OSR_8 = 7864320, + IFX_DPS310_SF_OSR_16 = 253952, + IFX_DPS310_SF_OSR_32 = 516096, + IFX_DPS310_SF_OSR_64 = 1040384, + IFX_DPS310_SF_OSR_128 = 2088960 +}; + +/* + * Oversampling and measurement rates configuration for pressure and temperature + * sensor According to Table 16 of the datasheet + */ +enum { + IFX_DPS310_RATE_1 = 0x00, + IFX_DPS310_RATE_2 = 0x01, + IFX_DPS310_RATE_4 = 0x02, + IFX_DPS310_RATE_8 = 0x03, + IFX_DPS310_RATE_16 = 0x04, + IFX_DPS310_RATE_32 = 0x05, + IFX_DPS310_RATE_64 = 0x06, + IFX_DPS310_RATE_128 = 0x07 +}; + +/* Helper macro to set temperature and pressure config register */ +#define CFG_REG(MEAS_RATE, OSR_RATE) \ + ((((MEAS_RATE)&0x07) << 4) | ((OSR_RATE)&0x07)) + +/* Setup constants depending on temperature oversampling factor */ +#if defined CONFIG_DPS310_TEMP_OSR_1X +#define IFX_DPS310_SF_TMP IFX_DPS310_SF_OSR_1 +#define IFX_DPS310_TMP_MEAS_TIME IFX_DPS310_MEAS_TIME_OSR_1 +#define IFX_DPS310_TMP_CFG CFG_REG(IFX_DPS310_RATE_1, IFX_DPS310_RATE_1) +#define IFX_DPS310_T_SHIFT 0 +#elif defined CONFIG_DPS310_TEMP_OSR_2X +#define IFX_DPS310_SF_TMP IFX_DPS310_SF_OSR_2 +#define IFX_DPS310_TMP_MEAS_TIME IFX_DPS310_MEAS_TIME_OSR_2 +#define IFX_DPS310_TMP_CFG CFG_REG(IFX_DPS310_RATE_1, IFX_DPS310_RATE_2) +#define IFX_DPS310_T_SHIFT 0 +#elif defined CONFIG_DPS310_TEMP_OSR_4X +#define IFX_DPS310_SF_TMP IFX_DPS310_SF_OSR_4 +#define IFX_DPS310_TMP_MEAS_TIME IFX_DPS310_MEAS_TIME_OSR_4 +#define IFX_DPS310_TMP_CFG CFG_REG(IFX_DPS310_RATE_1, IFX_DPS310_RATE_4) +#define IFX_DPS310_T_SHIFT 0 +#elif defined CONFIG_DPS310_TEMP_OSR_8X +#define IFX_DPS310_SF_TMP IFX_DPS310_SF_OSR_8 +#define IFX_DPS310_TMP_MEAS_TIME IFX_DPS310_MEAS_TIME_OSR_8 +#define IFX_DPS310_TMP_CFG CFG_REG(IFX_DPS310_RATE_1, IFX_DPS310_RATE_8) +#define IFX_DPS310_T_SHIFT 0 +#elif defined CONFIG_DPS310_TEMP_OSR_16X +#define IFX_DPS310_SF_TMP IFX_DPS310_SF_OSR_16 +#define IFX_DPS310_TMP_MEAS_TIME IFX_DPS310_MEAS_TIME_OSR_16 +#define IFX_DPS310_TMP_CFG CFG_REG(IFX_DPS310_RATE_1, IFX_DPS310_RATE_16) +#define IFX_DPS310_T_SHIFT 1 +#elif defined CONFIG_DPS310_TEMP_OSR_32X +#define IFX_DPS310_SF_TMP IFX_DPS310_SF_OSR_32 +#define IFX_DPS310_TMP_MEAS_TIME IFX_DPS310_MEAS_TIME_OSR_32 +#define IFX_DPS310_TMP_CFG CFG_REG(IFX_DPS310_RATE_1, IFX_DPS310_RATE_32) +#define IFX_DPS310_T_SHIFT 1 +#elif defined CONFIG_DPS310_TEMP_OSR_64X +#define IFX_DPS310_SF_TMP IFX_DPS310_SF_OSR_64 +#define IFX_DPS310_TMP_MEAS_TIME IFX_DPS310_MEAS_TIME_OSR_64 +#define IFX_DPS310_TMP_CFG CFG_REG(IFX_DPS310_RATE_1, IFX_DPS310_RATE_64) +#define IFX_DPS310_T_SHIFT 1 +#elif defined CONFIG_DPS310_TEMP_OSR_128X +#define IFX_DPS310_SF_TMP IFX_DPS310_SF_OSR_128 +#define IFX_DPS310_TMP_MEAS_TIME IFX_DPS310_MEAS_TIME_OSR_128 +#define IFX_DPS310_TMP_CFG CFG_REG(IFX_DPS310_RATE_1, IFX_DPS310_RATE_128) +#define IFX_DPS310_T_SHIFT 1 +#endif + +/* Setup constants depending on pressure oversampling factor */ +#if defined CONFIG_DPS310_PRESS_OSR_1X +#define IFX_DPS310_SF_PSR IFX_DPS310_SF_OSR_1 +#define IFX_DPS310_PSR_MEAS_TIME IFX_DPS310_MEAS_TIME_OSR_1 +#define IFX_DPS310_PSR_CFG CFG_REG(IFX_DPS310_RATE_1, IFX_DPS310_RATE_1) +#define IFX_DPS310_P_SHIFT 0 +#elif defined CONFIG_DPS310_PRESS_OSR_2X +#define IFX_DPS310_SF_PSR IFX_DPS310_SF_OSR_2 +#define IFX_DPS310_PSR_MEAS_TIME IFX_DPS310_MEAS_TIME_OSR_2 +#define IFX_DPS310_PSR_CFG CFG_REG(IFX_DPS310_RATE_1, IFX_DPS310_RATE_2) +#define IFX_DPS310_P_SHIFT 0 +#elif defined CONFIG_DPS310_PRESS_OSR_4X +#define IFX_DPS310_SF_PSR IFX_DPS310_SF_OSR_4 +#define IFX_DPS310_PSR_MEAS_TIME IFX_DPS310_MEAS_TIME_OSR_4 +#define IFX_DPS310_PSR_CFG CFG_REG(IFX_DPS310_RATE_1, IFX_DPS310_RATE_4) +#define IFX_DPS310_P_SHIFT 0 +#elif defined CONFIG_DPS310_PRESS_OSR_8X +#define IFX_DPS310_SF_PSR IFX_DPS310_SF_OSR_8 +#define IFX_DPS310_PSR_MEAS_TIME IFX_DPS310_MEAS_TIME_OSR_8 +#define IFX_DPS310_PSR_CFG CFG_REG(IFX_DPS310_RATE_1, IFX_DPS310_RATE_8) +#define IFX_DPS310_P_SHIFT 0 +#elif defined CONFIG_DPS310_PRESS_OSR_16X +#define IFX_DPS310_SF_PSR IFX_DPS310_SF_OSR_16 +#define IFX_DPS310_PSR_MEAS_TIME IFX_DPS310_MEAS_TIME_OSR_16 +#define IFX_DPS310_PSR_CFG CFG_REG(IFX_DPS310_RATE_1, IFX_DPS310_RATE_16) +#define IFX_DPS310_P_SHIFT 1 +#elif defined CONFIG_DPS310_PRESS_OSR_32X +#define IFX_DPS310_SF_PSR IFX_DPS310_SF_OSR_32 +#define IFX_DPS310_PSR_MEAS_TIME IFX_DPS310_MEAS_TIME_OSR_32 +#define IFX_DPS310_PSR_CFG CFG_REG(IFX_DPS310_RATE_1, IFX_DPS310_RATE_32) +#define IFX_DPS310_P_SHIFT 1 +#elif defined CONFIG_DPS310_PRESS_OSR_64X +#define IFX_DPS310_SF_PSR IFX_DPS310_SF_OSR_64 +#define IFX_DPS310_PSR_MEAS_TIME IFX_DPS310_MEAS_TIME_OSR_64 +#define IFX_DPS310_PSR_CFG CFG_REG(IFX_DPS310_RATE_1, IFX_DPS310_RATE_64) +#define IFX_DPS310_P_SHIFT 1 +#elif defined CONFIG_DPS310_PRESS_OSR_128X +#define IFX_DPS310_SF_PSR IFX_DPS310_SF_OSR_128 +#define IFX_DPS310_PSR_MEAS_TIME IFX_DPS310_MEAS_TIME_OSR_128 +#define IFX_DPS310_PSR_CFG CFG_REG(IFX_DPS310_RATE_1, IFX_DPS310_RATE_128) +#define IFX_DPS310_P_SHIFT 1 +#endif + +#define DPS310_CFG_REG \ + (((IFX_DPS310_T_SHIFT & 0x01) << 3) | \ + ((IFX_DPS310_P_SHIFT & 0x01) << 2)) + +#define HW_BUG_FIX_SEQUENCE_LEN 5 + +#define POW_2_23_MINUS_1 BIT_MASK(23) +#define POW_2_24 BIT(24) +#define POW_2_15_MINUS_1 BIT_MASK(15) +#define POW_2_16 BIT(16) +#define POW_2_11_MINUS_1 BIT_MASK(11) +#define POW_2_12 BIT(12) +#define POW_2_20 BIT(20) +#define POW_2_19_MINUS_1 BIT_MASK(19) + +/* Needed because the values are referenced by pointer. */ +static const u8_t REG_ADDR_MEAS_CFG = IFX_DPS310_REG_ADDR_MEAS_CFG; +static const u8_t REG_ADDR_CALIB_COEFF_0 = IFX_DPS310_REG_ADDR_COEF_0; +static const u8_t REG_ADDR_TMP_B2 = IFX_DPS310_REG_ADDR_TMP_B2; +static const u8_t REG_ADDR_PSR_B2 = IFX_DPS310_REG_ADDR_PSR_B2; +static const u8_t REG_ADDR_COEF_SRCE = IFX_DPS310_REG_ADDR_COEF_SRCE; + +/* calibration coefficients */ +struct dps310_cal_coeff { + /* Pressure Sensor Calibration Coefficients */ + s32_t c00; /* 20bit */ + s32_t c10; /* 20bit */ + s16_t c01; /* 16bit */ + s16_t c11; /* 16bit */ + s16_t c20; /* 16bit */ + s16_t c21; /* 16bit */ + s16_t c30; /* 16bit */ + /* Temperature Sensor Calibration Coefficients */ + s16_t c0; /* 12bit */ + s16_t c1; /* 12bit */ +}; + +struct dps310_data { + struct device *i2c_master; + struct dps310_cal_coeff comp; + /* Temperature Values */ + s32_t tmp_val1; + s32_t tmp_val2; + /* Last raw temperature value for temperature compensation */ + s32_t raw_tmp; + /* Pressure Values */ + s32_t psr_val1; + s32_t psr_val2; +}; + +struct dps310_cfg { + char *i2c_bus_name; + u16_t i2c_addr; +}; + +/* + * Convert the bytes from calibration memory to calibration coefficients + * structure + */ +static void dps310_calib_coeff_creation(const u8_t raw_coef[18], + struct dps310_cal_coeff *comp) +{ + /* Temperature sensor compensation values */ + comp->c0 = (((u16_t)raw_coef[0]) << 4) + (raw_coef[1] >> 4); + /* coefficient is 2nd compliment */ + if (comp->c0 > POW_2_11_MINUS_1) { + comp->c0 = comp->c0 - POW_2_12; + } + + comp->c1 = (((u16_t)(raw_coef[1] & 0x0F)) << 8) + raw_coef[2]; + /* coefficient is 2nd compliment */ + if (comp->c1 > POW_2_11_MINUS_1) { + comp->c1 = comp->c1 - POW_2_12; + } + + /* Pressure sensor compensation values */ + comp->c00 = (((u32_t)raw_coef[3]) << 12) + (((u16_t)raw_coef[4]) << 4) + + (raw_coef[5] >> 4); + /* coefficient is 2nd compliment */ + if (comp->c00 > POW_2_19_MINUS_1) { + comp->c00 = comp->c00 - POW_2_20; + } + + comp->c10 = (((u32_t)(raw_coef[5] & 0x0F)) << 16) + + (((u16_t)raw_coef[6]) << 8) + raw_coef[7]; + /* coefficient is 2nd compliment */ + if (comp->c10 > POW_2_19_MINUS_1) { + comp->c10 = comp->c10 - POW_2_20; + } + + comp->c01 = (s16_t) sys_get_be16(&raw_coef[8]); + comp->c11 = (s16_t) sys_get_be16(&raw_coef[10]); + comp->c20 = (s16_t) sys_get_be16(&raw_coef[12]); + comp->c21 = (s16_t) sys_get_be16(&raw_coef[14]); + comp->c30 = (s16_t) sys_get_be16(&raw_coef[16]); +} + +/* Poll one or multiple bits given by ready_mask in reg_addr */ +static bool poll_rdy(struct dps310_data *data, const struct dps310_cfg *config, + u8_t reg_addr, u8_t ready_mask) +{ + /* Try only a finite number of times */ + for (int i = 0; i < POLL_TRIES; i++) { + u8_t reg = 0; + int res = i2c_reg_read_byte(data->i2c_master, config->i2c_addr, + reg_addr, ®); + if (res < 0) { + LOG_WRN("I2C error: %d", res); + return false; + } + + if ((reg & ready_mask) == ready_mask) { + /* measurement is ready */ + return true; + } + + /* give the sensor more time */ + k_sleep(POLL_TIME_MS); + } + + return false; +} + +/* Trigger a temperature measurement and wait until the result is stored */ +static bool dps310_trigger_temperature(struct dps310_data *data, + const struct dps310_cfg *config) +{ + /* command to start temperature measurement */ + static const u8_t tmp_meas_cmd[] = { + IFX_DPS310_REG_ADDR_MEAS_CFG, + IFX_DPS310_MODE_COMMAND_TEMPERATURE + }; + + /* trigger temperature measurement */ + int res = i2c_write(data->i2c_master, tmp_meas_cmd, + sizeof(tmp_meas_cmd), config->i2c_addr); + if (res < 0) { + LOG_WRN("I2C error: %d", res); + return false; + } + + /* give the sensor time to store measured values internally */ + k_msleep(IFX_DPS310_TMP_MEAS_TIME); + + if (!poll_rdy(data, config, IFX_DPS310_REG_ADDR_MEAS_CFG, + IFX_DPS310_REG_ADDR_MEAS_CFG_TMP_RDY)) { + LOG_DBG("Poll timeout for temperature"); + return false; + } + + return true; +} + +/* Trigger a pressure measurement and wait until the result is stored */ +static bool dps310_trigger_pressure(struct dps310_data *data, + const struct dps310_cfg *config) +{ + /* command to start pressure measurement */ + static const u8_t psr_meas_cmd[] = { + IFX_DPS310_REG_ADDR_MEAS_CFG, + IFX_DPS310_MODE_COMMAND_PRESSURE + }; + + /* trigger pressure measurement */ + int res = i2c_write(data->i2c_master, psr_meas_cmd, + sizeof(psr_meas_cmd), config->i2c_addr); + if (res < 0) { + LOG_WRN("I2C error: %d", res); + return false; + } + + /* give the sensor time to store measured values internally */ + k_msleep(IFX_DPS310_PSR_MEAS_TIME); + + if (!poll_rdy(data, config, IFX_DPS310_REG_ADDR_MEAS_CFG, + IFX_DPS310_REG_ADDR_MEAS_CFG_PRS_RDY)) { + LOG_DBG("Poll timeout for pressure"); + return false; + } + + return true; +} + +/* + * function to fix a hardware problem on some devices + * you have this bug if you measure around 60°C when temperature is around 20°C + * call dps310_hw_bug_fix() directly in the init() function to fix this issue + */ +static void dps310_hw_bug_fix(struct dps310_data *data, + const struct dps310_cfg *config) +{ + /* setup the necessary 5 sequences to fix the hw bug */ + static const u8_t hw_bug_fix_sequence[HW_BUG_FIX_SEQUENCE_LEN][2] = { + /* + * First write valid signature on 0x0e and 0x0f + * to unlock address 0x62 + */ + { 0x0E, 0xA5 }, + { 0x0F, 0x96 }, + /* Then update high gain value for Temperature */ + { 0x62, 0x02 }, + /* Finally lock back the location 0x62 */ + { 0x0E, 0x00 }, + { 0x0F, 0x00 } + }; + + /* execute sequence for hw bug fix */ + for (int i = 0; i < HW_BUG_FIX_SEQUENCE_LEN; i++) { + int res = i2c_write(data->i2c_master, hw_bug_fix_sequence[i], 2, + config->i2c_addr); + if (res < 0) { + LOG_WRN("I2C error: %d", res); + return; + } + } +} + +/* + * Scale and compensate the raw temperature measurement value to micro °C + * The formula is based on the Chapter 4.9.2 in the datasheet and was + * modified to need only integer arithmetic. + */ +static void dps310_scale_temperature(s32_t tmp_raw, struct dps310_data *data) +{ + const struct dps310_cal_coeff *comp = &data->comp; + + /* first term, rescaled to micro °C */ + s32_t tmp_p0 = (1000000 / 2) * comp->c0; + + /* second term, rescaled to mirco °C */ + s32_t tmp_p1 = + (((s64_t)1000000) * comp->c1 * tmp_raw) / IFX_DPS310_SF_TMP; + + /* calculate the temperature corresponding to the datasheet */ + s32_t tmp_final = tmp_p0 + tmp_p1; /* value is in micro °C */ + + /* store calculated value */ + data->tmp_val1 = tmp_final / 1000000; + data->tmp_val2 = tmp_final % 1000000; +} + +/* + * Scale and temperature compensate the raw pressure measurement value to + * Kilopascal. The formula is based on the Chapter 4.9.1 in the datasheet. + */ +static void dps310_scale_pressure(s32_t tmp_raw, s32_t psr_raw, + struct dps310_data *data) +{ + const struct dps310_cal_coeff *comp = &data->comp; + + float psr = ((float)psr_raw) / IFX_DPS310_SF_PSR; + float tmp = ((float)tmp_raw) / IFX_DPS310_SF_TMP; + + /* scale according to formula from datasheet */ + float psr_final = comp->c00; + + psr_final += psr * (comp->c10 + psr * (comp->c20 + psr * comp->c30)); + psr_final += tmp * comp->c01; + psr_final += tmp * psr * (comp->c11 + psr * comp->c21); + + /* rescale from Pascal to Kilopascal */ + psr_final /= 1000; + + /* store calculated value */ + data->psr_val1 = psr_final; + data->psr_val2 = (psr_final - data->psr_val1) * 1000000; +} + +/* Convert the raw sensor data to s32_t */ +static s32_t raw_to_int24(const u8_t raw[3]) +{ + /* convert from twos complement */ + s32_t res = (s32_t) sys_get_be24(raw); + + if (res > POW_2_23_MINUS_1) { + res -= POW_2_24; + } + + return res; +} + +/* perform a single measurement of temperature and pressure */ +static bool dps310_measure_tmp_psr(struct dps310_data *data, + const struct dps310_cfg *config) +{ + if (!dps310_trigger_temperature(data, config)) { + return false; + } + + if (!dps310_trigger_pressure(data, config)) { + return false; + } + + /* memory for pressure and temperature raw values */ + u8_t value_raw[6]; + + /* read pressure and temperature raw values in one continuous read */ + int res = i2c_write_read(data->i2c_master, config->i2c_addr, + ®_ADDR_PSR_B2, 1, &value_raw, + sizeof(value_raw)); + if (res < 0) { + LOG_WRN("I2C error: %d", res); + return false; + } + + /* convert raw data to int */ + s32_t psr_raw = raw_to_int24(&value_raw[0]); + + data->raw_tmp = raw_to_int24(&value_raw[3]); + + dps310_scale_temperature(data->raw_tmp, data); + dps310_scale_pressure(data->raw_tmp, psr_raw, data); + + return true; +} + +/* + * perform a single pressure measurement + * uses the stored temperature value for sensor temperature compensation + * temperature must be measured regularly for good temperature compensation + */ +static bool dps310_measure_psr(struct dps310_data *data, + const struct dps310_cfg *config) +{ + /* measure pressure */ + if (!dps310_trigger_pressure(data, config)) { + return false; + } + + /* memory for pressure raw value */ + u8_t value_raw[3]; + + /* read pressure raw values in one continuous read */ + int res = i2c_write_read(data->i2c_master, config->i2c_addr, + ®_ADDR_TMP_B2, 1, &value_raw, + sizeof(value_raw)); + if (res < 0) { + LOG_WRN("I2C error: %d", res); + return false; + } + + /* convert raw data to int */ + s32_t psr_raw = raw_to_int24(&value_raw[0]); + + dps310_scale_pressure(data->raw_tmp, psr_raw, data); + + return true; +} + +/* perform a single temperature measurement */ +static bool dps310_measure_tmp(struct dps310_data *data, + const struct dps310_cfg *config) +{ + /* measure temperature */ + if (!dps310_trigger_temperature(data, config)) { + return false; + } + + /* memory for temperature raw value */ + u8_t value_raw[3]; + + /* read temperature raw values in one continuous read */ + int res = i2c_write_read(data->i2c_master, config->i2c_addr, + ®_ADDR_PSR_B2, 1, &value_raw, + sizeof(value_raw)); + if (res < 0) { + LOG_WRN("I2C error: %d", res); + return false; + } + + /* convert raw data to int */ + data->raw_tmp = raw_to_int24(&value_raw[0]); + + dps310_scale_temperature(data->raw_tmp, data); + + return true; +} + +/* Initialize the sensor and apply the configuration */ +static int dps310_init(struct device *dev) +{ + struct dps310_data *data = dev->driver_data; + const struct dps310_cfg *config = dev->config_info; + + data->i2c_master = device_get_binding(config->i2c_bus_name); + if (data->i2c_master == NULL) { + LOG_ERR("Failed to get I2C device"); + return -EINVAL; + } + + u8_t product_id = 0; + int res = i2c_reg_read_byte(data->i2c_master, config->i2c_addr, + IFX_DPS310_REG_ADDR_PRODUCT_ID, &product_id); + + if (res < 0) { + LOG_ERR("No device found"); + return -EINVAL; + } + + if (product_id != IFX_DPS310_PRODUCT_ID) { + LOG_ERR("Device is not a DPS310"); + return -EINVAL; + } + + LOG_DBG("Init DPS310"); + /* give the sensor time to load the calibration data */ + k_sleep(K_MSEC(40)); + + /* wait for the sensor to load the calibration data */ + if (!poll_rdy(data, config, REG_ADDR_MEAS_CFG, + IFX_DPS310_REG_ADDR_MEAS_CFG_SELF_INIT_OK)) { + LOG_DBG("Sensor not ready"); + return -EIO; + } + + /* read calibration coefficients */ + u8_t raw_coef[18] = { 0 }; + + res = i2c_write_read(data->i2c_master, config->i2c_addr, + ®_ADDR_CALIB_COEFF_0, 1, &raw_coef, 18); + if (res < 0) { + LOG_WRN("I2C error: %d", res); + return -EIO; + } + + /* convert calibration coefficients */ + dps310_calib_coeff_creation(raw_coef, &data->comp); + + /* + * check which temperature sensor was used for calibration and use it + * for measurements. + */ + u8_t tmp_coef_srce = 0; + + res = i2c_write_read(data->i2c_master, config->i2c_addr, + ®_ADDR_COEF_SRCE, 1, &tmp_coef_srce, 18); + if (res < 0) { + LOG_WRN("I2C error: %d", res); + return -EIO; + } + + /* clear all other bits */ + tmp_coef_srce &= IFX_DPS310_COEF_SRCE_MASK; + + /* merge with temperature measurement configuration */ + tmp_coef_srce |= IFX_DPS310_TMP_CFG; + + /* set complete configuration in one write */ + const u8_t config_seq[] = { + IFX_DPS310_REG_ADDR_PRS_CFG, /* start register address */ + IFX_DPS310_PSR_CFG, /* PSR_CFG */ + tmp_coef_srce, /* TMP_CFG */ + 0x00, /* MEAS_CFG */ + DPS310_CFG_REG /* CFG_REG */ + }; + + res = i2c_write(data->i2c_master, config_seq, sizeof(config_seq), + config->i2c_addr); + if (res < 0) { + LOG_WRN("I2C error: %d", res); + return -EIO; + } + + dps310_hw_bug_fix(data, config); + dps310_measure_tmp_psr(data, config); + + LOG_DBG("Init OK"); + return 0; +} + +/* Do a measurement and fetch the data from the sensor */ +static int dps310_sample_fetch(struct device *dev, enum sensor_channel chan) +{ + struct dps310_data *data = dev->driver_data; + const struct dps310_cfg *config = dev->config_info; + + LOG_DBG("Fetching sample from DPS310"); + + switch (chan) { + case SENSOR_CHAN_AMBIENT_TEMP: + if (!dps310_measure_tmp(data, config)) { + LOG_ERR("Failed to measure temperature"); + return -EIO; + } + break; + case SENSOR_CHAN_PRESS: + if (!dps310_measure_psr(data, config)) { + LOG_ERR("Failed to measure pressure"); + return -EIO; + } + break; + case SENSOR_CHAN_ALL: + if (!dps310_measure_tmp_psr(data, config)) { + LOG_ERR("Failed to measure temperature and pressure"); + return -EIO; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +/* Get the measurement data */ +static int dps310_channel_get(struct device *dev, enum sensor_channel chan, + struct sensor_value *val) +{ + struct dps310_data *data = dev->driver_data; + + switch (chan) { + case SENSOR_CHAN_AMBIENT_TEMP: + val->val1 = data->tmp_val1; + val->val2 = data->tmp_val2; + break; + case SENSOR_CHAN_PRESS: + val->val1 = data->psr_val1; + val->val2 = data->psr_val2; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct sensor_driver_api dps310_api_funcs = { + .sample_fetch = dps310_sample_fetch, + .channel_get = dps310_channel_get, +}; + +/* support up to 2 instances */ + +#if DT_NODE_HAS_STATUS(DT_DRV_INST(0), okay) +static struct dps310_data dps310_data_0; +static const struct dps310_cfg dps310_cfg_0 = { + .i2c_bus_name = DT_INST_BUS_LABEL(0), + .i2c_addr = DT_INST_REG_ADDR(0) +}; + +DEVICE_AND_API_INIT(dps310, DT_INST_LABEL(0), dps310_init, + &dps310_data_0, &dps310_cfg_0, POST_KERNEL, + CONFIG_SENSOR_INIT_PRIORITY, &dps310_api_funcs); +#endif + +#if DT_NODE_HAS_STATUS(DT_DRV_INST(1), okay) +static struct dps310_data dps310_data_1; +static const struct dps310_cfg dps310_cfg_1 = { + .i2c_bus_name = DT_INST_BUS_LABEL(1), + .i2c_addr = DT_INST_REG_ADDR(1) +}; + +DEVICE_AND_API_INIT(dps310, DT_INST_LABEL(1), dps310_init, + &dps310_data_1, &dps310_cfg_1, POST_KERNEL, + CONFIG_SENSOR_INIT_PRIORITY, &dps310_api_funcs); +#endif diff --git a/dts/bindings/sensor/infineon,dps310.yaml b/dts/bindings/sensor/infineon,dps310.yaml new file mode 100644 index 00000000000..96cb2c5d735 --- /dev/null +++ b/dts/bindings/sensor/infineon,dps310.yaml @@ -0,0 +1,11 @@ +# +# Copyright (c) 2019 Infineon Technologies AG +# +# SPDX-License-Identifier: Apache-2.0 +# + +description: Infineon DPS310 temperature and pressure sensor + +include: i2c-device.yaml + +compatible: "infineon,dps310" diff --git a/tests/drivers/build_all/i2c.dtsi b/tests/drivers/build_all/i2c.dtsi index a7afe297be7..c0511fde4ca 100644 --- a/tests/drivers/build_all/i2c.dtsi +++ b/tests/drivers/build_all/i2c.dtsi @@ -523,3 +523,9 @@ test_i2c_mpr: mpr@18 { label = "MPR"; reg = <0x18>; }; + +test_i2c_dps310: dps310@48 { + compatible = "infineon,dps310"; + label = "DPS310"; + reg = <0x48>; +}; diff --git a/tests/drivers/build_all/sensors_a_h.conf b/tests/drivers/build_all/sensors_a_h.conf index eb94e81cd83..6e7b0afdb73 100644 --- a/tests/drivers/build_all/sensors_a_h.conf +++ b/tests/drivers/build_all/sensors_a_h.conf @@ -23,6 +23,7 @@ CONFIG_BMM150=y CONFIG_BQ274XX=y CONFIG_CCS811=y CONFIG_DHT=y +CONFIG_DPS310=y CONFIG_ENS210=y CONFIG_FXAS21002=y CONFIG_FXOS8700=y