drivers: sensor: add ScioSense ENS160 driver
Add driver for ScioSense ENS160 multi-gas sensor. The driver includes support for I2C and SPI, attributes for setting temperature and humidity compensation and data ready trigger. Also add ScioSense to the list of vendor prefixes. Signed-off-by: Gustavo Silva <gustavograzs@gmail.com>
This commit is contained in:
parent
05f9440347
commit
3850f4ca64
20 changed files with 999 additions and 1 deletions
|
@ -37,6 +37,7 @@ add_subdirectory_ifdef(CONFIG_CURRENT_AMP current_amp)
|
|||
add_subdirectory_ifdef(CONFIG_DHT dht)
|
||||
add_subdirectory_ifdef(CONFIG_DPS310 dps310)
|
||||
add_subdirectory_ifdef(CONFIG_DS18B20 ds18b20)
|
||||
add_subdirectory_ifdef(CONFIG_ENS160 ens160)
|
||||
add_subdirectory_ifdef(CONFIG_ENS210 ens210)
|
||||
add_subdirectory_ifdef(CONFIG_ESP32_TEMP esp32_temp)
|
||||
add_subdirectory_ifdef(CONFIG_EXPLORIR_M explorir_m)
|
||||
|
|
|
@ -117,6 +117,7 @@ source "drivers/sensor/current_amp/Kconfig"
|
|||
source "drivers/sensor/dht/Kconfig"
|
||||
source "drivers/sensor/dps310/Kconfig"
|
||||
source "drivers/sensor/ds18b20/Kconfig"
|
||||
source "drivers/sensor/ens160/Kconfig"
|
||||
source "drivers/sensor/ens210/Kconfig"
|
||||
source "drivers/sensor/esp32_temp/Kconfig"
|
||||
source "drivers/sensor/explorir_m/Kconfig"
|
||||
|
|
8
drivers/sensor/ens160/CMakeLists.txt
Normal file
8
drivers/sensor/ens160/CMakeLists.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2024 Gustavo Silva
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
zephyr_library_sources(ens160.c)
|
||||
zephyr_library_sources(ens160_i2c.c)
|
||||
zephyr_library_sources(ens160_spi.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_ENS160_TRIGGER ens160_trigger.c)
|
53
drivers/sensor/ens160/Kconfig
Normal file
53
drivers/sensor/ens160/Kconfig
Normal file
|
@ -0,0 +1,53 @@
|
|||
# Copyright (c) 2024 Gustavo Silva
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig ENS160
|
||||
bool "ENS160 multi-gas sensor"
|
||||
default y
|
||||
depends on DT_HAS_SCIOSENSE_ENS160_ENABLED
|
||||
select I2C if $(dt_compat_on_bus,$(DT_COMPAT_SCIOSENSE_ENS160),i2c)
|
||||
select SPI if $(dt_compat_on_bus,$(DT_COMPAT_SCIOSENSE_ENS160),spi)
|
||||
help
|
||||
Enable driver for ENS160 Digital Metal Oxide Multi-Gas Sensor.
|
||||
|
||||
if ENS160
|
||||
|
||||
choice
|
||||
prompt "Trigger Mode"
|
||||
default ENS160_TRIGGER_NONE
|
||||
help
|
||||
Specify the type of triggering to be used by the driver.
|
||||
|
||||
config ENS160_TRIGGER_NONE
|
||||
bool "No trigger"
|
||||
|
||||
config ENS160_TRIGGER_GLOBAL_THREAD
|
||||
bool "Use global thread"
|
||||
depends on GPIO
|
||||
select ENS160_TRIGGER
|
||||
|
||||
config ENS160_TRIGGER_OWN_THREAD
|
||||
bool "Use own thread"
|
||||
depends on GPIO
|
||||
select ENS160_TRIGGER
|
||||
|
||||
endchoice # Trigger Mode
|
||||
|
||||
config ENS160_TRIGGER
|
||||
bool
|
||||
|
||||
config ENS160_THREAD_PRIORITY
|
||||
int "Thread priority"
|
||||
depends on ENS160_TRIGGER_OWN_THREAD && ENS160_TRIGGER
|
||||
default 10
|
||||
help
|
||||
Priority of thread used by the driver to handle interrupts.
|
||||
|
||||
config ENS160_THREAD_STACK_SIZE
|
||||
int "Thread stack size"
|
||||
depends on ENS160_TRIGGER_OWN_THREAD && ENS160_TRIGGER
|
||||
default 1024
|
||||
help
|
||||
Stack size of thread used by the driver to handle interrupts.
|
||||
|
||||
endif # ENS160
|
329
drivers/sensor/ens160/ens160.c
Normal file
329
drivers/sensor/ens160/ens160.c
Normal file
|
@ -0,0 +1,329 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Gustavo Silva
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT sciosense_ens160
|
||||
|
||||
#include <zephyr/drivers/sensor/ens160.h>
|
||||
#include <zephyr/init.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/pm/pm.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/sys/__assert.h>
|
||||
|
||||
#include "ens160.h"
|
||||
|
||||
LOG_MODULE_REGISTER(ENS160, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
static int ens160_set_temperature(const struct device *dev, const struct sensor_value *val)
|
||||
{
|
||||
struct ens160_data *data = dev->data;
|
||||
uint8_t buf[2];
|
||||
int64_t temp;
|
||||
int ret;
|
||||
|
||||
/* Recommended operation: -5 to 60 degrees Celsius */
|
||||
if (!IN_RANGE(val->val1, -5, 60)) {
|
||||
LOG_ERR("Invalid temperature value");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Convert temperature from Celsius to Kelvin */
|
||||
temp = sensor_value_to_micro(val) + 273150000U;
|
||||
/* Temperature is stored in 64 * Kelvin */
|
||||
temp *= 64;
|
||||
sys_put_le16(DIV_ROUND_CLOSEST(temp, 1000000U), buf);
|
||||
|
||||
ret = data->tf->write_data(dev, ENS160_REG_TEMP_IN, buf, 2U);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to write temperature");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ens160_set_humidity(const struct device *dev, const struct sensor_value *val)
|
||||
{
|
||||
struct ens160_data *data = dev->data;
|
||||
uint8_t buf[2];
|
||||
uint64_t rh;
|
||||
int ret;
|
||||
|
||||
/* Recommended operation: 20 to 80% RH */
|
||||
if (!IN_RANGE(val->val1, 20, 80)) {
|
||||
LOG_ERR("Invalid RH value");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rh = sensor_value_to_micro(val);
|
||||
/* RH value is stored in 512 * %RH */
|
||||
rh *= 512;
|
||||
sys_put_le16(DIV_ROUND_CLOSEST(rh, 1000000U), buf);
|
||||
|
||||
ret = data->tf->write_data(dev, ENS160_REG_RH_IN, buf, 2U);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to write RH");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool ens160_new_data(const struct device *dev)
|
||||
{
|
||||
struct ens160_data *data = dev->data;
|
||||
uint8_t status;
|
||||
int ret;
|
||||
|
||||
ret = data->tf->read_reg(dev, ENS160_REG_DEVICE_STATUS, &status);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return FIELD_GET(ENS160_STATUS_NEWDAT, status) != 0;
|
||||
}
|
||||
|
||||
static int ens160_sample_fetch(const struct device *dev, enum sensor_channel chan)
|
||||
{
|
||||
struct ens160_data *data = dev->data;
|
||||
uint16_t le16_buffer;
|
||||
uint8_t buffer;
|
||||
int ret;
|
||||
|
||||
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_CO2 ||
|
||||
chan == SENSOR_CHAN_VOC ||
|
||||
chan == (enum sensor_channel)SENSOR_CHAN_ENS160_AQI);
|
||||
|
||||
if (!IS_ENABLED(CONFIG_ENS160_TRIGGER)) {
|
||||
WAIT_FOR(ens160_new_data(dev), ENS160_TIMEOUT_US, k_msleep(10));
|
||||
}
|
||||
|
||||
ret = data->tf->read_data(dev, ENS160_REG_DATA_ECO2, (uint8_t *)&le16_buffer,
|
||||
sizeof(le16_buffer));
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to fetch CO2");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->eco2 = sys_le16_to_cpu(le16_buffer);
|
||||
|
||||
ret = data->tf->read_data(dev, ENS160_REG_DATA_TVOC, (uint8_t *)&le16_buffer,
|
||||
sizeof(le16_buffer));
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to fetch VOC");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->tvoc = sys_le16_to_cpu(le16_buffer);
|
||||
|
||||
ret = data->tf->read_reg(dev, ENS160_REG_DATA_AQI, &buffer);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to fetch AQI");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->aqi = FIELD_GET(ENS160_DATA_AQI_UBA, buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ens160_channel_get(const struct device *dev, enum sensor_channel chan,
|
||||
struct sensor_value *val)
|
||||
{
|
||||
struct ens160_data *data = dev->data;
|
||||
|
||||
switch (chan) {
|
||||
case SENSOR_CHAN_CO2:
|
||||
val->val1 = data->eco2;
|
||||
val->val2 = 0;
|
||||
break;
|
||||
case SENSOR_CHAN_VOC:
|
||||
val->val1 = data->tvoc;
|
||||
val->val2 = 0;
|
||||
break;
|
||||
case SENSOR_CHAN_ENS160_AQI:
|
||||
val->val1 = data->aqi;
|
||||
val->val2 = 0;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ens160_attr_set(const struct device *dev, enum sensor_channel chan,
|
||||
enum sensor_attribute attr, const struct sensor_value *val)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch ((uint32_t)attr) {
|
||||
case SENSOR_ATTR_ENS160_TEMP:
|
||||
ret = ens160_set_temperature(dev, val);
|
||||
break;
|
||||
case SENSOR_ATTR_ENS160_RH:
|
||||
ret = ens160_set_humidity(dev, val);
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct sensor_driver_api ens160_driver_api = {
|
||||
.sample_fetch = ens160_sample_fetch,
|
||||
.channel_get = ens160_channel_get,
|
||||
.attr_set = ens160_attr_set,
|
||||
#ifdef CONFIG_ENS160_TRIGGER
|
||||
.trigger_set = ens160_trigger_set,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int ens160_init(const struct device *dev)
|
||||
{
|
||||
const struct ens160_config *config = dev->config;
|
||||
struct ens160_data *data = dev->data;
|
||||
uint8_t fw_version[3];
|
||||
uint16_t part_id;
|
||||
uint8_t status;
|
||||
int ret;
|
||||
|
||||
ret = config->bus_init(dev);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = data->tf->write_reg(dev, ENS160_REG_OPMODE, ENS160_OPMODE_RESET);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to reset the device");
|
||||
return ret;
|
||||
}
|
||||
|
||||
k_msleep(ENS160_BOOTING_TIME_MS);
|
||||
|
||||
ret = data->tf->read_data(dev, ENS160_REG_PART_ID, (uint8_t *)&part_id, sizeof(part_id));
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to read Part ID");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (sys_le16_to_cpu(part_id) != ENS160_PART_ID) {
|
||||
LOG_ERR("Part ID is invalid. Expected: 0x%x; read: 0x%x", ENS160_PART_ID, part_id);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = data->tf->write_reg(dev, ENS160_REG_OPMODE, ENS160_OPMODE_IDLE);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to set operation mode");
|
||||
return ret;
|
||||
}
|
||||
|
||||
k_msleep(ENS160_BOOTING_TIME_MS);
|
||||
|
||||
ret = data->tf->write_reg(dev, ENS160_REG_COMMAND, ENS160_COMMAND_CLRGPR);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to clear GPR registers");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = data->tf->write_reg(dev, ENS160_REG_COMMAND, ENS160_COMMAND_GET_APPVER);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to write GET_APPVER command");
|
||||
return ret;
|
||||
}
|
||||
|
||||
k_msleep(ENS160_BOOTING_TIME_MS);
|
||||
|
||||
ret = data->tf->read_data(dev, ENS160_REG_GPR_READ4, fw_version, sizeof(fw_version));
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to read firmware version");
|
||||
return ret;
|
||||
}
|
||||
LOG_INF("Firmware version: %u.%u.%u", fw_version[2], fw_version[1], fw_version[0]);
|
||||
|
||||
#ifdef CONFIG_ENS160_TRIGGER
|
||||
ret = ens160_init_interrupt(dev);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to initialize interrupt");
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_ENS160_TRIGGER */
|
||||
|
||||
ret = data->tf->write_reg(dev, ENS160_REG_OPMODE, ENS160_OPMODE_STANDARD);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to set operation mode");
|
||||
return ret;
|
||||
}
|
||||
|
||||
k_msleep(ENS160_BOOTING_TIME_MS);
|
||||
|
||||
ret = data->tf->read_reg(dev, ENS160_REG_DEVICE_STATUS, &status);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to read device status");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (FIELD_GET(ENS160_STATUS_VALIDITY_FLAG, status) != ENS160_STATUS_NORMAL) {
|
||||
LOG_ERR("Status 0x%02x is invalid", status);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_DEVICE
|
||||
static int ens160_pm_action(const struct device *dev, enum pm_device_action action)
|
||||
{
|
||||
struct ens160_data *data = dev->data;
|
||||
int ret = 0;
|
||||
|
||||
switch (action) {
|
||||
case PM_DEVICE_ACTION_RESUME:
|
||||
ret = data->tf->write_reg(dev, ENS160_REG_OPMODE, ENS160_OPMODE_IDLE);
|
||||
k_msleep(ENS160_BOOTING_TIME_MS);
|
||||
ret = data->tf->write_reg(dev, ENS160_REG_OPMODE, ENS160_OPMODE_STANDARD);
|
||||
break;
|
||||
case PM_DEVICE_ACTION_SUSPEND:
|
||||
ret = data->tf->write_reg(dev, ENS160_REG_OPMODE, ENS160_OPMODE_DEEP_SLEEP);
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
k_msleep(ENS160_BOOTING_TIME_MS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define ENS160_SPI_OPERATION \
|
||||
(SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_MODE_CPOL | SPI_MODE_CPHA | SPI_TRANSFER_MSB)
|
||||
|
||||
#define ENS160_CONFIG_SPI(inst) \
|
||||
.bus_init = &ens160_spi_init, \
|
||||
.spi = SPI_DT_SPEC_INST_GET(inst, ENS160_SPI_OPERATION, 0),
|
||||
|
||||
#define ENS160_CONFIG_I2C(inst) \
|
||||
.bus_init = &ens160_i2c_init, \
|
||||
.i2c = I2C_DT_SPEC_INST_GET(inst),
|
||||
|
||||
#define ENS160_DEFINE(inst) \
|
||||
static struct ens160_data ens160_data_##inst; \
|
||||
static const struct ens160_config ens160_config_##inst = { \
|
||||
IF_ENABLED(CONFIG_ENS160_TRIGGER, \
|
||||
(.int_gpio = GPIO_DT_SPEC_INST_GET(inst, int_gpios),)) \
|
||||
COND_CODE_1(DT_INST_ON_BUS(inst, spi), \
|
||||
(ENS160_CONFIG_SPI(inst)), \
|
||||
(ENS160_CONFIG_I2C(inst))) \
|
||||
}; \
|
||||
\
|
||||
PM_DEVICE_DT_INST_DEFINE(inst, ens160_pm_action); \
|
||||
SENSOR_DEVICE_DT_INST_DEFINE(inst, ens160_init, PM_DEVICE_DT_INST_GET(inst), \
|
||||
&ens160_data_##inst, &ens160_config_##inst, POST_KERNEL, \
|
||||
CONFIG_SENSOR_INIT_PRIORITY, &ens160_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(ENS160_DEFINE)
|
143
drivers/sensor/ens160/ens160.h
Normal file
143
drivers/sensor/ens160/ens160.h
Normal file
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Gustavo Silva
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_SENSOR_ENS160_ENS160_H_
|
||||
#define ZEPHYR_DRIVERS_SENSOR_ENS160_ENS160_H_
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
|
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#endif
|
||||
|
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
|
||||
#include <zephyr/drivers/spi.h>
|
||||
#endif
|
||||
|
||||
/* Registers */
|
||||
#define ENS160_REG_PART_ID 0x00
|
||||
#define ENS160_REG_OPMODE 0x10
|
||||
#define ENS160_REG_CONFIG 0x11
|
||||
#define ENS160_REG_COMMAND 0x12
|
||||
#define ENS160_REG_TEMP_IN 0x13
|
||||
#define ENS160_REG_RH_IN 0x15
|
||||
#define ENS160_REG_DEVICE_STATUS 0x20
|
||||
#define ENS160_REG_DATA_AQI 0x21
|
||||
#define ENS160_REG_DATA_TVOC 0x22
|
||||
#define ENS160_REG_DATA_ECO2 0x24
|
||||
#define ENS160_REG_DATA_T 0x30
|
||||
#define ENS160_REG_DATA_RH 0x32
|
||||
#define ENS160_REG_DATA_MISR 0x38
|
||||
#define ENS160_REG_GPR_WRITE0 0x40
|
||||
#define ENS160_REG_GPR_WRITE1 0x41
|
||||
#define ENS160_REG_GPR_WRITE2 0x42
|
||||
#define ENS160_REG_GPR_WRITE3 0x43
|
||||
#define ENS160_REG_GPR_WRITE4 0x44
|
||||
#define ENS160_REG_GPR_WRITE5 0x45
|
||||
#define ENS160_REG_GPR_WRITE6 0x46
|
||||
#define ENS160_REG_GPR_WRITE7 0x47
|
||||
#define ENS160_REG_GPR_READ0 0x48
|
||||
#define ENS160_REG_GPR_READ1 0x49
|
||||
#define ENS160_REG_GPR_READ2 0x4A
|
||||
#define ENS160_REG_GPR_READ3 0x4B
|
||||
#define ENS160_REG_GPR_READ4 0x4C
|
||||
#define ENS160_REG_GPR_READ5 0x4D
|
||||
#define ENS160_REG_GPR_READ6 0x4E
|
||||
#define ENS160_REG_GPR_READ7 0x4F
|
||||
|
||||
#define ENS160_PART_ID 0x160
|
||||
|
||||
#define ENS160_TIMEOUT_US (1000000U)
|
||||
#define ENS160_BOOTING_TIME_MS (10U)
|
||||
|
||||
/* Operation modes */
|
||||
#define ENS160_OPMODE_DEEP_SLEEP 0x00
|
||||
#define ENS160_OPMODE_IDLE 0x01
|
||||
#define ENS160_OPMODE_STANDARD 0x02
|
||||
#define ENS160_OPMODE_RESET 0xF0
|
||||
|
||||
/* Device status */
|
||||
#define ENS160_STATUS_STATER BIT(6)
|
||||
#define ENS160_STATUS_VALIDITY_FLAG GENMASK(3, 2)
|
||||
#define ENS160_STATUS_NEWDAT BIT(1)
|
||||
|
||||
#define ENS160_STATUS_NORMAL 0x00
|
||||
#define ENS160_STATUS_WARM_UP 0x01
|
||||
#define ENS160_STATUS_START_UP 0x02
|
||||
#define ENS160_STATUS_INVALID 0x03
|
||||
|
||||
/* Commands */
|
||||
#define ENS160_COMMAND_NOP 0x00
|
||||
#define ENS160_COMMAND_GET_APPVER 0x0E
|
||||
#define ENS160_COMMAND_CLRGPR 0xCC
|
||||
|
||||
/* Config register fields */
|
||||
#define ENS160_CONFIG_INTPOL BIT(6)
|
||||
#define ENS160_CONFIG_INT_CFG BIT(5)
|
||||
#define ENS160_CONFIG_INTGPR BIT(3)
|
||||
#define ENS160_CONFIG_INTDAT BIT(1)
|
||||
#define ENS160_CONFIG_INTEN BIT(0)
|
||||
|
||||
#define ENS160_DATA_AQI_UBA GENMASK(2, 0)
|
||||
|
||||
struct ens160_config {
|
||||
int (*bus_init)(const struct device *dev);
|
||||
const union {
|
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
|
||||
struct i2c_dt_spec i2c;
|
||||
#endif
|
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
|
||||
struct spi_dt_spec spi;
|
||||
#endif
|
||||
};
|
||||
#ifdef CONFIG_ENS160_TRIGGER
|
||||
struct gpio_dt_spec int_gpio;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct ens160_transfer_function {
|
||||
int (*read_reg)(const struct device *dev, uint8_t reg, uint8_t *val);
|
||||
int (*read_data)(const struct device *dev, uint8_t reg, uint8_t *data, size_t len);
|
||||
int (*write_reg)(const struct device *dev, uint8_t reg, uint8_t val);
|
||||
int (*write_data)(const struct device *dev, uint8_t reg, uint8_t *data, size_t len);
|
||||
};
|
||||
|
||||
struct ens160_data {
|
||||
uint16_t eco2;
|
||||
uint16_t tvoc;
|
||||
uint8_t aqi;
|
||||
const struct ens160_transfer_function *tf;
|
||||
#ifdef CONFIG_ENS160_TRIGGER
|
||||
struct gpio_callback gpio_cb;
|
||||
const struct device *dev;
|
||||
|
||||
sensor_trigger_handler_t data_ready_handler;
|
||||
const struct sensor_trigger *data_ready_trigger;
|
||||
#if defined(CONFIG_ENS160_TRIGGER_OWN_THREAD)
|
||||
K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_ENS160_THREAD_STACK_SIZE);
|
||||
struct k_sem gpio_sem;
|
||||
struct k_thread thread;
|
||||
#elif defined(CONFIG_ENS160_TRIGGER_GLOBAL_THREAD)
|
||||
struct k_work work;
|
||||
#endif
|
||||
#endif /* CONFIG_ENS160_TRIGGER */
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ENS160_TRIGGER
|
||||
int ens160_trigger_set(const struct device *dev, const struct sensor_trigger *trigg,
|
||||
sensor_trigger_handler_t handler);
|
||||
|
||||
int ens160_init_interrupt(const struct device *dev);
|
||||
#endif
|
||||
|
||||
int ens160_i2c_init(const struct device *dev);
|
||||
int ens160_spi_init(const struct device *dev);
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_SENSOR_ENS160_ENS160_H_ */
|
71
drivers/sensor/ens160/ens160_i2c.c
Normal file
71
drivers/sensor/ens160/ens160_i2c.c
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Gustavo Silva
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT sciosense_ens160
|
||||
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
|
||||
#include "ens160.h"
|
||||
|
||||
LOG_MODULE_DECLARE(ENS160, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
|
||||
|
||||
static int ens160_read_reg_i2c(const struct device *dev, uint8_t reg, uint8_t *val)
|
||||
{
|
||||
const struct ens160_config *config = dev->config;
|
||||
|
||||
return i2c_reg_read_byte_dt(&config->i2c, reg, val);
|
||||
}
|
||||
|
||||
static int ens160_read_data_i2c(const struct device *dev, uint8_t reg, uint8_t *data, size_t len)
|
||||
{
|
||||
const struct ens160_config *config = dev->config;
|
||||
|
||||
return i2c_burst_read_dt(&config->i2c, reg, data, len);
|
||||
}
|
||||
|
||||
static int ens160_write_reg_i2c(const struct device *dev, uint8_t reg, uint8_t val)
|
||||
{
|
||||
const struct ens160_config *config = dev->config;
|
||||
|
||||
return i2c_reg_write_byte_dt(&config->i2c, reg, val);
|
||||
}
|
||||
|
||||
static int ens160_write_data_i2c(const struct device *dev, uint8_t reg, uint8_t *data, size_t len)
|
||||
{
|
||||
const struct ens160_config *config = dev->config;
|
||||
|
||||
__ASSERT(len == 2, "Only 2 byte write are supported");
|
||||
|
||||
uint8_t buff[] = {reg, data[0], data[1]};
|
||||
|
||||
return i2c_write_dt(&config->i2c, buff, sizeof(buff));
|
||||
}
|
||||
|
||||
const struct ens160_transfer_function ens160_i2c_transfer_function = {
|
||||
.read_reg = ens160_read_reg_i2c,
|
||||
.read_data = ens160_read_data_i2c,
|
||||
.write_reg = ens160_write_reg_i2c,
|
||||
.write_data = ens160_write_data_i2c,
|
||||
};
|
||||
|
||||
int ens160_i2c_init(const struct device *dev)
|
||||
{
|
||||
const struct ens160_config *config = dev->config;
|
||||
struct ens160_data *data = dev->data;
|
||||
|
||||
if (!i2c_is_ready_dt(&config->i2c)) {
|
||||
LOG_DBG("I2C bus device not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
data->tf = &ens160_i2c_transfer_function;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */
|
161
drivers/sensor/ens160/ens160_spi.c
Normal file
161
drivers/sensor/ens160/ens160_spi.c
Normal file
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Gustavo Silva
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT sciosense_ens160
|
||||
|
||||
#include <zephyr/drivers/spi.h>
|
||||
|
||||
#include "ens160.h"
|
||||
|
||||
LOG_MODULE_DECLARE(ENS160, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
|
||||
|
||||
#define ENS160_SPI_READ_BIT BIT(0)
|
||||
|
||||
static int ens160_read_reg_spi(const struct device *dev, uint8_t reg, uint8_t *val)
|
||||
{
|
||||
const struct ens160_config *config = dev->config;
|
||||
|
||||
uint8_t tx_buffer = (reg << 1) | ENS160_SPI_READ_BIT;
|
||||
|
||||
const struct spi_buf tx_buf = {
|
||||
.buf = &tx_buffer,
|
||||
.len = 1,
|
||||
};
|
||||
|
||||
const struct spi_buf_set tx = {
|
||||
.buffers = &tx_buf,
|
||||
.count = 1,
|
||||
};
|
||||
|
||||
struct spi_buf rx_buf[2] = {
|
||||
{
|
||||
.buf = NULL,
|
||||
.len = 1,
|
||||
},
|
||||
{
|
||||
.buf = val,
|
||||
.len = 1,
|
||||
}
|
||||
};
|
||||
|
||||
const struct spi_buf_set rx = {
|
||||
.buffers = rx_buf,
|
||||
.count = 2,
|
||||
};
|
||||
|
||||
return spi_transceive_dt(&config->spi, &tx, &rx);
|
||||
}
|
||||
|
||||
static int ens160_read_data_spi(const struct device *dev, uint8_t start, uint8_t *data, size_t len)
|
||||
{
|
||||
const struct ens160_config *config = dev->config;
|
||||
|
||||
uint8_t tx_buffer = (start << 1) | ENS160_SPI_READ_BIT;
|
||||
|
||||
const struct spi_buf tx_buf = {
|
||||
.buf = &tx_buffer,
|
||||
.len = 1,
|
||||
};
|
||||
|
||||
const struct spi_buf_set tx = {
|
||||
.buffers = &tx_buf,
|
||||
.count = 1,
|
||||
};
|
||||
|
||||
struct spi_buf rx_buf[2] = {
|
||||
{
|
||||
.buf = NULL,
|
||||
.len = 1,
|
||||
},
|
||||
{
|
||||
.buf = data,
|
||||
.len = len,
|
||||
}
|
||||
};
|
||||
|
||||
const struct spi_buf_set rx = {
|
||||
.buffers = rx_buf,
|
||||
.count = 2,
|
||||
};
|
||||
|
||||
return spi_transceive_dt(&config->spi, &tx, &rx);
|
||||
}
|
||||
|
||||
static int ens160_write_reg_spi(const struct device *dev, uint8_t reg, uint8_t val)
|
||||
{
|
||||
const struct ens160_config *config = dev->config;
|
||||
|
||||
uint8_t tx_buffer = reg << 1;
|
||||
|
||||
const struct spi_buf buf[2] = {
|
||||
{
|
||||
.buf = &tx_buffer,
|
||||
.len = 1,
|
||||
},
|
||||
{
|
||||
.buf = &val,
|
||||
.len = 1,
|
||||
}
|
||||
};
|
||||
|
||||
const struct spi_buf_set tx = {
|
||||
.buffers = buf,
|
||||
.count = 2,
|
||||
};
|
||||
|
||||
return spi_write_dt(&config->spi, &tx);
|
||||
}
|
||||
|
||||
static int ens160_write_data_spi(const struct device *dev, uint8_t reg, uint8_t *data, size_t len)
|
||||
{
|
||||
const struct ens160_config *config = dev->config;
|
||||
|
||||
uint8_t tx_buffer = reg << 1;
|
||||
|
||||
const struct spi_buf buf[2] = {
|
||||
{
|
||||
.buf = &tx_buffer,
|
||||
.len = 1,
|
||||
},
|
||||
{
|
||||
.buf = &data,
|
||||
.len = len,
|
||||
}
|
||||
};
|
||||
|
||||
const struct spi_buf_set tx = {
|
||||
.buffers = buf,
|
||||
.count = 2,
|
||||
};
|
||||
|
||||
return spi_write_dt(&config->spi, &tx);
|
||||
}
|
||||
|
||||
const struct ens160_transfer_function ens160_spi_transfer_function = {
|
||||
.read_reg = ens160_read_reg_spi,
|
||||
.read_data = ens160_read_data_spi,
|
||||
.write_reg = ens160_write_reg_spi,
|
||||
.write_data = ens160_write_data_spi,
|
||||
};
|
||||
|
||||
int ens160_spi_init(const struct device *dev)
|
||||
{
|
||||
const struct ens160_config *config = dev->config;
|
||||
struct ens160_data *data = dev->data;
|
||||
|
||||
if (!spi_is_ready_dt(&config->spi)) {
|
||||
LOG_DBG("SPI bus not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
data->tf = &ens160_spi_transfer_function;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) */
|
152
drivers/sensor/ens160/ens160_trigger.c
Normal file
152
drivers/sensor/ens160/ens160_trigger.c
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Gustavo Silva
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT sciosense_ens160
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#include "ens160.h"
|
||||
|
||||
LOG_MODULE_DECLARE(ENS160, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
static inline void ens160_setup_int(const struct device *dev, bool enable)
|
||||
{
|
||||
const struct ens160_config *config = dev->config;
|
||||
gpio_flags_t flags = enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE;
|
||||
|
||||
(void)gpio_pin_interrupt_configure_dt(&config->int_gpio, flags);
|
||||
}
|
||||
|
||||
static void ens160_gpio_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
|
||||
{
|
||||
struct ens160_data *data = CONTAINER_OF(cb, struct ens160_data, gpio_cb);
|
||||
|
||||
ens160_setup_int(data->dev, false);
|
||||
|
||||
#if defined(CONFIG_ENS160_TRIGGER_OWN_THREAD)
|
||||
k_sem_give(&data->gpio_sem);
|
||||
#elif defined(CONFIG_ENS160_TRIGGER_GLOBAL_THREAD)
|
||||
k_work_submit(&data->work);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ens160_thread_cb(const struct device *dev)
|
||||
{
|
||||
struct ens160_data *data = dev->data;
|
||||
uint8_t status;
|
||||
int ret;
|
||||
|
||||
ret = data->tf->read_reg(dev, ENS160_REG_DEVICE_STATUS, &status);
|
||||
if (ret < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (FIELD_GET(ENS160_STATUS_NEWDAT, status) != 0x01) {
|
||||
LOG_ERR("Data is not ready");
|
||||
return;
|
||||
}
|
||||
|
||||
if (data->data_ready_handler != NULL) {
|
||||
data->data_ready_handler(dev, data->data_ready_trigger);
|
||||
}
|
||||
|
||||
ens160_setup_int(dev, true);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ENS160_TRIGGER_OWN_THREAD
|
||||
static void ens160_thread(void *p1, void *p2, void *p3)
|
||||
{
|
||||
ARG_UNUSED(p2);
|
||||
ARG_UNUSED(p3);
|
||||
|
||||
struct ens160_data *data = p1;
|
||||
|
||||
while (1) {
|
||||
k_sem_take(&data->gpio_sem, K_FOREVER);
|
||||
ens160_thread_cb(data->dev);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_ENS160_TRIGGER_OWN_THREAD */
|
||||
|
||||
#ifdef CONFIG_ENS160_TRIGGER_GLOBAL_THREAD
|
||||
static void ens160_work_cb(struct k_work *work)
|
||||
{
|
||||
struct ens160_data *data = CONTAINER_OF(work, struct ens160_data, work);
|
||||
|
||||
ens160_thread_cb(data->dev);
|
||||
}
|
||||
#endif /* CONFIG_ENS160_TRIGGER_GLOBAL_THREAD */
|
||||
|
||||
int ens160_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
|
||||
sensor_trigger_handler_t handler)
|
||||
{
|
||||
const struct ens160_config *config = dev->config;
|
||||
struct ens160_data *data = dev->data;
|
||||
|
||||
if (!config->int_gpio.port) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
ens160_setup_int(dev, false);
|
||||
|
||||
if (trig->type == SENSOR_TRIG_DATA_READY) {
|
||||
data->data_ready_handler = handler;
|
||||
data->data_ready_trigger = trig;
|
||||
}
|
||||
|
||||
ens160_setup_int(dev, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ens160_init_interrupt(const struct device *dev)
|
||||
{
|
||||
const struct ens160_config *config = dev->config;
|
||||
struct ens160_data *data = dev->data;
|
||||
int ret;
|
||||
|
||||
uint8_t int_cfg = ENS160_CONFIG_INTEN | ENS160_CONFIG_INTDAT | ENS160_CONFIG_INT_CFG;
|
||||
|
||||
ret = data->tf->write_reg(dev, ENS160_REG_CONFIG, int_cfg);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to write to config register");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!gpio_is_ready_dt(&config->int_gpio)) {
|
||||
LOG_ERR("%s: device %s is not ready", dev->name, config->int_gpio.port->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT);
|
||||
|
||||
gpio_init_callback(&data->gpio_cb, ens160_gpio_callback, BIT(config->int_gpio.pin));
|
||||
|
||||
ret = gpio_add_callback(config->int_gpio.port, &data->gpio_cb);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to set gpio callback");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->dev = dev;
|
||||
|
||||
#if defined(CONFIG_ENS160_TRIGGER_OWN_THREAD)
|
||||
k_sem_init(&data->gpio_sem, 0, K_SEM_MAX_LIMIT);
|
||||
|
||||
k_thread_create(&data->thread, data->thread_stack, CONFIG_ENS160_THREAD_STACK_SIZE,
|
||||
ens160_thread, data, NULL, NULL, K_PRIO_COOP(CONFIG_ENS160_THREAD_PRIORITY),
|
||||
0, K_NO_WAIT);
|
||||
#elif defined(CONFIG_ENS160_TRIGGER_GLOBAL_THREAD)
|
||||
data->work.handler = ens160_work_cb;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
14
dts/bindings/sensor/sciosense,ens160-common.yaml
Normal file
14
dts/bindings/sensor/sciosense,ens160-common.yaml
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Copyright (c) 2024 Gustavo Silva
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: ScioSense ENS160 Digital Metal Oxide Multi-Gas Sensor
|
||||
|
||||
compatible: "sciosense,ens160"
|
||||
|
||||
include: sensor-device.yaml
|
||||
|
||||
properties:
|
||||
int-gpios:
|
||||
type: phandle-array
|
||||
description: |
|
||||
The interrupt pin of ENS160.
|
9
dts/bindings/sensor/sciosense,ens160-i2c.yaml
Normal file
9
dts/bindings/sensor/sciosense,ens160-i2c.yaml
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Copyright (c) 2024 Gustavo Silva
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
ScioSense ENS160 Digital Metal Oxide Multi-Gas Sensor on I2C bus
|
||||
|
||||
compatible: "sciosense,ens160"
|
||||
|
||||
include: ["i2c-device.yaml", "sciosense,ens160-common.yaml"]
|
9
dts/bindings/sensor/sciosense,ens160-spi.yaml
Normal file
9
dts/bindings/sensor/sciosense,ens160-spi.yaml
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Copyright (c) 2024 Gustavo Silva
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
ScioSense ENS160 Digital Metal Oxide Multi-Gas Sensor on SPI bus
|
||||
|
||||
compatible: "sciosense,ens160"
|
||||
|
||||
include: ["spi-device.yaml", "sciosense,ens160-common.yaml"]
|
|
@ -537,6 +537,7 @@ sandisk Sandisk Corporation
|
|||
satoz Satoz International Co., Ltd
|
||||
sbs Smart Battery System
|
||||
sc Space Cubics, LLC
|
||||
sciosense Sciosense B.V.
|
||||
schindler Schindler
|
||||
seagate Seagate Technology PLC
|
||||
segger SEGGER Microcontroller GmbH
|
||||
|
|
29
include/zephyr/drivers/sensor/ens160.h
Normal file
29
include/zephyr/drivers/sensor/ens160.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Gustavo Silva
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_ENS160_H_
|
||||
#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_ENS160_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
|
||||
/* Channel for Air Quality Index */
|
||||
enum sensor_channel_ens160 {
|
||||
SENSOR_CHAN_ENS160_AQI = SENSOR_CHAN_PRIV_START,
|
||||
};
|
||||
|
||||
enum sensor_attribute_ens160 {
|
||||
SENSOR_ATTR_ENS160_TEMP = SENSOR_ATTR_PRIV_START,
|
||||
SENSOR_ATTR_ENS160_RH,
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -124,7 +124,8 @@
|
|||
<&test_gpio 0 0>, /* 0x26 */
|
||||
<&test_gpio 0 0>, /* 0x27 */
|
||||
<&test_gpio 0 0>, /* 0x28 */
|
||||
<&test_gpio 0 0>; /* 0x29 */
|
||||
<&test_gpio 0 0>, /* 0x29 */
|
||||
<&test_gpio 0 0>; /* 0x2A */
|
||||
|
||||
#include "spi.dtsi"
|
||||
};
|
||||
|
|
|
@ -926,3 +926,9 @@ test_i2c_tmag5273: tmag5273@82 {
|
|||
operation-mode = <TMAG5273_DT_OPER_MODE_CONTINUOUS>;
|
||||
angle-magnitude-axis = <TMAG5273_DT_ANGLE_MAG_XY>;
|
||||
};
|
||||
|
||||
test_i2c_ens160: ens160@83 {
|
||||
compatible = "sciosense,ens160";
|
||||
reg = <0x83>;
|
||||
int-gpios = <&test_gpio 0 0>;
|
||||
};
|
||||
|
|
|
@ -15,6 +15,7 @@ CONFIG_BMM150_TRIGGER_GLOBAL_THREAD=y
|
|||
CONFIG_BMP388_TRIGGER_GLOBAL_THREAD=y
|
||||
CONFIG_BQ274XX_TRIGGER_GLOBAL_THREAD=y
|
||||
CONFIG_CCS811_TRIGGER_GLOBAL_THREAD=y
|
||||
CONFIG_ENS160_TRIGGER_GLOBAL_THREAD=y
|
||||
CONFIG_FDC2X1X_TRIGGER_GLOBAL_THREAD=y
|
||||
CONFIG_FXAS21002_TRIGGER_GLOBAL_THREAD=y
|
||||
CONFIG_FXOS8700_TRIGGER_GLOBAL_THREAD=y
|
||||
|
|
|
@ -15,6 +15,7 @@ CONFIG_BMM150_TRIGGER_NONE=y
|
|||
CONFIG_BMP388_TRIGGER_NONE=y
|
||||
CONFIG_BQ274XX_TRIGGER_NONE=y
|
||||
CONFIG_CCS811_TRIGGER_NONE=y
|
||||
CONFIG_ENS160_TRIGGER_NONE=y
|
||||
CONFIG_FDC2X1X_TRIGGER_NONE=y
|
||||
CONFIG_FXAS21002_TRIGGER_NONE=y
|
||||
CONFIG_FXOS8700_TRIGGER_NONE=y
|
||||
|
|
|
@ -14,6 +14,7 @@ CONFIG_BMM150_TRIGGER_OWN_THREAD=y
|
|||
CONFIG_BMP388_TRIGGER_OWN_THREAD=y
|
||||
CONFIG_BQ274XX_TRIGGER_OWN_THREAD=y
|
||||
CONFIG_CCS811_TRIGGER_OWN_THREAD=y
|
||||
CONFIG_ENS160_TRIGGER_OWN_THREAD=y
|
||||
CONFIG_FDC2X1X_TRIGGER_OWN_THREAD=y
|
||||
CONFIG_FXAS21002_TRIGGER_OWN_THREAD=y
|
||||
CONFIG_FXOS8700_TRIGGER_OWN_THREAD=y
|
||||
|
|
|
@ -330,3 +330,10 @@ test_spi_lis2de12: lis2de12@29 {
|
|||
int2-gpios = <&test_gpio 0 0>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
test_spi_ens160: ens160@2a {
|
||||
compatible = "sciosense,ens160";
|
||||
reg = <0x2a>;
|
||||
spi-max-frequency = <0>;
|
||||
int-gpios = <&test_gpio 0 0>;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue