diff --git a/Documentation/components/drivers/special/sensors.rst b/Documentation/components/drivers/special/sensors.rst index 9cd4ee931fc..439acdfde86 100644 --- a/Documentation/components/drivers/special/sensors.rst +++ b/Documentation/components/drivers/special/sensors.rst @@ -27,6 +27,7 @@ tool for monitoring sensor activity at runtime. sensors/adt7320.rst sensors/adxl345.rst sensors/adxl362.rst + sensors/adxl367.rst sensors/adxl372.rst sensors/aht10.rst sensors/ak09912.rst diff --git a/Documentation/components/drivers/special/sensors/adxl367.rst b/Documentation/components/drivers/special/sensors/adxl367.rst new file mode 100644 index 00000000000..431a5d8866f --- /dev/null +++ b/Documentation/components/drivers/special/sensors/adxl367.rst @@ -0,0 +1,12 @@ +ADXL367 Accelerometer +===================== + +The ADXL367 driver provides I2C accelerometer support through the sensor +framework (uORB). It reports three-axis acceleration at a fixed range of ++/-2 g and die temperature, with output data rates from 12.5 to 400 Hz. + +Enable ``CONFIG_SENSORS_ADXL367`` and register an instance with +``adxl367_register()`` from ````. The function accepts +a device number, an I2C interface, and the sensor address (``0x1d`` or ``0x53``). +Samples are read on demand through ``/dev/uorb/sensor_accelN``, where ``N`` is +the device number. diff --git a/Documentation/components/drivers/special/sensors/sensors_uorb.rst b/Documentation/components/drivers/special/sensors/sensors_uorb.rst index fbccd910baf..e2dc8a40639 100644 --- a/Documentation/components/drivers/special/sensors/sensors_uorb.rst +++ b/Documentation/components/drivers/special/sensors/sensors_uorb.rst @@ -530,6 +530,7 @@ Implemented Drivers =================== - :doc:`adxl362` +- :doc:`adxl367` - :doc:`adxl372` - bh1749nuc - bme680 diff --git a/drivers/sensors/CMakeLists.txt b/drivers/sensors/CMakeLists.txt index 53f8d38b581..58fc93c6d54 100644 --- a/drivers/sensors/CMakeLists.txt +++ b/drivers/sensors/CMakeLists.txt @@ -384,6 +384,10 @@ if(CONFIG_SENSORS) list(APPEND SRCS adxl362_uorb.c) endif() + if(CONFIG_SENSORS_ADXL367) + list(APPEND SRCS adxl367.c) + endif() + if(CONFIG_SENSORS_ADXL372) if(CONFIG_SENSORS_ADXL372_UORB) list(APPEND SRCS adxl372_uorb.c) diff --git a/drivers/sensors/Kconfig b/drivers/sensors/Kconfig index 8e08788fe7f..0335bfd7bc0 100644 --- a/drivers/sensors/Kconfig +++ b/drivers/sensors/Kconfig @@ -1507,6 +1507,15 @@ config SENSORS_ADXL362_THREAD_STACKSIZE endif # SENSORS_ADXL362 +config SENSORS_ADXL367 + bool "Analog Devices ADXL367 accelerometer" + default n + select I2C + ---help--- + Enable the ADXL367 I2C accelerometer driver using the sensor + framework. Supports +/-2 g acceleration and die temperature, + with output data rates from 12.5 to 400 Hz. + config SENSORS_ADXL372 bool "Analog Devices ADXL372 Sensor support" default n diff --git a/drivers/sensors/Make.defs b/drivers/sensors/Make.defs index 9ad42cb74ee..c3627e43be2 100644 --- a/drivers/sensors/Make.defs +++ b/drivers/sensors/Make.defs @@ -372,6 +372,10 @@ ifeq ($(CONFIG_SENSORS_ADXL362),y) CSRCS += adxl362_uorb.c endif +ifeq ($(CONFIG_SENSORS_ADXL367),y) + CSRCS += adxl367.c +endif + ifeq ($(CONFIG_SENSORS_ADXL372),y) ifeq ($(CONFIG_SENSORS_ADXL372_UORB),y) CSRCS += adxl372_uorb.c diff --git a/drivers/sensors/adxl367.c b/drivers/sensors/adxl367.c new file mode 100644 index 00000000000..b519b7552ed --- /dev/null +++ b/drivers/sensors/adxl367.c @@ -0,0 +1,420 @@ +/**************************************************************************** + * drivers/sensors/adxl367.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 + +#include +#include +#include + +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ADXL367_I2C_FREQUENCY 100000 +#define ADXL367_DEVID_AD 0x00 +#define ADXL367_DEVID_AD_VALUE 0xad +#define ADXL367_DEVID_MST_VALUE 0x1d +#define ADXL367_PARTID_VALUE 0xf7 +#define ADXL367_STATUS 0x0b +#define ADXL367_STATUS_DATA_READY (1 << 0) +#define ADXL367_XDATA_H 0x0e +#define ADXL367_SOFT_RESET 0x1f +#define ADXL367_SOFT_RESET_VALUE 0x52 +#define ADXL367_FILTER_CTL 0x2c +#define ADXL367_POWER_CTL 0x2d +#define ADXL367_POWER_MEASURE 2 +#define ADXL367_TEMP_CTL 0x3d +#define ADXL367_TEMP_ENABLE (1 << 0) +#define ADXL367_DEFAULT_ODR 3 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct adxl367_dev_s +{ + struct sensor_lowerhalf_s lower; + FAR struct i2c_master_s *i2c; + mutex_t lock; + uint8_t addr; + uint8_t odr; + bool enabled; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int adxl367_activate(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, bool enable); +static int adxl367_set_interval(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, + FAR uint32_t *period_us); +static int adxl367_fetch(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, FAR char *buffer, + size_t buflen); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct sensor_ops_s g_adxl367_ops = +{ + .activate = adxl367_activate, + .set_interval = adxl367_set_interval, + .fetch = adxl367_fetch, +}; + +static const uint32_t g_adxl367_periods[] = +{ + 80000, 40000, 20000, 10000, 5000, 2500 +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: adxl367_getregs + ****************************************************************************/ + +static int adxl367_getregs(FAR struct adxl367_dev_s *priv, uint8_t reg, + FAR uint8_t *buffer, int len) +{ + struct i2c_msg_s msg[2] = + { + { + .frequency = ADXL367_I2C_FREQUENCY, + .addr = priv->addr, + .flags = I2C_M_NOSTOP, + .buffer = ®, + .length = 1, + }, + { + .frequency = ADXL367_I2C_FREQUENCY, + .addr = priv->addr, + .flags = I2C_M_READ, + .buffer = buffer, + .length = len, + } + }; + + int ret = I2C_TRANSFER(priv->i2c, msg, 2); + + return ret < 0 ? ret : OK; +} + +/**************************************************************************** + * Name: adxl367_putreg + ****************************************************************************/ + +static int adxl367_putreg(FAR struct adxl367_dev_s *priv, uint8_t reg, + uint8_t value) +{ + uint8_t buffer[2]; + struct i2c_config_s config = + { + .frequency = ADXL367_I2C_FREQUENCY, + .address = priv->addr, + .addrlen = 7, + }; + + int ret; + + buffer[0] = reg; + buffer[1] = value; + ret = i2c_write(priv->i2c, &config, buffer, sizeof(buffer)); + + return ret < 0 ? ret : OK; +} + +/**************************************************************************** + * Name: adxl367_set_power + ****************************************************************************/ + +static int adxl367_set_power(FAR struct adxl367_dev_s *priv, bool enable) +{ + int ret; + + ret = adxl367_putreg(priv, ADXL367_POWER_CTL, + enable ? ADXL367_POWER_MEASURE : 0); + if (ret == OK) + { + priv->enabled = enable; + } + + return ret; +} + +/**************************************************************************** + * Name: adxl367_activate + ****************************************************************************/ + +static int adxl367_activate(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, bool enable) +{ + FAR struct adxl367_dev_s *priv = (FAR struct adxl367_dev_s *)lower; + int ret; + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + ret = adxl367_set_power(priv, enable); + nxmutex_unlock(&priv->lock); + return ret; +} + +/**************************************************************************** + * Name: adxl367_set_interval + ****************************************************************************/ + +static int adxl367_set_interval(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, + FAR uint32_t *period_us) +{ + FAR struct adxl367_dev_s *priv = (FAR struct adxl367_dev_s *)lower; + bool enabled; + uint8_t odr = 0; + int ret; + int restore; + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + enabled = priv->enabled; + + while (odr < nitems(g_adxl367_periods) - 1 && + *period_us < g_adxl367_periods[odr]) + { + odr++; + } + + if (odr == priv->odr) + { + *period_us = g_adxl367_periods[odr]; + goto out; + } + + /* FILTER_CTL must only be changed in standby. Keep the range at +/-2 g + * and disable the nonstandard high speed I2C mode. + */ + + ret = adxl367_set_power(priv, false); + if (ret < 0) + { + goto out; + } + + ret = adxl367_putreg(priv, ADXL367_FILTER_CTL, odr); + if (ret == OK) + { + priv->odr = odr; + *period_us = g_adxl367_periods[odr]; + } + + if (enabled) + { + restore = adxl367_set_power(priv, true); + if (ret == OK) + { + ret = restore; + } + } + +out: + nxmutex_unlock(&priv->lock); + return ret; +} + +/**************************************************************************** + * Name: adxl367_data + ****************************************************************************/ + +static int16_t adxl367_data(FAR const uint8_t *data) +{ + return (int16_t)((data[0] << 8) | data[1]) / 4; +} + +/**************************************************************************** + * Name: adxl367_fetch + ****************************************************************************/ + +static int adxl367_fetch(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct adxl367_dev_s *priv = (FAR struct adxl367_dev_s *)lower; + struct sensor_accel sample; + uint8_t data[8]; + uint8_t status; + int ret; + + if (buflen < sizeof(sample)) + { + return -EINVAL; + } + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + ret = adxl367_getregs(priv, ADXL367_STATUS, &status, 1); + if (ret < 0) + { + goto out; + } + + if (!(status & ADXL367_STATUS_DATA_READY)) + { + ret = -EAGAIN; + goto out; + } + + ret = adxl367_getregs(priv, ADXL367_XDATA_H, data, sizeof(data)); + if (ret < 0) + { + goto out; + } + + sample.timestamp = sensor_get_timestamp(); + sample.x = sensor_data_muli(sensor_data_ftof(9.80665f / 4000), + adxl367_data(&data[0])); + sample.y = sensor_data_muli(sensor_data_ftof(9.80665f / 4000), + adxl367_data(&data[2])); + sample.z = sensor_data_muli(sensor_data_ftof(9.80665f / 4000), + adxl367_data(&data[4])); + + /* Typical uncalibrated temperature: 165 LSB at 25 C, 54 LSB/C. */ + + sample.temperature = + sensor_data_add(sensor_data_ftof(25.0f), + sensor_data_divi( + sensor_data_itof(adxl367_data(&data[6]) - 165), + 54)); + memcpy(buffer, &sample, sizeof(sample)); + ret = sizeof(sample); + +out: + nxmutex_unlock(&priv->lock); + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: adxl367_register + ****************************************************************************/ + +int adxl367_register(int devno, FAR struct i2c_master_s *i2c, uint8_t addr) +{ + FAR struct adxl367_dev_s *priv; + uint8_t id[3]; + int ret; + + if (i2c == NULL || (addr != 0x1d && addr != 0x53)) + { + return -EINVAL; + } + + priv = kmm_zalloc(sizeof(*priv)); + if (priv == NULL) + { + return -ENOMEM; + } + + priv->i2c = i2c; + priv->addr = addr; + priv->odr = ADXL367_DEFAULT_ODR; + priv->lower.ops = &g_adxl367_ops; + priv->lower.type = SENSOR_TYPE_ACCELEROMETER; + nxmutex_init(&priv->lock); + + /* Allow power-up to standby before the first bus access. */ + + nxsig_usleep(10000); + ret = adxl367_getregs(priv, ADXL367_DEVID_AD, id, sizeof(id)); + if (ret < 0) + { + goto errout; + } + + if (id[0] != ADXL367_DEVID_AD_VALUE || + id[1] != ADXL367_DEVID_MST_VALUE || + id[2] != ADXL367_PARTID_VALUE) + { + ret = -ENODEV; + goto errout; + } + + ret = adxl367_putreg(priv, ADXL367_SOFT_RESET, ADXL367_SOFT_RESET_VALUE); + if (ret < 0) + { + goto errout; + } + + nxsig_usleep(10000); + ret = adxl367_putreg(priv, ADXL367_FILTER_CTL, priv->odr); + if (ret < 0) + { + goto errout; + } + + ret = adxl367_putreg(priv, ADXL367_TEMP_CTL, ADXL367_TEMP_ENABLE); + if (ret < 0) + { + goto errout; + } + + ret = sensor_register(&priv->lower, devno); + if (ret < 0) + { + goto errout; + } + + return OK; + +errout: + nxmutex_destroy(&priv->lock); + kmm_free(priv); + return ret; +} diff --git a/include/nuttx/sensors/adxl367.h b/include/nuttx/sensors/adxl367.h new file mode 100644 index 00000000000..2be8d7638a6 --- /dev/null +++ b/include/nuttx/sensors/adxl367.h @@ -0,0 +1,68 @@ +/**************************************************************************** + * include/nuttx/sensors/adxl367.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_SENSORS_ADXL367_H +#define __INCLUDE_NUTTX_SENSORS_ADXL367_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: adxl367_register + * + * Description: + * Register the ADXL367 character device as uorb + * + * Input Parameters: + * devno - device instance + * i2c - An instance of the I2C interface to use to communicate with + * ADXL367 + * addr - I2C address (0x1d or 0x53). + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int adxl367_register(int devno, FAR struct i2c_master_s *i2c, uint8_t addr); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_NUTTX_SENSORS_ADXL367_H */