Skip to content

Commit

Permalink
icm42688: Implement streaming APIs
Browse files Browse the repository at this point in the history
Add streaming implementation for icm42688 using both threshold and
full FIFO triggers.

Signed-off-by: Yuval Peress <peress@google.com>
  • Loading branch information
yperess committed Jul 26, 2023
1 parent d058365 commit f707e7a
Show file tree
Hide file tree
Showing 12 changed files with 562 additions and 12 deletions.
1 change: 1 addition & 0 deletions drivers/sensor/icm42688/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ zephyr_library_sources(

zephyr_library_sources_ifdef(CONFIG_SENSOR_ASYNC_API icm42688_rtio.c)
zephyr_library_sources_ifdef(CONFIG_ICM42688_DECODER icm42688_decoder.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 .)
10 changes: 10 additions & 0 deletions drivers/sensor/icm42688/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ if ICM42688

choice
prompt "Trigger mode"
default ICM42688_TRIGGER_NONE if ICM42688_STREAM
default ICM42688_TRIGGER_GLOBAL_THREAD
help
Specify the type of triggering to be used by the driver
Expand All @@ -50,6 +51,15 @@ 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
depends on SENSOR_ASYNC_API
help
Use this config option to enable streaming sensor data via RTIO subsystem.

config ICM42688_TRIGGER
bool

Expand Down
24 changes: 22 additions & 2 deletions drivers/sensor/icm42688/icm42688.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,19 @@ static int icm42688_attr_set(const struct device *dev, enum sensor_channel chan,
res = -EINVAL;
}
break;
case SENSOR_CHAN_ALL:
if (attr == SENSOR_ATTR_FIFO_WATERMARK) {
int64_t mval = sensor_value_to_micro(val);

if (mval < 0 || mval > 1000000) {
return -EINVAL;
}
new_config.fifo_wm = CLAMP(mval * 2048 / 1000000, 0, 2048);
} else {
LOG_ERR("Unsupported attribute");
res = -EINVAL;
}
break;
default:
LOG_ERR("Unsupported channel");
res = -EINVAL;
Expand Down Expand Up @@ -258,7 +271,7 @@ int icm42688_init(const struct device *dev)
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 = false;
data->cfg.fifo_en = IS_ENABLED(CONFIG_ICM42688_STREAM);

res = icm42688_configure(dev, &data->cfg);
if (res != 0) {
Expand All @@ -284,8 +297,15 @@ 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) \
static struct icm42688_dev_data icm42688_driver_##inst;
IF_ENABLED(CONFIG_ICM42688_STREAM, (ICM42688_RTIO_DEFINE(inst))); \
static struct icm42688_dev_data icm42688_driver_##inst = { \
IF_ENABLED(CONFIG_ICM42688_STREAM, (.r = &icm42688_rtio_##inst, \
.spi_iodev = &icm42688_spi_iodev_##inst,))};

#define ICM42688_INIT(inst) \
ICM42688_DEFINE_DATA(inst); \
Expand Down
12 changes: 12 additions & 0 deletions drivers/sensor/icm42688/icm42688.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -405,6 +408,15 @@ 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;
uint64_t timestamp;
atomic_t reading_fifo;
#endif /* CONFIG_ICM42688_STREAM */
const struct device *dev;
struct gpio_callback gpio_cb;
sensor_trigger_handler_t data_ready_handler;
Expand Down
9 changes: 7 additions & 2 deletions drivers/sensor/icm42688/icm42688_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "icm42688.h"
#include "icm42688_reg.h"
#include "icm42688_spi.h"
#include "icm42688_trigger.h"

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(ICM42688_LL, CONFIG_SENSOR_LOG_LEVEL);
Expand Down Expand Up @@ -165,8 +166,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;
Expand Down
157 changes: 155 additions & 2 deletions drivers/sensor/icm42688/icm42688_decoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,98 @@ int icm42688_encode(const struct device *dev, const enum sensor_channel *const c
return 0;
}

static int icm42688_fifo_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)
{
const struct icm42688_fifo_data *edata = (const struct icm42688_fifo_data *)buffer;

if (edata->fifo_count <= *fit) {
return 0;
}

/* Jump to the frame start */
buffer += sizeof(struct icm42688_fifo_data) + *fit;

const bool is_20b = FIELD_GET(FIFO_HEADER_20, buffer[0]);
const bool has_accel = FIELD_GET(FIFO_HEADER_ACCEL, buffer[0]);
const bool has_gyro = FIELD_GET(FIFO_HEADER_GYRO, buffer[0]);
const int channel_count = 1 + (has_accel ? 3 : 0) + (has_gyro ? 3 : 0);
const uint8_t *frame_end = buffer + /* header */ 1 + /* accel */ (has_accel ? 6 : 0) +
/* gyro */ (has_gyro ? 6 : 0) +
/* timestamp */ (has_accel && has_gyro ? 2 : 0) +
/* temperature */ (is_20b ? 2 : 1);
int header_channels[7];
int count = 0;

if (*cit >= channel_count) {
return -1;
}

if (has_accel) {
header_channels[count++] = SENSOR_CHAN_ACCEL_X;
header_channels[count++] = SENSOR_CHAN_ACCEL_Y;
header_channels[count++] = SENSOR_CHAN_ACCEL_Z;
}
if (has_gyro) {
header_channels[count++] = SENSOR_CHAN_GYRO_X;
header_channels[count++] = SENSOR_CHAN_GYRO_Y;
header_channels[count++] = SENSOR_CHAN_GYRO_Z;
}
header_channels[count] = SENSOR_CHAN_DIE_TEMP;
count = 0;

while (count < max_count && *cit < channel_count) {
/*
* [0]@0 - accel_x
* [1]@2 - accel_y
* [2]@4 - accel_z
* [3]@6 - gyro_x
* [4]@8 - gyro_y
* [5]@a - gyro_z
* [6]@c - temp
*/
int offset = 1 + *cit * 2;

channels[count] = header_channels[*cit];
int32_t value;
int32_t value_max = INT16_MAX;

if (channels[count] == SENSOR_CHAN_DIE_TEMP) {
value = buffer[offset];
if (is_20b) {
value |= (buffer[offset + 1] << 8);
} else {
value_max = INT8_MAX;
}
} else {
value = (buffer[offset] << 8) | buffer[offset + 1];
if (is_20b) {
int mask = channels[count] < SENSOR_CHAN_ACCEL_XYZ ? GENMASK(7, 4)
: GENMASK(3, 0);
/* Get the offset after the end of the frame data */
int ext_offset =
(channels[count] - (channels[count] < SENSOR_CHAN_ACCEL_XYZ
? SENSOR_CHAN_ACCEL_X
: SENSOR_CHAN_GYRO_X));

value = (value << 4) | FIELD_GET(mask, frame_end[ext_offset]);
value_max = (1 << 19) - 1;
}
}
values[count] = (q31_t)((int64_t)value * INT32_MAX / value_max);
count++;
*cit += 1;
}

if (*cit == channel_count) {
/* Reached the end of the frame */
*fit += (uintptr_t)frame_end - (uintptr_t)buffer + (is_20b ? 3 : 0);
*cit = 0;
}
return count;
}

static int icm42688_one_shot_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)
Expand Down Expand Up @@ -271,13 +363,53 @@ static int icm42688_decoder_decode(const uint8_t *buffer, sensor_frame_iterator_
sensor_channel_iterator_t *cit, enum sensor_channel *channels,
q31_t *values, uint8_t max_count)
{
const struct icm42688_decoder_header *header =
(const struct icm42688_decoder_header *)buffer;

if (header->is_fifo) {
return icm42688_fifo_decode(buffer, fit, cit, channels, values, max_count);
}
return icm42688_one_shot_decode(buffer, fit, cit, channels, values, max_count);
}

static int icm42688_decoder_get_frame_count(const uint8_t *buffer, uint16_t *frame_count)
{
ARG_UNUSED(buffer);
*frame_count = 1;
const struct icm42688_fifo_data *data = (const struct icm42688_fifo_data *)buffer;
const struct icm42688_decoder_header *header = &data->header;

if (!header->is_fifo) {
*frame_count = 1;
return 0;
}

/* Skip the header */
buffer += sizeof(struct icm42688_fifo_data);

uint16_t count = 0;
const uint8_t *end = buffer + data->fifo_count;

while (buffer < end) {
bool is_20b = FIELD_GET(FIFO_HEADER_20, buffer[0]);
int size = is_20b ? 3 : 2;

if (FIELD_GET(FIFO_HEADER_ACCEL, buffer[0])) {
size += 6;
}
if (FIELD_GET(FIFO_HEADER_GYRO, buffer[0])) {
size += 6;
}
if (FIELD_GET(FIFO_HEADER_TIMESTAMP_FSYNC, buffer[0])) {
size += 2;
}
if (is_20b) {
size += 3;
}

buffer += size;
++count;
}

*frame_count = count;
return 0;
}

Expand All @@ -290,6 +422,26 @@ static int icm42688_decoder_get_timestamp(const uint8_t *buffer, uint64_t *times
return 0;
}

static bool icm24688_decoder_has_trigger(const uint8_t *buffer, enum sensor_trigger_type trigger)
{
const struct icm42688_fifo_data *edata = (const struct icm42688_fifo_data *)buffer;

if (!edata->header.is_fifo) {
return false;
}

switch (trigger) {
case SENSOR_TRIG_DATA_READY:
return FIELD_GET(BIT_INT_STATUS_DATA_RDY, edata->int_status);
case SENSOR_TRIG_FIFO_THRESHOLD:
return FIELD_GET(BIT_INT_STATUS_FIFO_THS, edata->int_status);
case SENSOR_TRIG_FIFO_FULL:
return FIELD_GET(BIT_INT_STATUS_FIFO_FULL, edata->int_status);
default:
return false;
}
}

static int icm42688_decoder_get_shift(const uint8_t *buffer, enum sensor_channel channel_type,
int8_t *shift)
{
Expand All @@ -303,6 +455,7 @@ 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,
.has_trigger = icm24688_decoder_has_trigger,
.decode = icm42688_decoder_decode,
};

Expand Down
9 changes: 9 additions & 0 deletions drivers/sensor/icm42688/icm42688_decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ struct icm42688_decoder_header {
uint8_t reserved: 2;
} __attribute__((__packed__));

struct icm42688_fifo_data {
struct icm42688_decoder_header header;
uint8_t int_status;
uint16_t gyro_odr: 4;
uint16_t accel_odr: 4;
uint16_t fifo_count: 11;
uint16_t reserved: 5;
} __attribute__((__packed__));

struct icm42688_encoded_data {
struct icm42688_decoder_header header;
struct {
Expand Down
8 changes: 8 additions & 0 deletions drivers/sensor/icm42688/icm42688_reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,4 +286,12 @@
#define MCLK_POLL_ATTEMPTS 100
#define SOFT_RESET_TIME_MS 2 /* 1ms + elbow room */

/* FIFO header */
#define FIFO_HEADER_ACCEL BIT(6)
#define FIFO_HEADER_GYRO BIT(5)
#define FIFO_HEADER_20 BIT(4)
#define FIFO_HEADER_TIMESTAMP_FSYNC GENMASK(3, 2)
#define FIFO_HEADER_ODR_ACCEL BIT(1)
#define FIFO_HEADER_ODR_GYRO BIT(0)

#endif /* ZEPHYR_DRIVERS_SENSOR_ICM42688_REG_H_ */
Loading

0 comments on commit f707e7a

Please sign in to comment.