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 */ 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/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_ */ 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 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>; +};