From c53ac71dbc1a71aae2ec6c0653080108df9d35d7 Mon Sep 17 00:00:00 2001 From: Al Semjonovs Date: Mon, 1 May 2023 11:43:43 -0600 Subject: [PATCH] WIP: icm42688 v2 icm42688 RTIO implementation Signed-off-by: Al Semjonovs --- drivers/sensor/Kconfig | 9 + drivers/sensor/default_rtio_sensor.c | 2 +- drivers/sensor/icm42688/CMakeLists.txt | 2 + drivers/sensor/icm42688/Kconfig | 15 + drivers/sensor/icm42688/icm42688.c | 134 ++++++--- drivers/sensor/icm42688/icm42688.h | 28 +- drivers/sensor/icm42688/icm42688_common.c | 13 +- drivers/sensor/icm42688/icm42688_decoder.c | 295 +++++++++++++++++++ drivers/sensor/icm42688/icm42688_decoder.h | 29 ++ drivers/sensor/icm42688/icm42688_rtio.c | 312 +++++++++++++++++++++ drivers/sensor/icm42688/icm42688_rtio.h | 14 + drivers/sensor/icm42688/icm42688_trigger.c | 42 ++- drivers/sensor/icm42688/icm42688_trigger.h | 3 +- drivers/sensor/sensor_shell.c | 191 +++++++++++-- include/zephyr/drivers/sensor.h | 45 ++- samples/sensor/sensor_shell/prj.conf | 2 + 16 files changed, 1050 insertions(+), 86 deletions(-) create mode 100644 drivers/sensor/icm42688/icm42688_decoder.c create mode 100644 drivers/sensor/icm42688/icm42688_decoder.h create mode 100644 drivers/sensor/icm42688/icm42688_rtio.c create mode 100644 drivers/sensor/icm42688/icm42688_rtio.h diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index 6ea8ed1adb0b6a6..167de2f23f7dd41 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -30,6 +30,15 @@ config SENSOR_SHELL help This shell provides access to basic sensor data. +config SENSOR_SHELL_THREAD_STACK_SIZE + int "Stack size for the sensor shell data processing thread" + depends on SENSOR_SHELL + default 1024 + help + The sensor shell uses a dedicated thread to process data coming from the + sensors in either one-shot or streaming mode. Use this config to control + the size of that thread's stack. + config SENSOR_SHELL_BATTERY bool "Sensor shell 'battery' command" depends on SHELL diff --git a/drivers/sensor/default_rtio_sensor.c b/drivers/sensor/default_rtio_sensor.c index 7cf72316c1f4460..7f74e2d3c8b69f7 100644 --- a/drivers/sensor/default_rtio_sensor.c +++ b/drivers/sensor/default_rtio_sensor.c @@ -22,7 +22,7 @@ static void sensor_iodev_submit(struct rtio_iodev_sqe *iodev_sqe) if (api->submit != NULL) { api->submit(dev, iodev_sqe); - } else { + } else if (!cfg->is_streaming) { sensor_submit_fallback(dev, iodev_sqe); } } diff --git a/drivers/sensor/icm42688/CMakeLists.txt b/drivers/sensor/icm42688/CMakeLists.txt index 1e9acaa3731a546..1c8d16326329818 100644 --- a/drivers/sensor/icm42688/CMakeLists.txt +++ b/drivers/sensor/icm42688/CMakeLists.txt @@ -5,9 +5,11 @@ zephyr_library() zephyr_library_sources( icm42688.c icm42688_common.c + icm42688_decoder.c icm42688_spi.c ) +zephyr_library_sources_ifdef(CONFIG_ICM42688_STREAM icm42688_rtio.c) zephyr_library_sources_ifdef(CONFIG_ICM42688_TRIGGER icm42688_trigger.c) zephyr_library_sources_ifdef(CONFIG_EMUL_ICM42688 icm42688_emul.c) zephyr_include_directories_ifdef(CONFIG_EMUL_ICM42688 .) diff --git a/drivers/sensor/icm42688/Kconfig b/drivers/sensor/icm42688/Kconfig index 5cdfb3cdc1eef87..a2e07ecac5a484f 100644 --- a/drivers/sensor/icm42688/Kconfig +++ b/drivers/sensor/icm42688/Kconfig @@ -21,6 +21,13 @@ config EMUL_ICM42688 Enable the hardware emulator for the ICM42688. Doing so allows exercising sensor APIs for this IMU in native_posix and qemu. +config ICM42688_DECODER + bool "ICM42688 decoder logic" + default y if ICM42688 + help + Compile the ICM42688 decoder API which allows decoding raw data returned + from the sensor. + if ICM42688 choice @@ -42,6 +49,14 @@ config ICM42688_TRIGGER_OWN_THREAD endchoice +config ICM42688_STREAM + bool "Use hardware FIFO to stream data" + select ICM42688_TRIGGER + default y + depends on SPI_RTIO + help + Use this config option to enable streaming sensor data via RTIO subsystem. + config ICM42688_TRIGGER bool diff --git a/drivers/sensor/icm42688/icm42688.c b/drivers/sensor/icm42688/icm42688.c index 329907a010d9343..1382318a6964ba5 100644 --- a/drivers/sensor/icm42688/icm42688.c +++ b/drivers/sensor/icm42688/icm42688.c @@ -13,23 +13,15 @@ #include #include "icm42688.h" +#include "icm42688_decoder.h" #include "icm42688_reg.h" +#include "icm42688_rtio.h" #include "icm42688_spi.h" #include "icm42688_trigger.h" #include LOG_MODULE_REGISTER(ICM42688, CONFIG_SENSOR_LOG_LEVEL); -struct icm42688_sensor_data { - struct icm42688_dev_data dev_data; - - int16_t readings[7]; -}; - -struct icm42688_sensor_config { - struct icm42688_dev_cfg dev_cfg; -}; - static void icm42688_convert_accel(struct sensor_value *val, int16_t raw_val, struct icm42688_cfg *cfg) { @@ -47,42 +39,81 @@ static inline void icm42688_convert_temp(struct sensor_value *val, int16_t raw_v icm42688_temp_c((int32_t)raw_val, &val->val1, &val->val2); } -static int icm42688_channel_get(const struct device *dev, enum sensor_channel chan, - struct sensor_value *val) +int icm42688_get_channel_reading(enum sensor_channel chan, int16_t readings[7], int16_t out[3]) { - struct icm42688_sensor_data *data = dev->data; + switch (chan) { + case SENSOR_CHAN_ACCEL_XYZ: + out[0] = readings[1]; + out[1] = readings[2]; + out[2] = readings[3]; + break; + case SENSOR_CHAN_ACCEL_X: + out[0] = readings[1]; + break; + case SENSOR_CHAN_ACCEL_Y: + out[0] = readings[2]; + break; + case SENSOR_CHAN_ACCEL_Z: + out[0] = readings[3]; + break; + case SENSOR_CHAN_GYRO_XYZ: + out[0] = readings[4]; + out[1] = readings[5]; + out[2] = readings[6]; + break; + case SENSOR_CHAN_GYRO_X: + out[0] = readings[4]; + break; + case SENSOR_CHAN_GYRO_Y: + out[0] = readings[5]; + break; + case SENSOR_CHAN_GYRO_Z: + out[0] = readings[6]; + break; + case SENSOR_CHAN_DIE_TEMP: + out[0] = readings[0]; + break; + default: + return -ENOTSUP; + } + return 0; +} + +int icm42688_channel_parse_readings(enum sensor_channel chan, int16_t readings[7], + struct icm42688_cfg *cfg, struct sensor_value *val) +{ switch (chan) { case SENSOR_CHAN_ACCEL_XYZ: - icm42688_convert_accel(&val[0], data->readings[1], &data->dev_data.cfg); - icm42688_convert_accel(&val[1], data->readings[2], &data->dev_data.cfg); - icm42688_convert_accel(&val[2], data->readings[3], &data->dev_data.cfg); + icm42688_convert_accel(&val[0], readings[1], cfg); + icm42688_convert_accel(&val[1], readings[2], cfg); + icm42688_convert_accel(&val[2], readings[3], cfg); break; case SENSOR_CHAN_ACCEL_X: - icm42688_convert_accel(val, data->readings[1], &data->dev_data.cfg); + icm42688_convert_accel(val, readings[1], cfg); break; case SENSOR_CHAN_ACCEL_Y: - icm42688_convert_accel(val, data->readings[2], &data->dev_data.cfg); + icm42688_convert_accel(val, readings[2], cfg); break; case SENSOR_CHAN_ACCEL_Z: - icm42688_convert_accel(val, data->readings[3], &data->dev_data.cfg); + icm42688_convert_accel(val, readings[3], cfg); break; case SENSOR_CHAN_GYRO_XYZ: - icm42688_convert_gyro(&val[0], data->readings[4], &data->dev_data.cfg); - icm42688_convert_gyro(&val[1], data->readings[5], &data->dev_data.cfg); - icm42688_convert_gyro(&val[2], data->readings[6], &data->dev_data.cfg); + icm42688_convert_gyro(&val[0], readings[4], cfg); + icm42688_convert_gyro(&val[1], readings[5], cfg); + icm42688_convert_gyro(&val[2], readings[6], cfg); break; case SENSOR_CHAN_GYRO_X: - icm42688_convert_gyro(val, data->readings[4], &data->dev_data.cfg); + icm42688_convert_gyro(val, readings[4], cfg); break; case SENSOR_CHAN_GYRO_Y: - icm42688_convert_gyro(val, data->readings[5], &data->dev_data.cfg); + icm42688_convert_gyro(val, readings[5], cfg); break; case SENSOR_CHAN_GYRO_Z: - icm42688_convert_gyro(val, data->readings[6], &data->dev_data.cfg); + icm42688_convert_gyro(val, readings[6], cfg); break; case SENSOR_CHAN_DIE_TEMP: - icm42688_convert_temp(val, data->readings[0]); + icm42688_convert_temp(val, readings[0]); break; default: return -ENOTSUP; @@ -91,7 +122,15 @@ static int icm42688_channel_get(const struct device *dev, enum sensor_channel ch return 0; } -static int icm42688_sample_fetch(const struct device *dev, enum sensor_channel chan) +int icm42688_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) +{ + struct icm42688_sensor_data *data = dev->data; + + return icm42688_channel_parse_readings(chan, data->readings, &data->cfg, val); +} + +int icm42688_sample_fetch(const struct device *dev, enum sensor_channel chan) { uint8_t status; struct icm42688_sensor_data *data = dev->data; @@ -126,7 +165,7 @@ static int icm42688_attr_set(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, const struct sensor_value *val) { const struct icm42688_sensor_data *data = dev->data; - struct icm42688_cfg new_config = data->dev_data.cfg; + struct icm42688_cfg new_config = data->cfg; int res = 0; __ASSERT_NO_MSG(val != NULL); @@ -174,7 +213,7 @@ static int icm42688_attr_get(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, struct sensor_value *val) { const struct icm42688_sensor_data *data = dev->data; - const struct icm42688_cfg *cfg = &data->dev_data.cfg; + const struct icm42688_cfg *cfg = &data->cfg; int res = 0; __ASSERT_NO_MSG(val != NULL); @@ -223,6 +262,8 @@ static const struct sensor_driver_api icm42688_driver_api = { #ifdef CONFIG_ICM42688_TRIGGER .trigger_set = icm42688_trigger_set, #endif + .get_decoder = icm42688_get_decoder, + .submit = icm42688_submit, }; int icm42688_init(const struct device *dev) @@ -247,22 +288,17 @@ int icm42688_init(const struct device *dev) LOG_ERR("Failed to initialize triggers"); return res; } - - res = icm42688_trigger_enable_interrupt(dev); - if (res != 0) { - LOG_ERR("Failed to enable triggers"); - return res; - } #endif - data->dev_data.cfg.accel_mode = ICM42688_ACCEL_LN; - data->dev_data.cfg.gyro_mode = ICM42688_GYRO_LN; - data->dev_data.cfg.accel_fs = ICM42688_ACCEL_FS_2G; - data->dev_data.cfg.gyro_fs = ICM42688_GYRO_FS_125; - data->dev_data.cfg.accel_odr = ICM42688_ACCEL_ODR_1000; - data->dev_data.cfg.gyro_odr = ICM42688_GYRO_ODR_1000; + data->cfg.accel_mode = ICM42688_ACCEL_LN; + data->cfg.gyro_mode = ICM42688_GYRO_LN; + data->cfg.accel_fs = ICM42688_ACCEL_FS_2G; + data->cfg.gyro_fs = ICM42688_GYRO_FS_125; + data->cfg.accel_odr = ICM42688_ACCEL_ODR_1000; + data->cfg.gyro_odr = ICM42688_GYRO_ODR_1000; + data->cfg.fifo_en = IS_ENABLED(CONFIG_ICM42688_STREAM); - res = icm42688_configure(dev, &data->dev_data.cfg); + res = icm42688_configure(dev, &data->cfg); if (res != 0) { LOG_ERR("Failed to configure"); return res; @@ -286,9 +322,21 @@ void icm42688_unlock(const struct device *dev) #define ICM42688_SPI_CFG \ SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA | SPI_WORD_SET(8) | SPI_TRANSFER_MSB +#define ICM42688_RTIO_DEFINE(inst) \ + SPI_DT_IODEV_DEFINE(icm42688_spi_iodev_##inst, DT_DRV_INST(inst), ICM42688_SPI_CFG, 0U); \ + RTIO_DEFINE(icm42688_rtio_##inst, 8, 4); + +#define ICM42688_DEFINE_DATA(inst) \ + IF_ENABLED(CONFIG_ICM42688_STREAM, (ICM42688_RTIO_DEFINE(inst))); \ + static struct icm42688_sensor_data icm42688_driver_##inst = { \ + IF_ENABLED(CONFIG_ICM42688_STREAM, ( \ + .r = &icm42688_rtio_##inst, \ + .spi_iodev = &icm42688_spi_iodev_##inst, \ + )) \ + }; #define ICM42688_INIT(inst) \ - static struct icm42688_sensor_data icm42688_driver_##inst = { 0 }; \ + ICM42688_DEFINE_DATA(inst); \ \ static const struct icm42688_sensor_config icm42688_cfg_##inst = { \ .dev_cfg = \ diff --git a/drivers/sensor/icm42688/icm42688.h b/drivers/sensor/icm42688/icm42688.h index ac7c531c99d6846..0c8b3f4632221a4 100644 --- a/drivers/sensor/icm42688/icm42688.h +++ b/drivers/sensor/icm42688/icm42688.h @@ -385,6 +385,9 @@ struct icm42688_cfg { /* TODO additional FIFO options */ /* TODO interrupt options */ + bool interrupt1_drdy; + bool interrupt1_fifo_ths; + bool interrupt1_fifo_full; }; struct icm42688_trigger_entry { @@ -392,10 +395,7 @@ struct icm42688_trigger_entry { sensor_trigger_handler_t handler; }; -/** - * @brief Device data (struct device) - */ -struct icm42688_dev_data { +struct icm42688_sensor_data { struct icm42688_cfg cfg; #ifdef CONFIG_ICM42688_TRIGGER #if defined(CONFIG_ICM42688_TRIGGER_OWN_THREAD) @@ -405,12 +405,22 @@ struct icm42688_dev_data { #elif defined(CONFIG_ICM42688_TRIGGER_GLOBAL_THREAD) struct k_work work; #endif +#ifdef CONFIG_ICM42688_STREAM + struct rtio_iodev_sqe *streaming_sqe; + struct rtio *r; + struct rtio_iodev *spi_iodev; + uint8_t int_status; + uint16_t fifo_count; + atomic_t reading_fifo; +#endif /* CONFIG_ICM42688_STREAM */ const struct device *dev; struct gpio_callback gpio_cb; sensor_trigger_handler_t data_ready_handler; const struct sensor_trigger *data_ready_trigger; struct k_mutex mutex; #endif /* CONFIG_ICM42688_TRIGGER */ + + int16_t readings[7]; }; /** @@ -422,6 +432,10 @@ struct icm42688_dev_cfg { struct gpio_dt_spec gpio_int2; }; +struct icm42688_sensor_config { + struct icm42688_dev_cfg dev_cfg; +}; + /** * @brief Reset the sensor * @@ -443,7 +457,6 @@ int icm42688_reset(const struct device *dev); */ int icm42688_configure(const struct device *dev, struct icm42688_cfg *cfg); - /** * @brief Safely (re)Configure the sensor with the given configuration * @@ -667,4 +680,9 @@ static inline void icm42688_temp_c(int32_t in, int32_t *out_c, uint32_t *out_uc) *out_uc = ((in100 - (*out_c) * sensitivity) * INT64_C(1000000)) / sensitivity; } +int icm42688_sample_fetch(const struct device *dev, enum sensor_channel chan); +int icm42688_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val); +int icm42688_get_channel_reading(enum sensor_channel chan, int16_t readings[7], int16_t out[3]); + #endif /* ZEPHYR_DRIVERS_SENSOR_ICM42688_H_ */ diff --git a/drivers/sensor/icm42688/icm42688_common.c b/drivers/sensor/icm42688/icm42688_common.c index 6e82f22678bd3e9..d48de56799c17e7 100644 --- a/drivers/sensor/icm42688/icm42688_common.c +++ b/drivers/sensor/icm42688/icm42688_common.c @@ -12,6 +12,7 @@ #include "icm42688.h" #include "icm42688_reg.h" #include "icm42688_spi.h" +#include "icm42688_trigger.h" #include LOG_MODULE_REGISTER(ICM42688_LL, CONFIG_SENSOR_LOG_LEVEL); @@ -71,7 +72,7 @@ int icm42688_reset(const struct device *dev) int icm42688_configure(const struct device *dev, struct icm42688_cfg *cfg) { - struct icm42688_dev_data *dev_data = dev->data; + struct icm42688_sensor_data *dev_data = dev->data; const struct icm42688_dev_cfg *dev_cfg = dev->config; int res; @@ -173,8 +174,12 @@ int icm42688_configure(const struct device *dev, struct icm42688_cfg *cfg) } /* Pulse mode with async reset (resets interrupt line on int status read) */ - res = icm42688_spi_single_write(&dev_cfg->spi, REG_INT_CONFIG, - BIT_INT1_DRIVE_CIRCUIT | BIT_INT1_POLARITY); + if (IS_ENABLED(CONFIG_ICM42688_TRIGGER)) { + res = icm42688_trigger_enable_interrupt(dev, cfg); + } else { + res = icm42688_spi_single_write(&dev_cfg->spi, REG_INT_CONFIG, + BIT_INT1_DRIVE_CIRCUIT | BIT_INT1_POLARITY); + } if (res) { LOG_ERR("Error writing to INT_CONFIG"); return res; @@ -263,7 +268,7 @@ int icm42688_configure(const struct device *dev, struct icm42688_cfg *cfg) int icm42688_safely_configure(const struct device *dev, struct icm42688_cfg *cfg) { - struct icm42688_dev_data *drv_data = dev->data; + struct icm42688_sensor_data *drv_data = dev->data; int ret = icm42688_configure(dev, cfg); if (ret == 0) { diff --git a/drivers/sensor/icm42688/icm42688_decoder.c b/drivers/sensor/icm42688/icm42688_decoder.c new file mode 100644 index 000000000000000..97cb3a00ad210f5 --- /dev/null +++ b/drivers/sensor/icm42688/icm42688_decoder.c @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2023 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "icm42688_decoder.h" +#include "icm42688.h" +#include + +#include +LOG_MODULE_REGISTER(ICM42688_DECODER, CONFIG_SENSOR_LOG_LEVEL); + +#define DT_DRV_COMPAT invensense_icm42688 + +int icm42688_get_shift(enum sensor_channel chan) +{ + /* TODO: May need fine tuning */ + switch (chan) { + case SENSOR_CHAN_ACCEL_XYZ: + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + return 16; + case SENSOR_CHAN_GYRO_XYZ: + case SENSOR_CHAN_GYRO_X: + case SENSOR_CHAN_GYRO_Y: + case SENSOR_CHAN_GYRO_Z: + return 16; + case SENSOR_CHAN_DIE_TEMP: + return 16; + default: + return -ENOTSUP; + } +} + +int icm42688_convert_raw_to_q31(struct icm42688_cfg *cfg, enum sensor_channel chan, + icm42688_sample_t reading, q31_t *out) +{ + int32_t whole; + uint32_t fraction; + + switch (chan) { + case SENSOR_CHAN_ACCEL_XYZ: + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + icm42688_accel_ms(cfg, reading, &whole, &fraction); + *out = (whole << icm42688_get_shift(chan)) | fraction; + break; + case SENSOR_CHAN_GYRO_XYZ: + case SENSOR_CHAN_GYRO_X: + case SENSOR_CHAN_GYRO_Y: + case SENSOR_CHAN_GYRO_Z: + icm42688_gyro_rads(cfg, reading, &whole, &fraction); + *out = (whole << icm42688_get_shift(chan)) | fraction; + break; + case SENSOR_CHAN_DIE_TEMP: + icm42688_temp_c(reading, &whole, &fraction); + *out = (whole << icm42688_get_shift(chan)) | fraction; + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static int icm42688_get_channel_position(enum sensor_channel chan) +{ + switch (chan) { + case SENSOR_CHAN_DIE_TEMP: + return 0; + case SENSOR_CHAN_ACCEL_XYZ: + case SENSOR_CHAN_ACCEL_X: + return 1; + case SENSOR_CHAN_ACCEL_Y: + return 2; + case SENSOR_CHAN_ACCEL_Z: + return 3; + case SENSOR_CHAN_GYRO_XYZ: + case SENSOR_CHAN_GYRO_X: + return 4; + case SENSOR_CHAN_GYRO_Y: + return 5; + case SENSOR_CHAN_GYRO_Z: + return 6; + default: + return 0; + } +} + +static uint8_t icm42688_encode_channel(enum sensor_channel chan) +{ + uint8_t encode_bmask = 0; + + switch (chan) { + case SENSOR_CHAN_DIE_TEMP: + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_GYRO_X: + case SENSOR_CHAN_GYRO_Y: + case SENSOR_CHAN_GYRO_Z: + encode_bmask = BIT(icm42688_get_channel_position(chan)); + break; + case SENSOR_CHAN_ACCEL_XYZ: + case SENSOR_CHAN_GYRO_XYZ: + encode_bmask = BIT(icm42688_get_channel_position(chan)) | + BIT(icm42688_get_channel_position(chan + 1)) | + BIT(icm42688_get_channel_position(chan + 2)); + break; + default: + break; + } + + return encode_bmask; +} + +int icm42688_encode(const struct device *dev, const enum sensor_channel *const channels, + const size_t num_channels, uint8_t *buf) +{ + struct icm42688_sensor_data *data = dev->data; + struct icm42688_encoded_data *edata = (struct icm42688_encoded_data *)buf; + + edata->channels = 0; + + for (int i = 0; i < num_channels; i++) { + edata->channels |= icm42688_encode_channel(channels[i]); + } + + edata->accelerometer_scale = data->cfg.accel_fs; + edata->gyroscope_scale = data->cfg.gyro_fs; + edata->timestamp = k_ticks_to_ns_floor64(k_uptime_ticks()); + + return 0; +} + +static int calc_num_samples(uint8_t channels_read) +{ + int chan; + int num_samples = 0; + + while (channels_read) { + chan = __builtin_ctz(channels_read); + num_samples += SENSOR_CHANNEL_3_AXIS(chan) ? 3 : 1; + channels_read &= ~BIT(chan); + } + + return num_samples; +} + +static int icm42688_decoder_decode(const uint8_t *buffer, sensor_frame_iterator_t *fit, + sensor_channel_iterator_t *cit, enum sensor_channel *channels, + q31_t *values, uint8_t max_count) +{ + struct icm42688_encoded_data *edata = (struct icm42688_encoded_data *)buffer; + uint8_t channels_read = edata->channels; + struct icm42688_cfg cfg; + int chan; + int pos; + int count = 0; + int num_samples = __builtin_popcount(channels_read); + + cfg.accel_fs = edata->accelerometer_scale; + cfg.gyro_fs = edata->gyroscope_scale; + + channels_read = edata->channels; + + if (*fit != 0) { + return 0; + } + + /* Skip channels already decoded */ + for (int i = 0; i < *cit && channels_read; i++) { + chan = __builtin_ctz(channels_read); + channels_read &= ~BIT(chan); + } + + /* Decode remaining channels */ + while (channels_read && *cit < num_samples && count < max_count) { + chan = __builtin_ctz(channels_read); + + channels[count] = chan; + pos = icm42688_get_channel_position(chan); + + icm42688_convert_raw_to_q31(&cfg, chan, edata->readings[pos], &values[count]); + + count++; + channels_read &= ~BIT(chan); + *cit += 1; + } + + if (*cit >= __builtin_popcount(edata->channels)) { + *fit += 1; + *cit = 0; + } + + return count; +} + +static int icm42688_decoder_get_frame_count(const uint8_t *buffer, uint16_t *frame_count) +{ + *frame_count = 1; + return 0; +} + +static int icm42688_decoder_get_timestamp(const uint8_t *buffer, uint64_t *timestamp_ns) +{ + const struct icm42688_encoded_data *edata = (const struct icm42688_encoded_data *)buffer; + + *timestamp_ns = edata->timestamp; + return 0; +} + +static int icm42688_decoder_get_shift(const uint8_t *buffer, enum sensor_channel channel_type, + int8_t *shift) +{ + const struct icm42688_encoded_data *edata = (const struct icm42688_encoded_data *)buffer; + + switch (channel_type) { + case SENSOR_CHAN_ACCEL_XYZ: + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + switch (edata->accelerometer_scale) { + case ICM42688_ACCEL_FS_2G: + *shift = 1; + return 0; + case ICM42688_ACCEL_FS_4G: + *shift = 2; + return 0; + case ICM42688_ACCEL_FS_8G: + *shift = 3; + return 0; + case ICM42688_ACCEL_FS_16G: + *shift = 4; + return 0; + default: + return -EINVAL; + } + case SENSOR_CHAN_GYRO_XYZ: + case SENSOR_CHAN_GYRO_X: + case SENSOR_CHAN_GYRO_Y: + case SENSOR_CHAN_GYRO_Z: + switch (edata->gyroscope_scale) { + case ICM42688_GYRO_FS_15_625: + *shift = 4; + return 0; + case ICM42688_GYRO_FS_31_25: + *shift = 5; + return 0; + case ICM42688_GYRO_FS_62_5: + *shift = 6; + return 0; + case ICM42688_GYRO_FS_125: + *shift = 7; + return 0; + case ICM42688_GYRO_FS_250: + *shift = 8; + return 0; + case ICM42688_GYRO_FS_500: + *shift = 9; + return 0; + case ICM42688_GYRO_FS_1000: + *shift = 10; + return 0; + case ICM42688_GYRO_FS_2000: + *shift = 11; + return 0; + default: + return -EINVAL; + } + case SENSOR_CHAN_DIE_TEMP: + *shift = 0; + return 0; + default: + return -EINVAL; + } +} + +SENSOR_DECODER_API_DT_DEFINE() = { + .get_frame_count = icm42688_decoder_get_frame_count, + .get_timestamp = icm42688_decoder_get_timestamp, + .get_shift = icm42688_decoder_get_shift, + .decode = icm42688_decoder_decode, +}; + + int icm42688_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder) +{ + ARG_UNUSED(dev); + *decoder = &SENSOR_DECODER_NAME(); + + return 0; + } diff --git a/drivers/sensor/icm42688/icm42688_decoder.h b/drivers/sensor/icm42688/icm42688_decoder.h new file mode 100644 index 000000000000000..b2699c7b60c4f80 --- /dev/null +++ b/drivers/sensor/icm42688/icm42688_decoder.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_ICM42688_DECODER_H_ +#define ZEPHYR_DRIVERS_SENSOR_ICM42688_DECODER_H_ + +#include +#include + +typedef int16_t icm42688_sample_t; + +struct icm42688_encoded_data { + uint16_t channels: 7; + uint16_t accelerometer_scale: 2; + uint16_t gyroscope_scale: 3; + uint16_t reserved: 4; + uint64_t timestamp; + int16_t readings[7]; +}; + +int icm42688_encode(const struct device *dev, const enum sensor_channel *const channels, + const size_t num_channels, uint8_t *buf); + +int icm42688_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder); + +#endif /* ZEPHYR_DRIVERS_SENSOR_ICM42688_DECODER_H_ */ diff --git a/drivers/sensor/icm42688/icm42688_rtio.c b/drivers/sensor/icm42688/icm42688_rtio.c new file mode 100644 index 000000000000000..d8197196c833b9b --- /dev/null +++ b/drivers/sensor/icm42688/icm42688_rtio.c @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2023 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "icm42688.h" +#include "icm42688_decoder.h" +#include "icm42688_reg.h" +#include "icm42688_spi.h" + +#include +LOG_MODULE_REGISTER(ICM42688_RTIO, CONFIG_SENSOR_LOG_LEVEL); + +static inline uint32_t compute_min_buf_len(size_t num_channels, int num_output_samples) +{ + return sizeof(struct icm42688_encoded_data); +} + +static int icm42688_rtio_sample_fetch(const struct device *dev, int16_t readings[7]) +{ + uint8_t status; + const struct icm42688_sensor_config *cfg = dev->config; + uint8_t *buffer = (uint8_t *)readings; + + int res = icm42688_spi_read(&cfg->dev_cfg.spi, REG_INT_STATUS, &status, 1); + + if (res) { + return res; + } + + if (!FIELD_GET(BIT_INT_STATUS_DATA_RDY, status)) { + return -EBUSY; + } + + res = icm42688_read_all(dev, buffer); + + if (res) { + return res; + } + + for (int i = 0; i < 7; i++) { + readings[i] = sys_le16_to_cpu((buffer[i * 2] << 8) | buffer[i * 2 + 1]); + } + + return 0; +} + +static int icm42688_submit_one_shot(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) +{ + const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data; + const enum sensor_channel *const channels = cfg->channels; + const size_t num_channels = cfg->count; + int num_output_samples = 7; + uint32_t min_buf_len = compute_min_buf_len(num_channels, num_output_samples); + int rc; + uint8_t *buf; + uint32_t buf_len; + struct icm42688_encoded_data *edata; + + /* Get the buffer for the frame, it may be allocated dynamically by the rtio context */ + rc = rtio_sqe_rx_buf(iodev_sqe, min_buf_len, min_buf_len, &buf, &buf_len); + if (rc != 0) { + LOG_ERR("Failed to get a read buffer of size %u bytes", min_buf_len); + rtio_iodev_sqe_err(iodev_sqe, rc); + return rc; + } + + edata = (struct icm42688_encoded_data *)buf; + + icm42688_encode(dev, channels, num_channels, buf); + + rc = icm42688_rtio_sample_fetch(dev, edata->readings); + /* Check that the fetch succeeded */ + if (rc != 0) { + LOG_ERR("Failed to fetch samples"); + rtio_iodev_sqe_err(iodev_sqe, rc); + return rc; + } + + rtio_iodev_sqe_ok(iodev_sqe, 0); + + return 0; +} + +int icm42688_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) +{ + struct icm42688_sensor_data *data = dev->data; + const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data; + int rc; + + if (!cfg->is_streaming) { + return icm42688_submit_one_shot(dev, iodev_sqe); + } + + /* Only support */ + struct icm42688_cfg new_config = data->cfg; + + new_config.interrupt1_drdy = false; + new_config.interrupt1_fifo_ths = false; + new_config.interrupt1_fifo_full = false; + for (int i = 0; i < cfg->count; ++i) { + switch (cfg->triggers[i]) { + case SENSOR_TRIG_DATA_READY: + new_config.interrupt1_drdy = true; + break; + case SENSOR_TRIG_FIFO_THRESHOLD: + new_config.interrupt1_fifo_ths = true; + break; + case SENSOR_TRIG_FIFO_FULL: + new_config.interrupt1_fifo_full = true; + break; + default: + LOG_DBG("Trigger (%d) not supported", cfg->triggers[i]); + break; + } + } + + if (new_config.interrupt1_drdy != data->cfg.interrupt1_drdy || + new_config.interrupt1_fifo_ths != data->cfg.interrupt1_fifo_ths || + new_config.interrupt1_fifo_full != data->cfg.interrupt1_fifo_full) { + rc = icm42688_safely_configure(dev, &new_config); + if (rc != 0) { + LOG_ERR("Failed to configure sensor"); + return rc; + } + } + + data->streaming_sqe = iodev_sqe; + return 0; +} + +struct fifo_header { + uint8_t int_status; + uint16_t gyro_odr: 4; + uint16_t accel_odr: 4; + uint16_t gyro_fs: 3; + uint16_t accel_fs: 3; + uint16_t packet_format: 2; +} __attribute__((__packed__)); + +BUILD_ASSERT(sizeof(struct fifo_header) == 3); + +static void icm42688_complete_cb(struct rtio *r, const struct rtio_sqe *sqe, void *arg) +{ + const struct device *dev = arg; + struct icm42688_sensor_data *drv_data = dev->data; + const struct icm42688_sensor_config *drv_cfg = dev->config; + struct rtio_iodev_sqe *iodev_sqe = sqe->userdata; + + /* TODO report number of frames or bytes read here */ + rtio_iodev_sqe_ok(iodev_sqe, drv_data->fifo_count); + + gpio_pin_interrupt_configure_dt(&drv_cfg->dev_cfg.gpio_int1, GPIO_INT_EDGE_TO_ACTIVE); +} + +static void icm42688_fifo_count_cb(struct rtio *r, const struct rtio_sqe *sqe, void *arg) +{ + const struct device *dev = arg; + struct icm42688_sensor_data *drv_data = dev->data; + const struct icm42688_sensor_config *drv_cfg = dev->config; + struct rtio_iodev *spi_iodev = drv_data->spi_iodev; + uint8_t *fifo_count_buf = (uint8_t *)&drv_data->fifo_count; + uint16_t fifo_count = ((fifo_count_buf[0] << 8) | fifo_count_buf[1]); + + drv_data->fifo_count = fifo_count; + + /* Pull a operation from our device iodev queue, validated to only be reads */ + struct rtio_iodev_sqe *iodev_sqe = drv_data->streaming_sqe; + + drv_data->streaming_sqe = NULL; + + /* Not inherently an underrun/overrun as we may have a buffer to fill next time */ + if (iodev_sqe == NULL) { + LOG_DBG("No pending SQE"); + gpio_pin_interrupt_configure_dt(&drv_cfg->dev_cfg.gpio_int1, + GPIO_INT_EDGE_TO_ACTIVE); + return; + } + + const size_t packet_size = drv_data->cfg.fifo_hires ? 20 : 16; + const size_t min_read_size = sizeof(struct fifo_header) + packet_size; + const size_t ideal_read_size = sizeof(struct fifo_header) + fifo_count; + uint8_t *buf; + uint32_t buf_len; + + if (rtio_sqe_rx_buf(iodev_sqe, min_read_size, ideal_read_size, &buf, &buf_len) != 0) { + LOG_ERR("Failed to get buffer"); + rtio_iodev_sqe_err(iodev_sqe, -ENOMEM); + return; + } + + /* Read FIFO and call back to rtio with rtio_sqe completion */ + /* TODO is packet format even needed? the fifo has a header per packet + * already + */ + struct fifo_header hdr = { + .int_status = drv_data->int_status, + .gyro_odr = drv_data->cfg.gyro_odr, + .gyro_fs = drv_data->cfg.gyro_fs, + .accel_odr = drv_data->cfg.accel_odr, + .accel_fs = drv_data->cfg.accel_fs, + .packet_format = 0, + }; + uint32_t buf_avail = buf_len; + + memcpy(buf, &hdr, sizeof(hdr)); + buf_avail -= sizeof(hdr); + + uint32_t read_len = MIN(fifo_count, buf_avail); + uint32_t pkts = read_len / packet_size; + + read_len = pkts * packet_size; + + __ASSERT_NO_MSG(read_len % pkt_size == 0); + + uint8_t *read_buf = buf + sizeof(hdr); + + /* Flush out completions */ + struct rtio_cqe *cqe; + do { + cqe = rtio_cqe_consume(r); + if (cqe != NULL) { + rtio_cqe_release(r, cqe); + } + } while (cqe != NULL); + + /* Setup new rtio chain to read the fifo data and report then check the + * result + */ + struct rtio_sqe *write_fifo_addr = rtio_sqe_acquire(r); + struct rtio_sqe *read_fifo_data = rtio_sqe_acquire(r); + struct rtio_sqe *complete_op = rtio_sqe_acquire(r); + const uint8_t reg_addr = REG_SPI_READ_BIT | FIELD_GET(REG_ADDRESS_MASK, REG_FIFO_DATA); + + rtio_sqe_prep_tiny_write(write_fifo_addr, spi_iodev, + RTIO_PRIO_NORM, ®_addr, 1, NULL); + write_fifo_addr->flags = RTIO_SQE_TRANSACTION; + rtio_sqe_prep_read(read_fifo_data, spi_iodev, RTIO_PRIO_NORM, + read_buf, read_len, iodev_sqe); + + rtio_sqe_prep_callback(complete_op, icm42688_complete_cb, + (void *)dev, iodev_sqe); + + rtio_submit(r, 0); +} + +static void icm42688_int_status_cb(struct rtio *r, const struct rtio_sqe *sqr, void *arg) +{ + const struct device *dev = arg; + struct icm42688_sensor_data *drv_data = dev->data; + const struct icm42688_sensor_config *drv_cfg = dev->config; + struct rtio_iodev *spi_iodev = drv_data->spi_iodev; + + if (FIELD_GET(BIT_INT_STATUS_FIFO_THS, drv_data->int_status) == 0 && + FIELD_GET(BIT_INT_STATUS_FIFO_FULL, drv_data->int_status) == 0) { + gpio_pin_interrupt_configure_dt(&drv_cfg->dev_cfg.gpio_int1, + GPIO_INT_EDGE_TO_ACTIVE); + return; + } + + /* Flush completions */ + struct rtio_cqe *cqe; + + do { + cqe = rtio_cqe_consume(r); + if (cqe != NULL) { + rtio_cqe_release(r, cqe); + } + } while (cqe != NULL); + + struct rtio_sqe *write_fifo_count_reg = rtio_sqe_acquire(r); + struct rtio_sqe *read_fifo_count = rtio_sqe_acquire(r); + struct rtio_sqe *check_fifo_count = rtio_sqe_acquire(r); + uint8_t reg = REG_SPI_READ_BIT | FIELD_GET(REG_ADDRESS_MASK, REG_FIFO_COUNTH); + uint8_t *read_buf = (uint8_t *)&drv_data->fifo_count; + + rtio_sqe_prep_tiny_write(write_fifo_count_reg, spi_iodev, RTIO_PRIO_NORM, ®, 1, NULL); + write_fifo_count_reg->flags = RTIO_SQE_TRANSACTION; + rtio_sqe_prep_read(read_fifo_count, spi_iodev, RTIO_PRIO_NORM, read_buf, 2, NULL); + rtio_sqe_prep_callback(check_fifo_count, icm42688_fifo_count_cb, arg, NULL); + + rtio_submit(r, 0); +} + +void icm42688_fifo_event(const struct device *dev) +{ + struct icm42688_sensor_data *drv_data = dev->data; + struct rtio_iodev *spi_iodev = drv_data->spi_iodev; + struct rtio *r = drv_data->r; + + /* + * Setup rtio chain of ops with inline calls to make decisions + * 1. read int status + * 2. call to check int status and get pending RX operation + * 4. read fifo len + * 5. call to determine read len + * 6. read fifo + * 7. call to report completion + */ + struct rtio_sqe *write_int_reg = rtio_sqe_acquire(r); + struct rtio_sqe *read_int_reg = rtio_sqe_acquire(r); + struct rtio_sqe *check_int_status = rtio_sqe_acquire(r); + uint8_t reg = REG_SPI_READ_BIT | FIELD_GET(REG_ADDRESS_MASK, REG_INT_STATUS); + + rtio_sqe_prep_tiny_write(write_int_reg, spi_iodev, RTIO_PRIO_NORM, ®, 1, NULL); + write_int_reg->flags = RTIO_SQE_TRANSACTION; + rtio_sqe_prep_read(read_int_reg, spi_iodev, RTIO_PRIO_NORM, &drv_data->int_status, 1, NULL); + rtio_sqe_prep_callback(check_int_status, icm42688_int_status_cb, (void *)dev, NULL); + rtio_submit(r, 0); +} diff --git a/drivers/sensor/icm42688/icm42688_rtio.h b/drivers/sensor/icm42688/icm42688_rtio.h new file mode 100644 index 000000000000000..9b236aa41f73e25 --- /dev/null +++ b/drivers/sensor/icm42688/icm42688_rtio.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2023 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_ICM42688_RTIO_H_ +#define ZEPHYR_DRIVERS_SENSOR_ICM42688_RTIO_H_ + +int icm42688_submit(const struct device *sensor, struct rtio_iodev_sqe *iodev_sqe); + +void icm42688_fifo_event(const struct device *dev); + +#endif /* ZEPHYR_DRIVERS_SENSOR_ICM42688_RTIO_H_ */ diff --git a/drivers/sensor/icm42688/icm42688_trigger.c b/drivers/sensor/icm42688/icm42688_trigger.c index 4bf728b40545a87..151ffd6b587933e 100644 --- a/drivers/sensor/icm42688/icm42688_trigger.c +++ b/drivers/sensor/icm42688/icm42688_trigger.c @@ -12,6 +12,7 @@ #include "icm42688.h" #include "icm42688_reg.h" +#include "icm42688_rtio.h" #include "icm42688_spi.h" #include "icm42688_trigger.h" @@ -20,7 +21,7 @@ LOG_MODULE_DECLARE(ICM42688, CONFIG_SENSOR_LOG_LEVEL); static void icm42688_gpio_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { - struct icm42688_dev_data *data = CONTAINER_OF(cb, struct icm42688_dev_data, gpio_cb); + struct icm42688_sensor_data *data = CONTAINER_OF(cb, struct icm42688_sensor_data, gpio_cb); ARG_UNUSED(dev); ARG_UNUSED(pins); @@ -30,8 +31,12 @@ static void icm42688_gpio_callback(const struct device *dev, struct gpio_callbac #elif defined(CONFIG_ICM42688_TRIGGER_GLOBAL_THREAD) k_work_submit(&data->work); #endif + if (IS_ENABLED(CONFIG_ICM42688_STREAM)) { + icm42688_fifo_event(data->dev); + } } +#ifdef CONFIG_ICM42688_TRIGGER_OWN_THREAD static void icm42688_thread_cb(const struct device *dev) { struct icm42688_dev_data *data = dev->data; @@ -45,14 +50,12 @@ static void icm42688_thread_cb(const struct device *dev) icm42688_unlock(dev); } -#if defined(CONFIG_ICM42688_TRIGGER_OWN_THREAD) - static void icm42688_thread(void *p1, void *p2, void *p3) { ARG_UNUSED(p2); ARG_UNUSED(p3); - struct icm42688_dev_data *data = p1; + struct icm42688_sensor_data *data = p1; while (1) { k_sem_take(&data->gpio_sem, K_FOREVER); @@ -64,7 +67,7 @@ static void icm42688_thread(void *p1, void *p2, void *p3) static void icm42688_work_handler(struct k_work *work) { - struct icm42688_dev_data *data = CONTAINER_OF(work, struct icm42688_dev_data, work); + struct icm42688_sensor_data *data = CONTAINER_OF(work, struct icm42688_sensor_data, work); icm42688_thread_cb(data->dev); } @@ -74,7 +77,7 @@ static void icm42688_work_handler(struct k_work *work) int icm42688_trigger_set(const struct device *dev, const struct sensor_trigger *trig, sensor_trigger_handler_t handler) { - struct icm42688_dev_data *data = dev->data; + struct icm42688_sensor_data *data = dev->data; const struct icm42688_dev_cfg *cfg = dev->config; uint8_t status; int res = 0; @@ -88,6 +91,8 @@ int icm42688_trigger_set(const struct device *dev, const struct sensor_trigger * switch (trig->type) { case SENSOR_TRIG_DATA_READY: + case SENSOR_TRIG_FIFO_THRESHOLD: + case SENSOR_TRIG_FIFO_FULL: data->data_ready_handler = handler; data->data_ready_trigger = trig; @@ -108,7 +113,7 @@ int icm42688_trigger_set(const struct device *dev, const struct sensor_trigger * int icm42688_trigger_init(const struct device *dev) { - struct icm42688_dev_data *data = dev->data; + struct icm42688_sensor_data *data = dev->data; const struct icm42688_dev_cfg *cfg = dev->config; int res = 0; @@ -144,10 +149,11 @@ int icm42688_trigger_init(const struct device *dev) return gpio_pin_interrupt_configure_dt(&cfg->gpio_int1, GPIO_INT_EDGE_TO_ACTIVE); } -int icm42688_trigger_enable_interrupt(const struct device *dev) +int icm42688_trigger_enable_interrupt(const struct device *dev, struct icm42688_cfg *new_cfg) { int res; const struct icm42688_dev_cfg *cfg = dev->config; + struct icm42688_sensor_data *data = dev->data; /* pulse-mode (auto clearing), push-pull and active-high */ res = icm42688_spi_single_write(&cfg->spi, REG_INT_CONFIG, @@ -162,21 +168,31 @@ int icm42688_trigger_enable_interrupt(const struct device *dev) return res; } - /* enable data ready interrupt on INT1 pin */ - return icm42688_spi_single_write(&cfg->spi, REG_INT_SOURCE0, - FIELD_PREP(BIT_UI_DRDY_INT1_EN, 1)); + /* enable interrupts on INT1 pin */ + uint8_t value = 0; + + if (new_cfg->interrupt1_drdy) { + value |= FIELD_PREP(BIT_UI_DRDY_INT1_EN, 1); + } + if (new_cfg->interrupt1_fifo_ths) { + value |= FIELD_PREP(BIT_FIFO_THS_INT1_EN, 1); + } + if (new_cfg->interrupt1_fifo_full) { + value |= FIELD_PREP(BIT_FIFO_FULL_INT1_EN, 1); + } + return icm42688_spi_single_write(&cfg->spi, REG_INT_SOURCE0, value); } void icm42688_lock(const struct device *dev) { - struct icm42688_dev_data *data = dev->data; + struct icm42688_sensor_data *data = dev->data; k_mutex_lock(&data->mutex, K_FOREVER); } void icm42688_unlock(const struct device *dev) { - struct icm42688_dev_data *data = dev->data; + struct icm42688_sensor_data *data = dev->data; k_mutex_unlock(&data->mutex); } diff --git a/drivers/sensor/icm42688/icm42688_trigger.h b/drivers/sensor/icm42688/icm42688_trigger.h index 5ed382eb0d403a1..e03975916181203 100644 --- a/drivers/sensor/icm42688/icm42688_trigger.h +++ b/drivers/sensor/icm42688/icm42688_trigger.h @@ -26,9 +26,10 @@ int icm42688_trigger_init(const struct device *dev); * @brief enable the trigger gpio interrupt * * @param dev icm42688 device pointer + * @param new_cfg New configuration to use for the device * @return int 0 on success, negative error code otherwise */ -int icm42688_trigger_enable_interrupt(const struct device *dev); +int icm42688_trigger_enable_interrupt(const struct device *dev, struct icm42688_cfg *new_cfg); /** * @brief lock access to the icm42688 device driver diff --git a/drivers/sensor/sensor_shell.c b/drivers/sensor/sensor_shell.c index 7bddedc7a48cea2..c9340867e2adc6a 100644 --- a/drivers/sensor/sensor_shell.c +++ b/drivers/sensor/sensor_shell.c @@ -18,6 +18,11 @@ "when no channels are provided. Syntax:\n" \ " .. " +#define SENSOR_STREAM_HELP \ + "Start/stop streaming sensor data. Data ready trigger will be used if no triggers " \ + "are provided. Syntax:\n" \ + " on|off .. " + #define SENSOR_ATTR_GET_HELP \ "Get the sensor's channel attribute. Syntax:\n" \ " [ .. " \ @@ -107,10 +112,26 @@ static const char *sensor_attribute_name[SENSOR_ATTR_COMMON_COUNT] = { [SENSOR_ATTR_FF_DUR] = "ff_dur", }; +static const char *sensor_trigger_type_name[SENSOR_TRIG_COMMON_COUNT] = { + [SENSOR_TRIG_TIMER] = "timer", + [SENSOR_TRIG_DATA_READY] = "data_ready", + [SENSOR_TRIG_DELTA] = "delta", + [SENSOR_TRIG_NEAR_FAR] = "near_far", + [SENSOR_TRIG_THRESHOLD] = "threshold", + [SENSOR_TRIG_TAP] = "tap", + [SENSOR_TRIG_DOUBLE_TAP] = "double_tap", + [SENSOR_TRIG_FREEFALL] = "freefall", + [SENSOR_TRIG_MOTION] = "motion", + [SENSOR_TRIG_STATIONARY] = "stationary", + [SENSOR_TRIG_FIFO_THRESHOLD] = "fifo_thres", + [SENSOR_TRIG_FIFO_FULL] = "fifo_full", +}; + enum dynamic_command_context { NONE, CTX_GET, CTX_ATTR_GET_SET, + CTX_STREAM_ON_OFF, }; static enum dynamic_command_context current_cmd_ctx = NONE; @@ -119,12 +140,25 @@ static enum dynamic_command_context current_cmd_ctx = NONE; static enum sensor_channel iodev_sensor_shell_channels[SENSOR_CHAN_ALL]; static struct sensor_read_config iodev_sensor_shell_read_config = { .sensor = NULL, + .is_streaming = false, .channels = iodev_sensor_shell_channels, .count = 0, .max = ARRAY_SIZE(iodev_sensor_shell_channels), }; RTIO_IODEV_DEFINE(iodev_sensor_shell_read, &__sensor_iodev_api, &iodev_sensor_shell_read_config); +/* Create a single common config for streaming */ +static enum sensor_trigger_type iodev_sensor_shell_trigger_types[SENSOR_TRIG_COMMON_COUNT]; +static struct sensor_read_config iodev_sensor_shell_stream_config = { + .sensor = NULL, + .is_streaming = true, + .triggers = iodev_sensor_shell_trigger_types, + .count = 0, + .max = ARRAY_SIZE(iodev_sensor_shell_trigger_types), +}; +RTIO_IODEV_DEFINE(iodev_sensor_shell_stream, &__sensor_iodev_api, + &iodev_sensor_shell_stream_config); + /* Create the RTIO context to service the reading */ RTIO_DEFINE_WITH_MEMPOOL(sensor_read_rtio, 8, 8, 32, 64, 4); @@ -259,6 +293,7 @@ static void sensor_shell_processing_callback(int result, uint8_t *buf, uint32_t static int cmd_get_sensor(const struct shell *sh, size_t argc, char *argv[]) { + static struct sensor_shell_processing_context ctx; const struct device *dev; int err; @@ -302,10 +337,8 @@ static int cmd_get_sensor(const struct shell *sh, size_t argc, char *argv[]) } iodev_sensor_shell_read_config.sensor = dev; - struct sensor_shell_processing_context ctx = { - .dev = dev, - .sh = sh, - }; + ctx.dev = dev; + ctx.sh = sh; err = sensor_read(&iodev_sensor_shell_read, &sensor_read_rtio, &ctx); if (err < 0) { shell_error(sh, "Failed to read sensor: %d", err); @@ -315,6 +348,20 @@ static int cmd_get_sensor(const struct shell *sh, size_t argc, char *argv[]) return 0; } +static void sensor_shell_processing_entry_point(void *a, void *b, void *c) +{ + ARG_UNUSED(a); + ARG_UNUSED(b); + ARG_UNUSED(c); + + while (true) { + sensor_processing_with_callback(&sensor_read_rtio, + sensor_shell_processing_callback); + } +} +K_THREAD_DEFINE(sensor_shell_processing_tid, CONFIG_SENSOR_SHELL_THREAD_STACK_SIZE, + sensor_shell_processing_entry_point, NULL, NULL, NULL, 0, 0, 0); + static int cmd_sensor_attr_set(const struct shell *shell_ptr, size_t argc, char *argv[]) { const struct device *dev; @@ -424,10 +471,94 @@ static int cmd_sensor_attr_get(const struct shell *shell_ptr, size_t argc, char return 0; } -static void channel_name_get(size_t idx, struct shell_static_entry *entry); +static int cmd_sensor_stream(const struct shell *shell_ptr, size_t argc, char *argv[]) +{ + static struct rtio_sqe *current_streaming_handle; + static struct sensor_shell_processing_context ctx; + const struct device *dev = device_get_binding(argv[1]); + + if (dev == NULL) { + shell_error(shell_ptr, "Device unknown (%s)", argv[1]); + return -ENODEV; + } + if (current_streaming_handle != NULL) { + shell_print(shell_ptr, "Disabling existing stream"); + rtio_sqe_cancel(current_streaming_handle); + } + + if (strcmp("off", argv[2]) == 0) { + return 0; + } + + if (strcmp("on", argv[2]) != 0) { + shell_error(shell_ptr, "Unknown streaming operation (%s)", argv[2]); + return -EINVAL; + } + + shell_print(shell_ptr, "Enabling stream..."); + iodev_sensor_shell_stream_config.sensor = dev; + if (argc == 3) { + iodev_sensor_shell_stream_config.count = 1; + iodev_sensor_shell_trigger_types[0] = SENSOR_TRIG_DATA_READY; + } else { + iodev_sensor_shell_stream_config.count = argc - 3; + for (int i = 3; i < argc; ++i) { + int trigger = parse_named_int(argv[i], sensor_trigger_type_name, + ARRAY_SIZE(sensor_trigger_type_name)); + + if (trigger < 0) { + shell_error(shell_ptr, "Invalid trigger (%s)", argv[i]); + return -EINVAL; + } + iodev_sensor_shell_trigger_types[i - 3] = trigger; + } + } + + ctx.dev = dev; + ctx.sh = shell_ptr; + + int rc = sensor_stream(&iodev_sensor_shell_stream, &sensor_read_rtio, &ctx, + ¤t_streaming_handle); + + if (rc != 0) { + shell_error(shell_ptr, "Failed to start stream"); + } + return rc; +} + +static void channel_name_get(size_t idx, struct shell_static_entry *entry); SHELL_DYNAMIC_CMD_CREATE(dsub_channel_name, channel_name_get); +static void attribute_name_get(size_t idx, struct shell_static_entry *entry); +SHELL_DYNAMIC_CMD_CREATE(dsub_attribute_name, attribute_name_get); + +static void channel_name_get(size_t idx, struct shell_static_entry *entry) +{ + int cnt = 0; + + entry->syntax = NULL; + entry->handler = NULL; + entry->help = NULL; + if (current_cmd_ctx == CTX_GET) { + entry->subcmd = &dsub_channel_name; + } else if (current_cmd_ctx == CTX_ATTR_GET_SET) { + entry->subcmd = &dsub_attribute_name; + } else { + entry->subcmd = NULL; + } + + for (int i = 0; i < SENSOR_CHAN_ALL; i++) { + if (sensor_channel_name[i] != NULL) { + if (cnt == idx) { + entry->syntax = sensor_channel_name[i]; + break; + } + cnt++; + } + } +} + static void attribute_name_get(size_t idx, struct shell_static_entry *entry) { int cnt = 0; @@ -447,27 +578,23 @@ static void attribute_name_get(size_t idx, struct shell_static_entry *entry) } } } -SHELL_DYNAMIC_CMD_CREATE(dsub_attribute_name, attribute_name_get); -static void channel_name_get(size_t idx, struct shell_static_entry *entry) + +static void trigger_name_get(size_t idx, struct shell_static_entry *entry); +SHELL_DYNAMIC_CMD_CREATE(dsub_trigger_name, trigger_name_get); +static void trigger_name_get(size_t idx, struct shell_static_entry *entry) { int cnt = 0; entry->syntax = NULL; entry->handler = NULL; entry->help = NULL; - if (current_cmd_ctx == CTX_GET) { - entry->subcmd = &dsub_channel_name; - } else if (current_cmd_ctx == CTX_ATTR_GET_SET) { - entry->subcmd = &dsub_attribute_name; - } else { - entry->subcmd = NULL; - } + entry->subcmd = &dsub_trigger_name; - for (int i = 0; i < SENSOR_CHAN_ALL; i++) { - if (sensor_channel_name[i] != NULL) { + for (int i = 0; i < SENSOR_TRIG_COMMON_COUNT; i++) { + if (sensor_trigger_type_name[i] != NULL) { if (cnt == idx) { - entry->syntax = sensor_channel_name[i]; + entry->syntax = sensor_trigger_type_name[i]; break; } cnt++; @@ -475,6 +602,22 @@ static void channel_name_get(size_t idx, struct shell_static_entry *entry) } } +static void stream_on_off(size_t idx, struct shell_static_entry *entry) +{ + entry->syntax = NULL; + entry->handler = NULL; + entry->help = NULL; + + if (idx == 0) { + entry->syntax = "on"; + entry->subcmd = &dsub_trigger_name; + } else if (idx == 1) { + entry->syntax = "off"; + entry->subcmd = NULL; + } +} +SHELL_DYNAMIC_CMD_CREATE(dsub_stream_on_off, stream_on_off); + static void device_name_get(size_t idx, struct shell_static_entry *entry); SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get); @@ -502,6 +645,18 @@ static void device_name_get_for_attr(size_t idx, struct shell_static_entry *entr } SHELL_DYNAMIC_CMD_CREATE(dsub_device_name_for_attr, device_name_get_for_attr); +static void device_name_get_for_trig(size_t idx, struct shell_static_entry *entry) +{ + const struct device *dev = shell_device_lookup(idx, NULL); + + current_cmd_ctx = CTX_STREAM_ON_OFF; + entry->syntax = (dev != NULL) ? dev->name : NULL; + entry->handler = NULL; + entry->help = NULL; + entry->subcmd = &dsub_stream_on_off; +} +SHELL_DYNAMIC_CMD_CREATE(dsub_device_name_for_trig, device_name_get_for_trig); + static int cmd_get_sensor_info(const struct shell *sh, size_t argc, char **argv) { ARG_UNUSED(argc); @@ -533,6 +688,8 @@ SHELL_STATIC_SUBCMD_SET_CREATE(sub_sensor, cmd_sensor_attr_set, 2, 255), SHELL_CMD_ARG(attr_get, &dsub_device_name_for_attr, SENSOR_ATTR_GET_HELP, cmd_sensor_attr_get, 2, 255), + SHELL_CMD_ARG(stream, &dsub_device_name_for_trig, SENSOR_STREAM_HELP, + cmd_sensor_stream, 2, 255), SHELL_COND_CMD(CONFIG_SENSOR_INFO, info, NULL, SENSOR_INFO_HELP, cmd_get_sensor_info), SHELL_SUBCMD_SET_END diff --git a/include/zephyr/drivers/sensor.h b/include/zephyr/drivers/sensor.h index 84a610da6961f39..69ea3a25a66a73c 100644 --- a/include/zephyr/drivers/sensor.h +++ b/include/zephyr/drivers/sensor.h @@ -245,6 +245,9 @@ enum sensor_trigger_type { /** Trigger fires when no motion has been detected for a while. */ SENSOR_TRIG_STATIONARY, + + SENSOR_TRIG_FIFO_THRESHOLD, + SENSOR_TRIG_FIFO_FULL, /** * Number of all common sensor triggers. */ @@ -516,7 +519,11 @@ typedef int (*sensor_get_decoder_t)(const struct device *dev, */ struct sensor_read_config { const struct device *sensor; - enum sensor_channel *const channels; + const bool is_streaming; + union { + enum sensor_channel *const channels; + enum sensor_trigger_type *const triggers; + }; size_t count; const size_t max; }; @@ -539,12 +546,24 @@ struct sensor_read_config { static enum sensor_channel __channel_array_##name[] = {__VA_ARGS__}; \ static struct sensor_read_config __sensor_read_config_##name = { \ .sensor = DEVICE_DT_GET(dt_node), \ + .is_streaming = false, \ .channels = __channel_array_##name, \ .count = ARRAY_SIZE(__channel_array_##name), \ .max = ARRAY_SIZE(__channel_array_##name), \ }; \ RTIO_IODEV_DEFINE(name, &__sensor_iodev_api, &__sensor_read_config_##name) +#define SENSOR_DT_STREAM_IODEV(name, dt_node, ...) \ + static enum sensor_trigger_type __trigger_array_##name[] = {__VA_ARGS__}; \ + static struct sensor_read_config __sensor_read_config_##name = { \ + .sensor = DEVICE_DT_GET(dt_node), \ + .is_streaming = true, \ + .triggers = __trigger_array_##name, \ + .count = ARRAY_SIZE(__trigger_array_##name), \ + .max = ARRAY_SIZE(__trigger_array_##name), \ + }; \ + RTIO_IODEV_DEFINE(name &__sensor_iodev_api, &__sensor_read_config_##name) + /* Used to submit an RTIO sqe to the sensor's iodev */ typedef int (*sensor_submit_t)(const struct device *sensor, struct rtio_iodev_sqe *sqe); @@ -845,7 +864,7 @@ static inline int z_impl_sensor_reconfigure_read_iodev(struct rtio_iodev *iodev, { struct sensor_read_config *cfg = (struct sensor_read_config *)iodev->data; - if (cfg->max < num_channels) { + if (cfg->max < num_channels || cfg->is_streaming) { return -ENOMEM; } @@ -853,6 +872,28 @@ static inline int z_impl_sensor_reconfigure_read_iodev(struct rtio_iodev *iodev, memcpy(cfg->channels, channels, num_channels * sizeof(enum sensor_channel)); cfg->count = num_channels; return 0; +} + +static inline int sensor_stream(struct rtio_iodev *iodev, struct rtio *ctx, void *userdata, + struct rtio_sqe **handle) +{ + if (IS_ENABLED(CONFIG_USERSPACE)) { + struct rtio_sqe sqe; + + rtio_sqe_prep_read_multishot(&sqe, iodev, RTIO_PRIO_NORM, userdata); + rtio_sqe_copy_in_get_handles(ctx, &sqe, handle, 1); + } else { + struct rtio_sqe *sqe = rtio_sqe_acquire(ctx); + + if (sqe == NULL) { + return -ENOMEM; + } + if (handle != NULL) { + *handle = sqe; + } + rtio_sqe_prep_read_multishot(sqe, iodev, RTIO_PRIO_NORM, userdata); + } + rtio_submit(ctx, 0); return 0; } diff --git a/samples/sensor/sensor_shell/prj.conf b/samples/sensor/sensor_shell/prj.conf index d3189e39aeac769..b9c823bc9cddf5c 100644 --- a/samples/sensor/sensor_shell/prj.conf +++ b/samples/sensor/sensor_shell/prj.conf @@ -5,4 +5,6 @@ CONFIG_SENSOR_SHELL=y CONFIG_SENSOR_INFO=y CONFIG_LOG=y +CONFIG_ICM42688_TRIGGER_NONE=y +# CONFIG_ADC_ADS7052_INIT_PRIORITY=99 CONFIG_RTIO_CONSUME_SEM=y