From 1f7c33fcc4251f69cc3c9c1a4838006e8e1e2226 Mon Sep 17 00:00:00 2001 From: Miguel Gazquez Date: Wed, 24 Apr 2024 16:23:11 +0200 Subject: [PATCH 1/4] dts: bindings: add DT binding for lsm9ds1 This commit adds a description for the lsm9ds1 sensor, with a .h file containing all configuration options. Signed-off-by: Miguel Gazquez --- dts/bindings/sensor/st,lsm9ds1.yaml | 90 +++++++++++++++++++++ include/zephyr/dt-bindings/sensor/lsm9ds1.h | 46 +++++++++++ 2 files changed, 136 insertions(+) create mode 100644 dts/bindings/sensor/st,lsm9ds1.yaml create mode 100644 include/zephyr/dt-bindings/sensor/lsm9ds1.h diff --git a/dts/bindings/sensor/st,lsm9ds1.yaml b/dts/bindings/sensor/st,lsm9ds1.yaml new file mode 100644 index 0000000000000..88fc8ea662cb5 --- /dev/null +++ b/dts/bindings/sensor/st,lsm9ds1.yaml @@ -0,0 +1,90 @@ +# Copyright (c) 2024 Bootlin +# SPDX-License-Identifier: Apache-2.0 + +description: | + STMicroelectronics LSM9DS1 9-axis IMU (Inertial Measurement Unit) sensor + accessed through I2C bus. + + This binding describe only the inertial part : accelerometer and gyroscope. + + When setting the accel-range, gyro-range, imu-odr properties in + a .dts or .dtsi file you may include lsm9ds1.h and use the macros + defined there. + + Example: + #include + + lsm9ds1: lsm9ds1@0 { + ... + + accel-range = ; + imu-odr = ; + gyro-range = ; + }; + +compatible: "st,lsm9ds1" + +include: [sensor-device.yaml, i2c-device.yaml] + +properties: + accel-range: + type: int + default: 0 + description: | + Range of the accelerometer. Unit : g. Default is power-up configuration. + + - 0 # LSM9DS1_DT_FS_2G (0.061 mg/LSB) + - 1 # LSM9DS1_DT_FS_16G (0.732 mg/LSB) + - 2 # LSM9DS1_DT_FS_4G (0.122 mg/LSB) + - 3 # LSM9DS1_DT_FS_8G (0.244 mg/LSB) + + enum: [0, 1, 2, 3] + + gyro-range: + type: int + default: 0 + description: | + Range in dps. Default is power-up configuration. + + - 0 # LSM9DS1_DT_FS_245DPS (8.75 mdps/LSB) + - 1 # LSM9DS1_DT_FS_500DPS (17.50 mdps/LSB) + - 3 # LSM9DS1_DT_FS_2000DPS (70 mdps/LSB) + + enum: [0, 1, 3] + + imu-odr: + type: int + default: 0 + description: | + Specify the default accelerometer and gyroscope output data rate expressed in samples + per second (Hz). + Default is power-up configuration. + + - 0x00 # LSM9DS1_IMU_OFF + - 0x10 # LSM9DS1_GY_OFF_XL_10Hz + - 0x20 # LSM9DS1_GY_OFF_XL_50Hz + - 0x30 # LSM9DS1_GY_OFF_XL_119Hz + - 0x40 # LSM9DS1_GY_OFF_XL_238Hz + - 0x50 # LSM9DS1_GY_OFF_XL_476Hz + - 0x60 # LSM9DS1_GY_OFF_XL_952Hz + - 0x01 # LSM9DS1_XL_OFF_GY_14Hz9 + - 0x02 # LSM9DS1_XL_OFF_GY_59Hz5 + - 0x03 # LSM9DS1_XL_OFF_GY_119Hz + - 0x04 # LSM9DS1_XL_OFF_GY_238Hz + - 0x05 # LSM9DS1_XL_OFF_GY_476Hz + - 0x06 # LSM9DS1_XL_OFF_GY_952Hz + - 0x11 # LSM9DS1_IMU_14Hz9 + - 0x22 # LSM9DS1_IMU_59Hz5 + - 0x33 # LSM9DS1_IMU_119Hz + - 0x44 # LSM9DS1_IMU_238Hz + - 0x55 # LSM9DS1_IMU_476Hz + - 0x66 # LSM9DS1_IMU_952Hz + - 0x81 # LSM9DS1_XL_OFF_GY_14Hz9_LP + - 0x82 # LSM9DS1_XL_OFF_GY_59Hz5_LP + - 0x83 # LSM9DS1_XL_OFF_GY_119Hz_LP + - 0x91 # LSM9DS1_IMU_14Hz9_LP + - 0xA2 # LSM9DS1_IMU_59Hz5_LP + - 0xB3 # LSM9DS1_IMU_119Hz_LP + + enum: [0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x11, + 0x22, 0x33, 0x44, 0x55, 0x66, 0x81, 0x82, 0x83, 0x91, 0xA2, 0xB3] diff --git a/include/zephyr/dt-bindings/sensor/lsm9ds1.h b/include/zephyr/dt-bindings/sensor/lsm9ds1.h new file mode 100644 index 0000000000000..747d145e550a0 --- /dev/null +++ b/include/zephyr/dt-bindings/sensor/lsm9ds1.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024 Bootlin + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_ST_LSM9DS1_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_ST_LSM9DS1_H_ + +/* Accel range */ +#define LSM9DS1_DT_FS_2G 0 +#define LSM9DS1_DT_FS_16G 1 +#define LSM9DS1_DT_FS_4G 2 +#define LSM9DS1_DT_FS_8G 3 + +#define LSM9DS1_DT_FS_245DPS 0 +#define LSM9DS1_DT_FS_500DPS 1 +#define LSM9DS1_DT_FS_2000DPS 3 + +#define LSM9DS1_IMU_OFF 0x00 +#define LSM9DS1_GY_OFF_XL_10Hz 0x10 +#define LSM9DS1_GY_OFF_XL_50Hz 0x20 +#define LSM9DS1_GY_OFF_XL_119Hz 0x30 +#define LSM9DS1_GY_OFF_XL_238Hz 0x40 +#define LSM9DS1_GY_OFF_XL_476Hz 0x50 +#define LSM9DS1_GY_OFF_XL_952Hz 0x60 +#define LSM9DS1_XL_OFF_GY_14Hz9 0x01 +#define LSM9DS1_XL_OFF_GY_59Hz5 0x02 +#define LSM9DS1_XL_OFF_GY_119Hz 0x03 +#define LSM9DS1_XL_OFF_GY_238Hz 0x04 +#define LSM9DS1_XL_OFF_GY_476Hz 0x05 +#define LSM9DS1_XL_OFF_GY_952Hz 0x06 +#define LSM9DS1_IMU_14Hz9 0x11 +#define LSM9DS1_IMU_59Hz5 0x22 +#define LSM9DS1_IMU_119Hz 0x33 +#define LSM9DS1_IMU_238Hz 0x44 +#define LSM9DS1_IMU_476Hz 0x55 +#define LSM9DS1_IMU_952Hz 0x66 +#define LSM9DS1_XL_OFF_GY_14Hz9_LP 0x81 +#define LSM9DS1_XL_OFF_GY_59Hz5_LP 0x82 +#define LSM9DS1_XL_OFF_GY_119Hz_LP 0x83 +#define LSM9DS1_IMU_14Hz9_LP 0x91 +#define LSM9DS1_IMU_59Hz5_LP 0xA2 +#define LSM9DS1_IMU_119Hz_LP 0xB3 + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_ST_LSM9DS1_H_ */ From 01988a0b64c001f117c49c9ffee6d3ad1b285160 Mon Sep 17 00:00:00 2001 From: Miguel Gazquez Date: Wed, 24 Apr 2024 16:53:06 +0200 Subject: [PATCH 2/4] drivers: sensor: add driver for lsm9ds1 sensor The LSM9DS1 is a system-in-package featuring a 3D digital linear acceleration sensor, a 3D digital angular rate sensor and a 3D digital magnetic sensor. This driver implements only the linear acceleration sensor and the angular rate sensor, on the I2C bus. This driver is without trigger support. The driver is based on the stmemsc HAL. link: https://www.st.com/resource/en/datasheet/lsm9ds1.pdf Signed-off-by: Miguel Gazquez --- drivers/sensor/st/CMakeLists.txt | 1 + drivers/sensor/st/Kconfig | 1 + drivers/sensor/st/lsm9ds1/CMakeLists.txt | 7 + drivers/sensor/st/lsm9ds1/Kconfig | 19 + drivers/sensor/st/lsm9ds1/lsm9ds1.c | 651 +++++++++++++++++++++++ drivers/sensor/st/lsm9ds1/lsm9ds1.h | 56 ++ tests/drivers/build_all/sensor/i2c.dtsi | 5 + 7 files changed, 740 insertions(+) create mode 100644 drivers/sensor/st/lsm9ds1/CMakeLists.txt create mode 100644 drivers/sensor/st/lsm9ds1/Kconfig create mode 100644 drivers/sensor/st/lsm9ds1/lsm9ds1.c create mode 100644 drivers/sensor/st/lsm9ds1/lsm9ds1.h diff --git a/drivers/sensor/st/CMakeLists.txt b/drivers/sensor/st/CMakeLists.txt index b833043ae6c66..816a4cf0d4f4a 100644 --- a/drivers/sensor/st/CMakeLists.txt +++ b/drivers/sensor/st/CMakeLists.txt @@ -32,6 +32,7 @@ add_subdirectory_ifdef(CONFIG_LSM6DSO16IS lsm6dso16is) add_subdirectory_ifdef(CONFIG_LSM6DSV16X lsm6dsv16x) add_subdirectory_ifdef(CONFIG_LSM9DS0_GYRO lsm9ds0_gyro) add_subdirectory_ifdef(CONFIG_LSM9DS0_MFD lsm9ds0_mfd) +add_subdirectory_ifdef(CONFIG_LSM9DS1 lsm9ds1) add_subdirectory_ifdef(CONFIG_QDEC_STM32 qdec_stm32) add_subdirectory_ifdef(CONFIG_STM32_DIGI_TEMP stm32_digi_temp) add_subdirectory_ifdef(CONFIG_STM32_TEMP stm32_temp) diff --git a/drivers/sensor/st/Kconfig b/drivers/sensor/st/Kconfig index a3f03a36b668d..79a57bb6168ad 100644 --- a/drivers/sensor/st/Kconfig +++ b/drivers/sensor/st/Kconfig @@ -31,6 +31,7 @@ source "drivers/sensor/st/lsm6dso16is/Kconfig" source "drivers/sensor/st/lsm6dsv16x/Kconfig" source "drivers/sensor/st/lsm9ds0_gyro/Kconfig" source "drivers/sensor/st/lsm9ds0_mfd/Kconfig" +source "drivers/sensor/st/lsm9ds1/Kconfig" source "drivers/sensor/st/qdec_stm32/Kconfig" source "drivers/sensor/st/stm32_digi_temp/Kconfig" source "drivers/sensor/st/stm32_temp/Kconfig" diff --git a/drivers/sensor/st/lsm9ds1/CMakeLists.txt b/drivers/sensor/st/lsm9ds1/CMakeLists.txt new file mode 100644 index 0000000000000..1dc9559d9f178 --- /dev/null +++ b/drivers/sensor/st/lsm9ds1/CMakeLists.txt @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources(lsm9ds1.c) + +zephyr_library_include_directories(../stmemsc) diff --git a/drivers/sensor/st/lsm9ds1/Kconfig b/drivers/sensor/st/lsm9ds1/Kconfig new file mode 100644 index 0000000000000..fb6564e625087 --- /dev/null +++ b/drivers/sensor/st/lsm9ds1/Kconfig @@ -0,0 +1,19 @@ +# Copyright (c) 2024 Bootlin +# SPDX-License-Identifier: Apache-2.0 + +menuconfig LSM9DS1 + bool "LSM9DS1 I2C accelerometer and gyroscope chip" + default y + depends on DT_HAS_ST_LSM9DS1_ENABLED + select I2C + select HAS_STMEMSC + select USE_STDC_LSM9DS1 + +if LSM9DS1 + +config LSM9DS1_ENABLE_TEMP + bool "LSM9DS1 Temperature sensor" + help + Enable/disable temperature + +endif #LSM9DS1 diff --git a/drivers/sensor/st/lsm9ds1/lsm9ds1.c b/drivers/sensor/st/lsm9ds1/lsm9ds1.c new file mode 100644 index 0000000000000..4f6131e6b687d --- /dev/null +++ b/drivers/sensor/st/lsm9ds1/lsm9ds1.c @@ -0,0 +1,651 @@ +/* + * Copyright (c) 2024 Bootlin + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT st_lsm9ds1 + +#include +#include + +#include "lsm9ds1.h" +#include + +LOG_MODULE_REGISTER(LSM9DS1, CONFIG_SENSOR_LOG_LEVEL); + +/* Sensitivity of the accelerometer, indexed by the raw full scale value. Unit is µg/ LSB */ +static const uint16_t lsm9ds1_accel_fs_sens[] = {61, 732, 122, 244}; + +/* + * Sensitivity of the gyroscope, indexed by the raw full scale value. + * The value here is just a factor applied to GAIN_UNIT_G, as the sensitivity is + * proportional to the full scale size. + * The index 2 is never used : the value `0` is just a placeholder. + */ +static const uint16_t lsm9ds1_gyro_fs_sens[] = {1, 2, 0, 8}; + +/* + * Values of the different sampling frequencies of the accelerometer, indexed by the raw odr + * value that the sensor understands. + */ +static const uint16_t lsm9ds1_odr_map[] = {0, 10, 50, 119, 238, 476, 952}; + +/* + * Value of the different sampling frequencies of the gyroscope, indexed by the raw odr value + * that the sensor understands. + */ +static const uint16_t lsm9ds1_gyro_odr_map[] = {0, 15, 59, 119, 238, 476, 952}; + +static int lsm9ds1_reboot(const struct device *dev) +{ + const struct lsm9ds1_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + lsm9ds1_ctrl_reg8_t ctrl8_reg; + int ret; + + ret = lsm9ds1_read_reg(ctx, LSM9DS1_CTRL_REG8, (uint8_t *)&ctrl8_reg, 1); + if (ret < 0) { + return ret; + } + + ctrl8_reg.boot = 1; + + ret = lsm9ds1_write_reg(ctx, LSM9DS1_CTRL_REG8, (uint8_t *)&ctrl8_reg, 1); + if (ret < 0) { + return ret; + } + + k_msleep(50); + + return 0; +} + +static int lsm9ds1_accel_range_to_fs_val(int32_t range) +{ + switch (range) { + case 2: + return LSM9DS1_2g; + case 4: + return LSM9DS1_4g; + case 8: + return LSM9DS1_8g; + case 16: + return LSM9DS1_16g; + default: + return -EINVAL; + } +} + +static int lsm9ds1_gyro_range_to_fs_val(int32_t range) +{ + switch (range) { + case 245: + return LSM9DS1_245dps; + case 500: + return LSM9DS1_500dps; + case 2000: + return LSM9DS1_2000dps; + default: + return -EINVAL; + } +} + +static int lsm9ds1_accel_fs_val_to_gain(int fs) +{ + return lsm9ds1_accel_fs_sens[fs]; +} + +static int lsm9ds1_accel_freq_to_odr_val(uint16_t freq) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(lsm9ds1_odr_map); i++) { + if (freq <= lsm9ds1_odr_map[i]) { + return i; + } + } + + return -EINVAL; +} + +static int lsm9ds1_gyro_freq_to_odr_val(uint16_t freq) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(lsm9ds1_gyro_odr_map); i++) { + if (freq <= lsm9ds1_gyro_odr_map[i]) { + return i; + } + } + + return -EINVAL; +} + +static int lsm9ds1_accel_set_odr_raw(const struct device *dev, uint8_t odr) +{ + const struct lsm9ds1_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + struct lsm9ds1_data *data = dev->data; + int ret; + + lsm9ds1_ctrl_reg6_xl_t ctrl_reg6_xl; + + ret = lsm9ds1_read_reg(ctx, LSM9DS1_CTRL_REG6_XL, (uint8_t *)&ctrl_reg6_xl, 1); + if (ret < 0) { + return ret; + } + + ctrl_reg6_xl.odr_xl = odr; + + ret = lsm9ds1_write_reg(ctx, LSM9DS1_CTRL_REG6_XL, (uint8_t *)&ctrl_reg6_xl, 1); + if (ret < 0) { + return ret; + } + + data->accel_odr = odr; + + return 0; +} + +static int lsm9ds1_gyro_set_odr_raw(const struct device *dev, uint8_t odr) +{ + const struct lsm9ds1_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + struct lsm9ds1_data *data = dev->data; + int ret; + + lsm9ds1_ctrl_reg1_g_t ctrl_reg1; + + ret = lsm9ds1_read_reg(ctx, LSM9DS1_CTRL_REG1_G, (uint8_t *)&ctrl_reg1, 1); + if (ret < 0) { + return ret; + } + + ctrl_reg1.odr_g = odr; + + ret = lsm9ds1_write_reg(ctx, LSM9DS1_CTRL_REG1_G, (uint8_t *)&ctrl_reg1, 1); + if (ret < 0) { + return ret; + } + + data->gyro_odr = odr; + return 0; +} + +static int lsm9ds1_gyro_odr_set(const struct device *dev, uint16_t freq) +{ + struct lsm9ds1_data *data = dev->data; + int odr; + int ret; + + odr = lsm9ds1_gyro_freq_to_odr_val(freq); + + if (odr == data->gyro_odr) { + return 0; + } + + LOG_INF("You are also changing the odr of the accelerometer"); + + ret = lsm9ds1_gyro_set_odr_raw(dev, odr); + if (ret < 0) { + LOG_DBG("failed to set gyroscope sampling rate"); + return ret; + } + + /* + * When the gyroscope is on, the value of the accelerometer odr must be + * the same as the value of the gyroscope. + */ + + ret = lsm9ds1_accel_set_odr_raw(dev, odr); + if (ret < 0) { + LOG_ERR("failed to set accelerometer sampling rate"); + return ret; + } + + return 0; +} + +static int lsm9ds1_accel_odr_set(const struct device *dev, uint16_t freq) +{ + const struct lsm9ds1_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + struct lsm9ds1_data *data = dev->data; + int odr, ret; + lsm9ds1_imu_odr_t old_odr; + + ret = lsm9ds1_imu_data_rate_get(ctx, &old_odr); + if (ret < 0) { + return ret; + } + + /* + * The gyroscope is on : + * we have to change the odr on both the accelerometer and the gyroscope + */ + if (old_odr & GYRO_ODR_MASK) { + + odr = lsm9ds1_gyro_freq_to_odr_val(freq); + + if (odr == data->gyro_odr) { + return 0; + } + + LOG_INF("You are also changing the odr of the gyroscope"); + + ret = lsm9ds1_accel_set_odr_raw(dev, odr); + if (ret < 0) { + LOG_DBG("failed to set accelerometer sampling rate"); + return ret; + } + + ret = lsm9ds1_gyro_set_odr_raw(dev, odr); + if (ret < 0) { + LOG_ERR("failed to set gyroscope sampling rate"); + return ret; + } + + /* The gyroscope is off, we have to change the odr of just the accelerometer */ + } else { + + odr = lsm9ds1_accel_freq_to_odr_val(freq); + + if (odr == data->accel_odr) { + return 0; + } + + if (odr < 0) { + return odr; + } + + ret = lsm9ds1_accel_set_odr_raw(dev, odr); + if (ret < 0) { + LOG_DBG("failed to set accelerometer sampling rate"); + return ret; + } + } + return 0; +} + +static int lsm9ds1_accel_range_set(const struct device *dev, int32_t range) +{ + int fs; + struct lsm9ds1_data *data = dev->data; + const struct lsm9ds1_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + int ret; + + fs = lsm9ds1_accel_range_to_fs_val(range); + if (fs < 0) { + LOG_DBG("full scale value not supported"); + return fs; + } + + ret = lsm9ds1_xl_full_scale_set(ctx, fs); + if (ret < 0) { + LOG_DBG("failed to set accelerometer full-scale"); + return ret; + } + + data->acc_gain = lsm9ds1_accel_fs_val_to_gain(fs); + return 0; +} + +static int lsm9ds1_gyro_range_set(const struct device *dev, int32_t range) +{ + int fs; + struct lsm9ds1_data *data = dev->data; + const struct lsm9ds1_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + int ret; + + fs = lsm9ds1_gyro_range_to_fs_val(range); + if (fs < 0) { + return fs; + } + + ret = lsm9ds1_gy_full_scale_set(ctx, fs); + if (ret < 0) { + LOG_DBG("failed to set gyroscope full-scale"); + return ret; + } + + data->gyro_gain = (lsm9ds1_gyro_fs_sens[fs] * GAIN_UNIT_G); + return 0; +} + +static int lsm9ds1_accel_config(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, const struct sensor_value *val) +{ + switch (attr) { + case SENSOR_ATTR_FULL_SCALE: + return lsm9ds1_accel_range_set(dev, sensor_ms2_to_g(val)); + case SENSOR_ATTR_SAMPLING_FREQUENCY: + return lsm9ds1_accel_odr_set(dev, val->val1); + default: + LOG_DBG("Accel attribute not supported."); + return -ENOTSUP; + } + + return 0; +} + +static int lsm9ds1_gyro_config(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, const struct sensor_value *val) +{ + switch (attr) { + case SENSOR_ATTR_FULL_SCALE: + return lsm9ds1_gyro_range_set(dev, sensor_rad_to_degrees(val)); + case SENSOR_ATTR_SAMPLING_FREQUENCY: + return lsm9ds1_gyro_odr_set(dev, val->val1); + default: + LOG_DBG("Gyro attribute not supported."); + return -ENOTSUP; + } + + return 0; +} + +static int lsm9ds1_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 lsm9ds1_accel_config(dev, chan, attr, val); + case SENSOR_CHAN_GYRO_XYZ: + return lsm9ds1_gyro_config(dev, chan, attr, val); + default: + LOG_WRN("attr_set() not supported on this channel."); + return -ENOTSUP; + } + return 0; +} + +static int lsm9ds1_sample_fetch_accel(const struct device *dev) +{ + const struct lsm9ds1_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + struct lsm9ds1_data *data = dev->data; + int ret; + + ret = lsm9ds1_acceleration_raw_get(ctx, data->acc); + if (ret < 0) { + LOG_DBG("Failed to read sample"); + return ret; + } + + return 0; +} + +static int lsm9ds1_sample_fetch_gyro(const struct device *dev) +{ + const struct lsm9ds1_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + struct lsm9ds1_data *data = dev->data; + int ret; + + ret = lsm9ds1_angular_rate_raw_get(ctx, data->gyro); + if (ret < 0) { + LOG_DBG("Failed to read sample"); + return ret; + } + return 0; +} + +#if IS_ENABLED(CONFIG_LSM9DS1_ENABLE_TEMP) +static int lsm9ds1_sample_fetch_temp(const struct device *dev) +{ + const struct lsm9ds1_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + struct lsm9ds1_data *data = dev->data; + int ret; + + ret = lsm9ds1_temperature_raw_get(ctx, &data->temp_sample); + if (ret < 0) { + LOG_DBG("Failed to read sample"); + return ret; + } + + return 0; +} +#endif + +static int lsm9ds1_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + switch (chan) { + case SENSOR_CHAN_ACCEL_XYZ: + lsm9ds1_sample_fetch_accel(dev); + break; + case SENSOR_CHAN_GYRO_XYZ: + lsm9ds1_sample_fetch_gyro(dev); + break; +#if IS_ENABLED(CONFIG_LSM9DS1_ENABLE_TEMP) + case SENSOR_CHAN_DIE_TEMP: + lsm9ds1_sample_fetch_temp(dev); + break; +#endif + case SENSOR_CHAN_ALL: + lsm9ds1_sample_fetch_accel(dev); + lsm9ds1_sample_fetch_gyro(dev); +#if IS_ENABLED(CONFIG_LSM9DS1_ENABLE_TEMP) + lsm9ds1_sample_fetch_temp(dev); +#endif + break; + default: + return -ENOTSUP; + } + return 0; +} + +static inline void lsm9ds1_accel_convert(struct sensor_value *val, int raw_val, + uint32_t sensitivity) +{ + /* Sensitivity is exposed in ug/LSB */ + /* Convert to m/s^2 */ + sensor_ug_to_ms2(raw_val * sensitivity, val); +} + +static inline int lsm9ds1_accel_get_channel(enum sensor_channel chan, struct sensor_value *val, + struct lsm9ds1_data *data, uint32_t sensitivity) +{ + uint8_t i; + + switch (chan) { + case SENSOR_CHAN_ACCEL_X: + lsm9ds1_accel_convert(val, data->acc[0], sensitivity); + break; + case SENSOR_CHAN_ACCEL_Y: + lsm9ds1_accel_convert(val, data->acc[1], sensitivity); + break; + case SENSOR_CHAN_ACCEL_Z: + lsm9ds1_accel_convert(val, data->acc[2], sensitivity); + break; + case SENSOR_CHAN_ACCEL_XYZ: + for (i = 0; i < 3; i++) { + lsm9ds1_accel_convert(val++, data->acc[i], sensitivity); + } + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static int lsm9ds1_accel_channel_get(enum sensor_channel chan, struct sensor_value *val, + struct lsm9ds1_data *data) +{ + return lsm9ds1_accel_get_channel(chan, val, data, data->acc_gain); +} + +static inline void lsm9ds1_gyro_convert(struct sensor_value *val, int raw_val, uint32_t sensitivity) +{ + /* Sensitivity is exposed in udps/LSB */ + /* Convert to rad/s */ + sensor_10udegrees_to_rad((raw_val * (int32_t)sensitivity) / 10, val); +} + +static inline int lsm9ds1_gyro_get_channel(enum sensor_channel chan, struct sensor_value *val, + struct lsm9ds1_data *data, uint32_t sensitivity) +{ + uint8_t i; + + switch (chan) { + case SENSOR_CHAN_GYRO_X: + lsm9ds1_gyro_convert(val, data->gyro[0], sensitivity); + break; + case SENSOR_CHAN_GYRO_Y: + lsm9ds1_gyro_convert(val, data->gyro[1], sensitivity); + break; + case SENSOR_CHAN_GYRO_Z: + lsm9ds1_gyro_convert(val, data->gyro[2], sensitivity); + break; + case SENSOR_CHAN_GYRO_XYZ: + for (i = 0; i < 3; i++) { + lsm9ds1_gyro_convert(val++, data->gyro[i], sensitivity); + } + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static int lsm9ds1_gyro_channel_get(enum sensor_channel chan, struct sensor_value *val, + struct lsm9ds1_data *data) +{ + return lsm9ds1_gyro_get_channel(chan, val, data, data->gyro_gain); +} + +#if IS_ENABLED(CONFIG_LSM9DS1_ENABLE_TEMP) +static void lsm9ds1_temp_channel_get(struct sensor_value *val, struct lsm9ds1_data *data) +{ + val->val1 = data->temp_sample / TEMP_SENSITIVITY + TEMP_OFFSET; + val->val2 = (data->temp_sample % TEMP_SENSITIVITY) * (1000000 / TEMP_SENSITIVITY); +} +#endif + +static int lsm9ds1_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) +{ + struct lsm9ds1_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: + lsm9ds1_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: + lsm9ds1_gyro_channel_get(chan, val, data); + break; +#if IS_ENABLED(CONFIG_LSM9DS1_ENABLE_TEMP) + case SENSOR_CHAN_DIE_TEMP: + lsm9ds1_temp_channel_get(val, data); + break; +#endif + default: + return -ENOTSUP; + } + + return 0; +} + +static const struct sensor_driver_api lsm9ds1_api_funcs = { + .sample_fetch = lsm9ds1_sample_fetch, + .channel_get = lsm9ds1_channel_get, + .attr_set = lsm9ds1_attr_set, +}; + +static int lsm9ds1_init(const struct device *dev) +{ + const struct lsm9ds1_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + struct lsm9ds1_data *lsm9ds1 = dev->data; + uint8_t chip_id, fs; + int ret; + + ret = lsm9ds1_reboot(dev); + if (ret < 0) { + LOG_ERR("Failed to reboot device"); + return ret; + } + + ret = lsm9ds1_read_reg(ctx, LSM9DS1_WHO_AM_I, &chip_id, 1); + if (ret < 0) { + LOG_ERR("failed reading chip id"); + return ret; + } + + if (chip_id != LSM9DS1_IMU_ID) { + LOG_ERR("Invalid ID : got %x", chip_id); + return -EIO; + } + LOG_DBG("chip_id : %x", chip_id); + + LOG_DBG("output data rate is %d\n", cfg->imu_odr); + ret = lsm9ds1_imu_data_rate_set(ctx, cfg->imu_odr); + if (ret < 0) { + LOG_ERR("failed to set IMU odr"); + return ret; + } + + fs = cfg->accel_range; + LOG_DBG("accel range is %d\n", fs); + ret = lsm9ds1_xl_full_scale_set(ctx, fs); + if (ret < 0) { + LOG_ERR("failed to set accelerometer range %d", fs); + return ret; + } + + lsm9ds1->acc_gain = lsm9ds1_accel_fs_val_to_gain(fs); + + fs = cfg->gyro_range; + LOG_DBG("gyro range is %d", fs); + ret = lsm9ds1_gy_full_scale_set(ctx, fs); + if (ret < 0) { + LOG_ERR("failed to set gyroscope range %d\n", fs); + return ret; + } + lsm9ds1->gyro_gain = (lsm9ds1_gyro_fs_sens[fs] * GAIN_UNIT_G); + + return 0; +} + +#define LSM9DS1_CONFIG_COMMON(inst) \ + .imu_odr = DT_INST_PROP(inst, imu_odr), \ + .accel_range = DT_INST_PROP(inst, accel_range), \ + .gyro_range = DT_INST_PROP(inst, gyro_range), + +/* + * Instantiation macros used when a device is on an I2C bus. + */ + +#define LSM9DS1_CONFIG_I2C(inst) \ + { \ + STMEMSC_CTX_I2C(&lsm9ds1_config_##inst.stmemsc_cfg), \ + .stmemsc_cfg = \ + { \ + .i2c = I2C_DT_SPEC_INST_GET(inst), \ + }, \ + LSM9DS1_CONFIG_COMMON(inst) \ + } + +#define LSM9DS1_DEFINE(inst) \ + static struct lsm9ds1_data lsm9ds1_data_##inst = { \ + .acc_gain = 0, \ + }; \ + \ + static struct lsm9ds1_config lsm9ds1_config_##inst = LSM9DS1_CONFIG_I2C(inst); \ + \ + SENSOR_DEVICE_DT_INST_DEFINE(inst, lsm9ds1_init, NULL, &lsm9ds1_data_##inst, \ + &lsm9ds1_config_##inst, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &lsm9ds1_api_funcs); + +DT_INST_FOREACH_STATUS_OKAY(LSM9DS1_DEFINE); diff --git a/drivers/sensor/st/lsm9ds1/lsm9ds1.h b/drivers/sensor/st/lsm9ds1/lsm9ds1.h new file mode 100644 index 0000000000000..a0dde3191a896 --- /dev/null +++ b/drivers/sensor/st/lsm9ds1/lsm9ds1.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024 Bootlin + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_LSM9DS1_LSM9DS1_H_ +#define ZEPHYR_DRIVERS_SENSOR_LSM9DS1_LSM9DS1_H_ + +#include "lsm9ds1_reg.h" + +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) +#include +#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) */ + +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) +#include +#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */ + +#define GAIN_UNIT_XL (61LL) +#define GAIN_UNIT_G (8750LL) + +#define TEMP_OFFSET 25 /* raw output of zero indicate 25°C */ +#define TEMP_SENSITIVITY 16 /* 16 LSB / °C */ + +#define GYRO_ODR_MASK 0x7 + +struct lsm9ds1_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_range; + uint8_t gyro_range; + uint8_t imu_odr; +}; + +struct lsm9ds1_data { + int16_t acc[3]; + uint32_t acc_gain; + int16_t gyro[3]; + uint32_t gyro_gain; + + uint16_t accel_odr; + uint16_t gyro_odr; +#if defined(CONFIG_LSM9DS1_ENABLE_TEMP) + int16_t temp_sample; +#endif +}; + +#endif /* ZEPHYR_DRIVERS_SENSOR_LSM9DS1_LSM9DS1_H_ */ diff --git a/tests/drivers/build_all/sensor/i2c.dtsi b/tests/drivers/build_all/sensor/i2c.dtsi index 992fe30136226..0a088e2ee624c 100644 --- a/tests/drivers/build_all/sensor/i2c.dtsi +++ b/tests/drivers/build_all/sensor/i2c.dtsi @@ -1067,3 +1067,8 @@ test_i2c_sht21@90 { compatible = "sensirion,sht21"; reg = <0x90>; }; + +test_i2c_lsm9ds1: lsm9ds1@91 { + compatible = "st,lsm9ds1"; + reg = <0x8e>; +}; From 7daa992753161c53621b3575e9f3e411757ddebe Mon Sep 17 00:00:00 2001 From: Miguel Gazquez Date: Thu, 25 Apr 2024 16:38:47 +0200 Subject: [PATCH 3/4] boards: arduino_nano_33_ble: add lsm9ds1 sensor to device tree Describe the lsm9ds1 sensor available in the Arduino Nano 33 BLE through I2C. Set the accel0 alias. Signed-off-by: Miguel Gazquez --- boards/arduino/nano_33_ble/arduino_nano_33_ble-common.dtsi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/boards/arduino/nano_33_ble/arduino_nano_33_ble-common.dtsi b/boards/arduino/nano_33_ble/arduino_nano_33_ble-common.dtsi index 3d31ede313fea..589d5b66241a5 100644 --- a/boards/arduino/nano_33_ble/arduino_nano_33_ble-common.dtsi +++ b/boards/arduino/nano_33_ble/arduino_nano_33_ble-common.dtsi @@ -104,6 +104,7 @@ blue-pwm-led = &blue_pwm_led; spi = &spi2; watchdog0 = &wdt0; + accel0 = &lsm9ds1; }; }; @@ -166,6 +167,12 @@ arduino_i2c: &i2c0 { pinctrl-0 = <&i2c1_default>; pinctrl-1 = <&i2c1_sleep>; pinctrl-names = "default", "sleep"; + + lsm9ds1: lsm9ds1@6b { + compatible = "st,lsm9ds1"; + reg = <0x6b>; + }; + }; /* SPI2 is used because SPI1/0 shares conflicts with I2C1/0 */ From 5d50211f08c4bc3cc2c540d416fc5d42b71361db Mon Sep 17 00:00:00 2001 From: Miguel Gazquez Date: Thu, 25 Apr 2024 17:39:20 +0200 Subject: [PATCH 4/4] samples: accel_polling: set sampling frequency when necessary The accel_polling sample uses various sensor, but doesn't set a sampling rate. But some sensors (like st,lsm6dso) have a default sampling frequency of 0. So, depending on the sensor, the sample may not always work. There are two ways to fix this: either all drivers must set a valid sampling rate, or the sample shall at least try to set a value if there is none. We propose here the second approach, wich should allow the sample to work on more sensors out of the box. Signed-off-by: Miguel Gazquez --- samples/sensor/accel_polling/src/main.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/samples/sensor/accel_polling/src/main.c b/samples/sensor/accel_polling/src/main.c index 13e8cc3787ced..1286969616016 100644 --- a/samples/sensor/accel_polling/src/main.c +++ b/samples/sensor/accel_polling/src/main.c @@ -51,6 +51,29 @@ static int print_accels(const struct device *dev) return 0; } +static int set_sampling_freq(const struct device *dev) +{ + int ret; + struct sensor_value odr; + + ret = sensor_attr_get(dev, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_SAMPLING_FREQUENCY, &odr); + + /* If we don't get a frequency > 0, we set one */ + if (ret != 0 || (odr.val1 == 0 && odr.val2 == 0)) { + odr.val1 = 100; + odr.val2 = 0; + + ret = sensor_attr_set(dev, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_SAMPLING_FREQUENCY, + &odr); + + if (ret != 0) { + printk("%s : failed to set sampling frequency\n", dev->name); + } + } + + return 0; +} + int main(void) { int ret; @@ -60,6 +83,7 @@ int main(void) printk("sensor: device %s not ready.\n", sensors[i]->name); return 0; } + set_sampling_freq(sensors[i]); } #ifndef CONFIG_COVERAGE