Fix the devicetree bindings to actually be used as the default configuration, following the example set by various ST sensor devices. This requires sadly dropping enums and using #defines for various options as well as repeating many numbers, but presumably is the way to do it given the precedent set by ST with sensors like the lsm6dso. Signed-off-by: Tom Burdick <thomas.burdick@intel.com>
702 lines
19 KiB
C
702 lines
19 KiB
C
/*
|
|
* Copyright (c) 2023 Google LLC
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include "icm42688_decoder.h"
|
|
#include "icm42688_reg.h"
|
|
#include "icm42688.h"
|
|
#include <errno.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(ICM42688_DECODER, CONFIG_SENSOR_LOG_LEVEL);
|
|
|
|
#define DT_DRV_COMPAT invensense_icm42688
|
|
|
|
static int icm42688_get_shift(enum sensor_channel channel, int accel_fs, int gyro_fs, int8_t *shift)
|
|
{
|
|
switch (channel) {
|
|
case SENSOR_CHAN_ACCEL_XYZ:
|
|
case SENSOR_CHAN_ACCEL_X:
|
|
case SENSOR_CHAN_ACCEL_Y:
|
|
case SENSOR_CHAN_ACCEL_Z:
|
|
switch (accel_fs) {
|
|
case ICM42688_DT_ACCEL_FS_2:
|
|
*shift = 5;
|
|
return 0;
|
|
case ICM42688_DT_ACCEL_FS_4:
|
|
*shift = 6;
|
|
return 0;
|
|
case ICM42688_DT_ACCEL_FS_8:
|
|
*shift = 7;
|
|
return 0;
|
|
case ICM42688_DT_ACCEL_FS_16:
|
|
*shift = 8;
|
|
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 (gyro_fs) {
|
|
case ICM42688_DT_GYRO_FS_15_625:
|
|
*shift = -1;
|
|
return 0;
|
|
case ICM42688_DT_GYRO_FS_31_25:
|
|
*shift = 0;
|
|
return 0;
|
|
case ICM42688_DT_GYRO_FS_62_5:
|
|
*shift = 1;
|
|
return 0;
|
|
case ICM42688_DT_GYRO_FS_125:
|
|
*shift = 2;
|
|
return 0;
|
|
case ICM42688_DT_GYRO_FS_250:
|
|
*shift = 3;
|
|
return 0;
|
|
case ICM42688_DT_GYRO_FS_500:
|
|
*shift = 4;
|
|
return 0;
|
|
case ICM42688_DT_GYRO_FS_1000:
|
|
*shift = 5;
|
|
return 0;
|
|
case ICM42688_DT_GYRO_FS_2000:
|
|
*shift = 6;
|
|
return 0;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
case SENSOR_CHAN_DIE_TEMP:
|
|
*shift = 9;
|
|
return 0;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
int icm42688_convert_raw_to_q31(struct icm42688_cfg *cfg, enum sensor_channel chan, int32_t reading,
|
|
q31_t *out)
|
|
{
|
|
int32_t whole;
|
|
int32_t fraction;
|
|
int64_t intermediate;
|
|
int8_t shift;
|
|
int rc;
|
|
|
|
rc = icm42688_get_shift(chan, cfg->accel_fs, cfg->gyro_fs, &shift);
|
|
if (rc != 0) {
|
|
return rc;
|
|
}
|
|
|
|
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);
|
|
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);
|
|
break;
|
|
case SENSOR_CHAN_DIE_TEMP:
|
|
icm42688_temp_c(reading, &whole, &fraction);
|
|
break;
|
|
default:
|
|
return -ENOTSUP;
|
|
}
|
|
intermediate = ((int64_t)whole * INT64_C(1000000) + fraction);
|
|
if (shift < 0) {
|
|
intermediate =
|
|
intermediate * ((int64_t)INT32_MAX + 1) * (1 << -shift) / INT64_C(1000000);
|
|
} else if (shift > 0) {
|
|
intermediate =
|
|
intermediate * ((int64_t)INT32_MAX + 1) / ((1 << shift) * INT64_C(1000000));
|
|
}
|
|
*out = CLAMP(intermediate, INT32_MIN, INT32_MAX);
|
|
|
|
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:
|
|
encode_bmask = BIT(icm42688_get_channel_position(SENSOR_CHAN_ACCEL_X)) |
|
|
BIT(icm42688_get_channel_position(SENSOR_CHAN_ACCEL_Y)) |
|
|
BIT(icm42688_get_channel_position(SENSOR_CHAN_ACCEL_Z));
|
|
break;
|
|
case SENSOR_CHAN_GYRO_XYZ:
|
|
encode_bmask = BIT(icm42688_get_channel_position(SENSOR_CHAN_GYRO_X)) |
|
|
BIT(icm42688_get_channel_position(SENSOR_CHAN_GYRO_Y)) |
|
|
BIT(icm42688_get_channel_position(SENSOR_CHAN_GYRO_Z));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return encode_bmask;
|
|
}
|
|
|
|
int icm42688_encode(const struct device *dev, const struct sensor_chan_spec *const channels,
|
|
const size_t num_channels, uint8_t *buf)
|
|
{
|
|
struct icm42688_dev_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].chan_type);
|
|
}
|
|
|
|
edata->header.is_fifo = false;
|
|
edata->header.accel_fs = data->cfg.accel_fs;
|
|
edata->header.gyro_fs = data->cfg.gyro_fs;
|
|
edata->header.timestamp = k_ticks_to_ns_floor64(k_uptime_ticks());
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define IS_ACCEL(chan) ((chan) >= SENSOR_CHAN_ACCEL_X && (chan) <= SENSOR_CHAN_ACCEL_XYZ)
|
|
#define IS_GYRO(chan) ((chan) >= SENSOR_CHAN_GYRO_X && (chan) <= SENSOR_CHAN_GYRO_XYZ)
|
|
|
|
static inline q31_t icm42688_read_temperature_from_packet(const uint8_t *pkt)
|
|
{
|
|
int32_t temperature;
|
|
int32_t whole;
|
|
int32_t fraction;
|
|
|
|
/* Temperature always assumes a shift of 9 for a range of (-273,273) C */
|
|
if (FIELD_GET(FIFO_HEADER_20, pkt[0]) == 1) {
|
|
temperature = (pkt[0xd] << 8) | pkt[0xe];
|
|
|
|
icm42688_temp_c(temperature, &whole, &fraction);
|
|
} else {
|
|
if (FIELD_GET(FIFO_HEADER_ACCEL, pkt[0]) == 1 &&
|
|
FIELD_GET(FIFO_HEADER_GYRO, pkt[0]) == 1) {
|
|
temperature = pkt[0xd];
|
|
} else {
|
|
temperature = pkt[0x7];
|
|
}
|
|
|
|
int64_t sensitivity = 207;
|
|
int64_t temperature100 = (temperature * 100) + (25 * sensitivity);
|
|
|
|
whole = temperature100 / sensitivity;
|
|
fraction =
|
|
((temperature100 - whole * sensitivity) * INT64_C(1000000)) / sensitivity;
|
|
}
|
|
__ASSERT_NO_MSG(whole >= -512 && whole <= 511);
|
|
return FIELD_PREP(GENMASK(31, 22), whole) | (fraction * GENMASK64(21, 0) / 1000000);
|
|
}
|
|
|
|
static int icm42688_read_imu_from_packet(const uint8_t *pkt, bool is_accel, int fs,
|
|
uint8_t axis_offset, q31_t *out)
|
|
{
|
|
int32_t value;
|
|
int64_t scale = 0;
|
|
int32_t max = BIT(15);
|
|
int offset = 1 + (axis_offset * 2);
|
|
|
|
if (is_accel) {
|
|
switch (fs) {
|
|
case ICM42688_DT_ACCEL_FS_2:
|
|
scale = INT64_C(2) * BIT(31 - 5) * 9.80665;
|
|
break;
|
|
case ICM42688_DT_ACCEL_FS_4:
|
|
scale = INT64_C(4) * BIT(31 - 6) * 9.80665;
|
|
break;
|
|
case ICM42688_DT_ACCEL_FS_8:
|
|
scale = INT64_C(8) * BIT(31 - 7) * 9.80665;
|
|
break;
|
|
case ICM42688_DT_ACCEL_FS_16:
|
|
scale = INT64_C(16) * BIT(31 - 8) * 9.80665;
|
|
break;
|
|
}
|
|
} else {
|
|
switch (fs) {
|
|
case ICM42688_DT_GYRO_FS_2000:
|
|
scale = 164;
|
|
break;
|
|
case ICM42688_DT_GYRO_FS_1000:
|
|
scale = 328;
|
|
break;
|
|
case ICM42688_DT_GYRO_FS_500:
|
|
scale = 655;
|
|
break;
|
|
case ICM42688_DT_GYRO_FS_250:
|
|
scale = 1310;
|
|
break;
|
|
case ICM42688_DT_GYRO_FS_125:
|
|
scale = 2620;
|
|
break;
|
|
case ICM42688_DT_GYRO_FS_62_5:
|
|
scale = 5243;
|
|
break;
|
|
case ICM42688_DT_GYRO_FS_31_25:
|
|
scale = 10486;
|
|
break;
|
|
case ICM42688_DT_GYRO_FS_15_625:
|
|
scale = 20972;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!is_accel && FIELD_GET(FIFO_HEADER_ACCEL, pkt[0]) == 1) {
|
|
offset += 7;
|
|
}
|
|
|
|
value = (int16_t)sys_le16_to_cpu((pkt[offset] << 8) | pkt[offset + 1]);
|
|
|
|
if (FIELD_GET(FIFO_HEADER_20, pkt[0]) == 1) {
|
|
uint32_t mask = is_accel ? GENMASK(7, 4) : GENMASK(3, 0);
|
|
|
|
offset = 0x11 + axis_offset;
|
|
value = (value << 4) | FIELD_GET(mask, pkt[offset]);
|
|
/* In 20 bit mode, FS can only be +/-16g and +/-2000dps */
|
|
scale = is_accel ? (INT64_C(16) * BIT(8) * 9.80665) : 131;
|
|
max = is_accel ? BIT(18) : BIT(19);
|
|
if (value == -524288) {
|
|
/* Invalid 20 bit value */
|
|
return -ENODATA;
|
|
}
|
|
} else {
|
|
if (value <= -32767) {
|
|
/* Invalid 16 bit value */
|
|
return -ENODATA;
|
|
}
|
|
}
|
|
|
|
*out = (q31_t)(value * scale / max);
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t accel_period_ns[] = {
|
|
[ICM42688_DT_ACCEL_ODR_1_5625] = UINT32_C(10000000000000) / 15625,
|
|
[ICM42688_DT_ACCEL_ODR_3_125] = UINT32_C(10000000000000) / 31250,
|
|
[ICM42688_DT_ACCEL_ODR_6_25] = UINT32_C(10000000000000) / 62500,
|
|
[ICM42688_DT_ACCEL_ODR_12_5] = UINT32_C(10000000000000) / 12500,
|
|
[ICM42688_DT_ACCEL_ODR_25] = UINT32_C(1000000000) / 25,
|
|
[ICM42688_DT_ACCEL_ODR_50] = UINT32_C(1000000000) / 50,
|
|
[ICM42688_DT_ACCEL_ODR_100] = UINT32_C(1000000000) / 100,
|
|
[ICM42688_DT_ACCEL_ODR_200] = UINT32_C(1000000000) / 200,
|
|
[ICM42688_DT_ACCEL_ODR_500] = UINT32_C(1000000000) / 500,
|
|
[ICM42688_DT_ACCEL_ODR_1000] = UINT32_C(1000000),
|
|
[ICM42688_DT_ACCEL_ODR_2000] = UINT32_C(1000000) / 2,
|
|
[ICM42688_DT_ACCEL_ODR_4000] = UINT32_C(1000000) / 4,
|
|
[ICM42688_DT_ACCEL_ODR_8000] = UINT32_C(1000000) / 8,
|
|
[ICM42688_DT_ACCEL_ODR_16000] = UINT32_C(1000000) / 16,
|
|
[ICM42688_DT_ACCEL_ODR_32000] = UINT32_C(1000000) / 32,
|
|
};
|
|
|
|
static uint32_t gyro_period_ns[] = {
|
|
[ICM42688_DT_GYRO_ODR_12_5] = UINT32_C(10000000000000) / 12500,
|
|
[ICM42688_DT_GYRO_ODR_25] = UINT32_C(1000000000) / 25,
|
|
[ICM42688_DT_GYRO_ODR_50] = UINT32_C(1000000000) / 50,
|
|
[ICM42688_DT_GYRO_ODR_100] = UINT32_C(1000000000) / 100,
|
|
[ICM42688_DT_GYRO_ODR_200] = UINT32_C(1000000000) / 200,
|
|
[ICM42688_DT_GYRO_ODR_500] = UINT32_C(1000000000) / 500,
|
|
[ICM42688_DT_GYRO_ODR_1000] = UINT32_C(1000000),
|
|
[ICM42688_DT_GYRO_ODR_2000] = UINT32_C(1000000) / 2,
|
|
[ICM42688_DT_GYRO_ODR_4000] = UINT32_C(1000000) / 4,
|
|
[ICM42688_DT_GYRO_ODR_8000] = UINT32_C(1000000) / 8,
|
|
[ICM42688_DT_GYRO_ODR_16000] = UINT32_C(1000000) / 16,
|
|
[ICM42688_DT_GYRO_ODR_32000] = UINT32_C(1000000) / 32,
|
|
};
|
|
|
|
static int icm42688_fifo_decode(const uint8_t *buffer, struct sensor_chan_spec chan_spec,
|
|
uint32_t *fit, uint16_t max_count, void *data_out)
|
|
{
|
|
const struct icm42688_fifo_data *edata = (const struct icm42688_fifo_data *)buffer;
|
|
const uint8_t *buffer_end = buffer + sizeof(struct icm42688_fifo_data) + edata->fifo_count;
|
|
int accel_frame_count = 0;
|
|
int gyro_frame_count = 0;
|
|
int count = 0;
|
|
int rc;
|
|
|
|
if ((uintptr_t)buffer_end <= *fit || chan_spec.chan_idx != 0) {
|
|
return 0;
|
|
}
|
|
|
|
((struct sensor_data_header *)data_out)->base_timestamp_ns = edata->header.timestamp;
|
|
|
|
buffer += sizeof(struct icm42688_fifo_data);
|
|
while (count < max_count && buffer < buffer_end) {
|
|
const bool is_20b = FIELD_GET(FIFO_HEADER_20, buffer[0]) == 1;
|
|
const bool has_accel = FIELD_GET(FIFO_HEADER_ACCEL, buffer[0]) == 1;
|
|
const bool has_gyro = FIELD_GET(FIFO_HEADER_GYRO, buffer[0]) == 1;
|
|
const uint8_t *frame_end = buffer;
|
|
|
|
if (is_20b) {
|
|
frame_end += 20;
|
|
} else if (has_accel && has_gyro) {
|
|
frame_end += 16;
|
|
} else {
|
|
frame_end += 8;
|
|
}
|
|
if (has_accel) {
|
|
accel_frame_count++;
|
|
}
|
|
if (has_gyro) {
|
|
gyro_frame_count++;
|
|
}
|
|
|
|
if ((uintptr_t)buffer < *fit) {
|
|
/* This frame was already decoded, move on to the next frame */
|
|
buffer = frame_end;
|
|
continue;
|
|
}
|
|
if (chan_spec.chan_type == SENSOR_CHAN_DIE_TEMP) {
|
|
struct sensor_q31_data *data = (struct sensor_q31_data *)data_out;
|
|
|
|
data->shift = 9;
|
|
if (has_accel) {
|
|
data->readings[count].timestamp_delta =
|
|
accel_period_ns[edata->accel_odr] * (accel_frame_count - 1);
|
|
} else {
|
|
data->readings[count].timestamp_delta =
|
|
gyro_period_ns[edata->gyro_odr] * (gyro_frame_count - 1);
|
|
}
|
|
data->readings[count].temperature =
|
|
icm42688_read_temperature_from_packet(buffer);
|
|
} else if (IS_ACCEL(chan_spec.chan_type) && has_accel) {
|
|
/* Decode accel */
|
|
struct sensor_three_axis_data *data =
|
|
(struct sensor_three_axis_data *)data_out;
|
|
uint64_t period_ns = accel_period_ns[edata->accel_odr];
|
|
|
|
icm42688_get_shift(SENSOR_CHAN_ACCEL_XYZ, edata->header.accel_fs,
|
|
edata->header.gyro_fs, &data->shift);
|
|
|
|
data->readings[count].timestamp_delta = (accel_frame_count - 1) * period_ns;
|
|
rc = icm42688_read_imu_from_packet(buffer, true, edata->header.accel_fs, 0,
|
|
&data->readings[count].x);
|
|
rc |= icm42688_read_imu_from_packet(buffer, true, edata->header.accel_fs, 1,
|
|
&data->readings[count].y);
|
|
rc |= icm42688_read_imu_from_packet(buffer, true, edata->header.accel_fs, 2,
|
|
&data->readings[count].z);
|
|
if (rc != 0) {
|
|
accel_frame_count--;
|
|
buffer = frame_end;
|
|
continue;
|
|
}
|
|
} else if (IS_GYRO(chan_spec.chan_type) && has_gyro) {
|
|
/* Decode gyro */
|
|
struct sensor_three_axis_data *data =
|
|
(struct sensor_three_axis_data *)data_out;
|
|
uint64_t period_ns = accel_period_ns[edata->gyro_odr];
|
|
|
|
icm42688_get_shift(SENSOR_CHAN_GYRO_XYZ, edata->header.accel_fs,
|
|
edata->header.gyro_fs, &data->shift);
|
|
|
|
data->readings[count].timestamp_delta = (gyro_frame_count - 1) * period_ns;
|
|
rc = icm42688_read_imu_from_packet(buffer, false, edata->header.gyro_fs, 0,
|
|
&data->readings[count].x);
|
|
rc |= icm42688_read_imu_from_packet(buffer, false, edata->header.gyro_fs, 1,
|
|
&data->readings[count].y);
|
|
rc |= icm42688_read_imu_from_packet(buffer, false, edata->header.gyro_fs, 2,
|
|
&data->readings[count].z);
|
|
if (rc != 0) {
|
|
gyro_frame_count--;
|
|
buffer = frame_end;
|
|
continue;
|
|
}
|
|
}
|
|
buffer = frame_end;
|
|
*fit = (uintptr_t)frame_end;
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static int icm42688_one_shot_decode(const uint8_t *buffer, struct sensor_chan_spec chan_spec,
|
|
uint32_t *fit, uint16_t max_count, void *data_out)
|
|
{
|
|
const struct icm42688_encoded_data *edata = (const struct icm42688_encoded_data *)buffer;
|
|
const struct icm42688_decoder_header *header = &edata->header;
|
|
struct icm42688_cfg cfg = {
|
|
.accel_fs = edata->header.accel_fs,
|
|
.gyro_fs = edata->header.gyro_fs,
|
|
};
|
|
uint8_t channel_request;
|
|
int rc;
|
|
|
|
if (*fit != 0) {
|
|
return 0;
|
|
}
|
|
if (max_count == 0 || chan_spec.chan_idx != 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (chan_spec.chan_type) {
|
|
case SENSOR_CHAN_ACCEL_X:
|
|
case SENSOR_CHAN_ACCEL_Y:
|
|
case SENSOR_CHAN_ACCEL_Z:
|
|
case SENSOR_CHAN_ACCEL_XYZ: {
|
|
channel_request = icm42688_encode_channel(SENSOR_CHAN_ACCEL_XYZ);
|
|
if ((channel_request & edata->channels) != channel_request) {
|
|
return -ENODATA;
|
|
}
|
|
|
|
struct sensor_three_axis_data *out = data_out;
|
|
|
|
out->header.base_timestamp_ns = edata->header.timestamp;
|
|
out->header.reading_count = 1;
|
|
rc = icm42688_get_shift(SENSOR_CHAN_ACCEL_XYZ, header->accel_fs, header->gyro_fs,
|
|
&out->shift);
|
|
if (rc != 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
icm42688_convert_raw_to_q31(
|
|
&cfg, SENSOR_CHAN_ACCEL_X,
|
|
edata->readings[icm42688_get_channel_position(SENSOR_CHAN_ACCEL_X)],
|
|
&out->readings[0].x);
|
|
icm42688_convert_raw_to_q31(
|
|
&cfg, SENSOR_CHAN_ACCEL_Y,
|
|
edata->readings[icm42688_get_channel_position(SENSOR_CHAN_ACCEL_Y)],
|
|
&out->readings[0].y);
|
|
icm42688_convert_raw_to_q31(
|
|
&cfg, SENSOR_CHAN_ACCEL_Z,
|
|
edata->readings[icm42688_get_channel_position(SENSOR_CHAN_ACCEL_Z)],
|
|
&out->readings[0].z);
|
|
*fit = 1;
|
|
return 1;
|
|
}
|
|
case SENSOR_CHAN_GYRO_X:
|
|
case SENSOR_CHAN_GYRO_Y:
|
|
case SENSOR_CHAN_GYRO_Z:
|
|
case SENSOR_CHAN_GYRO_XYZ: {
|
|
channel_request = icm42688_encode_channel(SENSOR_CHAN_GYRO_XYZ);
|
|
if ((channel_request & edata->channels) != channel_request) {
|
|
return -ENODATA;
|
|
}
|
|
|
|
struct sensor_three_axis_data *out = data_out;
|
|
|
|
out->header.base_timestamp_ns = edata->header.timestamp;
|
|
out->header.reading_count = 1;
|
|
rc = icm42688_get_shift(SENSOR_CHAN_GYRO_XYZ, header->accel_fs, header->gyro_fs,
|
|
&out->shift);
|
|
if (rc != 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
out->readings[0].timestamp_delta = 0;
|
|
icm42688_convert_raw_to_q31(
|
|
&cfg, SENSOR_CHAN_GYRO_X,
|
|
edata->readings[icm42688_get_channel_position(SENSOR_CHAN_GYRO_X)],
|
|
&out->readings[0].x);
|
|
icm42688_convert_raw_to_q31(
|
|
&cfg, SENSOR_CHAN_GYRO_Y,
|
|
edata->readings[icm42688_get_channel_position(SENSOR_CHAN_GYRO_Y)],
|
|
&out->readings[0].y);
|
|
icm42688_convert_raw_to_q31(
|
|
&cfg, SENSOR_CHAN_GYRO_Z,
|
|
edata->readings[icm42688_get_channel_position(SENSOR_CHAN_GYRO_Z)],
|
|
&out->readings[0].z);
|
|
*fit = 1;
|
|
return 1;
|
|
}
|
|
case SENSOR_CHAN_DIE_TEMP: {
|
|
channel_request = icm42688_encode_channel(SENSOR_CHAN_DIE_TEMP);
|
|
if ((channel_request & edata->channels) != channel_request) {
|
|
return -ENODATA;
|
|
}
|
|
|
|
struct sensor_q31_data *out = data_out;
|
|
|
|
out->header.base_timestamp_ns = edata->header.timestamp;
|
|
out->header.reading_count = 1;
|
|
|
|
rc = icm42688_get_shift(SENSOR_CHAN_DIE_TEMP, header->accel_fs, header->gyro_fs,
|
|
&out->shift);
|
|
if (rc != 0) {
|
|
return -EINVAL;
|
|
}
|
|
out->readings[0].timestamp_delta = 0;
|
|
icm42688_convert_raw_to_q31(
|
|
&cfg, SENSOR_CHAN_DIE_TEMP,
|
|
edata->readings[icm42688_get_channel_position(SENSOR_CHAN_DIE_TEMP)],
|
|
&out->readings[0].temperature);
|
|
*fit = 1;
|
|
return 1;
|
|
}
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int icm42688_decoder_decode(const uint8_t *buffer, struct sensor_chan_spec chan_spec,
|
|
uint32_t *fit, uint16_t max_count, void *data_out)
|
|
{
|
|
const struct icm42688_decoder_header *header =
|
|
(const struct icm42688_decoder_header *)buffer;
|
|
|
|
if (header->is_fifo) {
|
|
return icm42688_fifo_decode(buffer, chan_spec, fit, max_count, data_out);
|
|
}
|
|
return icm42688_one_shot_decode(buffer, chan_spec, fit, max_count, data_out);
|
|
}
|
|
|
|
static int icm42688_decoder_get_frame_count(const uint8_t *buffer,
|
|
struct sensor_chan_spec chan_spec,
|
|
uint16_t *frame_count)
|
|
{
|
|
const struct icm42688_fifo_data *data = (const struct icm42688_fifo_data *)buffer;
|
|
const struct icm42688_decoder_header *header = &data->header;
|
|
|
|
if (chan_spec.chan_idx != 0) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
if (!header->is_fifo) {
|
|
switch (chan_spec.chan_type) {
|
|
case SENSOR_CHAN_ACCEL_X:
|
|
case SENSOR_CHAN_ACCEL_Y:
|
|
case SENSOR_CHAN_ACCEL_Z:
|
|
case SENSOR_CHAN_ACCEL_XYZ:
|
|
case SENSOR_CHAN_GYRO_X:
|
|
case SENSOR_CHAN_GYRO_Y:
|
|
case SENSOR_CHAN_GYRO_Z:
|
|
case SENSOR_CHAN_GYRO_XYZ:
|
|
case SENSOR_CHAN_DIE_TEMP:
|
|
*frame_count = 1;
|
|
return 0;
|
|
default:
|
|
return -ENOTSUP;
|
|
}
|
|
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;
|
|
}
|
|
|
|
static int icm42688_decoder_get_size_info(struct sensor_chan_spec chan_spec, size_t *base_size,
|
|
size_t *frame_size)
|
|
{
|
|
switch (chan_spec.chan_type) {
|
|
case SENSOR_CHAN_ACCEL_X:
|
|
case SENSOR_CHAN_ACCEL_Y:
|
|
case SENSOR_CHAN_ACCEL_Z:
|
|
case SENSOR_CHAN_ACCEL_XYZ:
|
|
case SENSOR_CHAN_GYRO_X:
|
|
case SENSOR_CHAN_GYRO_Y:
|
|
case SENSOR_CHAN_GYRO_Z:
|
|
case SENSOR_CHAN_GYRO_XYZ:
|
|
*base_size = sizeof(struct sensor_three_axis_data);
|
|
*frame_size = sizeof(struct sensor_three_axis_sample_data);
|
|
return 0;
|
|
case SENSOR_CHAN_DIE_TEMP:
|
|
*base_size = sizeof(struct sensor_q31_data);
|
|
*frame_size = sizeof(struct sensor_q31_sample_data);
|
|
return 0;
|
|
default:
|
|
return -ENOTSUP;
|
|
}
|
|
}
|
|
|
|
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_WATERMARK:
|
|
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;
|
|
}
|
|
}
|
|
|
|
SENSOR_DECODER_API_DT_DEFINE() = {
|
|
.get_frame_count = icm42688_decoder_get_frame_count,
|
|
.get_size_info = icm42688_decoder_get_size_info,
|
|
.decode = icm42688_decoder_decode,
|
|
.has_trigger = icm24688_decoder_has_trigger,
|
|
};
|
|
|
|
int icm42688_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
*decoder = &SENSOR_DECODER_NAME();
|
|
|
|
return 0;
|
|
}
|