drivers/sensor/st: add support to LSM6DSVXXX IMU family
This driver is currently only supporting the polling-mode read_and_decode APIs (both blocking and non-blocking). The driver implements a chip_api structure which has to be used to provide device specific callbacks. The only lsm6dsvxxx family device currently supported is lsm6dsv320x. More information about LSM6DSV16X: https://www.st.com/resource/en/datasheet/lsm6dsv320x.pdf Signed-off-by: Armando Visconti <armando.visconti@st.com>
This commit is contained in:
committed by
Maureen Helm
parent
5372b41979
commit
d6eaae4d53
@@ -31,6 +31,7 @@ add_subdirectory_ifdef(CONFIG_LSM6DSL lsm6dsl)
|
||||
add_subdirectory_ifdef(CONFIG_LSM6DSO lsm6dso)
|
||||
add_subdirectory_ifdef(CONFIG_LSM6DSO16IS lsm6dso16is)
|
||||
add_subdirectory_ifdef(CONFIG_LSM6DSV16X lsm6dsv16x)
|
||||
add_subdirectory_ifdef(CONFIG_LSM6DSVXXX lsm6dsvxxx)
|
||||
add_subdirectory_ifdef(CONFIG_LSM9DS0_GYRO lsm9ds0_gyro)
|
||||
add_subdirectory_ifdef(CONFIG_LSM9DS0_MFD lsm9ds0_mfd)
|
||||
add_subdirectory_ifdef(CONFIG_LSM9DS1 lsm9ds1)
|
||||
|
||||
@@ -30,6 +30,7 @@ source "drivers/sensor/st/lsm6dsl/Kconfig"
|
||||
source "drivers/sensor/st/lsm6dso/Kconfig"
|
||||
source "drivers/sensor/st/lsm6dso16is/Kconfig"
|
||||
source "drivers/sensor/st/lsm6dsv16x/Kconfig"
|
||||
source "drivers/sensor/st/lsm6dsvxxx/Kconfig"
|
||||
source "drivers/sensor/st/lsm9ds0_gyro/Kconfig"
|
||||
source "drivers/sensor/st/lsm9ds0_mfd/Kconfig"
|
||||
source "drivers/sensor/st/lsm9ds1/Kconfig"
|
||||
|
||||
13
drivers/sensor/st/lsm6dsvxxx/CMakeLists.txt
Normal file
13
drivers/sensor/st/lsm6dsvxxx/CMakeLists.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
# ST Microelectronics LSM6DSVXXX accelerometer sensor
|
||||
#
|
||||
# Copyright (c) 2025 STMicroelectronics
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
zephyr_library()
|
||||
|
||||
zephyr_library_sources(lsm6dsvxxx.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_DT_HAS_ST_LSM6DSV320X_ENABLED lsm6dsv320x.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_SENSOR_ASYNC_API lsm6dsvxxx_decoder.c)
|
||||
|
||||
zephyr_library_include_directories(../stmemsc)
|
||||
27
drivers/sensor/st/lsm6dsvxxx/Kconfig
Normal file
27
drivers/sensor/st/lsm6dsvxxx/Kconfig
Normal file
@@ -0,0 +1,27 @@
|
||||
# ST Microelectronics LSM6DSVXXX accelerometer sensor
|
||||
|
||||
# Copyright (c) 2025 STMicroelectronics
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig LSM6DSVXXX
|
||||
bool "LSM6DSVXXX IMU sensor"
|
||||
default y
|
||||
depends on DT_HAS_ST_LSM6DSV320X_ENABLED
|
||||
depends on ZEPHYR_HAL_ST_MODULE
|
||||
select I2C if $(dt_compat_on_bus,$(DT_COMPAT_ST_LSM6DSV320X),i2c)
|
||||
select I3C if $(dt_compat_on_bus,$(DT_COMPAT_ST_LSM6DSV320X),i3c)
|
||||
select SPI if $(dt_compat_on_bus,$(DT_COMPAT_ST_LSM6DSV320X),spi)
|
||||
select HAS_STMEMSC
|
||||
select USE_STDC_LSM6DSV320X if DT_HAS_ST_LSM6DSV320X_ENABLED
|
||||
help
|
||||
Enable driver for LSM6DSVXXX family IMU sensors.
|
||||
|
||||
if LSM6DSVXXX
|
||||
|
||||
config LSM6DSVXXX_ENABLE_TEMP
|
||||
bool "Temperature"
|
||||
help
|
||||
Enable/disable temperature sensor
|
||||
|
||||
|
||||
endif # LSM6DSVXXX
|
||||
648
drivers/sensor/st/lsm6dsvxxx/lsm6dsv320x.c
Normal file
648
drivers/sensor/st/lsm6dsvxxx/lsm6dsv320x.c
Normal file
@@ -0,0 +1,648 @@
|
||||
/* ST Microelectronics LSM6DSVXXX family IMU sensor
|
||||
*
|
||||
* Copyright (c) 2025 STMicroelectronics
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Datasheet:
|
||||
* https://www.st.com/resource/en/datasheet/lsm6dsv320x.pdf
|
||||
*/
|
||||
|
||||
#include "lsm6dsv320x.h"
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(LSM6DSVXXX, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
/*
|
||||
* XL configuration
|
||||
*/
|
||||
|
||||
static uint16_t lsm6dsv320x_accel_gain_ug(uint8_t fs)
|
||||
{
|
||||
if (fs == 8) {
|
||||
/* range is 320g */
|
||||
return 10417;
|
||||
}
|
||||
|
||||
return (61 * (1 << fs));
|
||||
}
|
||||
|
||||
/* The first nibble of fs tells if it is High-G or not */
|
||||
static int lsm6dsv320x_accel_range_to_fs_val(const struct device *dev,
|
||||
int32_t range,
|
||||
uint8_t *fs)
|
||||
{
|
||||
switch (range) {
|
||||
case LSM6DSV320X_DT_FS_2G:
|
||||
*fs = 0;
|
||||
break;
|
||||
|
||||
case LSM6DSV320X_DT_FS_4G:
|
||||
*fs = 1;
|
||||
break;
|
||||
|
||||
case LSM6DSV320X_DT_FS_8G:
|
||||
*fs = 2;
|
||||
break;
|
||||
|
||||
case LSM6DSV320X_DT_FS_16G:
|
||||
*fs = 3;
|
||||
break;
|
||||
|
||||
case LSM6DSV320X_DT_FS_32G:
|
||||
*fs = 4;
|
||||
break;
|
||||
|
||||
case LSM6DSV320X_DT_FS_64G:
|
||||
*fs = 5;
|
||||
break;
|
||||
|
||||
case LSM6DSV320X_DT_FS_128G:
|
||||
*fs = 6;
|
||||
break;
|
||||
|
||||
case LSM6DSV320X_DT_FS_256G:
|
||||
*fs = 7;
|
||||
break;
|
||||
|
||||
case LSM6DSV320X_DT_FS_320G:
|
||||
*fs = 8;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lsm6dsv320x_accel_set_fs_raw(const struct device *dev, uint8_t fs)
|
||||
{
|
||||
const struct lsm6dsvxxx_config *cfg = dev->config;
|
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
||||
struct lsm6dsvxxx_data *data = dev->data;
|
||||
|
||||
if (fs < 4) { /* 2g/4g/8g/16g */
|
||||
lsm6dsv320x_xl_full_scale_t val = fs;
|
||||
|
||||
if (lsm6dsv320x_xl_full_scale_set(ctx, val) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
data->out_xl = LSM6DSV320X_OUTX_L_A;
|
||||
} else if (fs <= 8) { /* 32g/64g/128g/256g/320g */
|
||||
lsm6dsv320x_hg_xl_full_scale_t val = (fs - 4);
|
||||
|
||||
if (lsm6dsv320x_hg_xl_full_scale_set(ctx, val) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
data->out_xl = LSM6DSV320X_UI_OUTX_L_A_OIS_HG;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data->accel_fs = fs;
|
||||
data->acc_gain = lsm6dsv320x_accel_gain_ug(fs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lsm6dsv320x_accel_set_fs(const struct device *dev, int32_t range)
|
||||
{
|
||||
uint8_t fs;
|
||||
int ret;
|
||||
|
||||
ret = lsm6dsv320x_accel_range_to_fs_val(dev, range, &fs);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = lsm6dsv320x_accel_set_fs_raw(dev, fs);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lsm6dsv320x_accel_set_odr_raw(const struct device *dev, uint8_t odr)
|
||||
{
|
||||
const struct lsm6dsvxxx_config *cfg = dev->config;
|
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
||||
struct lsm6dsvxxx_data *data = dev->data;
|
||||
|
||||
if (cfg->accel_hg_odr != LSM6DSV320X_HG_XL_ODR_OFF) {
|
||||
|
||||
if (lsm6dsv320x_hg_xl_data_rate_set(ctx, cfg->accel_hg_odr, 1) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
if (lsm6dsv320x_xl_data_rate_set(ctx, odr) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
data->accel_freq = odr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* values taken from lsm6dsv320x_data_rate_t in hal/st module. The mode/accuracy
|
||||
* should be selected through accel-odr property in DT
|
||||
*/
|
||||
static const float lsm6dsv320x_odr_map[3][13] = {
|
||||
/* High Accuracy off */
|
||||
{0.0f, 1.875f, 7.5f, 15.0f, 30.0f, 60.0f,
|
||||
120.0f, 240.0f, 480.0f, 960.0f, 1920.0f,
|
||||
3840.0f, 7680.0f},
|
||||
|
||||
/* High Accuracy 1 */
|
||||
{0.0f, 1.875f, 7.5f, 15.625f, 31.25f, 62.5f,
|
||||
125.0f, 250.0f, 500.0f, 1000.0f, 2000.0f,
|
||||
4000.0f, 8000.0f},
|
||||
|
||||
/* High Accuracy 2 */
|
||||
{0.0f, 1.875f, 7.5f, 12.5f, 25.0f, 50.0f,
|
||||
100.0f, 200.0f, 400.0f, 800.0f, 1600.0f,
|
||||
3200.0f, 6400.0f},
|
||||
};
|
||||
|
||||
static uint8_t lsm6dsv320x_freq_to_odr_val(const struct device *dev, int32_t freq)
|
||||
{
|
||||
const struct lsm6dsvxxx_config *cfg = dev->config;
|
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
||||
lsm6dsv320x_data_rate_t odr;
|
||||
int8_t mode;
|
||||
size_t i;
|
||||
|
||||
if (lsm6dsv320x_xl_data_rate_get(ctx, &odr) < 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mode = (odr >> 4) & 0xf;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(lsm6dsv320x_odr_map[mode]); i++) {
|
||||
if (freq <= lsm6dsv320x_odr_map[mode][i]) {
|
||||
LOG_DBG("mode: %d - odr: %d", mode, i);
|
||||
return i | (mode << 4);
|
||||
}
|
||||
}
|
||||
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
static int lsm6dsv320x_accel_set_odr(const struct device *dev, int32_t freq)
|
||||
{
|
||||
uint8_t odr;
|
||||
|
||||
odr = lsm6dsv320x_freq_to_odr_val(dev, freq);
|
||||
if (odr == 0xFF) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (lsm6dsv320x_accel_set_odr_raw(dev, odr) < 0) {
|
||||
LOG_DBG("failed to set accelerometer sampling rate");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t lsm6dsv320x_accel_set_mode(const struct device *dev, int32_t mode)
|
||||
{
|
||||
const struct lsm6dsvxxx_config *cfg = dev->config;
|
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
||||
|
||||
switch (mode) {
|
||||
case 0: /* High Performance */
|
||||
mode = LSM6DSV320X_XL_HIGH_PERFORMANCE_MD;
|
||||
break;
|
||||
case 1: /* High Accuracy */
|
||||
mode = LSM6DSV320X_XL_HIGH_ACCURACY_ODR_MD;
|
||||
break;
|
||||
case 3: /* ODR triggered */
|
||||
mode = LSM6DSV320X_XL_ODR_TRIGGERED_MD;
|
||||
break;
|
||||
case 4: /* Low Power 2 */
|
||||
mode = LSM6DSV320X_XL_LOW_POWER_2_AVG_MD;
|
||||
break;
|
||||
case 5: /* Low Power 4 */
|
||||
mode = LSM6DSV320X_XL_LOW_POWER_4_AVG_MD;
|
||||
break;
|
||||
case 6: /* Low Power 8 */
|
||||
mode = LSM6DSV320X_XL_LOW_POWER_8_AVG_MD;
|
||||
break;
|
||||
case 7: /* Normal */
|
||||
mode = LSM6DSV320X_XL_NORMAL_MD;
|
||||
break;
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return lsm6dsv320x_xl_mode_set(ctx, mode);
|
||||
}
|
||||
|
||||
static int32_t lsm6dsv320x_accel_get_fs(const struct device *dev, int32_t *range)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int32_t lsm6dsv320x_accel_get_odr(const struct device *dev, int32_t *freq)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int32_t lsm6dsv320x_accel_get_mode(const struct device *dev, int32_t *mode)
|
||||
{
|
||||
const struct lsm6dsvxxx_config *cfg = dev->config;
|
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
||||
lsm6dsv320x_xl_mode_t md;
|
||||
|
||||
lsm6dsv320x_xl_mode_get(ctx, &md);
|
||||
|
||||
switch (md) {
|
||||
case LSM6DSV320X_XL_HIGH_PERFORMANCE_MD:
|
||||
*mode = 0;
|
||||
break;
|
||||
case LSM6DSV320X_XL_HIGH_ACCURACY_ODR_MD:
|
||||
*mode = 1;
|
||||
break;
|
||||
case LSM6DSV320X_XL_ODR_TRIGGERED_MD:
|
||||
*mode = 3;
|
||||
break;
|
||||
case LSM6DSV320X_XL_LOW_POWER_2_AVG_MD:
|
||||
*mode = 4;
|
||||
break;
|
||||
case LSM6DSV320X_XL_LOW_POWER_4_AVG_MD:
|
||||
*mode = 5;
|
||||
break;
|
||||
case LSM6DSV320X_XL_LOW_POWER_8_AVG_MD:
|
||||
*mode = 6;
|
||||
break;
|
||||
case LSM6DSV320X_XL_NORMAL_MD:
|
||||
*mode = 7;
|
||||
break;
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* GY configuration
|
||||
*/
|
||||
|
||||
static int lsm6dsv320x_gyro_range_to_fs_val(const struct device *dev,
|
||||
int32_t range,
|
||||
uint8_t *fs)
|
||||
{
|
||||
switch (range) {
|
||||
case 0:
|
||||
*fs = 0;
|
||||
break;
|
||||
|
||||
case 250:
|
||||
*fs = LSM6DSV320X_DT_FS_250DPS;
|
||||
break;
|
||||
|
||||
case 500:
|
||||
*fs = LSM6DSV320X_DT_FS_500DPS;
|
||||
break;
|
||||
|
||||
case 1000:
|
||||
*fs = LSM6DSV320X_DT_FS_1000DPS;
|
||||
break;
|
||||
|
||||
case 2000:
|
||||
*fs = LSM6DSV320X_DT_FS_2000DPS;
|
||||
break;
|
||||
|
||||
case 4000:
|
||||
*fs = LSM6DSV320X_DT_FS_4000DPS;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t lsm6dsv320x_gyro_gain_udps(uint8_t fs)
|
||||
{
|
||||
return (4375 * (1 << fs));
|
||||
}
|
||||
|
||||
static int lsm6dsv320x_gyro_set_fs_raw(const struct device *dev, uint8_t fs)
|
||||
{
|
||||
const struct lsm6dsvxxx_config *cfg = dev->config;
|
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
||||
struct lsm6dsvxxx_data *data = dev->data;
|
||||
|
||||
if (fs == 0) {
|
||||
/* skip power-up value */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lsm6dsv320x_gy_full_scale_set(ctx, fs) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
data->gyro_fs = fs;
|
||||
data->gyro_gain = lsm6dsv320x_gyro_gain_udps(fs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lsm6dsv320x_gyro_set_fs(const struct device *dev, int32_t range)
|
||||
{
|
||||
uint8_t fs;
|
||||
int ret;
|
||||
|
||||
ret = lsm6dsv320x_gyro_range_to_fs_val(dev, range, &fs);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (lsm6dsv320x_gyro_set_fs_raw(dev, fs) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lsm6dsv320x_gyro_set_odr_raw(const struct device *dev, uint8_t odr)
|
||||
{
|
||||
const struct lsm6dsvxxx_config *cfg = dev->config;
|
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
||||
struct lsm6dsvxxx_data *data = dev->data;
|
||||
|
||||
if (lsm6dsv320x_gy_data_rate_set(ctx, odr) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
data->gyro_freq = odr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lsm6dsv320x_gyro_set_odr(const struct device *dev, int32_t freq)
|
||||
{
|
||||
uint8_t odr;
|
||||
|
||||
odr = lsm6dsv320x_freq_to_odr_val(dev, freq);
|
||||
if (odr == 0xFF) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (lsm6dsv320x_gyro_set_odr_raw(dev, odr) < 0) {
|
||||
LOG_DBG("failed to set gyroscope sampling rate");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t lsm6dsv320x_gyro_set_mode(const struct device *dev, int32_t mode)
|
||||
{
|
||||
const struct lsm6dsvxxx_config *cfg = dev->config;
|
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
||||
|
||||
switch (mode) {
|
||||
case 0: /* High Performance */
|
||||
mode = LSM6DSV320X_GY_HIGH_PERFORMANCE_MD;
|
||||
break;
|
||||
case 1: /* High Accuracy */
|
||||
mode = LSM6DSV320X_GY_HIGH_ACCURACY_ODR_MD;
|
||||
break;
|
||||
case 4: /* Sleep */
|
||||
mode = LSM6DSV320X_GY_SLEEP_MD;
|
||||
break;
|
||||
case 5: /* Low Power */
|
||||
mode = LSM6DSV320X_GY_LOW_POWER_MD;
|
||||
break;
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return lsm6dsv320x_gy_mode_set(ctx, mode);
|
||||
}
|
||||
|
||||
static int32_t lsm6dsv320x_gyro_get_fs(const struct device *dev, int32_t *range)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int32_t lsm6dsv320x_gyro_get_odr(const struct device *dev, int32_t *freq)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int32_t lsm6dsv320x_gyro_get_mode(const struct device *dev, int32_t *mode)
|
||||
{
|
||||
const struct lsm6dsvxxx_config *cfg = dev->config;
|
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
||||
lsm6dsv320x_gy_mode_t md;
|
||||
|
||||
lsm6dsv320x_gy_mode_get(ctx, &md);
|
||||
|
||||
switch (md) {
|
||||
case LSM6DSV320X_GY_HIGH_PERFORMANCE_MD:
|
||||
*mode = 0;
|
||||
break;
|
||||
case LSM6DSV320X_GY_HIGH_ACCURACY_ODR_MD:
|
||||
*mode = 1;
|
||||
break;
|
||||
case LSM6DSV320X_GY_SLEEP_MD:
|
||||
*mode = 4;
|
||||
break;
|
||||
case LSM6DSV320X_GY_LOW_POWER_MD:
|
||||
*mode = 5;
|
||||
break;
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lsm6dsv320x_init_chip(const struct device *dev)
|
||||
{
|
||||
const struct lsm6dsvxxx_config *cfg = dev->config;
|
||||
struct lsm6dsvxxx_data *data = dev->data;
|
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
||||
uint8_t chip_id;
|
||||
uint8_t odr, fs;
|
||||
|
||||
#if LSM6DSVXXX_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
|
||||
if (cfg->i3c.bus != NULL) {
|
||||
/*
|
||||
* Need to grab the pointer to the I3C device descriptor
|
||||
* before we can talk to the sensor.
|
||||
*/
|
||||
lsm6dsvxxx->i3c_dev = i3c_device_find(cfg->i3c.bus, &cfg->i3c.dev_id);
|
||||
if (lsm6dsvxxx->i3c_dev == NULL) {
|
||||
LOG_ERR("Cannot find I3C device descriptor");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* All registers except 0x01 are different between banks, including the WHO_AM_I
|
||||
* register and the register used for a SW reset. If the lsm6dsvxxx wasn't on the user
|
||||
* bank when it reset, then both the chip id check and the sw reset will fail unless we
|
||||
* set the bank now.
|
||||
*/
|
||||
if (lsm6dsv320x_mem_bank_set(ctx, LSM6DSV320X_MAIN_MEM_BANK) < 0) {
|
||||
LOG_DBG("Failed to set user bank");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (lsm6dsv320x_device_id_get(ctx, &chip_id) < 0) {
|
||||
LOG_DBG("Failed reading chip id");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
LOG_INF("chip id 0x%x", chip_id);
|
||||
|
||||
if (chip_id != LSM6DSV320X_ID) {
|
||||
LOG_DBG("Invalid chip id 0x%x", chip_id);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Resetting the whole device while using I3C will also reset the DA, therefore perform
|
||||
* only a software reset if the bus is I3C. It should be assumed that the device was
|
||||
* already fully reset by the I3C CCC RSTACT (whole chip) done as apart of the I3C Bus
|
||||
* initialization.
|
||||
*/
|
||||
if (ON_I3C_BUS(cfg)) {
|
||||
/* Restore default configuration */
|
||||
lsm6dsv320x_reboot(ctx);
|
||||
|
||||
/* wait 150us as reported in AN5763 */
|
||||
k_sleep(K_USEC(150));
|
||||
} else {
|
||||
/* reset device (sw_por) */
|
||||
if (lsm6dsv320x_sw_por(ctx) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* wait 30ms as reported in AN5763 */
|
||||
k_sleep(K_MSEC(30));
|
||||
}
|
||||
|
||||
data->out_xl = LSM6DSV320X_OUTX_L_A;
|
||||
data->out_tp = LSM6DSV320X_OUT_TEMP_L;
|
||||
|
||||
fs = cfg->accel_range;
|
||||
LOG_DBG("accel range is %d", fs);
|
||||
if (lsm6dsv320x_accel_set_fs_raw(dev, fs) < 0) {
|
||||
LOG_ERR("failed to set accelerometer range %d", fs);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
odr = cfg->accel_odr;
|
||||
LOG_DBG("accel odr is %d", odr);
|
||||
if (lsm6dsv320x_accel_set_odr_raw(dev, odr) < 0) {
|
||||
LOG_ERR("failed to set accelerometer odr %d", odr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
fs = cfg->gyro_range;
|
||||
LOG_DBG("gyro range is %d", fs);
|
||||
if (lsm6dsv320x_gyro_set_fs_raw(dev, fs) < 0) {
|
||||
LOG_ERR("failed to set gyroscope range %d", fs);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
odr = cfg->gyro_odr;
|
||||
LOG_DBG("gyro odr is %d", odr);
|
||||
if (lsm6dsv320x_gyro_set_odr_raw(dev, odr) < 0) {
|
||||
LOG_ERR("failed to set gyroscope odr %d", odr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
#if LSM6DSVXXX_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
|
||||
if (IS_ENABLED(CONFIG_LSM6DSVXXX_STREAM) && (ON_I3C_BUS(cfg))) {
|
||||
/*
|
||||
* Set MRL to the Max Size of the FIFO so the entire FIFO can be read
|
||||
* out at once
|
||||
*/
|
||||
struct i3c_ccc_mrl setmrl = {
|
||||
.len = 0x0700,
|
||||
.ibi_len = lsm6dsvxxx->i3c_dev->data_length.max_ibi,
|
||||
};
|
||||
if (i3c_ccc_do_setmrl(lsm6dsvxxx->i3c_dev, &setmrl) < 0) {
|
||||
LOG_ERR("failed to set mrl");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (lsm6dsv320x_block_data_update_set(ctx, 1) < 0) {
|
||||
LOG_DBG("failed to set BDU mode");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PM_DEVICE)
|
||||
static int lsm6dsv320x_pm_action(const struct device *dev, enum pm_device_action action)
|
||||
{
|
||||
struct lsm6dsvxxx_data *data = dev->data;
|
||||
const struct lsm6dsvxxx_config *cfg = dev->config;
|
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
||||
int ret = 0;
|
||||
|
||||
LOG_DBG("PM action: %d", (int)action);
|
||||
|
||||
switch (action) {
|
||||
case PM_DEVICE_ACTION_RESUME:
|
||||
if (lsm6dsv320x_xl_data_rate_set(ctx, data->accel_freq) < 0) {
|
||||
LOG_ERR("failed to set accelerometer odr %d", (int)data->accel_freq);
|
||||
ret = -EIO;
|
||||
}
|
||||
if (lsm6dsv320x_gy_data_rate_set(ctx, data->gyro_freq) < 0) {
|
||||
LOG_ERR("failed to set gyroscope odr %d", (int)data->gyro_freq);
|
||||
ret = -EIO;
|
||||
}
|
||||
break;
|
||||
case PM_DEVICE_ACTION_SUSPEND:
|
||||
if (lsm6dsv320x_xl_data_rate_set(ctx, LSM6DSVXXX_DT_ODR_OFF) < 0) {
|
||||
LOG_ERR("failed to disable accelerometer");
|
||||
ret = -EIO;
|
||||
}
|
||||
if (lsm6dsv320x_gy_data_rate_set(ctx, LSM6DSVXXX_DT_ODR_OFF) < 0) {
|
||||
LOG_ERR("failed to disable gyroscope");
|
||||
ret = -EIO;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -ENOTSUP;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM_DEVICE */
|
||||
|
||||
const struct lsm6dsvxxx_chip_api st_lsm6dsv320x_chip_api = {
|
||||
.init_chip = lsm6dsv320x_init_chip,
|
||||
#if defined(CONFIG_PM_DEVICE)
|
||||
.pm_action = lsm6dsv320x_pm_action,
|
||||
#endif /* CONFIG_PM_DEVICE */
|
||||
.accel_fs_set = lsm6dsv320x_accel_set_fs,
|
||||
.accel_odr_set = lsm6dsv320x_accel_set_odr,
|
||||
.accel_mode_set = lsm6dsv320x_accel_set_mode,
|
||||
.accel_fs_get = lsm6dsv320x_accel_get_fs,
|
||||
.accel_odr_get = lsm6dsv320x_accel_get_odr,
|
||||
.accel_mode_get = lsm6dsv320x_accel_get_mode,
|
||||
.gyro_fs_set = lsm6dsv320x_gyro_set_fs,
|
||||
.gyro_odr_set = lsm6dsv320x_gyro_set_odr,
|
||||
.gyro_mode_set = lsm6dsv320x_gyro_set_mode,
|
||||
.gyro_fs_get = lsm6dsv320x_gyro_get_fs,
|
||||
.gyro_odr_get = lsm6dsv320x_gyro_get_odr,
|
||||
.gyro_mode_get = lsm6dsv320x_gyro_get_mode,
|
||||
};
|
||||
23
drivers/sensor/st/lsm6dsvxxx/lsm6dsv320x.h
Normal file
23
drivers/sensor/st/lsm6dsvxxx/lsm6dsv320x.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/* ST Microelectronics LSM6DSVXXX family IMU sensor
|
||||
*
|
||||
* Copyright (c) 2025 STMicroelectronics
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Datasheet:
|
||||
* https://www.st.com/resource/en/datasheet/lsm6dsv320x.pdf
|
||||
*/
|
||||
#ifndef ZEPHYR_DRIVERS_SENSOR_LSM6DSV320X_H_
|
||||
#define ZEPHYR_DRIVERS_SENSOR_LSM6DSV320X_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stmemsc.h>
|
||||
|
||||
#include "lsm6dsvxxx.h"
|
||||
#include "lsm6dsv320x_reg.h"
|
||||
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
|
||||
extern const struct lsm6dsvxxx_chip_api st_lsm6dsv320x_chip_api;
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_SENSOR_LSM6DSV320X_H_ */
|
||||
502
drivers/sensor/st/lsm6dsvxxx/lsm6dsvxxx.c
Normal file
502
drivers/sensor/st/lsm6dsvxxx/lsm6dsvxxx.c
Normal file
@@ -0,0 +1,502 @@
|
||||
/* ST Microelectronics LSM6DSVXXX family IMU sensor
|
||||
*
|
||||
* Copyright (c) 2025 STMicroelectronics
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Datasheet:
|
||||
* https://www.st.com/resource/en/datasheet/lsm6dsv320x.pdf
|
||||
*/
|
||||
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/init.h>
|
||||
#include <string.h>
|
||||
#include <zephyr/sys/__assert.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "lsm6dsvxxx.h"
|
||||
#include "lsm6dsvxxx_rtio.h"
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(st_lsm6dsv320x)
|
||||
#include "lsm6dsv320x.h"
|
||||
#endif
|
||||
|
||||
LOG_MODULE_REGISTER(LSM6DSVXXX, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
static int lsm6dsvxxx_accel_config(const struct device *dev,
|
||||
enum sensor_channel chan,
|
||||
enum sensor_attribute attr,
|
||||
const struct sensor_value *val)
|
||||
{
|
||||
const struct lsm6dsvxxx_config *cfg = dev->config;
|
||||
|
||||
switch (attr) {
|
||||
case SENSOR_ATTR_FULL_SCALE:
|
||||
return cfg->chip_api->accel_fs_set(dev, sensor_ms2_to_g(val));
|
||||
case SENSOR_ATTR_SAMPLING_FREQUENCY:
|
||||
return cfg->chip_api->accel_odr_set(dev, val->val1);
|
||||
case SENSOR_ATTR_CONFIGURATION:
|
||||
return cfg->chip_api->accel_mode_set(dev, val->val1);
|
||||
default:
|
||||
LOG_DBG("Accel attribute not supported.");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lsm6dsvxxx_gyro_config(const struct device *dev,
|
||||
enum sensor_channel chan,
|
||||
enum sensor_attribute attr,
|
||||
const struct sensor_value *val)
|
||||
{
|
||||
const struct lsm6dsvxxx_config *cfg = dev->config;
|
||||
|
||||
switch (attr) {
|
||||
case SENSOR_ATTR_FULL_SCALE:
|
||||
return cfg->chip_api->gyro_fs_set(dev, sensor_rad_to_degrees(val));
|
||||
case SENSOR_ATTR_SAMPLING_FREQUENCY:
|
||||
return cfg->chip_api->gyro_odr_set(dev, val->val1);
|
||||
case SENSOR_ATTR_CONFIGURATION:
|
||||
return cfg->chip_api->gyro_mode_set(dev, val->val1);
|
||||
default:
|
||||
LOG_DBG("Gyro attribute not supported.");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lsm6dsvxxx_attr_set(const struct device *dev,
|
||||
enum sensor_channel chan,
|
||||
enum sensor_attribute attr,
|
||||
const struct sensor_value *val)
|
||||
{
|
||||
switch (chan) {
|
||||
case SENSOR_CHAN_ACCEL_XYZ:
|
||||
return lsm6dsvxxx_accel_config(dev, chan, attr, val);
|
||||
case SENSOR_CHAN_GYRO_XYZ:
|
||||
return lsm6dsvxxx_gyro_config(dev, chan, attr, val);
|
||||
#ifdef CONFIG_LSM6DSVXXX_STREAM
|
||||
case SENSOR_CHAN_GBIAS_XYZ:
|
||||
return lsm6dsvxxx_gbias_config(dev, chan, attr, val);
|
||||
#endif /* CONFIG_LSM6DSVXXX_STREAM */
|
||||
default:
|
||||
LOG_WRN("attr_set() not supported on this channel.");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lsm6dsvxxx_accel_get_config(const struct device *dev,
|
||||
enum sensor_channel chan,
|
||||
enum sensor_attribute attr,
|
||||
struct sensor_value *val)
|
||||
{
|
||||
const struct lsm6dsvxxx_config *cfg = dev->config;
|
||||
|
||||
switch (attr) {
|
||||
case SENSOR_ATTR_CONFIGURATION:
|
||||
return cfg->chip_api->accel_mode_get(dev, &val->val1);
|
||||
default:
|
||||
LOG_DBG("Accel attribute not supported.");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lsm6dsvxxx_gyro_get_config(const struct device *dev,
|
||||
enum sensor_channel chan,
|
||||
enum sensor_attribute attr,
|
||||
struct sensor_value *val)
|
||||
{
|
||||
const struct lsm6dsvxxx_config *cfg = dev->config;
|
||||
|
||||
switch (attr) {
|
||||
case SENSOR_ATTR_CONFIGURATION:
|
||||
return cfg->chip_api->gyro_mode_get(dev, &val->val1);
|
||||
default:
|
||||
LOG_DBG("Gyro attribute not supported.");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lsm6dsvxxx_attr_get(const struct device *dev, enum sensor_channel chan,
|
||||
enum sensor_attribute attr, struct sensor_value *val)
|
||||
{
|
||||
switch (chan) {
|
||||
case SENSOR_CHAN_ACCEL_XYZ:
|
||||
return lsm6dsvxxx_accel_get_config(dev, chan, attr, val);
|
||||
case SENSOR_CHAN_GYRO_XYZ:
|
||||
return lsm6dsvxxx_gyro_get_config(dev, chan, attr, val);
|
||||
#ifdef CONFIG_LSM6DSVXXX_STREAM
|
||||
case SENSOR_CHAN_GBIAS_XYZ:
|
||||
return lsm6dsvxxx_gbias_get_config(dev, chan, attr, val);
|
||||
#endif /* CONFIG_LSM6DSVXXX_STREAM */
|
||||
default:
|
||||
LOG_WRN("attr_get() not supported on this channel.");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lsm6dsvxxx_one_shot_complete_cb(struct rtio *ctx, const struct rtio_sqe *sqe,
|
||||
int result, void *arg)
|
||||
{
|
||||
ARG_UNUSED(result);
|
||||
|
||||
struct rtio_iodev_sqe *iodev_sqe = (struct rtio_iodev_sqe *)sqe->userdata;
|
||||
int err = 0;
|
||||
|
||||
err = rtio_flush_completion_queue(ctx);
|
||||
|
||||
if (err) {
|
||||
rtio_iodev_sqe_err(iodev_sqe, err);
|
||||
} else {
|
||||
rtio_iodev_sqe_ok(iodev_sqe, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void lsm6dsvxxx_submit_one_shot(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
|
||||
{
|
||||
const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data;
|
||||
const struct sensor_chan_spec *const channels = cfg->channels;
|
||||
const size_t num_channels = cfg->count;
|
||||
uint32_t min_buf_len = sizeof(struct lsm6dsvxxx_rtio_data);
|
||||
uint64_t cycles;
|
||||
int rc = 0;
|
||||
uint8_t *buf;
|
||||
uint32_t buf_len;
|
||||
struct lsm6dsvxxx_rtio_data *edata;
|
||||
struct lsm6dsvxxx_data *data = dev->data;
|
||||
|
||||
/* Get the buffer for the frame, it may be allocated dynamically by the rtio context */
|
||||
rc = rtio_sqe_rx_buf(iodev_sqe, min_buf_len, min_buf_len, &buf, &buf_len);
|
||||
if (rc != 0) {
|
||||
LOG_ERR("Failed to get a read buffer of size %u bytes", min_buf_len);
|
||||
return;
|
||||
}
|
||||
|
||||
edata = (struct lsm6dsvxxx_rtio_data *)buf;
|
||||
|
||||
edata->has_accel = 0;
|
||||
edata->has_temp = 0;
|
||||
|
||||
rc = sensor_clock_get_cycles(&cycles);
|
||||
if (rc != 0) {
|
||||
LOG_ERR("Failed to get sensor clock cycles");
|
||||
rtio_iodev_sqe_err(iodev_sqe, rc);
|
||||
return;
|
||||
}
|
||||
|
||||
edata->header.is_fifo = false;
|
||||
edata->header.range = data->accel_fs;
|
||||
edata->header.timestamp = sensor_clock_cycles_to_ns(cycles);
|
||||
|
||||
for (int i = 0; i < num_channels; i++) {
|
||||
switch (channels[i].chan_type) {
|
||||
case SENSOR_CHAN_ACCEL_X:
|
||||
case SENSOR_CHAN_ACCEL_Y:
|
||||
case SENSOR_CHAN_ACCEL_Z:
|
||||
case SENSOR_CHAN_ACCEL_XYZ:
|
||||
edata->has_accel = 1;
|
||||
|
||||
uint8_t xl_addr = lsm6dsvxxx_bus_reg(data->bus_type, data->out_xl);
|
||||
struct rtio_regs outx_regs;
|
||||
struct rtio_regs_list xl_regs_list[] = {
|
||||
{
|
||||
xl_addr,
|
||||
(uint8_t *)edata->accel,
|
||||
6,
|
||||
},
|
||||
};
|
||||
|
||||
outx_regs.rtio_regs_list = xl_regs_list;
|
||||
outx_regs.rtio_regs_num = ARRAY_SIZE(xl_regs_list);
|
||||
|
||||
/*
|
||||
* Prepare rtio enabled bus to read LSM6DSVXXX_OUTX_L_A register
|
||||
* where accelerometer data is available.
|
||||
* Then lsm6dsvxxx_one_shot_complete_cb callback will be invoked.
|
||||
*
|
||||
* STMEMSC API equivalent code:
|
||||
*
|
||||
* uint8_t accel_raw[6];
|
||||
*
|
||||
* lsm6dsvxxx_acceleration_raw_get(&dev_ctx, accel_raw);
|
||||
*/
|
||||
rtio_read_regs_async(data->rtio_ctx, data->iodev, data->bus_type,
|
||||
&outx_regs, iodev_sqe, dev,
|
||||
lsm6dsvxxx_one_shot_complete_cb);
|
||||
break;
|
||||
|
||||
#if defined(CONFIG_LSM6DSVXXX_ENABLE_TEMP)
|
||||
case SENSOR_CHAN_DIE_TEMP:
|
||||
edata->has_temp = 1;
|
||||
|
||||
uint8_t t_addr = lsm6dsvxxx_bus_reg(data->bus_type, data->out_tp);
|
||||
struct rtio_regs outt_regs;
|
||||
struct rtio_regs_list t_regs_list[] = {
|
||||
{
|
||||
t_addr,
|
||||
(uint8_t *)&edata->temp,
|
||||
2,
|
||||
},
|
||||
};
|
||||
|
||||
outt_regs.rtio_regs_list = t_regs_list;
|
||||
outt_regs.rtio_regs_num = ARRAY_SIZE(t_regs_list);
|
||||
|
||||
/*
|
||||
* Prepare rtio enabled bus to read LSM6DSVXX0X_OUT_TEMP_L register
|
||||
* where temperature data is available.
|
||||
* Then lsm6dsvxxx_one_shot_complete_cb callback will be invoked.
|
||||
*
|
||||
* STMEMSC API equivalent code:
|
||||
*
|
||||
* int16_t val;
|
||||
*
|
||||
* lsm6dsvxxx_temperature_raw_get(&dev_ctx, &val);
|
||||
*/
|
||||
rtio_read_regs_async(data->rtio_ctx, data->iodev, data->bus_type,
|
||||
&outt_regs, iodev_sqe, dev,
|
||||
lsm6dsvxxx_one_shot_complete_cb);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (edata->has_accel == 0) {
|
||||
rtio_iodev_sqe_err(iodev_sqe, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
void lsm6dsvxxx_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
|
||||
{
|
||||
const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data;
|
||||
|
||||
if (!cfg->is_streaming) {
|
||||
lsm6dsvxxx_submit_one_shot(dev, iodev_sqe);
|
||||
} else {
|
||||
rtio_iodev_sqe_err(iodev_sqe, -ENOTSUP);
|
||||
}
|
||||
}
|
||||
|
||||
static DEVICE_API(sensor, lsm6dsvxxx_driver_api) = {
|
||||
.attr_set = lsm6dsvxxx_attr_set,
|
||||
.attr_get = lsm6dsvxxx_attr_get,
|
||||
#ifdef CONFIG_SENSOR_ASYNC_API
|
||||
.get_decoder = lsm6dsvxxx_get_decoder,
|
||||
.submit = lsm6dsvxxx_submit,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int lsm6dsvxxx_init(const struct device *dev)
|
||||
{
|
||||
const struct lsm6dsvxxx_config *cfg = dev->config;
|
||||
struct lsm6dsvxxx_data *data = dev->data;
|
||||
|
||||
LOG_INF("Initialize device %s", dev->name);
|
||||
data->dev = dev;
|
||||
|
||||
if (cfg->chip_api->init_chip(dev) < 0) {
|
||||
LOG_DBG("failed to initialize chip");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LSM6DSVXXX_TRIGGER
|
||||
if (cfg->trig_enabled && (lsm6dsvxxx_init_interrupt(dev) < 0)) {
|
||||
LOG_ERR("Failed to initialize interrupt.");
|
||||
return -EIO;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PM_DEVICE)
|
||||
static int lsm6dsvxxx_pm_action(const struct device *dev, enum pm_device_action action)
|
||||
{
|
||||
const struct lsm6dsvxxx_config *cfg = dev->config;
|
||||
|
||||
return cfg->chip_api->pm_action(dev, action);
|
||||
}
|
||||
#endif /* CONFIG_PM_DEVICE */
|
||||
|
||||
/*
|
||||
* Device creation macro, shared by LSM6DSVXXX_DEFINE_SPI() and
|
||||
* LSM6DSVXXX_DEFINE_I2C().
|
||||
*/
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
#define LSM6DSVXXX_DEVICE_INIT(inst, prefix) \
|
||||
PM_DEVICE_DT_INST_DEFINE(inst, lsm6dsvxxx_pm_action); \
|
||||
SENSOR_DEVICE_DT_INST_DEFINE(inst, lsm6dsvxxx_init, PM_DEVICE_DT_INST_GET(inst), \
|
||||
&prefix##_data_##inst, &prefix##_config_##inst, \
|
||||
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
|
||||
&lsm6dsvxxx_driver_api);
|
||||
|
||||
#ifdef CONFIG_LSM6DSVXXX_TRIGGER
|
||||
#define LSM6DSVXXX_CFG_IRQ(inst) \
|
||||
.trig_enabled = true, \
|
||||
.int1_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int1_gpios, { 0 }), \
|
||||
.int2_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int2_gpios, { 0 }), \
|
||||
.drdy_pulsed = DT_INST_PROP(inst, drdy_pulsed), \
|
||||
.drdy_pin = DT_INST_PROP(inst, drdy_pin)
|
||||
#else
|
||||
#define LSM6DSVXXX_CFG_IRQ(inst)
|
||||
#endif /* CONFIG_LSM6DSVXXX_TRIGGER */
|
||||
|
||||
#define LSM6DSVXXX_CONFIG_COMMON(inst, prefix) \
|
||||
.chip_api = &prefix##_chip_api, \
|
||||
.accel_odr = DT_INST_PROP(inst, accel_odr), \
|
||||
IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, accel_hg_odr), \
|
||||
(.accel_hg_odr = DT_INST_PROP(inst, accel_hg_odr),)) \
|
||||
.accel_range = DT_INST_ENUM_IDX(inst, accel_range), \
|
||||
.gyro_odr = DT_INST_PROP(inst, gyro_odr), \
|
||||
.gyro_range = DT_INST_PROP(inst, gyro_range), \
|
||||
IF_ENABLED(CONFIG_LSM6DSVXXX_STREAM, \
|
||||
(.fifo_wtm = DT_INST_PROP(inst, fifo_watermark), \
|
||||
.accel_batch = DT_INST_PROP(inst, accel_fifo_batch_rate), \
|
||||
.gyro_batch = DT_INST_PROP(inst, gyro_fifo_batch_rate), \
|
||||
.sflp_odr = DT_INST_PROP(inst, sflp_odr), \
|
||||
.sflp_fifo_en = DT_INST_PROP(inst, sflp_fifo_enable), \
|
||||
.temp_batch = DT_INST_PROP(inst, temp_fifo_batch_rate),)) \
|
||||
IF_ENABLED(UTIL_OR(DT_INST_NODE_HAS_PROP(inst, int1_gpios), \
|
||||
DT_INST_NODE_HAS_PROP(inst, int2_gpios)), \
|
||||
(LSM6DSVXXX_CFG_IRQ(inst)))
|
||||
|
||||
/*
|
||||
* Instantiation macros used when a device is on a SPI bus.
|
||||
*/
|
||||
|
||||
#define LSM6DSVXXX_SPI_OP (SPI_WORD_SET(8) | \
|
||||
SPI_OP_MODE_MASTER | \
|
||||
SPI_MODE_CPOL | \
|
||||
SPI_MODE_CPHA) \
|
||||
|
||||
#define LSM6DSVXXX_SPI_RTIO_DEFINE(inst, prefix) \
|
||||
SPI_DT_IODEV_DEFINE(prefix##_iodev_##inst, \
|
||||
DT_DRV_INST(inst), LSM6DSVXXX_SPI_OP, 0U); \
|
||||
RTIO_DEFINE(prefix##_rtio_ctx_##inst, 8, 8);
|
||||
|
||||
#define LSM6DSVXXX_CONFIG_SPI(inst, prefix) \
|
||||
{ \
|
||||
STMEMSC_CTX_SPI(&prefix##_config_##inst.stmemsc_cfg), \
|
||||
.stmemsc_cfg = { \
|
||||
.spi = SPI_DT_SPEC_INST_GET(inst, \
|
||||
LSM6DSVXXX_SPI_OP, \
|
||||
0), \
|
||||
}, \
|
||||
LSM6DSVXXX_CONFIG_COMMON(inst, prefix) \
|
||||
}
|
||||
|
||||
#define LSM6DSVXXX_DEFINE_SPI(inst, prefix) \
|
||||
IF_ENABLED(CONFIG_SPI_RTIO, \
|
||||
(LSM6DSVXXX_SPI_RTIO_DEFINE(inst, prefix))); \
|
||||
static struct lsm6dsvxxx_data prefix##_data_##inst = { \
|
||||
IF_ENABLED(CONFIG_SPI_RTIO, \
|
||||
(.rtio_ctx = &prefix##_rtio_ctx_##inst, \
|
||||
.iodev = &prefix##_iodev_##inst, \
|
||||
.bus_type = RTIO_BUS_SPI,)) \
|
||||
}; \
|
||||
static const struct lsm6dsvxxx_config prefix##_config_##inst = \
|
||||
LSM6DSVXXX_CONFIG_SPI(inst, prefix);
|
||||
|
||||
/*
|
||||
* Instantiation macros used when a device is on an I2C bus.
|
||||
*/
|
||||
|
||||
#define LSM6DSVXXX_I2C_RTIO_DEFINE(inst, prefix) \
|
||||
I2C_DT_IODEV_DEFINE(prefix##_iodev_##inst, DT_DRV_INST(inst)); \
|
||||
RTIO_DEFINE(prefix##_rtio_ctx_##inst, 8, 8);
|
||||
|
||||
#define LSM6DSVXXX_CONFIG_I2C(inst, prefix) \
|
||||
{ \
|
||||
STMEMSC_CTX_I2C(&prefix##_config_##inst.stmemsc_cfg), \
|
||||
.stmemsc_cfg = { \
|
||||
.i2c = I2C_DT_SPEC_INST_GET(inst), \
|
||||
}, \
|
||||
LSM6DSVXXX_CONFIG_COMMON(inst, prefix) \
|
||||
}
|
||||
|
||||
#define LSM6DSVXXX_DEFINE_I2C(inst, prefix) \
|
||||
IF_ENABLED(CONFIG_I2C_RTIO, \
|
||||
(LSM6DSVXXX_I2C_RTIO_DEFINE(inst, prefix))); \
|
||||
static struct lsm6dsvxxx_data prefix##_data_##inst = { \
|
||||
IF_ENABLED(CONFIG_I2C_RTIO, \
|
||||
(.rtio_ctx = &prefix##_rtio_ctx_##inst, \
|
||||
.iodev = &prefix##_iodev_##inst, \
|
||||
.bus_type = RTIO_BUS_I2C,)) \
|
||||
}; \
|
||||
static const struct lsm6dsvxxx_config prefix##_config_##inst = \
|
||||
LSM6DSVXXX_CONFIG_I2C(inst, prefix);
|
||||
|
||||
/*
|
||||
* Instantiation macros used when a device is on an I3C bus.
|
||||
*/
|
||||
|
||||
#define LSM6DSVXXX_I3C_RTIO_DEFINE(inst, prefix) \
|
||||
I3C_DT_IODEV_DEFINE(prefix##_i3c_iodev_##inst, DT_DRV_INST(inst)); \
|
||||
RTIO_DEFINE(prefix##_rtio_ctx_##inst, 8, 8);
|
||||
|
||||
#define LSM6DSVXXX_CONFIG_I3C(inst, prefix) \
|
||||
{ \
|
||||
STMEMSC_CTX_I3C(&prefix##_config_##inst.stmemsc_cfg), \
|
||||
.stmemsc_cfg = { \
|
||||
.i3c = &prefix##_data_##inst.i3c_dev, \
|
||||
}, \
|
||||
.i3c.bus = DEVICE_DT_GET(DT_INST_BUS(inst)), \
|
||||
.i3c.dev_id = I3C_DEVICE_ID_DT_INST(inst), \
|
||||
IF_ENABLED(CONFIG_LSM6DSVXXX_TRIGGER, \
|
||||
(.int_en_i3c = DT_INST_PROP(inst, int_en_i3c), \
|
||||
.bus_act_sel = DT_INST_ENUM_IDX(inst, bus_act_sel_us),)) \
|
||||
LSM6DSVXXX_CONFIG_COMMON(inst, prefix) \
|
||||
}
|
||||
|
||||
#define LSM6DSVXXX_DEFINE_I3C(inst, prefix) \
|
||||
IF_ENABLED(CONFIG_I3C_RTIO, \
|
||||
(LSM6DSVXXX_I3C_RTIO_DEFINE(inst, prefix))); \
|
||||
static struct lsm6dsvxxx_data prefix##_data_##inst = { \
|
||||
IF_ENABLED(CONFIG_I3C_RTIO, \
|
||||
(.rtio_ctx = &prefix##_rtio_ctx_##inst, \
|
||||
.iodev = &prefix##_i3c_iodev_##inst, \
|
||||
.bus_type = RTIO_BUS_I3C,)) \
|
||||
}; \
|
||||
static const struct lsm6dsvxxx_config prefix##_config_##inst = \
|
||||
LSM6DSVXXX_CONFIG_I3C(inst, prefix);
|
||||
|
||||
#define LSM6DSVXXX_DEFINE_I3C_OR_I2C(inst, prefix) \
|
||||
COND_CODE_0(DT_INST_PROP_BY_IDX(inst, reg, 1), \
|
||||
(LSM6DSVXXX_DEFINE_I2C(inst, prefix)), \
|
||||
(LSM6DSVXXX_DEFINE_I3C(inst, prefix)))
|
||||
|
||||
/*
|
||||
* Main instantiation macro. Use of COND_CODE_1() selects the right
|
||||
* bus-specific macro at preprocessor time.
|
||||
*/
|
||||
|
||||
#define LSM6DSVXXX_DEFINE(inst, prefix) \
|
||||
COND_CODE_1(DT_INST_ON_BUS(inst, spi), \
|
||||
(LSM6DSVXXX_DEFINE_SPI(inst, prefix)), \
|
||||
(COND_CODE_1(DT_INST_ON_BUS(inst, i3c), \
|
||||
(LSM6DSVXXX_DEFINE_I3C_OR_I2C(inst, prefix)), \
|
||||
(LSM6DSVXXX_DEFINE_I2C(inst, prefix))))); \
|
||||
LSM6DSVXXX_DEVICE_INIT(inst, prefix)
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
#define DT_DRV_COMPAT st_lsm6dsv320x
|
||||
DT_INST_FOREACH_STATUS_OKAY_VARGS(LSM6DSVXXX_DEFINE, DT_DRV_COMPAT)
|
||||
#undef DT_DRV_COMPAT
|
||||
209
drivers/sensor/st/lsm6dsvxxx/lsm6dsvxxx.h
Normal file
209
drivers/sensor/st/lsm6dsvxxx/lsm6dsvxxx.h
Normal file
@@ -0,0 +1,209 @@
|
||||
/* ST Microelectronics LSM6DSVXXX family IMU sensor
|
||||
*
|
||||
* Copyright (c) 2025 STMicroelectronics
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Datasheet:
|
||||
* https://www.st.com/resource/en/datasheet/lsm6dsv320x.pdf
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_SENSOR_LSM6DSVXXX_LSM6DSVXXX_H_
|
||||
#define ZEPHYR_DRIVERS_SENSOR_LSM6DSVXXX_LSM6DSVXXX_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <zephyr/drivers/spi.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/types.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <stmemsc.h>
|
||||
#include <zephyr/rtio/regmap.h>
|
||||
#include <zephyr/drivers/sensor_clock.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
|
||||
#define LSM6DSVXXX_ANY_INST_ON_BUS_STATUS_OKAY(bus) \
|
||||
(DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(st_lsm6dsv320x, bus))
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(st_lsm6dsv320x)
|
||||
#include "lsm6dsv320x_reg.h"
|
||||
#include <zephyr/dt-bindings/sensor/lsm6dsv320x.h>
|
||||
#endif
|
||||
|
||||
#if LSM6DSVXXX_ANY_INST_ON_BUS_STATUS_OKAY(spi)
|
||||
#include <zephyr/drivers/spi.h>
|
||||
#endif /* LSM6DSVXXX_ANY_INST_ON_BUS_STATUS_OKAY(spi) */
|
||||
|
||||
#if LSM6DSVXXX_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#endif /* LSM6DSVXXX_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */
|
||||
|
||||
#if LSM6DSVXXX_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
|
||||
#include <zephyr/drivers/i3c.h>
|
||||
#endif /* LSM6DSVXXX_ANY_INST_ON_BUS_STATUS_OKAY(i3c) */
|
||||
|
||||
#if (LSM6DSVXXX_ANY_INST_ON_BUS_STATUS_OKAY(i3c))
|
||||
#define ON_I3C_BUS(cfg) (cfg->i3c.bus != NULL)
|
||||
#else
|
||||
#define ON_I3C_BUS(cfg) (false)
|
||||
#endif
|
||||
|
||||
typedef int32_t (*api_lsm6dsvxxx_init_chip)(const struct device *dev);
|
||||
#if defined(CONFIG_PM_DEVICE)
|
||||
typedef int32_t (*api_lsm6dsvxxx_pm_action)(const struct device *dev, enum pm_device_action action);
|
||||
#endif /* CONFIG_PM_DEVICE */
|
||||
typedef int32_t (*api_lsm6dsvxxx_accel_set_fs)(const struct device *dev, int32_t range);
|
||||
typedef int32_t (*api_lsm6dsvxxx_accel_set_odr)(const struct device *dev, int32_t freq);
|
||||
typedef int32_t (*api_lsm6dsvxxx_accel_set_mode)(const struct device *dev, int32_t mode);
|
||||
typedef int32_t (*api_lsm6dsvxxx_accel_get_fs)(const struct device *dev, int32_t *range);
|
||||
typedef int32_t (*api_lsm6dsvxxx_accel_get_odr)(const struct device *dev, int32_t *freq);
|
||||
typedef int32_t (*api_lsm6dsvxxx_accel_get_mode)(const struct device *dev, int32_t *mode);
|
||||
typedef int32_t (*api_lsm6dsvxxx_gyro_set_fs)(const struct device *dev, int32_t range);
|
||||
typedef int32_t (*api_lsm6dsvxxx_gyro_set_odr)(const struct device *dev, int32_t freq);
|
||||
typedef int32_t (*api_lsm6dsvxxx_gyro_set_mode)(const struct device *dev, int32_t mode);
|
||||
typedef int32_t (*api_lsm6dsvxxx_gyro_get_fs)(const struct device *dev, int32_t *range);
|
||||
typedef int32_t (*api_lsm6dsvxxx_gyro_get_odr)(const struct device *dev, int32_t *freq);
|
||||
typedef int32_t (*api_lsm6dsvxxx_gyro_get_mode)(const struct device *dev, int32_t *mode);
|
||||
|
||||
struct lsm6dsvxxx_chip_api {
|
||||
api_lsm6dsvxxx_init_chip init_chip;
|
||||
#if defined(CONFIG_PM_DEVICE)
|
||||
api_lsm6dsvxxx_pm_action pm_action;
|
||||
#endif /* CONFIG_PM_DEVICE */
|
||||
api_lsm6dsvxxx_accel_set_fs accel_fs_set;
|
||||
api_lsm6dsvxxx_accel_set_odr accel_odr_set;
|
||||
api_lsm6dsvxxx_accel_set_mode accel_mode_set;
|
||||
api_lsm6dsvxxx_accel_get_fs accel_fs_get;
|
||||
api_lsm6dsvxxx_accel_get_odr accel_odr_get;
|
||||
api_lsm6dsvxxx_accel_get_mode accel_mode_get;
|
||||
api_lsm6dsvxxx_gyro_set_fs gyro_fs_set;
|
||||
api_lsm6dsvxxx_gyro_set_odr gyro_odr_set;
|
||||
api_lsm6dsvxxx_gyro_set_mode gyro_mode_set;
|
||||
api_lsm6dsvxxx_gyro_get_fs gyro_fs_get;
|
||||
api_lsm6dsvxxx_gyro_get_odr gyro_odr_get;
|
||||
api_lsm6dsvxxx_gyro_get_mode gyro_mode_get;
|
||||
};
|
||||
|
||||
struct lsm6dsvxxx_config {
|
||||
stmdev_ctx_t ctx;
|
||||
union {
|
||||
#if LSM6DSVXXX_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
|
||||
const struct i2c_dt_spec i2c;
|
||||
#endif
|
||||
#if LSM6DSVXXX_ANY_INST_ON_BUS_STATUS_OKAY(spi)
|
||||
const struct spi_dt_spec spi;
|
||||
#endif
|
||||
#if LSM6DSVXXX_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
|
||||
struct i3c_device_desc **i3c;
|
||||
#endif
|
||||
} stmemsc_cfg;
|
||||
uint8_t accel_pm;
|
||||
uint8_t accel_odr;
|
||||
uint8_t accel_hg_odr;
|
||||
uint8_t accel_range;
|
||||
uint8_t gyro_pm;
|
||||
uint8_t gyro_odr;
|
||||
uint8_t gyro_range;
|
||||
uint8_t drdy_pulsed;
|
||||
|
||||
#if LSM6DSVXXX_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
|
||||
struct {
|
||||
const struct device *bus;
|
||||
const struct i3c_device_id dev_id;
|
||||
} i3c;
|
||||
#endif
|
||||
const struct lsm6dsvxxx_chip_api *chip_api;
|
||||
};
|
||||
|
||||
struct lsm6dsvxxx_ibi_payload {
|
||||
uint8_t mdb;
|
||||
uint8_t fifo_status1;
|
||||
uint8_t fifo_status2;
|
||||
uint8_t all_int_src;
|
||||
uint8_t status_reg;
|
||||
uint8_t status_reg_ois;
|
||||
uint8_t status_master_main;
|
||||
uint8_t emb_func_status;
|
||||
uint8_t fsm_status;
|
||||
uint8_t mlc_status;
|
||||
} __packed;
|
||||
|
||||
struct lsm6dsvxxx_data {
|
||||
const struct device *dev;
|
||||
int16_t acc[3];
|
||||
uint32_t acc_gain;
|
||||
int16_t gyro[3];
|
||||
uint32_t gyro_gain;
|
||||
#if defined(CONFIG_LSM6DSV16X_ENABLE_TEMP)
|
||||
int16_t temp_sample;
|
||||
#endif
|
||||
|
||||
uint8_t accel_freq;
|
||||
uint8_t accel_fs;
|
||||
uint8_t gyro_freq;
|
||||
uint8_t gyro_fs;
|
||||
uint8_t out_xl;
|
||||
uint8_t out_tp;
|
||||
|
||||
struct rtio_iodev_sqe *streaming_sqe;
|
||||
struct rtio *rtio_ctx;
|
||||
struct rtio_iodev *iodev;
|
||||
|
||||
rtio_bus_type bus_type; /* I2C is 0, SPI is 1, I3C is 2 */
|
||||
|
||||
#if LSM6DSVXXX_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
|
||||
struct i3c_device_desc *i3c_dev;
|
||||
struct lsm6dsvxxx_ibi_payload ibi_payload;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline uint8_t lsm6dsvxxx_bus_reg(rtio_bus_type bus, uint8_t addr)
|
||||
{
|
||||
return (rtio_is_spi(bus)) ? addr | 0x80 : addr;
|
||||
}
|
||||
|
||||
int lsm6dsvxxx_spi_init(const struct device *dev);
|
||||
|
||||
/* decoder */
|
||||
struct lsm6dsvxxx_decoder_header {
|
||||
uint64_t timestamp;
|
||||
uint8_t is_fifo: 1;
|
||||
uint8_t range: 4;
|
||||
uint8_t reserved: 3;
|
||||
uint8_t int_status;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct lsm6dsvxxx_fifo_data {
|
||||
struct lsm6dsvxxx_decoder_header header;
|
||||
uint32_t accel_odr: 4;
|
||||
uint32_t fifo_mode_sel: 2;
|
||||
uint32_t fifo_count: 7;
|
||||
uint32_t reserved_1: 5;
|
||||
uint32_t accel_batch_odr: 3;
|
||||
uint32_t ts_batch_odr: 2;
|
||||
uint32_t reserved: 9;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct lsm6dsvxxx_rtio_data {
|
||||
struct lsm6dsvxxx_decoder_header header;
|
||||
struct {
|
||||
uint8_t has_accel: 1; /* set if accel channel has data */
|
||||
uint8_t has_temp: 1; /* set if temp channel has data */
|
||||
uint8_t reserved: 6;
|
||||
} __attribute__((__packed__));
|
||||
int16_t accel[3];
|
||||
int16_t temp;
|
||||
};
|
||||
|
||||
int lsm6dsvxxx_encode(const struct device *dev, const struct sensor_chan_spec *const channels,
|
||||
const size_t num_channels, uint8_t *buf);
|
||||
|
||||
int lsm6dsvxxx_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder);
|
||||
|
||||
void lsm6dsvxxx_rtio_rd_transaction(const struct device *dev,
|
||||
uint8_t *regs, uint8_t regs_num,
|
||||
struct spi_buf *buf,
|
||||
struct rtio_iodev_sqe *iodev_sqe,
|
||||
rtio_callback_t complete_op_cb);
|
||||
#endif /* ZEPHYR_DRIVERS_SENSOR_LSM6DSVXXX_LSM6DSVXXX_H_ */
|
||||
215
drivers/sensor/st/lsm6dsvxxx/lsm6dsvxxx_decoder.c
Normal file
215
drivers/sensor/st/lsm6dsvxxx/lsm6dsvxxx_decoder.c
Normal file
@@ -0,0 +1,215 @@
|
||||
/* ST Microelectronics LSM6DSVXXX family IMU sensor
|
||||
*
|
||||
* Copyright (c) 2025 STMicroelectronics
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Datasheet:
|
||||
* https://www.st.com/resource/en/datasheet/lsm6dsv320x.pdf
|
||||
*/
|
||||
|
||||
#include "lsm6dsvxxx.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(LSM6DSVXXX_DECODER, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
/*
|
||||
* Expand val to q31_t according to its range; this is achieved multiplying by 2^31/2^range.
|
||||
*/
|
||||
#define Q31_SHIFT_VAL(val, range) \
|
||||
(q31_t) (roundf((val) * ((int64_t)1 << (31 - (range)))))
|
||||
|
||||
/*
|
||||
* Expand micro_val (a generic micro unit) to q31_t according to its range; this is achieved
|
||||
* multiplying by 2^31/2^range. Then transform it to val.
|
||||
*/
|
||||
#define Q31_SHIFT_MICROVAL(micro_val, range) \
|
||||
(q31_t) ((int64_t)(micro_val) * ((int64_t)1 << (31 - (range))) / 1000000LL)
|
||||
|
||||
/* bit range for Accelerometer for a given range value */
|
||||
static const int8_t accel_range[] = {
|
||||
5, /* FS_2G */
|
||||
6, /* FS_4G */
|
||||
7, /* FS_8G */
|
||||
8, /* FS_16G */
|
||||
9, /* FS_32G */
|
||||
10, /* FS_64G */
|
||||
11, /* FS_128G */
|
||||
12, /* FS_256G */
|
||||
13, /* FS_320G */
|
||||
};
|
||||
|
||||
#if defined(CONFIG_LSM6DSVXXX_ENABLE_TEMP)
|
||||
/* bit range for Temperature sensor */
|
||||
static const int8_t temp_range = 9;
|
||||
|
||||
/* transform temperature LSB into micro-Celsius */
|
||||
#define SENSOR_TEMP_UCELSIUS(t_lsb) \
|
||||
(int64_t) (25000000LL + (((int64_t)(t_lsb) * 1000000LL) / 355LL))
|
||||
|
||||
#endif
|
||||
|
||||
/* Calculate scaling factor to transform micro-g/LSB unit into micro-ms2/LSB */
|
||||
#define SENSOR_SCALE_UG_TO_UMS2(ug_lsb) \
|
||||
(int32_t)((ug_lsb) * SENSOR_G / 1000000LL)
|
||||
|
||||
/*
|
||||
* Accelerometer scaling factors table for a given range value
|
||||
* GAIN_UNIT_XL is expressed in ug/LSB.
|
||||
*/
|
||||
static const int32_t accel_scaler[] = {
|
||||
SENSOR_SCALE_UG_TO_UMS2(61), /* FS_2G */
|
||||
SENSOR_SCALE_UG_TO_UMS2(122), /* FS_4G */
|
||||
SENSOR_SCALE_UG_TO_UMS2(244), /* FS_8G */
|
||||
SENSOR_SCALE_UG_TO_UMS2(488), /* FS_16G */
|
||||
SENSOR_SCALE_UG_TO_UMS2(976), /* FS_32G */
|
||||
SENSOR_SCALE_UG_TO_UMS2(1952), /* FS_64G */
|
||||
SENSOR_SCALE_UG_TO_UMS2(2904), /* FS_128G */
|
||||
SENSOR_SCALE_UG_TO_UMS2(7808), /* FS_256G */
|
||||
SENSOR_SCALE_UG_TO_UMS2(10417), /* FS_320G */
|
||||
};
|
||||
|
||||
/* Calculate scaling factor to transform micro-dps/LSB unit into micro-rads/LSB */
|
||||
#define SENSOR_SCALE_UDPS_TO_URADS(udps_lsb) \
|
||||
(int32_t)(((udps_lsb) * SENSOR_PI / 180LL) / 1000000LL)
|
||||
|
||||
static int lsm6dsvxxx_decoder_get_frame_count(const uint8_t *buffer,
|
||||
struct sensor_chan_spec chan_spec,
|
||||
uint16_t *frame_count)
|
||||
{
|
||||
const struct lsm6dsvxxx_fifo_data *data = (const struct lsm6dsvxxx_fifo_data *)buffer;
|
||||
const struct lsm6dsvxxx_rtio_data *rdata = (const struct lsm6dsvxxx_rtio_data *)buffer;
|
||||
const struct lsm6dsvxxx_decoder_header *header = &data->header;
|
||||
|
||||
if (chan_spec.chan_idx != 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (!header->is_fifo) {
|
||||
switch (chan_spec.chan_type) {
|
||||
case SENSOR_CHAN_ACCEL_X:
|
||||
case SENSOR_CHAN_ACCEL_Y:
|
||||
case SENSOR_CHAN_ACCEL_Z:
|
||||
case SENSOR_CHAN_ACCEL_XYZ:
|
||||
*frame_count = rdata->has_accel ? 1 : 0;
|
||||
return 0;
|
||||
|
||||
case SENSOR_CHAN_DIE_TEMP:
|
||||
*frame_count = rdata->has_temp ? 1 : 0;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
*frame_count = 0;
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lsm6dsvxxx_decode_sample(const uint8_t *buffer, struct sensor_chan_spec chan_spec,
|
||||
uint32_t *fit, uint16_t max_count, void *data_out)
|
||||
{
|
||||
const struct lsm6dsvxxx_rtio_data *edata = (const struct lsm6dsvxxx_rtio_data *)buffer;
|
||||
const struct lsm6dsvxxx_decoder_header *header = &edata->header;
|
||||
|
||||
if (*fit != 0) {
|
||||
return 0;
|
||||
}
|
||||
if (max_count == 0 || chan_spec.chan_idx != 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (chan_spec.chan_type) {
|
||||
case SENSOR_CHAN_ACCEL_X:
|
||||
case SENSOR_CHAN_ACCEL_Y:
|
||||
case SENSOR_CHAN_ACCEL_Z:
|
||||
case SENSOR_CHAN_ACCEL_XYZ: {
|
||||
const int32_t scale = accel_scaler[header->range];
|
||||
|
||||
if (edata->has_accel == 0) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
struct sensor_three_axis_data *out = data_out;
|
||||
|
||||
out->header.base_timestamp_ns = edata->header.timestamp;
|
||||
out->header.reading_count = 1;
|
||||
|
||||
out->shift = accel_range[header->range];
|
||||
|
||||
out->readings[0].x = Q31_SHIFT_MICROVAL(scale * edata->accel[0], out->shift);
|
||||
out->readings[0].y = Q31_SHIFT_MICROVAL(scale * edata->accel[1], out->shift);
|
||||
out->readings[0].z = Q31_SHIFT_MICROVAL(scale * edata->accel[2], out->shift);
|
||||
*fit = 1;
|
||||
return 1;
|
||||
}
|
||||
#if defined(CONFIG_LSM6DSVXXX_ENABLE_TEMP)
|
||||
case SENSOR_CHAN_DIE_TEMP: {
|
||||
int64_t t_uC;
|
||||
|
||||
if (edata->has_temp == 0) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
struct sensor_q31_data *out = data_out;
|
||||
|
||||
out->header.base_timestamp_ns = edata->header.timestamp;
|
||||
out->header.reading_count = 1;
|
||||
|
||||
out->shift = temp_range;
|
||||
|
||||
/* transform temperature LSB into micro-Celsius */
|
||||
t_uC = SENSOR_TEMP_UCELSIUS(edata->temp);
|
||||
|
||||
out->readings[0].temperature = Q31_SHIFT_MICROVAL(t_uC, out->shift);
|
||||
*fit = 1;
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int lsm6dsvxxx_decoder_decode(const uint8_t *buffer, struct sensor_chan_spec chan_spec,
|
||||
uint32_t *fit, uint16_t max_count, void *data_out)
|
||||
{
|
||||
return lsm6dsvxxx_decode_sample(buffer, chan_spec, fit, max_count, data_out);
|
||||
}
|
||||
|
||||
static int lsm6dsvxxx_decoder_get_size_info(struct sensor_chan_spec chan_spec, size_t *base_size,
|
||||
size_t *frame_size)
|
||||
{
|
||||
switch (chan_spec.chan_type) {
|
||||
case SENSOR_CHAN_ACCEL_X:
|
||||
case SENSOR_CHAN_ACCEL_Y:
|
||||
case SENSOR_CHAN_ACCEL_Z:
|
||||
case SENSOR_CHAN_ACCEL_XYZ:
|
||||
*base_size = sizeof(struct sensor_three_axis_data);
|
||||
*frame_size = sizeof(struct sensor_three_axis_sample_data);
|
||||
return 0;
|
||||
case SENSOR_CHAN_DIE_TEMP:
|
||||
*base_size = sizeof(struct sensor_q31_data);
|
||||
*frame_size = sizeof(struct sensor_q31_sample_data);
|
||||
return 0;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
SENSOR_DECODER_API_DT_DEFINE() = {
|
||||
.get_frame_count = lsm6dsvxxx_decoder_get_frame_count,
|
||||
.get_size_info = lsm6dsvxxx_decoder_get_size_info,
|
||||
.decode = lsm6dsvxxx_decoder_decode,
|
||||
};
|
||||
|
||||
int lsm6dsvxxx_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
*decoder = &SENSOR_DECODER_NAME();
|
||||
|
||||
return 0;
|
||||
}
|
||||
22
drivers/sensor/st/lsm6dsvxxx/lsm6dsvxxx_rtio.h
Normal file
22
drivers/sensor/st/lsm6dsvxxx/lsm6dsvxxx_rtio.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/* ST Microelectronics LSM6DSVXXX family IMU sensor
|
||||
*
|
||||
* Copyright (c) 2025 STMicroelectronics
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Datasheet:
|
||||
* https://www.st.com/resource/en/datasheet/lsm6dsv320x.pdf
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_SENSOR_LSM6DSVXXX_RTIO_H_
|
||||
#define ZEPHYR_DRIVERS_SENSOR_LSM6DSVXXX_RTIO_H_
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/rtio/rtio.h>
|
||||
#include <zephyr/rtio/work.h>
|
||||
|
||||
void lsm6dsvxxx_submit(const struct device *sensor, struct rtio_iodev_sqe *iodev_sqe);
|
||||
void lsm6dsvxxx_submit_stream(const struct device *sensor, struct rtio_iodev_sqe *iodev_sqe);
|
||||
void lsm6dsvxxx_stream_irq_handler(const struct device *dev);
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_SENSOR_LSM6DSVXXX_RTIO_H_ */
|
||||
76
dts/bindings/sensor/st,lsm6dsv320x-common.yaml
Normal file
76
dts/bindings/sensor/st,lsm6dsv320x-common.yaml
Normal file
@@ -0,0 +1,76 @@
|
||||
# Copyright (c) 2025 STMicroelectronics
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
When setting the accel-range, accel-odr, gyro-range, gyro-odr properties in
|
||||
a .dts or .dtsi file you may include lsm6dsv320x.h and use the macros
|
||||
defined there.
|
||||
|
||||
Example:
|
||||
#include <zephyr/dt-bindings/sensor/lsm6dsv320x.h>
|
||||
|
||||
lsm6dsv320x: lsm6dsv320x@0 {
|
||||
...
|
||||
|
||||
accel-range = <LSM6DSV320X_DT_FS_8G>;
|
||||
accel-odr = <LSM6DSVXXX_DT_ODR_AT_60Hz>;
|
||||
gyro-range = <LSM6DSV320X_DT_FS_4000DPS>;
|
||||
gyro-odr = <LSM6DSVXXX_DT_ODR_AT_60Hz>;
|
||||
};
|
||||
|
||||
include: st,lsm6dsvxxx-common.yaml
|
||||
|
||||
properties:
|
||||
accel-hg-odr:
|
||||
type: int
|
||||
default: 0x0
|
||||
description: |
|
||||
Specify the default accelerometer High-g output data rate expressed in samples per
|
||||
second (Hz).
|
||||
The values are taken in accordance to lsm6dsv320x_hg_xl_data_rate_t enumerative in hal/st
|
||||
module. Please note that this values will not change the operating mode, which will remain
|
||||
High Performance (device default)
|
||||
Default is power-up configuration.
|
||||
|
||||
- 0x0 # LSM6DSV320X_HG_XL_ODR_OFF
|
||||
- 0x3 # LSM6DSV320X_HG_XL_ODR_AT_480Hz
|
||||
- 0x4 # LSM6DSV320X_HG_XL_ODR_AT_960Hz
|
||||
- 0x5 # LSM6DSV320X_HG_XL_ODR_AT_1920Hz
|
||||
- 0x6 # LSM6DSV320X_HG_XL_ODR_AT_3840Hz
|
||||
- 0x7 # LSM6DSV320X_HG_XL_ODR_AT_7680Hz
|
||||
|
||||
enum: [0x0, 0x3, 0x4, 0x5, 0x6, 0x7]
|
||||
|
||||
accel-range:
|
||||
type: int
|
||||
default: 2
|
||||
description: |
|
||||
Range in g. Default is power-up configuration.
|
||||
Ranges from 32g and above requires the High-g sensor to be turned on (see accel-hg-odr).
|
||||
|
||||
- 2 # LSM6DSV320X_DT_FS_2G (0.061 mg/LSB)
|
||||
- 4 # LSM6DSV320X_DT_FS_4G (0.122 mg/LSB)
|
||||
- 8 # LSM6DSV320X_DT_FS_8G (0.244 mg/LSB)
|
||||
- 16 # LSM6DSV320X_DT_FS_16G (0.488 mg/LSB)
|
||||
- 32 # LSM6DSV320X_DT_FS_32G (0.976 mg/LSB)
|
||||
- 64 # LSM6DSV320X_DT_FS_64G (1.952 mg/LSB)
|
||||
- 128 # LSM6DSV320X_DT_FS_128G (3.904 mg/LSB)
|
||||
- 256 # LSM6DSV320X_DT_FS_256G (7.808 mg/LSB)
|
||||
- 320 # LSM6DSV320X_DT_FS_320G (10.417 mg/LSB)
|
||||
|
||||
enum: [2, 4, 8, 16, 32, 64, 128, 256, 320]
|
||||
|
||||
gyro-range:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
Range in dps. Default is power-up configuration.
|
||||
|
||||
- 0x0 # power-up configuration
|
||||
- 0x1 # LSM6DSV320X_DT_FS_250DPS (8.75 mdps/LSB)
|
||||
- 0x2 # LSM6DSV320X_DT_FS_500DPS (17.50 mdps/LSB)
|
||||
- 0x3 # LSM6DSV320X_DT_FS_1000DPS (35 mdps/LSB)
|
||||
- 0x4 # LSM6DSV320X_DT_FS_2000DPS (70 mdps/LSB)
|
||||
- 0x5 # LSM6DSV320X_DT_FS_4000DPS (140 mdps/LSB)
|
||||
|
||||
enum: [0x0, 0x1, 0x2, 0x3, 0x4, 0x5]
|
||||
10
dts/bindings/sensor/st,lsm6dsv320x-i2c.yaml
Normal file
10
dts/bindings/sensor/st,lsm6dsv320x-i2c.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2025 STMicroelectronics
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
STMicroelectronics LSM6DSV320X 6-axis IMU (Inertial Measurement Unit) sensor
|
||||
accessed through I2C bus
|
||||
|
||||
compatible: "st,lsm6dsv320x"
|
||||
|
||||
include: ["i2c-device.yaml", "st,lsm6dsv320x-common.yaml"]
|
||||
24
dts/bindings/sensor/st,lsm6dsv320x-i3c.yaml
Normal file
24
dts/bindings/sensor/st,lsm6dsv320x-i3c.yaml
Normal file
@@ -0,0 +1,24 @@
|
||||
# Copyright (c) 2025 STMicroelectronics
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
STMicroelectronics LSM6DSV320X 6-axis IMU (Inertial Measurement Unit) sensor
|
||||
accessed through I3C bus
|
||||
|
||||
compatible: "st,lsm6dsv320x"
|
||||
|
||||
include: ["i3c-device.yaml", "st,lsm6dsv320x-common.yaml"]
|
||||
|
||||
properties:
|
||||
int-en-i3c:
|
||||
type: boolean
|
||||
description: |
|
||||
Enables INT pin when I3C is enabled
|
||||
|
||||
bus-act-sel-us:
|
||||
type: int
|
||||
default: 50
|
||||
description: |
|
||||
Bus available time for I3C IBI in microseconds
|
||||
|
||||
enum: [50, 2, 1000, 25000]
|
||||
10
dts/bindings/sensor/st,lsm6dsv320x-spi.yaml
Normal file
10
dts/bindings/sensor/st,lsm6dsv320x-spi.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2025 STMicroelectronics
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
STMicroelectronics LSM6DSV320X 6-axis IMU (Inertial Measurement Unit) sensor
|
||||
accessed through SPI bus
|
||||
|
||||
compatible: "st,lsm6dsv320x"
|
||||
|
||||
include: ["spi-device.yaml", "st,lsm6dsv320x-common.yaml"]
|
||||
37
include/zephyr/dt-bindings/sensor/lsm6dsv320x.h
Normal file
37
include/zephyr/dt-bindings/sensor/lsm6dsv320x.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2025 STMicroelectronics
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_ST_LSM6DSV320X_H_
|
||||
#define ZEPHYR_INCLUDE_DT_BINDINGS_ST_LSM6DSV320X_H_
|
||||
|
||||
#include "lsm6dsvxxx.h"
|
||||
|
||||
/* Accel High-g odr */
|
||||
#define LSM6DSV320X_HG_XL_ODR_OFF 0x0
|
||||
#define LSM6DSV320X_HG_XL_ODR_AT_480Hz 0x3
|
||||
#define LSM6DSV320X_HG_XL_ODR_AT_960Hz 0x4
|
||||
#define LSM6DSV320X_HG_XL_ODR_AT_1920Hz 0x5
|
||||
#define LSM6DSV320X_HG_XL_ODR_AT_3840Hz 0x6
|
||||
#define LSM6DSV320X_HG_XL_ODR_AT_7680Hz 0x7
|
||||
|
||||
/* Accel range */
|
||||
#define LSM6DSV320X_DT_FS_2G 2
|
||||
#define LSM6DSV320X_DT_FS_4G 4
|
||||
#define LSM6DSV320X_DT_FS_8G 8
|
||||
#define LSM6DSV320X_DT_FS_16G 16
|
||||
#define LSM6DSV320X_DT_FS_32G 32
|
||||
#define LSM6DSV320X_DT_FS_64G 64
|
||||
#define LSM6DSV320X_DT_FS_128G 128
|
||||
#define LSM6DSV320X_DT_FS_256G 256
|
||||
#define LSM6DSV320X_DT_FS_320G 320
|
||||
|
||||
/* Gyro range */
|
||||
#define LSM6DSV320X_DT_FS_250DPS 0x1
|
||||
#define LSM6DSV320X_DT_FS_500DPS 0x2
|
||||
#define LSM6DSV320X_DT_FS_1000DPS 0x3
|
||||
#define LSM6DSV320X_DT_FS_2000DPS 0x4
|
||||
#define LSM6DSV320X_DT_FS_4000DPS 0x5
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_ST_LSM6DSV320X_H_ */
|
||||
@@ -192,6 +192,9 @@ config USE_STDC_LSM6DSRX
|
||||
config USE_STDC_LSM6DSV16X
|
||||
bool
|
||||
|
||||
config USE_STDC_LSM6DSV320X
|
||||
bool
|
||||
|
||||
config USE_STDC_LSM9DS1
|
||||
bool
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <zephyr/dt-bindings/sensor/lsm6dsv16x.h>
|
||||
#include <zephyr/dt-bindings/sensor/lsm6dsv32x.h>
|
||||
#include <zephyr/dt-bindings/sensor/lsm6dsv320x.h>
|
||||
#include <zephyr/dt-bindings/sensor/lsm6dso.h>
|
||||
#include <zephyr/dt-bindings/sensor/lsm6dso16is.h>
|
||||
#include <zephyr/dt-bindings/sensor/lps22hh.h>
|
||||
@@ -1489,3 +1490,14 @@ test_i2c_max30210: max30210@c5 {
|
||||
reg = <0xc5>;
|
||||
interrupt-gpios = <&test_gpio 0 0>;
|
||||
};
|
||||
|
||||
test_i2c_lsm6dsv320x: lsm6dsv320x@c6 {
|
||||
compatible = "st,lsm6dsv320x";
|
||||
reg = <0xc6>;
|
||||
int1-gpios = <&test_gpio 0 0>;
|
||||
int2-gpios = <&test_gpio 0 0>;
|
||||
accel-range = <LSM6DSV320X_DT_FS_8G>;
|
||||
accel-odr = <LSM6DSVXXX_DT_ODR_AT_60Hz>;
|
||||
gyro-range = <LSM6DSV320X_DT_FS_2000DPS>;
|
||||
gyro-odr = <LSM6DSVXXX_DT_ODR_AT_60Hz>;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user