Browse Source
The LSM6DSV16X is a system-in-package featuring a 3-axis digital accelerometer and a 3-axis digital gyroscope for industrial and IoT solutions. The LSM6DSV16X embeds advanced dedicated features such as a finite state machine (FSM) for configurable motion tracking and a machine learning core (MLC) for context awareness. https://www.st.com/en/mems-and-sensors/lsm6dsv16x.html This driver is based on stmemsc HAL i/f v2.02 Signed-off-by: Armando Visconti <armando.visconti@st.com>pull/58167/head
18 changed files with 2603 additions and 0 deletions
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
# ST Microelectronics LSM6DSV16X 6-axis IMU sensor driver |
||||
# |
||||
# Copyright (c) 2023 STMicroelectronics |
||||
# |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
# |
||||
zephyr_library() |
||||
|
||||
zephyr_library_sources(lsm6dsv16x.c) |
||||
zephyr_library_sources_ifdef(CONFIG_LSM6DSV16X_SENSORHUB lsm6dsv16x_shub.c) |
||||
zephyr_library_sources_ifdef(CONFIG_LSM6DSV16X_TRIGGER lsm6dsv16x_trigger.c) |
||||
|
||||
zephyr_library_include_directories(../stmemsc) |
@ -0,0 +1,94 @@
@@ -0,0 +1,94 @@
|
||||
# ST Microelectronics LSM6DSV16X 6-axis IMU sensor driver |
||||
|
||||
# Copyright (c) 2023 STMicroelectronics |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
menuconfig LSM6DSV16X |
||||
bool "LSM6DSV16X I2C/SPI accelerometer and gyroscope Chip" |
||||
default y |
||||
depends on DT_HAS_ST_LSM6DSV16X_ENABLED |
||||
select I2C if $(dt_compat_on_bus,$(DT_COMPAT_ST_LSM6DSV16X),i2c) |
||||
select SPI if $(dt_compat_on_bus,$(DT_COMPAT_ST_LSM6DSV16X),spi) |
||||
select HAS_STMEMSC |
||||
select USE_STDC_LSM6DSV16X |
||||
help |
||||
Enable driver for LSM6DSV16X accelerometer and gyroscope |
||||
sensor. |
||||
|
||||
if LSM6DSV16X |
||||
|
||||
choice LSM6DSV16X_TRIGGER_MODE |
||||
prompt "Trigger mode" |
||||
help |
||||
Specify the type of triggering to be used by the driver. |
||||
|
||||
config LSM6DSV16X_TRIGGER_NONE |
||||
bool "No trigger" |
||||
|
||||
config LSM6DSV16X_TRIGGER_GLOBAL_THREAD |
||||
bool "Use global thread" |
||||
depends on GPIO |
||||
select LSM6DSV16X_TRIGGER |
||||
|
||||
config LSM6DSV16X_TRIGGER_OWN_THREAD |
||||
bool "Use own thread" |
||||
depends on GPIO |
||||
select LSM6DSV16X_TRIGGER |
||||
|
||||
endchoice |
||||
|
||||
config LSM6DSV16X_TRIGGER |
||||
bool |
||||
|
||||
if LSM6DSV16X_TRIGGER |
||||
|
||||
config LSM6DSV16X_THREAD_PRIORITY |
||||
int "Thread priority" |
||||
depends on LSM6DSV16X_TRIGGER_OWN_THREAD |
||||
default 10 |
||||
help |
||||
Priority of thread used by the driver to handle interrupts. |
||||
|
||||
config LSM6DSV16X_THREAD_STACK_SIZE |
||||
int "Thread stack size" |
||||
depends on LSM6DSV16X_TRIGGER_OWN_THREAD |
||||
default 1024 |
||||
help |
||||
Stack size of thread used by the driver to handle interrupts. |
||||
|
||||
endif # LSM6DSV16X_TRIGGER |
||||
|
||||
config LSM6DSV16X_ENABLE_TEMP |
||||
bool "Temperature" |
||||
help |
||||
Enable/disable temperature |
||||
|
||||
config LSM6DSV16X_SENSORHUB |
||||
bool "I2C sensorhub feature" |
||||
help |
||||
Enable/disable internal sensorhub. You can enable |
||||
a maximum of two external sensors (if more than two are enabled |
||||
the system would enumerate only the first two found) |
||||
|
||||
if LSM6DSV16X_SENSORHUB |
||||
|
||||
config LSM6DSV16X_EXT_LIS2MDL |
||||
bool "LIS2MDL as external sensor" |
||||
default y |
||||
|
||||
config LSM6DSV16X_EXT_LPS22HH |
||||
bool "LPS22HH as external sensor" |
||||
|
||||
config LSM6DSV16X_EXT_HTS221 |
||||
bool "HTS221 as external sensor" |
||||
|
||||
config LSM6DSV16X_EXT_LPS22HB |
||||
bool "LPS22HB as external sensor" |
||||
|
||||
config LSM6DSV16X_EXT_LPS22DF |
||||
bool "LPS22DF as external sensor" |
||||
default y |
||||
|
||||
endif # LSM6DSV16X_SENSORHUB |
||||
|
||||
endif # LSM6DSV16X |
@ -0,0 +1,981 @@
@@ -0,0 +1,981 @@
|
||||
/* ST Microelectronics LSM6DSV16X 6-axis IMU sensor driver
|
||||
* |
||||
* Copyright (c) 2023 STMicroelectronics |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
* |
||||
* Datasheet: |
||||
* https://www.st.com/resource/en/datasheet/lsm6dsv16x.pdf
|
||||
*/ |
||||
|
||||
#define DT_DRV_COMPAT st_lsm6dsv16x |
||||
|
||||
#include <zephyr/drivers/sensor.h> |
||||
#include <zephyr/kernel.h> |
||||
#include <zephyr/device.h> |
||||
#include <zephyr/init.h> |
||||
#include <string.h> |
||||
#include <zephyr/sys/byteorder.h> |
||||
#include <zephyr/sys/__assert.h> |
||||
#include <zephyr/logging/log.h> |
||||
|
||||
#include "lsm6dsv16x.h" |
||||
|
||||
LOG_MODULE_REGISTER(LSM6DSV16X, CONFIG_SENSOR_LOG_LEVEL); |
||||
|
||||
/*
|
||||
* values taken from lsm6dsv16x_data_rate_t in hal/st module. The mode/accuracy |
||||
* should be selected through accel-odr property in DT |
||||
*/ |
||||
static const float lsm6dsv16x_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 int lsm6dsv16x_freq_to_odr_val(const struct device *dev, uint16_t freq) |
||||
{ |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; |
||||
lsm6dsv16x_data_rate_t odr; |
||||
int8_t mode; |
||||
size_t i; |
||||
|
||||
if (lsm6dsv16x_xl_data_rate_get(ctx, &odr) < 0) { |
||||
return -EINVAL; |
||||
} |
||||
|
||||
mode = (odr >> 4) & 0xf; |
||||
|
||||
for (i = 0; i < ARRAY_SIZE(lsm6dsv16x_odr_map[mode]); i++) { |
||||
if (freq <= lsm6dsv16x_odr_map[mode][i]) { |
||||
LOG_DBG("mode: %d - odr: %d", mode, i); |
||||
return i; |
||||
} |
||||
} |
||||
|
||||
return -EINVAL; |
||||
} |
||||
|
||||
static const uint16_t lsm6dsv16x_accel_fs_map[] = {2, 4, 8, 16}; |
||||
|
||||
static int lsm6dsv16x_accel_range_to_fs_val(int32_t range) |
||||
{ |
||||
size_t i; |
||||
|
||||
for (i = 0; i < ARRAY_SIZE(lsm6dsv16x_accel_fs_map); i++) { |
||||
if (range == lsm6dsv16x_accel_fs_map[i]) { |
||||
return i; |
||||
} |
||||
} |
||||
|
||||
return -EINVAL; |
||||
} |
||||
|
||||
static const uint16_t lsm6dsv16x_gyro_fs_map[] = {250, 125, 500, 0, 1000, 0, 2000}; |
||||
static const uint16_t lsm6dsv16x_gyro_fs_sens[] = {2, 1, 4, 0, 8, 0, 16}; |
||||
|
||||
static int lsm6dsv16x_gyro_range_to_fs_val(int32_t range) |
||||
{ |
||||
size_t i; |
||||
|
||||
for (i = 0; i < ARRAY_SIZE(lsm6dsv16x_gyro_fs_map); i++) { |
||||
if (range == lsm6dsv16x_gyro_fs_map[i]) { |
||||
return i; |
||||
} |
||||
} |
||||
|
||||
return -EINVAL; |
||||
} |
||||
|
||||
static int lsm6dsv16x_accel_set_fs_raw(const struct device *dev, uint8_t fs) |
||||
{ |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; |
||||
struct lsm6dsv16x_data *data = dev->data; |
||||
lsm6dsv16x_xl_full_scale_t val; |
||||
|
||||
switch (fs) { |
||||
case 0: |
||||
val = LSM6DSV16X_2g; |
||||
break; |
||||
case 1: |
||||
val = LSM6DSV16X_4g; |
||||
break; |
||||
case 2: |
||||
val = LSM6DSV16X_8g; |
||||
break; |
||||
case 3: |
||||
val = LSM6DSV16X_16g; |
||||
break; |
||||
default: |
||||
return -EIO; |
||||
} |
||||
|
||||
if (lsm6dsv16x_xl_full_scale_set(ctx, val) < 0) { |
||||
return -EIO; |
||||
} |
||||
|
||||
data->accel_fs = fs; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int lsm6dsv16x_accel_set_odr_raw(const struct device *dev, uint8_t odr) |
||||
{ |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; |
||||
struct lsm6dsv16x_data *data = dev->data; |
||||
|
||||
if (lsm6dsv16x_xl_data_rate_set(ctx, odr) < 0) { |
||||
return -EIO; |
||||
} |
||||
|
||||
data->accel_freq = odr; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int lsm6dsv16x_gyro_set_fs_raw(const struct device *dev, uint8_t fs) |
||||
{ |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; |
||||
|
||||
if (lsm6dsv16x_gy_full_scale_set(ctx, fs) < 0) { |
||||
return -EIO; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int lsm6dsv16x_gyro_set_odr_raw(const struct device *dev, uint8_t odr) |
||||
{ |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; |
||||
|
||||
if (lsm6dsv16x_gy_data_rate_set(ctx, odr) < 0) { |
||||
return -EIO; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int lsm6dsv16x_accel_odr_set(const struct device *dev, uint16_t freq) |
||||
{ |
||||
int odr; |
||||
|
||||
odr = lsm6dsv16x_freq_to_odr_val(dev, freq); |
||||
if (odr < 0) { |
||||
return odr; |
||||
} |
||||
|
||||
if (lsm6dsv16x_accel_set_odr_raw(dev, odr) < 0) { |
||||
LOG_DBG("failed to set accelerometer sampling rate"); |
||||
return -EIO; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int lsm6dsv16x_accel_range_set(const struct device *dev, int32_t range) |
||||
{ |
||||
int fs; |
||||
struct lsm6dsv16x_data *data = dev->data; |
||||
|
||||
fs = lsm6dsv16x_accel_range_to_fs_val(range); |
||||
if (fs < 0) { |
||||
return fs; |
||||
} |
||||
|
||||
if (lsm6dsv16x_accel_set_fs_raw(dev, fs) < 0) { |
||||
LOG_DBG("failed to set accelerometer full-scale"); |
||||
return -EIO; |
||||
} |
||||
|
||||
data->acc_gain = lsm6dsv16x_accel_fs_map[fs] * GAIN_UNIT_XL / 2; |
||||
return 0; |
||||
} |
||||
|
||||
static int lsm6dsv16x_accel_config(const struct device *dev, |
||||
enum sensor_channel chan, |
||||
enum sensor_attribute attr, |
||||
const struct sensor_value *val) |
||||
{ |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; |
||||
lsm6dsv16x_xl_mode_t mode; |
||||
|
||||
switch (attr) { |
||||
case SENSOR_ATTR_FULL_SCALE: |
||||
return lsm6dsv16x_accel_range_set(dev, sensor_ms2_to_g(val)); |
||||
case SENSOR_ATTR_SAMPLING_FREQUENCY: |
||||
return lsm6dsv16x_accel_odr_set(dev, val->val1); |
||||
case SENSOR_ATTR_CONFIGURATION: |
||||
switch (val->val1) { |
||||
case 0: /* High Performance */ |
||||
mode = LSM6DSV16X_XL_HIGH_PERFORMANCE_MD; |
||||
break; |
||||
case 1: /* High Accuracy */ |
||||
mode = LSM6DSV16X_XL_HIGH_ACCURACY_ODR_MD; |
||||
break; |
||||
case 3: /* ODR triggered */ |
||||
mode = LSM6DSV16X_XL_ODR_TRIGGERED_MD; |
||||
break; |
||||
case 4: /* Low Power 2 */ |
||||
mode = LSM6DSV16X_XL_LOW_POWER_2_AVG_MD; |
||||
break; |
||||
case 5: /* Low Power 4 */ |
||||
mode = LSM6DSV16X_XL_LOW_POWER_4_AVG_MD; |
||||
break; |
||||
case 6: /* Low Power 8 */ |
||||
mode = LSM6DSV16X_XL_LOW_POWER_8_AVG_MD; |
||||
break; |
||||
case 7: /* Normal */ |
||||
mode = LSM6DSV16X_XL_NORMAL_MD; |
||||
break; |
||||
default: |
||||
return -EIO; |
||||
} |
||||
|
||||
return lsm6dsv16x_xl_mode_set(ctx, mode); |
||||
default: |
||||
LOG_DBG("Accel attribute not supported."); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int lsm6dsv16x_gyro_odr_set(const struct device *dev, uint16_t freq) |
||||
{ |
||||
int odr; |
||||
|
||||
if (freq < 8) { |
||||
return -EIO; |
||||
} |
||||
|
||||
odr = lsm6dsv16x_freq_to_odr_val(dev, freq); |
||||
if (odr < 0) { |
||||
return odr; |
||||
} |
||||
|
||||
if (lsm6dsv16x_gyro_set_odr_raw(dev, odr) < 0) { |
||||
LOG_DBG("failed to set gyroscope sampling rate"); |
||||
return -EIO; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int lsm6dsv16x_gyro_range_set(const struct device *dev, int32_t range) |
||||
{ |
||||
int fs; |
||||
struct lsm6dsv16x_data *data = dev->data; |
||||
|
||||
fs = lsm6dsv16x_gyro_range_to_fs_val(range); |
||||
if (fs < 0) { |
||||
return fs; |
||||
} |
||||
|
||||
if (lsm6dsv16x_gyro_set_fs_raw(dev, fs) < 0) { |
||||
LOG_DBG("failed to set gyroscope full-scale"); |
||||
return -EIO; |
||||
} |
||||
|
||||
data->gyro_gain = (lsm6dsv16x_gyro_fs_sens[fs] * GAIN_UNIT_G); |
||||
return 0; |
||||
} |
||||
|
||||
static int lsm6dsv16x_gyro_config(const struct device *dev, |
||||
enum sensor_channel chan, |
||||
enum sensor_attribute attr, |
||||
const struct sensor_value *val) |
||||
{ |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; |
||||
lsm6dsv16x_gy_mode_t mode; |
||||
|
||||
switch (attr) { |
||||
case SENSOR_ATTR_FULL_SCALE: |
||||
return lsm6dsv16x_gyro_range_set(dev, sensor_rad_to_degrees(val)); |
||||
case SENSOR_ATTR_SAMPLING_FREQUENCY: |
||||
return lsm6dsv16x_gyro_odr_set(dev, val->val1); |
||||
case SENSOR_ATTR_CONFIGURATION: |
||||
switch (val->val1) { |
||||
case 0: /* High Performance */ |
||||
mode = LSM6DSV16X_GY_HIGH_PERFORMANCE_MD; |
||||
break; |
||||
case 1: /* High Accuracy */ |
||||
mode = LSM6DSV16X_GY_HIGH_ACCURACY_ODR_MD; |
||||
break; |
||||
case 4: /* Sleep */ |
||||
mode = LSM6DSV16X_GY_SLEEP_MD; |
||||
break; |
||||
case 5: /* Low Power */ |
||||
mode = LSM6DSV16X_GY_LOW_POWER_MD; |
||||
break; |
||||
default: |
||||
return -EIO; |
||||
} |
||||
|
||||
return lsm6dsv16x_gy_mode_set(ctx, mode); |
||||
default: |
||||
LOG_DBG("Gyro attribute not supported."); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int lsm6dsv16x_attr_set(const struct device *dev, |
||||
enum sensor_channel chan, |
||||
enum sensor_attribute attr, |
||||
const struct sensor_value *val) |
||||
{ |
||||
#if defined(CONFIG_LSM6DSV16X_SENSORHUB) |
||||
struct lsm6dsv16x_data *data = dev->data; |
||||
#endif /* CONFIG_LSM6DSV16X_SENSORHUB */ |
||||
|
||||
switch (chan) { |
||||
case SENSOR_CHAN_ACCEL_XYZ: |
||||
return lsm6dsv16x_accel_config(dev, chan, attr, val); |
||||
case SENSOR_CHAN_GYRO_XYZ: |
||||
return lsm6dsv16x_gyro_config(dev, chan, attr, val); |
||||
#if defined(CONFIG_LSM6DSV16X_SENSORHUB) |
||||
case SENSOR_CHAN_MAGN_XYZ: |
||||
case SENSOR_CHAN_PRESS: |
||||
case SENSOR_CHAN_HUMIDITY: |
||||
if (!data->shub_inited) { |
||||
LOG_ERR("shub not inited."); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
return lsm6dsv16x_shub_config(dev, chan, attr, val); |
||||
#endif /* CONFIG_LSM6DSV16X_SENSORHUB */ |
||||
default: |
||||
LOG_WRN("attr_set() not supported on this channel."); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int lsm6dsv16x_sample_fetch_accel(const struct device *dev) |
||||
{ |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; |
||||
struct lsm6dsv16x_data *data = dev->data; |
||||
|
||||
if (lsm6dsv16x_acceleration_raw_get(ctx, data->acc) < 0) { |
||||
LOG_DBG("Failed to read sample"); |
||||
return -EIO; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int lsm6dsv16x_sample_fetch_gyro(const struct device *dev) |
||||
{ |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; |
||||
struct lsm6dsv16x_data *data = dev->data; |
||||
|
||||
if (lsm6dsv16x_angular_rate_raw_get(ctx, data->gyro) < 0) { |
||||
LOG_DBG("Failed to read sample"); |
||||
return -EIO; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
#if defined(CONFIG_LSM6DSV16X_ENABLE_TEMP) |
||||
static int lsm6dsv16x_sample_fetch_temp(const struct device *dev) |
||||
{ |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; |
||||
struct lsm6dsv16x_data *data = dev->data; |
||||
|
||||
if (lsm6dsv16x_temperature_raw_get(ctx, &data->temp_sample) < 0) { |
||||
LOG_DBG("Failed to read sample"); |
||||
return -EIO; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
#endif |
||||
|
||||
#if defined(CONFIG_LSM6DSV16X_SENSORHUB) |
||||
static int lsm6dsv16x_sample_fetch_shub(const struct device *dev) |
||||
{ |
||||
if (lsm6dsv16x_shub_fetch_external_devs(dev) < 0) { |
||||
LOG_DBG("failed to read ext shub devices"); |
||||
return -EIO; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
#endif /* CONFIG_LSM6DSV16X_SENSORHUB */ |
||||
|
||||
static int lsm6dsv16x_sample_fetch(const struct device *dev, |
||||
enum sensor_channel chan) |
||||
{ |
||||
#if defined(CONFIG_LSM6DSV16X_SENSORHUB) |
||||
struct lsm6dsv16x_data *data = dev->data; |
||||
#endif /* CONFIG_LSM6DSV16X_SENSORHUB */ |
||||
|
||||
switch (chan) { |
||||
case SENSOR_CHAN_ACCEL_XYZ: |
||||
lsm6dsv16x_sample_fetch_accel(dev); |
||||
break; |
||||
case SENSOR_CHAN_GYRO_XYZ: |
||||
lsm6dsv16x_sample_fetch_gyro(dev); |
||||
break; |
||||
#if defined(CONFIG_LSM6DSV16X_ENABLE_TEMP) |
||||
case SENSOR_CHAN_DIE_TEMP: |
||||
lsm6dsv16x_sample_fetch_temp(dev); |
||||
break; |
||||
#endif |
||||
case SENSOR_CHAN_ALL: |
||||
lsm6dsv16x_sample_fetch_accel(dev); |
||||
lsm6dsv16x_sample_fetch_gyro(dev); |
||||
#if defined(CONFIG_LSM6DSV16X_ENABLE_TEMP) |
||||
lsm6dsv16x_sample_fetch_temp(dev); |
||||
#endif |
||||
#if defined(CONFIG_LSM6DSV16X_SENSORHUB) |
||||
if (data->shub_inited) { |
||||
lsm6dsv16x_sample_fetch_shub(dev); |
||||
} |
||||
#endif |
||||
break; |
||||
default: |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static inline void lsm6dsv16x_accel_convert(struct sensor_value *val, int raw_val, |
||||
uint32_t sensitivity) |
||||
{ |
||||
int64_t dval; |
||||
|
||||
/* Sensitivity is exposed in ug/LSB */ |
||||
/* Convert to m/s^2 */ |
||||
dval = (int64_t)(raw_val) * sensitivity * SENSOR_G_DOUBLE; |
||||
val->val1 = (int32_t)(dval / 1000000); |
||||
val->val2 = (int32_t)(dval % 1000000); |
||||
|
||||
} |
||||
|
||||
static inline int lsm6dsv16x_accel_get_channel(enum sensor_channel chan, |
||||
struct sensor_value *val, |
||||
struct lsm6dsv16x_data *data, |
||||
uint32_t sensitivity) |
||||
{ |
||||
uint8_t i; |
||||
|
||||
switch (chan) { |
||||
case SENSOR_CHAN_ACCEL_X: |
||||
lsm6dsv16x_accel_convert(val, data->acc[0], sensitivity); |
||||
break; |
||||
case SENSOR_CHAN_ACCEL_Y: |
||||
lsm6dsv16x_accel_convert(val, data->acc[1], sensitivity); |
||||
break; |
||||
case SENSOR_CHAN_ACCEL_Z: |
||||
lsm6dsv16x_accel_convert(val, data->acc[2], sensitivity); |
||||
break; |
||||
case SENSOR_CHAN_ACCEL_XYZ: |
||||
for (i = 0; i < 3; i++) { |
||||
lsm6dsv16x_accel_convert(val++, data->acc[i], sensitivity); |
||||
} |
||||
break; |
||||
default: |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int lsm6dsv16x_accel_channel_get(enum sensor_channel chan, |
||||
struct sensor_value *val, |
||||
struct lsm6dsv16x_data *data) |
||||
{ |
||||
return lsm6dsv16x_accel_get_channel(chan, val, data, data->acc_gain); |
||||
} |
||||
|
||||
static inline void lsm6dsv16x_gyro_convert(struct sensor_value *val, int raw_val, |
||||
uint32_t sensitivity) |
||||
{ |
||||
int64_t dval; |
||||
|
||||
/* Sensitivity is exposed in udps/LSB */ |
||||
/* Convert to rad/s */ |
||||
dval = (int64_t)(raw_val) * sensitivity * SENSOR_DEG2RAD_DOUBLE; |
||||
val->val1 = (int32_t)(dval / 1000000); |
||||
val->val2 = (int32_t)(dval % 1000000); |
||||
} |
||||
|
||||
static inline int lsm6dsv16x_gyro_get_channel(enum sensor_channel chan, |
||||
struct sensor_value *val, |
||||
struct lsm6dsv16x_data *data, |
||||
uint32_t sensitivity) |
||||
{ |
||||
uint8_t i; |
||||
|
||||
switch (chan) { |
||||
case SENSOR_CHAN_GYRO_X: |
||||
lsm6dsv16x_gyro_convert(val, data->gyro[0], sensitivity); |
||||
break; |
||||
case SENSOR_CHAN_GYRO_Y: |
||||
lsm6dsv16x_gyro_convert(val, data->gyro[1], sensitivity); |
||||
break; |
||||
case SENSOR_CHAN_GYRO_Z: |
||||
lsm6dsv16x_gyro_convert(val, data->gyro[2], sensitivity); |
||||
break; |
||||
case SENSOR_CHAN_GYRO_XYZ: |
||||
for (i = 0; i < 3; i++) { |
||||
lsm6dsv16x_gyro_convert(val++, data->gyro[i], sensitivity); |
||||
} |
||||
break; |
||||
default: |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int lsm6dsv16x_gyro_channel_get(enum sensor_channel chan, |
||||
struct sensor_value *val, |
||||
struct lsm6dsv16x_data *data) |
||||
{ |
||||
return lsm6dsv16x_gyro_get_channel(chan, val, data, data->gyro_gain); |
||||
} |
||||
|
||||
#if defined(CONFIG_LSM6DSV16X_ENABLE_TEMP) |
||||
static void lsm6dsv16x_gyro_channel_get_temp(struct sensor_value *val, |
||||
struct lsm6dsv16x_data *data) |
||||
{ |
||||
int32_t micro_c; |
||||
|
||||
/* convert units to micro Celsius. Raw temperature samples are
|
||||
* expressed in 256 LSB/deg_C units. And LSB output is 0 at 25 C. |
||||
*/ |
||||
micro_c = (data->temp_sample * 1000000) / 256; |
||||
|
||||
val->val1 = micro_c / 1000000 + 25; |
||||
val->val2 = micro_c % 1000000; |
||||
} |
||||
#endif |
||||
|
||||
#if defined(CONFIG_LSM6DSV16X_SENSORHUB) |
||||
static inline void lsm6dsv16x_magn_convert(struct sensor_value *val, int raw_val, |
||||
uint16_t sensitivity) |
||||
{ |
||||
double dval; |
||||
|
||||
/* Sensitivity is exposed in mgauss/LSB */ |
||||
dval = (double)(raw_val * sensitivity); |
||||
val->val1 = (int32_t)dval / 1000000; |
||||
val->val2 = (int32_t)dval % 1000000; |
||||
} |
||||
|
||||
static inline int lsm6dsv16x_magn_get_channel(enum sensor_channel chan, |
||||
struct sensor_value *val, |
||||
struct lsm6dsv16x_data *data) |
||||
{ |
||||
int16_t sample[3]; |
||||
int idx; |
||||
|
||||
idx = lsm6dsv16x_shub_get_idx(data->dev, SENSOR_CHAN_MAGN_XYZ); |
||||
if (idx < 0) { |
||||
LOG_DBG("external magn not supported"); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
|
||||
sample[0] = sys_le16_to_cpu((int16_t)(data->ext_data[idx][0] | |
||||
(data->ext_data[idx][1] << 8))); |
||||
sample[1] = sys_le16_to_cpu((int16_t)(data->ext_data[idx][2] | |
||||
(data->ext_data[idx][3] << 8))); |
||||
sample[2] = sys_le16_to_cpu((int16_t)(data->ext_data[idx][4] | |
||||
(data->ext_data[idx][5] << 8))); |
||||
|
||||
switch (chan) { |
||||
case SENSOR_CHAN_MAGN_X: |
||||
lsm6dsv16x_magn_convert(val, sample[0], data->magn_gain); |
||||
break; |
||||
case SENSOR_CHAN_MAGN_Y: |
||||
lsm6dsv16x_magn_convert(val, sample[1], data->magn_gain); |
||||
break; |
||||
case SENSOR_CHAN_MAGN_Z: |
||||
lsm6dsv16x_magn_convert(val, sample[2], data->magn_gain); |
||||
break; |
||||
case SENSOR_CHAN_MAGN_XYZ: |
||||
lsm6dsv16x_magn_convert(val, sample[0], data->magn_gain); |
||||
lsm6dsv16x_magn_convert(val + 1, sample[1], data->magn_gain); |
||||
lsm6dsv16x_magn_convert(val + 2, sample[2], data->magn_gain); |
||||
break; |
||||
default: |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static inline void lsm6dsv16x_hum_convert(struct sensor_value *val, |
||||
struct lsm6dsv16x_data *data) |
||||
{ |
||||
float rh; |
||||
int16_t raw_val; |
||||
struct hts221_data *ht = &data->hts221; |
||||
int idx; |
||||
|
||||
idx = lsm6dsv16x_shub_get_idx(data->dev, SENSOR_CHAN_HUMIDITY); |
||||
if (idx < 0) { |
||||
LOG_DBG("external press/temp not supported"); |
||||
return; |
||||
} |
||||
|
||||
raw_val = sys_le16_to_cpu((int16_t)(data->ext_data[idx][0] | |
||||
(data->ext_data[idx][1] << 8))); |
||||
|
||||
/* find relative humidty by linear interpolation */ |
||||
rh = (ht->y1 - ht->y0) * raw_val + ht->x1 * ht->y0 - ht->x0 * ht->y1; |
||||
rh /= (ht->x1 - ht->x0); |
||||
|
||||
/* convert humidity to integer and fractional part */ |
||||
val->val1 = rh; |
||||
val->val2 = rh * 1000000; |
||||
} |
||||
|
||||
static inline void lsm6dsv16x_press_convert(struct sensor_value *val, |
||||
struct lsm6dsv16x_data *data) |
||||
{ |
||||
int32_t raw_val; |
||||
int idx; |
||||
|
||||
idx = lsm6dsv16x_shub_get_idx(data->dev, SENSOR_CHAN_PRESS); |
||||
if (idx < 0) { |
||||
LOG_DBG("external press/temp not supported"); |
||||
return; |
||||
} |
||||
|
||||
raw_val = sys_le32_to_cpu((int32_t)(data->ext_data[idx][0] | |
||||
(data->ext_data[idx][1] << 8) | |
||||
(data->ext_data[idx][2] << 16))); |
||||
|
||||
/* Pressure sensitivity is 4096 LSB/hPa */ |
||||
/* Convert raw_val to val in kPa */ |
||||
val->val1 = (raw_val >> 12) / 10; |
||||
val->val2 = (raw_val >> 12) % 10 * 100000 + |
||||
(((int32_t)((raw_val) & 0x0FFF) * 100000L) >> 12); |
||||
} |
||||
|
||||
static inline void lsm6dsv16x_temp_convert(struct sensor_value *val, |
||||
struct lsm6dsv16x_data *data) |
||||
{ |
||||
int16_t raw_val; |
||||
int idx; |
||||
|
||||
idx = lsm6dsv16x_shub_get_idx(data->dev, SENSOR_CHAN_PRESS); |
||||
if (idx < 0) { |
||||
LOG_DBG("external press/temp not supported"); |
||||
return; |
||||
} |
||||
|
||||
raw_val = sys_le16_to_cpu((int16_t)(data->ext_data[idx][3] | |
||||
(data->ext_data[idx][4] << 8))); |
||||
|
||||
/* Temperature sensitivity is 100 LSB/deg C */ |
||||
val->val1 = raw_val / 100; |
||||
val->val2 = (int32_t)raw_val % 100 * (10000); |
||||
} |
||||
#endif |
||||
|
||||
static int lsm6dsv16x_channel_get(const struct device *dev, |
||||
enum sensor_channel chan, |
||||
struct sensor_value *val) |
||||
{ |
||||
struct lsm6dsv16x_data *data = dev->data; |
||||
|
||||
switch (chan) { |
||||
case SENSOR_CHAN_ACCEL_X: |
||||
case SENSOR_CHAN_ACCEL_Y: |
||||
case SENSOR_CHAN_ACCEL_Z: |
||||
case SENSOR_CHAN_ACCEL_XYZ: |
||||
lsm6dsv16x_accel_channel_get(chan, val, data); |
||||
break; |
||||
case SENSOR_CHAN_GYRO_X: |
||||
case SENSOR_CHAN_GYRO_Y: |
||||
case SENSOR_CHAN_GYRO_Z: |
||||
case SENSOR_CHAN_GYRO_XYZ: |
||||
lsm6dsv16x_gyro_channel_get(chan, val, data); |
||||
break; |
||||
#if defined(CONFIG_LSM6DSV16X_ENABLE_TEMP) |
||||
case SENSOR_CHAN_DIE_TEMP: |
||||
lsm6dsv16x_gyro_channel_get_temp(val, data); |
||||
break; |
||||
#endif |
||||
#if defined(CONFIG_LSM6DSV16X_SENSORHUB) |
||||
case SENSOR_CHAN_MAGN_X: |
||||
case SENSOR_CHAN_MAGN_Y: |
||||
case SENSOR_CHAN_MAGN_Z: |
||||
case SENSOR_CHAN_MAGN_XYZ: |
||||
if (!data->shub_inited) { |
||||
LOG_ERR("attr_set() shub not inited."); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
lsm6dsv16x_magn_get_channel(chan, val, data); |
||||
break; |
||||
|
||||
case SENSOR_CHAN_HUMIDITY: |
||||
if (!data->shub_inited) { |
||||
LOG_ERR("attr_set() shub not inited."); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
lsm6dsv16x_hum_convert(val, data); |
||||
break; |
||||
|
||||
case SENSOR_CHAN_PRESS: |
||||
if (!data->shub_inited) { |
||||
LOG_ERR("attr_set() shub not inited."); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
lsm6dsv16x_press_convert(val, data); |
||||
break; |
||||
|
||||
case SENSOR_CHAN_AMBIENT_TEMP: |
||||
if (!data->shub_inited) { |
||||
LOG_ERR("attr_set() shub not inited."); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
lsm6dsv16x_temp_convert(val, data); |
||||
break; |
||||
#endif |
||||
default: |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static const struct sensor_driver_api lsm6dsv16x_driver_api = { |
||||
.attr_set = lsm6dsv16x_attr_set, |
||||
#if CONFIG_LSM6DSV16X_TRIGGER |
||||
.trigger_set = lsm6dsv16x_trigger_set, |
||||
#endif |
||||
.sample_fetch = lsm6dsv16x_sample_fetch, |
||||
.channel_get = lsm6dsv16x_channel_get, |
||||
}; |
||||
|
||||
static int lsm6dsv16x_init_chip(const struct device *dev) |
||||
{ |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; |
||||
struct lsm6dsv16x_data *lsm6dsv16x = dev->data; |
||||
uint8_t chip_id; |
||||
uint8_t odr, fs; |
||||
|
||||
/* All registers except 0x01 are different between banks, including the WHO_AM_I
|
||||
* register and the register used for a SW reset. If the lsm6dsv16x 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 (lsm6dsv16x_mem_bank_set(ctx, LSM6DSV16X_MAIN_MEM_BANK) < 0) { |
||||
LOG_DBG("Failed to set user bank"); |
||||
return -EIO; |
||||
} |
||||
|
||||
if (lsm6dsv16x_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 != LSM6DSV16X_ID) { |
||||
LOG_DBG("Invalid chip id 0x%x", chip_id); |
||||
return -EIO; |
||||
} |
||||
|
||||
/* reset device (sw_por) */ |
||||
if (lsm6dsv16x_reset_set(ctx, LSM6DSV16X_GLOBAL_RST) < 0) { |
||||
return -EIO; |
||||
} |
||||
|
||||
/* wait 30ms as reported in AN5763 */ |
||||
k_sleep(K_MSEC(30)); |
||||
|
||||
fs = cfg->accel_range; |
||||
LOG_DBG("accel range is %d", fs); |
||||
if (lsm6dsv16x_accel_set_fs_raw(dev, fs) < 0) { |
||||
LOG_ERR("failed to set accelerometer range %d", fs); |
||||
return -EIO; |
||||
} |
||||
lsm6dsv16x->acc_gain = lsm6dsv16x_accel_fs_map[fs] * GAIN_UNIT_XL / 2; |
||||
|
||||
odr = cfg->accel_odr; |
||||
LOG_DBG("accel odr is %d", odr); |
||||
if (lsm6dsv16x_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 (lsm6dsv16x_gyro_set_fs_raw(dev, fs) < 0) { |
||||
LOG_ERR("failed to set gyroscope range %d", fs); |
||||
return -EIO; |
||||
} |
||||
lsm6dsv16x->gyro_gain = (lsm6dsv16x_gyro_fs_sens[fs] * GAIN_UNIT_G); |
||||
|
||||
odr = cfg->gyro_odr; |
||||
LOG_DBG("gyro odr is %d", odr); |
||||
lsm6dsv16x->gyro_freq = odr; |
||||
if (lsm6dsv16x_gyro_set_odr_raw(dev, odr) < 0) { |
||||
LOG_ERR("failed to set gyroscope odr %d", odr); |
||||
return -EIO; |
||||
} |
||||
|
||||
if (lsm6dsv16x_block_data_update_set(ctx, 1) < 0) { |
||||
LOG_DBG("failed to set BDU mode"); |
||||
return -EIO; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int lsm6dsv16x_init(const struct device *dev) |
||||
{ |
||||
#ifdef CONFIG_LSM6DSV16X_TRIGGER |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
#endif |
||||
struct lsm6dsv16x_data *data = dev->data; |
||||
|
||||
LOG_INF("Initialize device %s", dev->name); |
||||
data->dev = dev; |
||||
|
||||
if (lsm6dsv16x_init_chip(dev) < 0) { |
||||
LOG_DBG("failed to initialize chip"); |
||||
return -EIO; |
||||
} |
||||
|
||||
#ifdef CONFIG_LSM6DSV16X_TRIGGER |
||||
if (cfg->trig_enabled) { |
||||
if (lsm6dsv16x_init_interrupt(dev) < 0) { |
||||
LOG_ERR("Failed to initialize interrupt."); |
||||
return -EIO; |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
#ifdef CONFIG_LSM6DSV16X_SENSORHUB |
||||
data->shub_inited = true; |
||||
if (lsm6dsv16x_shub_init(dev) < 0) { |
||||
LOG_INF("shub: no external chips found"); |
||||
data->shub_inited = false; |
||||
} |
||||
#endif |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
#if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 0 |
||||
#warning "LSM6DSV16X driver enabled without any devices" |
||||
#endif |
||||
|
||||
/*
|
||||
* Device creation macro, shared by LSM6DSV16X_DEFINE_SPI() and |
||||
* LSM6DSV16X_DEFINE_I2C(). |
||||
*/ |
||||
|
||||
#define LSM6DSV16X_DEVICE_INIT(inst) \ |
||||
SENSOR_DEVICE_DT_INST_DEFINE(inst, \ |
||||
lsm6dsv16x_init, \ |
||||
NULL, \ |
||||
&lsm6dsv16x_data_##inst, \ |
||||
&lsm6dsv16x_config_##inst, \ |
||||
POST_KERNEL, \ |
||||
CONFIG_SENSOR_INIT_PRIORITY, \ |
||||
&lsm6dsv16x_driver_api); |
||||
|
||||
/*
|
||||
* Instantiation macros used when a device is on a SPI bus. |
||||
*/ |
||||
|
||||
#ifdef CONFIG_LSM6DSV16X_TRIGGER |
||||
#define LSM6DSV16X_CFG_IRQ(inst) \ |
||||
.trig_enabled = true, \ |
||||
.gpio_drdy = GPIO_DT_SPEC_INST_GET(inst, irq_gpios), \ |
||||
.drdy_pin = DT_INST_PROP(inst, drdy_pin) |
||||
#else |
||||
#define LSM6DSV16X_CFG_IRQ(inst) |
||||
#endif /* CONFIG_LSM6DSV16X_TRIGGER */ |
||||
|
||||
#define LSM6DSV16X_SPI_OP (SPI_WORD_SET(8) | \ |
||||
SPI_OP_MODE_MASTER | \ |
||||
SPI_MODE_CPOL | \ |
||||
SPI_MODE_CPHA) \ |
||||
|
||||
#define LSM6DSV16X_CONFIG_COMMON(inst) \ |
||||
.accel_odr = DT_INST_PROP(inst, accel_odr), \ |
||||
.accel_range = DT_INST_PROP(inst, accel_range), \ |
||||
.gyro_odr = DT_INST_PROP(inst, gyro_odr), \ |
||||
.gyro_range = DT_INST_PROP(inst, gyro_range), \ |
||||
.drdy_pulsed = DT_INST_PROP(inst, drdy_pulsed), \ |
||||
COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, irq_gpios), \ |
||||
(LSM6DSV16X_CFG_IRQ(inst)), ()) |
||||
|
||||
#define LSM6DSV16X_CONFIG_SPI(inst) \ |
||||
{ \ |
||||
STMEMSC_CTX_SPI(&lsm6dsv16x_config_##inst.stmemsc_cfg), \ |
||||
.stmemsc_cfg = { \ |
||||
.spi = SPI_DT_SPEC_INST_GET(inst, \ |
||||
LSM6DSV16X_SPI_OP, \ |
||||
0), \ |
||||
}, \ |
||||
LSM6DSV16X_CONFIG_COMMON(inst) \ |
||||
} |
||||
|
||||
/*
|
||||
* Instantiation macros used when a device is on an I2C bus. |
||||
*/ |
||||
|
||||
#define LSM6DSV16X_CONFIG_I2C(inst) \ |
||||
{ \ |
||||
STMEMSC_CTX_I2C(&lsm6dsv16x_config_##inst.stmemsc_cfg), \ |
||||
.stmemsc_cfg = { \ |
||||
.i2c = I2C_DT_SPEC_INST_GET(inst), \ |
||||
}, \ |
||||
LSM6DSV16X_CONFIG_COMMON(inst) \ |
||||
} |
||||
|
||||
/*
|
||||
* Main instantiation macro. Use of COND_CODE_1() selects the right |
||||
* bus-specific macro at preprocessor time. |
||||
*/ |
||||
|
||||
#define LSM6DSV16X_DEFINE(inst) \ |
||||
static struct lsm6dsv16x_data lsm6dsv16x_data_##inst; \ |
||||
static const struct lsm6dsv16x_config lsm6dsv16x_config_##inst = \ |
||||
COND_CODE_1(DT_INST_ON_BUS(inst, spi), \ |
||||
(LSM6DSV16X_CONFIG_SPI(inst)), \ |
||||
(LSM6DSV16X_CONFIG_I2C(inst))); \ |
||||
LSM6DSV16X_DEVICE_INIT(inst) |
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(LSM6DSV16X_DEFINE) |
@ -0,0 +1,140 @@
@@ -0,0 +1,140 @@
|
||||
/* ST Microelectronics LSM6DSV16X 6-axis IMU sensor driver
|
||||
* |
||||
* Copyright (c) 2023 STMicroelectronics |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
* |
||||
* Datasheet: |
||||
* https://www.st.com/resource/en/datasheet/lsm6dsv16x.pdf
|
||||
*/ |
||||
|
||||
#ifndef ZEPHYR_DRIVERS_SENSOR_LSM6DSV16X_LSM6DSV16X_H_ |
||||
#define ZEPHYR_DRIVERS_SENSOR_LSM6DSV16X_LSM6DSV16X_H_ |
||||
|
||||
#include <zephyr/drivers/sensor.h> |
||||
#include <zephyr/types.h> |
||||
#include <zephyr/drivers/gpio.h> |
||||
#include <zephyr/sys/util.h> |
||||
#include <stmemsc.h> |
||||
#include "lsm6dsv16x_reg.h" |
||||
|
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) |
||||
#include <zephyr/drivers/spi.h> |
||||
#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) */ |
||||
|
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) |
||||
#include <zephyr/drivers/i2c.h> |
||||
#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */ |
||||
|
||||
#define LSM6DSV16X_EN_BIT 0x01 |
||||
#define LSM6DSV16X_DIS_BIT 0x00 |
||||
|
||||
/* Accel sensor sensitivity grain is 61 ug/LSB */ |
||||
#define GAIN_UNIT_XL (61LL) |
||||
|
||||
/* Gyro sensor sensitivity grain is 4.375 udps/LSB */ |
||||
#define GAIN_UNIT_G (4375LL) |
||||
|
||||
#define SENSOR_PI_DOUBLE (SENSOR_PI / 1000000.0) |
||||
#define SENSOR_DEG2RAD_DOUBLE (SENSOR_PI_DOUBLE / 180) |
||||
#define SENSOR_G_DOUBLE (SENSOR_G / 1000000.0) |
||||
|
||||
struct lsm6dsv16x_config { |
||||
stmdev_ctx_t ctx; |
||||
union { |
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) |
||||
const struct i2c_dt_spec i2c; |
||||
#endif |
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) |
||||
const struct spi_dt_spec spi; |
||||
#endif |
||||
} stmemsc_cfg; |
||||
uint8_t accel_pm; |
||||
uint8_t accel_odr; |
||||
uint8_t accel_range; |
||||
uint8_t gyro_pm; |
||||
uint8_t gyro_odr; |
||||
uint8_t gyro_range; |
||||
uint8_t drdy_pulsed; |
||||
#ifdef CONFIG_LSM6DSV16X_TRIGGER |
||||
const struct gpio_dt_spec gpio_drdy; |
||||
uint8_t drdy_pin; |
||||
bool trig_enabled; |
||||
#endif /* CONFIG_LSM6DSV16X_TRIGGER */ |
||||
}; |
||||
|
||||
union samples { |
||||
uint8_t raw[6]; |
||||
struct { |
||||
int16_t axis[3]; |
||||
}; |
||||
} __aligned(2); |
||||
|
||||
#define LSM6DSV16X_SHUB_MAX_NUM_TARGETS 3 |
||||
|
||||
struct lsm6dsv16x_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 |
||||
#if defined(CONFIG_LSM6DSV16X_SENSORHUB) |
||||
uint8_t ext_data[LSM6DSV16X_SHUB_MAX_NUM_TARGETS][6]; |
||||
uint16_t magn_gain; |
||||
|
||||
struct hts221_data { |
||||
int16_t x0; |
||||
int16_t x1; |
||||
int16_t y0; |
||||
int16_t y1; |
||||
} hts221; |
||||
bool shub_inited; |
||||
uint8_t num_ext_dev; |
||||
uint8_t shub_ext[LSM6DSV16X_SHUB_MAX_NUM_TARGETS]; |
||||
#endif /* CONFIG_LSM6DSV16X_SENSORHUB */ |
||||
|
||||
uint8_t accel_freq; |
||||
uint8_t accel_fs; |
||||
uint8_t gyro_freq; |
||||
uint8_t gyro_fs; |
||||
|
||||
#ifdef CONFIG_LSM6DSV16X_TRIGGER |
||||
struct gpio_callback gpio_cb; |
||||
sensor_trigger_handler_t handler_drdy_acc; |
||||
const struct sensor_trigger *trig_drdy_acc; |
||||
sensor_trigger_handler_t handler_drdy_gyr; |
||||
const struct sensor_trigger *trig_drdy_gyr; |
||||
sensor_trigger_handler_t handler_drdy_temp; |
||||
const struct sensor_trigger *trig_drdy_temp; |
||||
|
||||
#if defined(CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD) |
||||
K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_LSM6DSV16X_THREAD_STACK_SIZE); |
||||
struct k_thread thread; |
||||
struct k_sem gpio_sem; |
||||
#elif defined(CONFIG_LSM6DSV16X_TRIGGER_GLOBAL_THREAD) |
||||
struct k_work work; |
||||
#endif |
||||
#endif /* CONFIG_LSM6DSV16X_TRIGGER */ |
||||
}; |
||||
|
||||
#if defined(CONFIG_LSM6DSV16X_SENSORHUB) |
||||
int lsm6dsv16x_shub_init(const struct device *dev); |
||||
int lsm6dsv16x_shub_fetch_external_devs(const struct device *dev); |
||||
int lsm6dsv16x_shub_get_idx(const struct device *dev, enum sensor_channel type); |
||||
int lsm6dsv16x_shub_config(const struct device *dev, enum sensor_channel chan, |
||||
enum sensor_attribute attr, |
||||
const struct sensor_value *val); |
||||
#endif /* CONFIG_LSM6DSV16X_SENSORHUB */ |
||||
|
||||
#ifdef CONFIG_LSM6DSV16X_TRIGGER |
||||
int lsm6dsv16x_trigger_set(const struct device *dev, |
||||
const struct sensor_trigger *trig, |
||||
sensor_trigger_handler_t handler); |
||||
|
||||
int lsm6dsv16x_init_interrupt(const struct device *dev); |
||||
#endif |
||||
|
||||
#endif /* ZEPHYR_DRIVERS_SENSOR_LSM6DSV16X_LSM6DSV16X_H_ */ |
@ -0,0 +1,848 @@
@@ -0,0 +1,848 @@
|
||||
/* ST Microelectronics LSM6DSV16X 6-axis IMU sensor driver
|
||||
* |
||||
* Copyright (c) 2023 STMicroelectronics |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
* |
||||
* Datasheet: |
||||
* https://www.st.com/resource/en/datasheet/lsm6dsv16x.pdf
|
||||
*/ |
||||
|
||||
#define DT_DRV_COMPAT st_lsm6dsv16x |
||||
|
||||
#include <zephyr/device.h> |
||||
#include <zephyr/drivers/i2c.h> |
||||
#include <zephyr/sys/byteorder.h> |
||||
#include <zephyr/sys/__assert.h> |
||||
#include <zephyr/sys/util.h> |
||||
#include <zephyr/kernel.h> |
||||
#include <zephyr/drivers/sensor.h> |
||||
#include <zephyr/logging/log.h> |
||||
|
||||
#include "lsm6dsv16x.h" |
||||
|
||||
LOG_MODULE_DECLARE(LSM6DSV16X, CONFIG_SENSOR_LOG_LEVEL); |
||||
|
||||
static int lsm6dsv16x_shub_write_target_reg(const struct device *dev, |
||||
uint8_t trgt_addr, uint8_t trgt_reg, |
||||
uint8_t *value, uint16_t len); |
||||
static int lsm6dsv16x_shub_read_target_reg(const struct device *dev, |
||||
uint8_t trgt_addr, uint8_t trgt_reg, |
||||
uint8_t *value, uint16_t len); |
||||
static void lsm6dsv16x_shub_enable(const struct device *dev, uint8_t enable); |
||||
|
||||
|
||||
/* ST HAL skips this register, only supports it via the slower lsm6dsv16x_sh_status_get() */ |
||||
static int32_t lsm6dsv16x_sh_status_mainpage_get(stmdev_ctx_t *ctx, |
||||
lsm6dsv16x_status_master_t *val) |
||||
{ |
||||
return lsm6dsv16x_read_reg(ctx, LSM6DSV16X_STATUS_MASTER_MAINPAGE, (uint8_t *)val, 1); |
||||
} |
||||
|
||||
/*
|
||||
* LIS2MDL magn device specific part |
||||
*/ |
||||
#ifdef CONFIG_LSM6DSV16X_EXT_LIS2MDL |
||||
|
||||
#define LIS2MDL_CFG_REG_A 0x60 |
||||
#define LIS2MDL_CFG_REG_B 0x61 |
||||
#define LIS2MDL_CFG_REG_C 0x62 |
||||
#define LIS2MDL_STATUS_REG 0x67 |
||||
|
||||
#define LIS2MDL_SW_RESET 0x20 |
||||
#define LIS2MDL_ODR_10HZ 0x00 |
||||
#define LIS2MDL_ODR_100HZ 0x0C |
||||
#define LIS2MDL_OFF_CANC 0x02 |
||||
#define LIS2MDL_SENSITIVITY 1500 |
||||
|
||||
static int lsm6dsv16x_lis2mdl_init(const struct device *dev, uint8_t i2c_addr) |
||||
{ |
||||
struct lsm6dsv16x_data *data = dev->data; |
||||
uint8_t mag_cfg[2]; |
||||
|
||||
data->magn_gain = LIS2MDL_SENSITIVITY; |
||||
|
||||
/* sw reset device */ |
||||
mag_cfg[0] = LIS2MDL_SW_RESET; |
||||
lsm6dsv16x_shub_write_target_reg(dev, i2c_addr, |
||||
LIS2MDL_CFG_REG_A, mag_cfg, 1); |
||||
|
||||
k_sleep(K_MSEC(10)); /* turn-on time in ms */ |
||||
|
||||
/* configure mag */ |
||||
mag_cfg[0] = LIS2MDL_ODR_10HZ; |
||||
mag_cfg[1] = LIS2MDL_OFF_CANC; |
||||
lsm6dsv16x_shub_write_target_reg(dev, i2c_addr, |
||||
LIS2MDL_CFG_REG_A, mag_cfg, 2); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static const uint16_t lis2mdl_map[] = {10, 20, 50, 100}; |
||||
|
||||
static int lsm6dsv16x_lis2mdl_odr_set(const struct device *dev, |
||||
uint8_t i2c_addr, uint16_t freq) |
||||
{ |
||||
uint8_t odr, cfg; |
||||
|
||||
for (odr = 0; odr < ARRAY_SIZE(lis2mdl_map); odr++) { |
||||
if (freq <= lis2mdl_map[odr]) { |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (odr == ARRAY_SIZE(lis2mdl_map)) { |
||||
LOG_DBG("shub: LIS2MDL freq val %d not supported.", freq); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
cfg = (odr << 2); |
||||
lsm6dsv16x_shub_write_target_reg(dev, i2c_addr, |
||||
LIS2MDL_CFG_REG_A, &cfg, 1); |
||||
|
||||
lsm6dsv16x_shub_enable(dev, 1); |
||||
return 0; |
||||
} |
||||
|
||||
static int lsm6dsv16x_lis2mdl_conf(const struct device *dev, uint8_t i2c_addr, |
||||
enum sensor_channel chan, |
||||
enum sensor_attribute attr, |
||||
const struct sensor_value *val) |
||||
{ |
||||
switch (attr) { |
||||
case SENSOR_ATTR_SAMPLING_FREQUENCY: |
||||
return lsm6dsv16x_lis2mdl_odr_set(dev, i2c_addr, val->val1); |
||||
default: |
||||
LOG_DBG("shub: LIS2MDL attribute not supported."); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
#endif /* CONFIG_LSM6DSV16X_EXT_LIS2MDL */ |
||||
|
||||
/*
|
||||
* HTS221 humidity device specific part |
||||
*/ |
||||
#ifdef CONFIG_LSM6DSV16X_EXT_HTS221 |
||||
|
||||
#define HTS221_AUTOINCREMENT BIT(7) |
||||
|
||||
#define HTS221_REG_CTRL1 0x20 |
||||
#define HTS221_ODR_1HZ 0x01 |
||||
#define HTS221_BDU 0x04 |
||||
#define HTS221_PD 0x80 |
||||
|
||||
#define HTS221_REG_CONV_START 0x30 |
||||
|
||||
static int lsm6dsv16x_hts221_read_conv_data(const struct device *dev, |
||||
uint8_t i2c_addr) |
||||
{ |
||||
struct lsm6dsv16x_data *data = dev->data; |
||||
uint8_t buf[16], i; |
||||
struct hts221_data *ht = &data->hts221; |
||||
|
||||
for (i = 0; i < sizeof(buf); i += 7) { |
||||
unsigned char len = MIN(7, sizeof(buf) - i); |
||||
|
||||
if (lsm6dsv16x_shub_read_target_reg(dev, i2c_addr, |
||||
(HTS221_REG_CONV_START + i) | |
||||
HTS221_AUTOINCREMENT, |
||||
&buf[i], len) < 0) { |
||||
LOG_DBG("shub: failed to read hts221 conv data"); |
||||
return -EIO; |
||||
} |
||||
} |
||||
|
||||
ht->y0 = buf[0] / 2; |
||||
ht->y1 = buf[1] / 2; |
||||
ht->x0 = sys_le16_to_cpu(buf[6] | (buf[7] << 8)); |
||||
ht->x1 = sys_le16_to_cpu(buf[10] | (buf[11] << 8)); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int lsm6dsv16x_hts221_init(const struct device *dev, uint8_t i2c_addr) |
||||
{ |
||||
uint8_t hum_cfg; |
||||
|
||||
/* configure ODR and BDU */ |
||||
hum_cfg = HTS221_ODR_1HZ | HTS221_BDU | HTS221_PD; |
||||
lsm6dsv16x_shub_write_target_reg(dev, i2c_addr, |
||||
HTS221_REG_CTRL1, &hum_cfg, 1); |
||||
|
||||
return lsm6dsv16x_hts221_read_conv_data(dev, i2c_addr); |
||||
} |
||||
|
||||
static const uint16_t hts221_map[] = {0, 1, 7, 12}; |
||||
|
||||
static int lsm6dsv16x_hts221_odr_set(const struct device *dev, |
||||
uint8_t i2c_addr, uint16_t freq) |
||||
{ |
||||
uint8_t odr, cfg; |
||||
|
||||
for (odr = 0; odr < ARRAY_SIZE(hts221_map); odr++) { |
||||
if (freq <= hts221_map[odr]) { |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (odr == ARRAY_SIZE(hts221_map)) { |
||||
LOG_DBG("shub: HTS221 freq val %d not supported.", freq); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
cfg = odr | HTS221_BDU | HTS221_PD; |
||||
lsm6dsv16x_shub_write_target_reg(dev, i2c_addr, |
||||
HTS221_REG_CTRL1, &cfg, 1); |
||||
|
||||
lsm6dsv16x_shub_enable(dev, 1); |
||||
return 0; |
||||
} |
||||
|
||||
static int lsm6dsv16x_hts221_conf(const struct device *dev, uint8_t i2c_addr, |
||||
enum sensor_channel chan, |
||||
enum sensor_attribute attr, |
||||
const struct sensor_value *val) |
||||
{ |
||||
switch (attr) { |
||||
case SENSOR_ATTR_SAMPLING_FREQUENCY: |
||||
return lsm6dsv16x_hts221_odr_set(dev, i2c_addr, val->val1); |
||||
default: |
||||
LOG_DBG("shub: HTS221 attribute not supported."); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
#endif /* CONFIG_LSM6DSV16X_EXT_HTS221 */ |
||||
|
||||
/*
|
||||
* LPS22HB baro/temp device specific part |
||||
*/ |
||||
#ifdef CONFIG_LSM6DSV16X_EXT_LPS22HB |
||||
|
||||
#define LPS22HB_CTRL_REG1 0x10 |
||||
#define LPS22HB_CTRL_REG2 0x11 |
||||
|
||||
#define LPS22HB_SW_RESET 0x04 |
||||
#define LPS22HB_ODR_10HZ 0x20 |
||||
#define LPS22HB_LPF_EN 0x08 |
||||
#define LPS22HB_BDU_EN 0x02 |
||||
|
||||
static int lsm6dsv16x_lps22hb_init(const struct device *dev, uint8_t i2c_addr) |
||||
{ |
||||
uint8_t baro_cfg[2]; |
||||
|
||||
/* sw reset device */ |
||||
baro_cfg[0] = LPS22HB_SW_RESET; |
||||
lsm6dsv16x_shub_write_target_reg(dev, i2c_addr, |
||||
LPS22HB_CTRL_REG2, baro_cfg, 1); |
||||
|
||||
k_sleep(K_MSEC(1)); /* turn-on time in ms */ |
||||
|
||||
/* configure device */ |
||||
baro_cfg[0] = LPS22HB_ODR_10HZ | LPS22HB_LPF_EN | LPS22HB_BDU_EN; |
||||
lsm6dsv16x_shub_write_target_reg(dev, i2c_addr, |
||||
LPS22HB_CTRL_REG1, baro_cfg, 1); |
||||
|
||||
return 0; |
||||
} |
||||
#endif /* CONFIG_LSM6DSV16X_EXT_LPS22HB */ |
||||
|
||||
/*
|
||||
* LPS22HH baro/temp device specific part |
||||
*/ |
||||
#ifdef CONFIG_LSM6DSV16X_EXT_LPS22HH |
||||
|
||||
#define LPS22HH_CTRL_REG1 0x10 |
||||
#define LPS22HH_CTRL_REG2 0x11 |
||||
|
||||
#define LPS22HH_SW_RESET 0x04 |
||||
#define LPS22HH_IF_ADD_INC 0x10 |
||||
#define LPS22HH_ODR_10HZ 0x20 |
||||
#define LPS22HH_LPF_EN 0x08 |
||||
#define LPS22HH_BDU_EN 0x02 |
||||
|
||||
static int lsm6dsv16x_lps22hh_init(const struct device *dev, uint8_t i2c_addr) |
||||
{ |
||||
uint8_t baro_cfg[2]; |
||||
|
||||
/* sw reset device */ |
||||
baro_cfg[0] = LPS22HH_SW_RESET; |
||||
lsm6dsv16x_shub_write_target_reg(dev, i2c_addr, |
||||
LPS22HH_CTRL_REG2, baro_cfg, 1); |
||||
|
||||
k_sleep(K_MSEC(100)); /* turn-on time in ms */ |
||||
|
||||
/* configure device */ |
||||
baro_cfg[0] = LPS22HH_IF_ADD_INC; |
||||
lsm6dsv16x_shub_write_target_reg(dev, i2c_addr, |
||||
LPS22HH_CTRL_REG2, baro_cfg, 1); |
||||
|
||||
baro_cfg[0] = LPS22HH_ODR_10HZ | LPS22HH_LPF_EN | LPS22HH_BDU_EN; |
||||
lsm6dsv16x_shub_write_target_reg(dev, i2c_addr, |
||||
LPS22HH_CTRL_REG1, baro_cfg, 1); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static const uint16_t lps22hh_map[] = {0, 1, 10, 25, 50, 75, 100, 200}; |
||||
|
||||
static int lsm6dsv16x_lps22hh_odr_set(const struct device *dev, |
||||
uint8_t i2c_addr, uint16_t freq) |
||||
{ |
||||
uint8_t odr, cfg; |
||||
|
||||
for (odr = 0; odr < ARRAY_SIZE(lps22hh_map); odr++) { |
||||
if (freq <= lps22hh_map[odr]) { |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (odr == ARRAY_SIZE(lps22hh_map)) { |
||||
LOG_DBG("shub: LPS22HH freq val %d not supported.", freq); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
cfg = (odr << 4) | LPS22HH_LPF_EN | LPS22HH_BDU_EN; |
||||
lsm6dsv16x_shub_write_target_reg(dev, i2c_addr, |
||||
LPS22HH_CTRL_REG1, &cfg, 1); |
||||
|
||||
lsm6dsv16x_shub_enable(dev, 1); |
||||
return 0; |
||||
} |
||||
|
||||
static int lsm6dsv16x_lps22hh_conf(const struct device *dev, uint8_t i2c_addr, |
||||
enum sensor_channel chan, |
||||
enum sensor_attribute attr, |
||||
const struct sensor_value *val) |
||||
{ |
||||
switch (attr) { |
||||
case SENSOR_ATTR_SAMPLING_FREQUENCY: |
||||
return lsm6dsv16x_lps22hh_odr_set(dev, i2c_addr, val->val1); |
||||
default: |
||||
LOG_DBG("shub: LPS22HH attribute not supported."); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
#endif /* CONFIG_LSM6DSV16X_EXT_LPS22HH */ |
||||
|
||||
/*
|
||||
* LPS22DF baro/temp device specific part |
||||
*/ |
||||
#ifdef CONFIG_LSM6DSV16X_EXT_LPS22DF |
||||
|
||||
#define LPS22DF_CTRL_REG1 0x10 |
||||
#define LPS22DF_CTRL_REG2 0x11 |
||||
|
||||
#define LPS22DF_SW_RESET 0x04 |
||||
#define LPS22DF_BDU_EN 0x08 |
||||
#define LPS22DF_EN_LPFP 0x10 |
||||
#define LPS22DF_ODR_10HZ 0x18 |
||||
#define LPS22DF_AVG_16 0x02 |
||||
|
||||
static int lsm6dsv16x_lps22df_init(const struct device *dev, uint8_t i2c_addr) |
||||
{ |
||||
uint8_t baro_cfg[2]; |
||||
|
||||
/* sw reset device */ |
||||
baro_cfg[0] = LPS22DF_SW_RESET; |
||||
lsm6dsv16x_shub_write_target_reg(dev, i2c_addr, |
||||
LPS22DF_CTRL_REG2, baro_cfg, 1); |
||||
|
||||
k_busy_wait(50); /* turn-on time in us */ |
||||
|
||||
/* configure device */ |
||||
baro_cfg[0] = LPS22DF_BDU_EN | LPS22DF_EN_LPFP; |
||||
lsm6dsv16x_shub_write_target_reg(dev, i2c_addr, |
||||
LPS22DF_CTRL_REG2, baro_cfg, 1); |
||||
|
||||
baro_cfg[0] = LPS22DF_ODR_10HZ | LPS22DF_AVG_16; |
||||
lsm6dsv16x_shub_write_target_reg(dev, i2c_addr, |
||||
LPS22DF_CTRL_REG1, baro_cfg, 1); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static const uint16_t lps22df_map[] = {0, 1, 4, 10, 25, 50, 75, 100, 200}; |
||||
|
||||
static int lsm6dsv16x_lps22df_odr_set(const struct device *dev, |
||||
uint8_t i2c_addr, uint16_t freq) |
||||
{ |
||||
uint8_t odr, cfg; |
||||
|
||||
for (odr = 0; odr < ARRAY_SIZE(lps22df_map); odr++) { |
||||
if (freq <= lps22df_map[odr]) { |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (odr == ARRAY_SIZE(lps22df_map)) { |
||||
LOG_DBG("shub: LPS22DF freq val %d not supported.", freq); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
cfg = (odr << 3) | LPS22DF_AVG_16; |
||||
lsm6dsv16x_shub_write_target_reg(dev, i2c_addr, |
||||
LPS22DF_CTRL_REG1, &cfg, 1); |
||||
|
||||
lsm6dsv16x_shub_enable(dev, 1); |
||||
return 0; |
||||
} |
||||
|
||||
static int lsm6dsv16x_lps22df_conf(const struct device *dev, uint8_t i2c_addr, |
||||
enum sensor_channel chan, |
||||
enum sensor_attribute attr, |
||||
const struct sensor_value *val) |
||||
{ |
||||
switch (attr) { |
||||
case SENSOR_ATTR_SAMPLING_FREQUENCY: |
||||
return lsm6dsv16x_lps22df_odr_set(dev, i2c_addr, val->val1); |
||||
default: |
||||
LOG_DBG("shub: LPS22DF attribute not supported."); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
#endif /* CONFIG_LSM6DSV16X_EXT_LPS22DF */ |
||||
|
||||
/* List of supported external sensors */ |
||||
static struct lsm6dsv16x_shub_slist { |
||||
enum sensor_channel type; |
||||
uint8_t i2c_addr[2]; |
||||
uint8_t ext_i2c_addr; |
||||
uint8_t wai_addr; |
||||
uint8_t wai_val; |
||||
uint8_t out_data_addr; |
||||
uint8_t out_data_len; |
||||
uint8_t sh_out_reg; |
||||
int (*dev_init)(const struct device *dev, uint8_t i2c_addr); |
||||
int (*dev_conf)(const struct device *dev, uint8_t i2c_addr, |
||||
enum sensor_channel chan, enum sensor_attribute attr, |
||||
const struct sensor_value *val); |
||||
} lsm6dsv16x_shub_slist[] = { |
||||
#ifdef CONFIG_LSM6DSV16X_EXT_LIS2MDL |
||||
{ |
||||
/* LIS2MDL */ |
||||
.type = SENSOR_CHAN_MAGN_XYZ, |
||||
.i2c_addr = { 0x1E }, |
||||
.wai_addr = 0x4F, |
||||
.wai_val = 0x40, |
||||
.out_data_addr = 0x68, |
||||
.out_data_len = 0x06, |
||||
.dev_init = (lsm6dsv16x_lis2mdl_init), |
||||
.dev_conf = (lsm6dsv16x_lis2mdl_conf), |
||||
}, |
||||
#endif /* CONFIG_LSM6DSV16X_EXT_LIS2MDL */ |
||||
|
||||
#ifdef CONFIG_LSM6DSV16X_EXT_HTS221 |
||||
{ |
||||
/* HTS221 */ |
||||
.type = SENSOR_CHAN_HUMIDITY, |
||||
.i2c_addr = { 0x5F }, |
||||
.wai_addr = 0x0F, |
||||
.wai_val = 0xBC, |
||||
.out_data_addr = 0x28 | HTS221_AUTOINCREMENT, |
||||
.out_data_len = 0x02, |
||||
.dev_init = (lsm6dsv16x_hts221_init), |
||||
.dev_conf = (lsm6dsv16x_hts221_conf), |
||||
}, |
||||
#endif /* CONFIG_LSM6DSV16X_EXT_HTS221 */ |
||||
|
||||
#ifdef CONFIG_LSM6DSV16X_EXT_LPS22HB |
||||
{ |
||||
/* LPS22HB */ |
||||
.type = SENSOR_CHAN_PRESS, |
||||
.i2c_addr = { 0x5C, 0x5D }, |
||||
.wai_addr = 0x0F, |
||||
.wai_val = 0xB1, |
||||
.out_data_addr = 0x28, |
||||
.out_data_len = 0x05, |
||||
.dev_init = (lsm6dsv16x_lps22hb_init), |
||||
}, |
||||
#endif /* CONFIG_LSM6DSV16X_EXT_LPS22HB */ |
||||
|
||||
#ifdef CONFIG_LSM6DSV16X_EXT_LPS22HH |
||||
{ |
||||
/* LPS22HH */ |
||||
.type = SENSOR_CHAN_PRESS, |
||||
.i2c_addr = { 0x5C, 0x5D }, |
||||
.wai_addr = 0x0F, |
||||
.wai_val = 0xB3, |
||||
.out_data_addr = 0x28, |
||||
.out_data_len = 0x05, |
||||
.dev_init = (lsm6dsv16x_lps22hh_init), |
||||
.dev_conf = (lsm6dsv16x_lps22hh_conf), |
||||
}, |
||||
#endif /* CONFIG_LSM6DSV16X_EXT_LPS22HH */ |
||||
|
||||
#ifdef CONFIG_LSM6DSV16X_EXT_LPS22DF |
||||
{ |
||||
/* LPS22DF */ |
||||
.type = SENSOR_CHAN_PRESS, |
||||
.i2c_addr = { 0x5C, 0x5D }, |
||||
.wai_addr = 0x0F, |
||||
.wai_val = 0xB4, |
||||
.out_data_addr = 0x28, |
||||
.out_data_len = 0x05, |
||||
.dev_init = (lsm6dsv16x_lps22df_init), |
||||
.dev_conf = (lsm6dsv16x_lps22df_conf), |
||||
}, |
||||
#endif /* CONFIG_LSM6DSV16X_EXT_LPS22DF */ |
||||
}; |
||||
|
||||
static int lsm6dsv16x_shub_wait_completed(stmdev_ctx_t *ctx) |
||||
{ |
||||
lsm6dsv16x_status_master_t status; |
||||
int tries = 200; /* Should be max ~160 ms, from 2 cycles at slowest ODR 12.5 Hz */ |
||||
|
||||
do { |
||||
if (!--tries) { |
||||
LOG_DBG("shub: Timeout waiting for operation to complete"); |
||||
return -ETIMEDOUT; |
||||
} |
||||
k_msleep(1); |
||||
lsm6dsv16x_sh_status_mainpage_get(ctx, &status); |
||||
} while (status.sens_hub_endop == 0); |
||||
|
||||
return 1; |
||||
} |
||||
|
||||
static void lsm6dsv16x_shub_enable(const struct device *dev, uint8_t enable) |
||||
{ |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; |
||||
struct lsm6dsv16x_data *data = dev->data; |
||||
|
||||
/* Enable Accel @26hz */ |
||||
if (!data->accel_freq) { |
||||
uint8_t odr = (enable) ? 2 : 0; |
||||
|
||||
if (lsm6dsv16x_xl_data_rate_set(ctx, odr) < 0) { |
||||
LOG_DBG("shub: failed to set XL sampling rate"); |
||||
return; |
||||
} |
||||
} |
||||
|
||||
if (enable) { |
||||
lsm6dsv16x_status_master_t status; |
||||
|
||||
/* Clear any pending status flags */ |
||||
lsm6dsv16x_sh_status_mainpage_get(ctx, &status); |
||||
} |
||||
|
||||
if (lsm6dsv16x_sh_master_set(ctx, enable) < 0) { |
||||
LOG_DBG("shub: failed to set master on"); |
||||
lsm6dsv16x_mem_bank_set(ctx, LSM6DSV16X_MAIN_MEM_BANK); |
||||
return; |
||||
} |
||||
|
||||
if (!enable) { |
||||
/* wait 300us (necessary per AN5763 §7.2.1) */ |
||||
k_busy_wait(300); |
||||
} |
||||
} |
||||
|
||||
/* must be called with master on */ |
||||
static int lsm6dsv16x_shub_check_slv0_nack(stmdev_ctx_t *ctx) |
||||
{ |
||||
lsm6dsv16x_all_sources_t status; |
||||
|
||||
if (lsm6dsv16x_all_sources_get(ctx, &status) < 0) { |
||||
LOG_DBG("shub: error reading embedded reg"); |
||||
return -EIO; |
||||
} |
||||
|
||||
if (status.sh_slave0_nack) { |
||||
LOG_DBG("shub: TRGT 0 nacked"); |
||||
return -EIO; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
/*
|
||||
* use TRGT 0 for generic read to target device |
||||
*/ |
||||
static int lsm6dsv16x_shub_read_target_reg(const struct device *dev, |
||||
uint8_t trgt_addr, uint8_t trgt_reg, |
||||
uint8_t *value, uint16_t len) |
||||
{ |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; |
||||
lsm6dsv16x_sh_cfg_read_t trgt_cfg; |
||||
|
||||
trgt_cfg.slv_add = trgt_addr; |
||||
trgt_cfg.slv_subadd = trgt_reg; |
||||
trgt_cfg.slv_len = len; |
||||
|
||||
lsm6dsv16x_sh_slv0_cfg_read(ctx, &trgt_cfg); |
||||
|
||||
/* turn SH on, wait for shub i2c read to finish */ |
||||
lsm6dsv16x_shub_enable(dev, 1); |
||||
lsm6dsv16x_shub_wait_completed(ctx); |
||||
|
||||
/* read data from external target */ |
||||
if (lsm6dsv16x_sh_read_data_raw_get(ctx, (lsm6dsv16x_emb_sh_read_t *)value, len) < 0) { |
||||
LOG_DBG("shub: error reading sensor data"); |
||||
return -EIO; |
||||
} |
||||
|
||||
if (lsm6dsv16x_shub_check_slv0_nack(ctx) < 0) { |
||||
lsm6dsv16x_shub_enable(dev, 0); |
||||
return -EIO; |
||||
} |
||||
|
||||
lsm6dsv16x_shub_enable(dev, 0); |
||||
return 0; |
||||
} |
||||
|
||||
/*
|
||||
* use TRGT 0 to configure target device |
||||
*/ |
||||
static int lsm6dsv16x_shub_write_target_reg(const struct device *dev, |
||||
uint8_t trgt_addr, uint8_t trgt_reg, |
||||
uint8_t *value, uint16_t len) |
||||
{ |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; |
||||
lsm6dsv16x_sh_cfg_write_t trgt_cfg; |
||||
uint8_t cnt = 0U; |
||||
|
||||
lsm6dsv16x_shub_enable(dev, 0); |
||||
|
||||
while (cnt < len) { |
||||
trgt_cfg.slv0_add = trgt_addr; |
||||
trgt_cfg.slv0_subadd = trgt_reg + cnt; |
||||
trgt_cfg.slv0_data = value[cnt]; |
||||
|
||||
lsm6dsv16x_sh_cfg_write(ctx, &trgt_cfg); |
||||
|
||||
/* turn SH on, wait for shub i2c write to finish */ |
||||
lsm6dsv16x_shub_enable(dev, 1); |
||||
lsm6dsv16x_shub_wait_completed(ctx); |
||||
|
||||
if (lsm6dsv16x_shub_check_slv0_nack(ctx) < 0) { |
||||
lsm6dsv16x_shub_enable(dev, 0); |
||||
return -EIO; |
||||
} |
||||
|
||||
lsm6dsv16x_shub_enable(dev, 0); |
||||
|
||||
cnt++; |
||||
} |
||||
|
||||
/* Put TRGT 0 in IDLE mode */ |
||||
trgt_cfg.slv0_add = 0x7; |
||||
trgt_cfg.slv0_subadd = 0x0; |
||||
trgt_cfg.slv0_data = 0x0; |
||||
lsm6dsv16x_sh_cfg_write(ctx, &trgt_cfg); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
/*
|
||||
* TARGETs configurations: |
||||
* |
||||
* - TARGET 0: used for configuring all target devices |
||||
* - TARGET 1: used as data read channel for external target device #1 |
||||
* - TARGET 2: used as data read channel for external target device #2 |
||||
* - TARGET 3: used for generic reads while data channel is enabled |
||||
*/ |
||||
static int lsm6dsv16x_shub_set_data_channel(const struct device *dev) |
||||
{ |
||||
struct lsm6dsv16x_data *data = dev->data; |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; |
||||
uint8_t n; |
||||
struct lsm6dsv16x_shub_slist *sp; |
||||
lsm6dsv16x_sh_cfg_read_t trgt_cfg; |
||||
|
||||
int32_t (*sh_chan_cfg[LSM6DSV16X_SHUB_MAX_NUM_TARGETS]) |
||||
(stmdev_ctx_t *ctx, lsm6dsv16x_sh_cfg_read_t *val) = { |
||||
lsm6dsv16x_sh_slv1_cfg_read, |
||||
lsm6dsv16x_sh_slv2_cfg_read, |
||||
lsm6dsv16x_sh_slv3_cfg_read, |
||||
}; |
||||
|
||||
/* Configure shub data channels to access external targets */ |
||||
for (n = 0; n < data->num_ext_dev; n++) { |
||||
sp = &lsm6dsv16x_shub_slist[data->shub_ext[n]]; |
||||
|
||||
trgt_cfg.slv_add = sp->ext_i2c_addr; |
||||
trgt_cfg.slv_subadd = sp->out_data_addr; |
||||
trgt_cfg.slv_len = sp->out_data_len; |
||||
|
||||
if (sh_chan_cfg[n](ctx, &trgt_cfg) < 0) { |
||||
LOG_DBG("shub: error configuring shub for ext targets"); |
||||
return -EIO; |
||||
} |
||||
} |
||||
|
||||
/* Configure the master */ |
||||
lsm6dsv16x_sh_slave_connected_t aux = LSM6DSV16X_SLV_0_1_2; |
||||
|
||||
if (lsm6dsv16x_sh_slave_connected_set(ctx, aux) < 0) { |
||||
LOG_DBG("shub: error setting aux sensors"); |
||||
return -EIO; |
||||
} |
||||
|
||||
|
||||
/* turn SH on, no need to wait for 1st shub i2c read, if any, to complete */ |
||||
lsm6dsv16x_shub_enable(dev, 1); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int lsm6dsv16x_shub_get_idx(const struct device *dev, enum sensor_channel type) |
||||
{ |
||||
uint8_t n; |
||||
struct lsm6dsv16x_data *data = dev->data; |
||||
struct lsm6dsv16x_shub_slist *sp; |
||||
|
||||
for (n = 0; n < data->num_ext_dev; n++) { |
||||
sp = &lsm6dsv16x_shub_slist[data->shub_ext[n]]; |
||||
|
||||
if (sp->type == type) { |
||||
return n; |
||||
} |
||||
} |
||||
|
||||
LOG_ERR("shub: dev %s type %d not supported", dev->name, type); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
int lsm6dsv16x_shub_fetch_external_devs(const struct device *dev) |
||||
{ |
||||
uint8_t n; |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; |
||||
struct lsm6dsv16x_data *data = dev->data; |
||||
struct lsm6dsv16x_shub_slist *sp; |
||||
|
||||
/* read data from external target */ |
||||
lsm6dsv16x_mem_bank_set(ctx, LSM6DSV16X_SENSOR_HUB_MEM_BANK); |
||||
|
||||
for (n = 0; n < data->num_ext_dev; n++) { |
||||
sp = &lsm6dsv16x_shub_slist[data->shub_ext[n]]; |
||||
|
||||
if (lsm6dsv16x_read_reg(ctx, sp->sh_out_reg, |
||||
data->ext_data[n], sp->out_data_len) < 0) { |
||||
LOG_DBG("shub: failed to read sample"); |
||||
lsm6dsv16x_mem_bank_set(ctx, LSM6DSV16X_MAIN_MEM_BANK); |
||||
return -EIO; |
||||
} |
||||
} |
||||
|
||||
lsm6dsv16x_mem_bank_set(ctx, LSM6DSV16X_MAIN_MEM_BANK); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int lsm6dsv16x_shub_config(const struct device *dev, enum sensor_channel chan, |
||||
enum sensor_attribute attr, |
||||
const struct sensor_value *val) |
||||
{ |
||||
struct lsm6dsv16x_data *data = dev->data; |
||||
struct lsm6dsv16x_shub_slist *sp = NULL; |
||||
uint8_t n; |
||||
|
||||
for (n = 0; n < data->num_ext_dev; n++) { |
||||
sp = &lsm6dsv16x_shub_slist[data->shub_ext[n]]; |
||||
|
||||
if (sp->type == chan) { |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (n == data->num_ext_dev) { |
||||
LOG_DBG("shub: %s chan %d not supported", dev->name, chan); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
if (sp == NULL || sp->dev_conf == NULL) { |
||||
LOG_DBG("shub: chan not configurable"); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
return sp->dev_conf(dev, sp->ext_i2c_addr, chan, attr, val); |
||||
} |
||||
|
||||
int lsm6dsv16x_shub_init(const struct device *dev) |
||||
{ |
||||
struct lsm6dsv16x_data *data = dev->data; |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; |
||||
uint8_t i, n = 0, regn; |
||||
uint8_t chip_id; |
||||
struct lsm6dsv16x_shub_slist *sp; |
||||
|
||||
LOG_INF("shub: start sensorhub for %s", dev->name); |
||||
|
||||
/*
|
||||
* This must be set or lsm6dsv16x_shub_write_target_reg() will |
||||
* repeatedly write the same regi |
||||
*/ |
||||
if (lsm6dsv16x_sh_write_mode_set(ctx, LSM6DSV16X_ONLY_FIRST_CYCLE) < 0) { |
||||
LOG_DBG("shub: error setting write once"); |
||||
return -EIO; |
||||
} |
||||
|
||||
for (n = 0; n < ARRAY_SIZE(lsm6dsv16x_shub_slist); n++) { |
||||
if (data->num_ext_dev >= LSM6DSV16X_SHUB_MAX_NUM_TARGETS) { |
||||
break; |
||||
} |
||||
|
||||
chip_id = 0; |
||||
sp = &lsm6dsv16x_shub_slist[n]; |
||||
|
||||
/*
|
||||
* The external sensor may have different I2C address. |
||||
* So, try them one by one until we read the correct |
||||
* chip ID. |
||||
*/ |
||||
for (i = 0U; i < ARRAY_SIZE(sp->i2c_addr); i++) { |
||||
if (lsm6dsv16x_shub_read_target_reg(dev, |
||||
sp->i2c_addr[i], |
||||
sp->wai_addr, |
||||
&chip_id, 1) < 0) { |
||||
LOG_DBG("shub: failed reading chip id"); |
||||
continue; |
||||
} |
||||
if (chip_id == sp->wai_val) { |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (i >= ARRAY_SIZE(sp->i2c_addr)) { |
||||
LOG_DBG("shub: invalid chip id 0x%x", chip_id); |
||||
continue; |
||||
} |
||||
LOG_INF("shub: Ext Device Chip Id: %02x", chip_id); |
||||
sp->ext_i2c_addr = sp->i2c_addr[i]; |
||||
|
||||
data->shub_ext[data->num_ext_dev++] = n; |
||||
} |
||||
|
||||
LOG_DBG("shub: dev %s - num_ext_dev %d", dev->name, data->num_ext_dev); |
||||
if (data->num_ext_dev == 0) { |
||||
LOG_ERR("shub: no target devices found"); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
/* init external devices */ |
||||
for (n = 0, regn = 0; n < data->num_ext_dev; n++) { |
||||
sp = &lsm6dsv16x_shub_slist[data->shub_ext[n]]; |
||||
sp->sh_out_reg = LSM6DSV16X_SENSOR_HUB_1 + regn; |
||||
regn += sp->out_data_len; |
||||
sp->dev_init(dev, sp->ext_i2c_addr); |
||||
} |
||||
|
||||
lsm6dsv16x_shub_set_data_channel(dev); |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,333 @@
@@ -0,0 +1,333 @@
|
||||
/* ST Microelectronics LSM6DSV16X 6-axis IMU sensor driver
|
||||
* |
||||
* Copyright (c) 2023 STMicroelectronics |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
* |
||||
* Datasheet: |
||||
* https://www.st.com/resource/en/datasheet/lsm6dsv16x.pdf
|
||||
*/ |
||||
|
||||
#define DT_DRV_COMPAT st_lsm6dsv16x |
||||
|
||||
#include <zephyr/kernel.h> |
||||
#include <zephyr/drivers/sensor.h> |
||||
#include <zephyr/drivers/gpio.h> |
||||
#include <zephyr/logging/log.h> |
||||
|
||||
#include "lsm6dsv16x.h" |
||||
|
||||
LOG_MODULE_DECLARE(LSM6DSV16X, CONFIG_SENSOR_LOG_LEVEL); |
||||
|
||||
#if defined(CONFIG_LSM6DSV16X_ENABLE_TEMP) |
||||
/**
|
||||
* lsm6dsv16x_enable_t_int - TEMP enable selected int pin to generate interrupt |
||||
*/ |
||||
static int lsm6dsv16x_enable_t_int(const struct device *dev, int enable) |
||||
{ |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; |
||||
lsm6dsv16x_pin_int_route_t val; |
||||
int ret; |
||||
|
||||
if (enable) { |
||||
int16_t buf; |
||||
|
||||
/* dummy read: re-trigger interrupt */ |
||||
lsm6dsv16x_temperature_raw_get(ctx, &buf); |
||||
} |
||||
|
||||
/* set interrupt (TEMP DRDY interrupt is only on INT2) */ |
||||
if (cfg->drdy_pin == 1) { |
||||
return -EIO; |
||||
} |
||||
|
||||
ret = lsm6dsv16x_pin_int2_route_get(ctx, &val); |
||||
if (ret < 0) { |
||||
LOG_ERR("pint_int2_route_get error"); |
||||
return ret; |
||||
} |
||||
|
||||
val.drdy_temp = 1; |
||||
|
||||
return lsm6dsv16x_pin_int2_route_set(ctx, &val); |
||||
} |
||||
#endif |
||||
|
||||
/**
|
||||
* lsm6dsv16x_enable_xl_int - XL enable selected int pin to generate interrupt |
||||
*/ |
||||
static int lsm6dsv16x_enable_xl_int(const struct device *dev, int enable) |
||||
{ |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; |
||||
int ret; |
||||
|
||||
if (enable) { |
||||
int16_t buf[3]; |
||||
|
||||
/* dummy read: re-trigger interrupt */ |
||||
lsm6dsv16x_acceleration_raw_get(ctx, buf); |
||||
} |
||||
|
||||
/* set interrupt */ |
||||
if (cfg->drdy_pin == 1) { |
||||
lsm6dsv16x_pin_int_route_t val; |
||||
|
||||
ret = lsm6dsv16x_pin_int1_route_get(ctx, &val); |
||||
if (ret < 0) { |
||||
LOG_ERR("pint_int1_route_get error"); |
||||
return ret; |
||||
} |
||||
|
||||
val.drdy_xl = 1; |
||||
|
||||
ret = lsm6dsv16x_pin_int1_route_set(ctx, &val); |
||||
} else { |
||||
lsm6dsv16x_pin_int_route_t val; |
||||
|
||||
ret = lsm6dsv16x_pin_int2_route_get(ctx, &val); |
||||
if (ret < 0) { |
||||
LOG_ERR("pint_int2_route_get error"); |
||||
return ret; |
||||
} |
||||
|
||||
val.drdy_xl = 1; |
||||
|
||||
ret = lsm6dsv16x_pin_int2_route_set(ctx, &val); |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
/**
|
||||
* lsm6dsv16x_enable_g_int - Gyro enable selected int pin to generate interrupt |
||||
*/ |
||||
static int lsm6dsv16x_enable_g_int(const struct device *dev, int enable) |
||||
{ |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; |
||||
int ret; |
||||
|
||||
if (enable) { |
||||
int16_t buf[3]; |
||||
|
||||
/* dummy read: re-trigger interrupt */ |
||||
lsm6dsv16x_angular_rate_raw_get(ctx, buf); |
||||
} |
||||
|
||||
/* set interrupt */ |
||||
if (cfg->drdy_pin == 1) { |
||||
lsm6dsv16x_pin_int_route_t val; |
||||
|
||||
ret = lsm6dsv16x_pin_int1_route_get(ctx, &val); |
||||
if (ret < 0) { |
||||
LOG_ERR("pint_int1_route_get error"); |
||||
return ret; |
||||
} |
||||
|
||||
val.drdy_g = 1; |
||||
|
||||
ret = lsm6dsv16x_pin_int1_route_set(ctx, &val); |
||||
} else { |
||||
lsm6dsv16x_pin_int_route_t val; |
||||
|
||||
ret = lsm6dsv16x_pin_int2_route_get(ctx, &val); |
||||
if (ret < 0) { |
||||
LOG_ERR("pint_int2_route_get error"); |
||||
return ret; |
||||
} |
||||
|
||||
val.drdy_g = 1; |
||||
|
||||
ret = lsm6dsv16x_pin_int2_route_set(ctx, &val); |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
/**
|
||||
* lsm6dsv16x_trigger_set - link external trigger to event data ready |
||||
*/ |
||||
int lsm6dsv16x_trigger_set(const struct device *dev, |
||||
const struct sensor_trigger *trig, |
||||
sensor_trigger_handler_t handler) |
||||
{ |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
struct lsm6dsv16x_data *lsm6dsv16x = dev->data; |
||||
|
||||
if (!cfg->trig_enabled) { |
||||
LOG_ERR("trigger_set op not supported"); |
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
if (trig->chan == SENSOR_CHAN_ACCEL_XYZ) { |
||||
lsm6dsv16x->handler_drdy_acc = handler; |
||||
lsm6dsv16x->trig_drdy_acc = trig; |
||||
if (handler) { |
||||
return lsm6dsv16x_enable_xl_int(dev, LSM6DSV16X_EN_BIT); |
||||
} else { |
||||
return lsm6dsv16x_enable_xl_int(dev, LSM6DSV16X_DIS_BIT); |
||||
} |
||||
} else if (trig->chan == SENSOR_CHAN_GYRO_XYZ) { |
||||
lsm6dsv16x->handler_drdy_gyr = handler; |
||||
lsm6dsv16x->trig_drdy_gyr = trig; |
||||
if (handler) { |
||||
return lsm6dsv16x_enable_g_int(dev, LSM6DSV16X_EN_BIT); |
||||
} else { |
||||
return lsm6dsv16x_enable_g_int(dev, LSM6DSV16X_DIS_BIT); |
||||
} |
||||
} |
||||
#if defined(CONFIG_LSM6DSV16X_ENABLE_TEMP) |
||||
else if (trig->chan == SENSOR_CHAN_DIE_TEMP) { |
||||
lsm6dsv16x->handler_drdy_temp = handler; |
||||
lsm6dsv16x->trig_drdy_temp = trig; |
||||
if (handler) { |
||||
return lsm6dsv16x_enable_t_int(dev, LSM6DSV16X_EN_BIT); |
||||
} else { |
||||
return lsm6dsv16x_enable_t_int(dev, LSM6DSV16X_DIS_BIT); |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
return -ENOTSUP; |
||||
} |
||||
|
||||
/**
|
||||
* lsm6dsv16x_handle_interrupt - handle the drdy event |
||||
* read data and call handler if registered any |
||||
*/ |
||||
static void lsm6dsv16x_handle_interrupt(const struct device *dev) |
||||
{ |
||||
struct lsm6dsv16x_data *lsm6dsv16x = dev->data; |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; |
||||
lsm6dsv16x_data_ready_t status; |
||||
|
||||
while (1) { |
||||
if (lsm6dsv16x_flag_data_ready_get(ctx, &status) < 0) { |
||||
LOG_DBG("failed reading status reg"); |
||||
return; |
||||
} |
||||
|
||||
if ((status.drdy_xl == 0) && (status.drdy_gy == 0) |
||||
#if defined(CONFIG_LSM6DSV16X_ENABLE_TEMP) |
||||
&& (status.drdy_temp == 0) |
||||
#endif |
||||
) { |
||||
break; |
||||
} |
||||
|
||||
if ((status.drdy_xl) && (lsm6dsv16x->handler_drdy_acc != NULL)) { |
||||
lsm6dsv16x->handler_drdy_acc(dev, lsm6dsv16x->trig_drdy_acc); |
||||
} |
||||
|
||||
if ((status.drdy_gy) && (lsm6dsv16x->handler_drdy_gyr != NULL)) { |
||||
lsm6dsv16x->handler_drdy_gyr(dev, lsm6dsv16x->trig_drdy_gyr); |
||||
} |
||||
|
||||
#if defined(CONFIG_LSM6DSV16X_ENABLE_TEMP) |
||||
if ((status.drdy_temp) && (lsm6dsv16x->handler_drdy_temp != NULL)) { |
||||
lsm6dsv16x->handler_drdy_temp(dev, lsm6dsv16x->trig_drdy_temp); |
||||
} |
||||
#endif |
||||
} |
||||
|
||||
gpio_pin_interrupt_configure_dt(&cfg->gpio_drdy, |
||||
GPIO_INT_EDGE_TO_ACTIVE); |
||||
} |
||||
|
||||
static void lsm6dsv16x_gpio_callback(const struct device *dev, |
||||
struct gpio_callback *cb, uint32_t pins) |
||||
{ |
||||
struct lsm6dsv16x_data *lsm6dsv16x = |
||||
CONTAINER_OF(cb, struct lsm6dsv16x_data, gpio_cb); |
||||
const struct lsm6dsv16x_config *cfg = lsm6dsv16x->dev->config; |
||||
|
||||
ARG_UNUSED(pins); |
||||
|
||||
gpio_pin_interrupt_configure_dt(&cfg->gpio_drdy, GPIO_INT_DISABLE); |
||||
|
||||
#if defined(CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD) |
||||
k_sem_give(&lsm6dsv16x->gpio_sem); |
||||
#elif defined(CONFIG_LSM6DSV16X_TRIGGER_GLOBAL_THREAD) |
||||
k_work_submit(&lsm6dsv16x->work); |
||||
#endif /* CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD */ |
||||
} |
||||
|
||||
#ifdef CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD |
||||
static void lsm6dsv16x_thread(struct lsm6dsv16x_data *lsm6dsv16x) |
||||
{ |
||||
while (1) { |
||||
k_sem_take(&lsm6dsv16x->gpio_sem, K_FOREVER); |
||||
lsm6dsv16x_handle_interrupt(lsm6dsv16x->dev); |
||||
} |
||||
} |
||||
#endif /* CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD */ |
||||
|
||||
#ifdef CONFIG_LSM6DSV16X_TRIGGER_GLOBAL_THREAD |
||||
static void lsm6dsv16x_work_cb(struct k_work *work) |
||||
{ |
||||
struct lsm6dsv16x_data *lsm6dsv16x = |
||||
CONTAINER_OF(work, struct lsm6dsv16x_data, work); |
||||
|
||||
lsm6dsv16x_handle_interrupt(lsm6dsv16x->dev); |
||||
} |
||||
#endif /* CONFIG_LSM6DSV16X_TRIGGER_GLOBAL_THREAD */ |
||||
|
||||
int lsm6dsv16x_init_interrupt(const struct device *dev) |
||||
{ |
||||
struct lsm6dsv16x_data *lsm6dsv16x = dev->data; |
||||
const struct lsm6dsv16x_config *cfg = dev->config; |
||||
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; |
||||
int ret; |
||||
|
||||
/* setup data ready gpio interrupt (INT1 or INT2) */ |
||||
if (!device_is_ready(cfg->gpio_drdy.port)) { |
||||
LOG_ERR("Cannot get pointer to drdy_gpio device"); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
#if defined(CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD) |
||||
k_sem_init(&lsm6dsv16x->gpio_sem, 0, K_SEM_MAX_LIMIT); |
||||
|
||||
k_thread_create(&lsm6dsv16x->thread, lsm6dsv16x->thread_stack, |
||||
CONFIG_LSM6DSV16X_THREAD_STACK_SIZE, |
||||
(k_thread_entry_t)lsm6dsv16x_thread, lsm6dsv16x, |
||||
NULL, NULL, K_PRIO_COOP(CONFIG_LSM6DSV16X_THREAD_PRIORITY), |
||||
0, K_NO_WAIT); |
||||
k_thread_name_set(&lsm6dsv16x->thread, "lsm6dsv16x"); |
||||
#elif defined(CONFIG_LSM6DSV16X_TRIGGER_GLOBAL_THREAD) |
||||
lsm6dsv16x->work.handler = lsm6dsv16x_work_cb; |
||||
#endif /* CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD */ |
||||
|
||||
ret = gpio_pin_configure_dt(&cfg->gpio_drdy, GPIO_INPUT); |
||||
if (ret < 0) { |
||||
LOG_DBG("Could not configure gpio"); |
||||
return ret; |
||||
} |
||||
|
||||
gpio_init_callback(&lsm6dsv16x->gpio_cb, |
||||
lsm6dsv16x_gpio_callback, |
||||
BIT(cfg->gpio_drdy.pin)); |
||||
|
||||
if (gpio_add_callback(cfg->gpio_drdy.port, &lsm6dsv16x->gpio_cb) < 0) { |
||||
LOG_DBG("Could not set gpio callback"); |
||||
return -EIO; |
||||
} |
||||
|
||||
|
||||
/* set data ready mode on int1/int2 */ |
||||
LOG_DBG("drdy_pulsed is %d", (int)cfg->drdy_pulsed); |
||||
lsm6dsv16x_data_ready_mode_t mode = cfg->drdy_pulsed ? LSM6DSV16X_DRDY_PULSED : |
||||
LSM6DSV16X_DRDY_LATCHED; |
||||
|
||||
ret = lsm6dsv16x_data_ready_mode_set(ctx, mode); |
||||
if (ret < 0) { |
||||
LOG_ERR("drdy_pulsed config error %d", (int)cfg->drdy_pulsed); |
||||
return ret; |
||||
} |
||||
|
||||
return gpio_pin_interrupt_configure_dt(&cfg->gpio_drdy, |
||||
GPIO_INT_EDGE_TO_ACTIVE); |
||||
} |
@ -0,0 +1,147 @@
@@ -0,0 +1,147 @@
|
||||
# Copyright (c) 2023 STMicroelectronics |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
include: sensor-device.yaml |
||||
|
||||
properties: |
||||
irq-gpios: |
||||
type: phandle-array |
||||
description: | |
||||
DRDY pin |
||||
|
||||
This pin defaults to active high when produced by the sensor. |
||||
The property value should ensure the flags properly describe |
||||
the signal that is presented to the driver. |
||||
|
||||
drdy-pin: |
||||
type: int |
||||
default: 1 |
||||
enum: |
||||
- 1 # drdy is generated from INT1 |
||||
- 2 # drdy is generated from INT2 |
||||
description: | |
||||
Select DRDY pin number (1 or 2). |
||||
|
||||
This number represents which of the two interrupt pins |
||||
(INT1 or INT2) the drdy line is attached to. This property is not |
||||
mandatory and if not present it defaults to 1 which is the |
||||
configuration at power-up. |
||||
|
||||
accel-range: |
||||
type: int |
||||
default: 0 |
||||
description: | |
||||
Range in g. Default is power-up configuration. |
||||
enum: |
||||
- 0 # 2g (0.061 mg/LSB) |
||||
- 1 # 4g (0.122 mg/LSB) |
||||
- 2 # 8g (0.244 mg/LSB) |
||||
- 3 # 16g (0.488 mg/LSB) |
||||
|
||||
accel-odr: |
||||
type: int |
||||
default: 0x0 |
||||
description: | |
||||
Specify the default accelerometer output data rate expressed in samples per second (Hz). |
||||
The values are taken in accordance to lsm6dsv16x_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. |
||||
enum: |
||||
- 0x00 # Power-Down |
||||
- 0x01 # 1Hz875 |
||||
- 0x02 # 7Hz5 |
||||
- 0x03 # 15Hz |
||||
- 0x04 # 30Hz |
||||
- 0x05 # 60Hz |
||||
- 0x06 # 120Hz |
||||
- 0x07 # 240Hz |
||||
- 0x08 # 480Hz |
||||
- 0x09 # 960Hz |
||||
- 0x0a # 1920Hz |
||||
- 0x0b # 3840Hz |
||||
- 0x0c # 7680Hz |
||||
- 0x13 # 15Hz625 (High Accuracy 1) |
||||
- 0x14 # 31Hz25 (High Accuracy 1) |
||||
- 0x15 # 62Hz5 (High Accuracy 1) |
||||
- 0x16 # 125Hz (High Accuracy 1) |
||||
- 0x17 # 250Hz (High Accuracy 1) |
||||
- 0x18 # 500Hz (High Accuracy 1) |
||||
- 0x19 # 1000Hz (High Accuracy 1) |
||||
- 0x1a # 2000Hz (High Accuracy 1) |
||||
- 0x1b # 4000Hz (High Accuracy 1) |
||||
- 0x1c # 8000Hz (High Accuracy 1) |
||||
- 0x23 # 12Hz5 (High Accuracy 2) |
||||
- 0x24 # 25Hz (High Accuracy 2) |
||||
- 0x25 # 50Hz (High Accuracy 2) |
||||
- 0x26 # 100Hz (High Accuracy 2) |
||||
- 0x27 # 200Hz (High Accuracy 2) |
||||
- 0x28 # 400Hz (High Accuracy 2) |
||||
- 0x29 # 800Hz (High Accuracy 2) |
||||
- 0x2a # 1600Hz (High Accuracy 2) |
||||
- 0x2b # 3200Hz (High Accuracy 2) |
||||
- 0x2c # 6400Hz (High Accuracy 2) |
||||
|
||||
gyro-range: |
||||
type: int |
||||
default: 0 |
||||
description: | |
||||
Range in dps. Default is power-up configuration. |
||||
enum: |
||||
- 0 # 125 dps (4.375 mdps/LSB) |
||||
- 1 # 250 dps (8.75 mdps/LSB) |
||||
- 2 # 500 dps (17.50 mdps/LSB) |
||||
- 3 # 1000 dps (35 mdps/LSB) |
||||
- 4 # 2000 dps (70 mdps/LSB) |
||||
- 5 # 4000 dps (140 mdps/LSB) |
||||
|
||||
gyro-odr: |
||||
type: int |
||||
default: 0x0 |
||||
description: | |
||||
Specify the default gyro output data rate expressed in samples per second (Hz). |
||||
The values are taken in accordance to lsm6dsv16x_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). Moreover, the values here which will be selected in the |
||||
DT are the only way to specifiy the odr accuracy even at runtime with |
||||
SENSOR_ATTR_SAMPLING_FREQUENCY. |
||||
Default is power-up configuration. |
||||
enum: |
||||
- 0x00 # Power-Down |
||||
- 0x02 # 7Hz5 |
||||
- 0x03 # 15Hz |
||||
- 0x04 # 30Hz |
||||
- 0x05 # 60Hz |
||||
- 0x06 # 120Hz |
||||
- 0x07 # 240Hz |
||||
- 0x08 # 480Hz |
||||
- 0x09 # 960Hz |
||||
- 0x0a # 1920Hz |
||||
- 0x0b # 3840Hz |
||||
- 0x0c # 7680Hz |
||||
- 0x13 # 15Hz625 (High Accuracy 1) |
||||
- 0x14 # 31Hz25 (High Accuracy 1) |
||||
- 0x15 # 62Hz5 (High Accuracy 1) |
||||
- 0x16 # 125Hz (High Accuracy 1) |
||||
- 0x17 # 250Hz (High Accuracy 1) |
||||
- 0x18 # 500Hz (High Accuracy 1) |
||||
- 0x19 # 1000Hz (High Accuracy 1) |
||||
- 0x1a # 2000Hz (High Accuracy 1) |
||||
- 0x1b # 4000Hz (High Accuracy 1) |
||||
- 0x1c # 8000Hz (High Accuracy 1) |
||||
- 0x23 # 12Hz5 (High Accuracy 2) |
||||
- 0x24 # 25Hz (High Accuracy 2) |
||||
- 0x25 # 50Hz (High Accuracy 2) |
||||
- 0x26 # 100Hz (High Accuracy 2) |
||||
- 0x27 # 200Hz (High Accuracy 2) |
||||
- 0x28 # 400Hz (High Accuracy 2) |
||||
- 0x29 # 800Hz (High Accuracy 2) |
||||
- 0x2a # 1600Hz (High Accuracy 2) |
||||
- 0x2b # 3200Hz (High Accuracy 2) |
||||
- 0x2c # 6400Hz (High Accuracy 2) |
||||
|
||||
drdy-pulsed: |
||||
type: boolean |
||||
description: | |
||||
Selects the pulsed mode for data-ready interrupt when enabled, |
||||
and the latched mode when disabled. |
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2023 STMicroelectronics |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
description: | |
||||
STMicroelectronics LSM6DSV16X 6-axis IMU (Inertial Measurement Unit) sensor |
||||
accessed through I2C bus |
||||
|
||||
compatible: "st,lsm6dsv16x" |
||||
|
||||
include: ["i2c-device.yaml", "st,lsm6dsv16x-common.yaml"] |
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2023 STMicroelectronics |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
description: | |
||||
STMicroelectronics LSM6DSV16X 6-axis IMU (Inertial Measurement Unit) sensor |
||||
accessed through SPI bus |
||||
|
||||
compatible: "st,lsm6dsv16x" |
||||
|
||||
include: ["spi-device.yaml", "st,lsm6dsv16x-common.yaml"] |
Loading…
Reference in new issue