/* * Copyright (c) 2024 Analog Devices Inc. * * SPDX-License-Identifier: Apache-2.0 */ #include "adxl362.h" #include #ifdef CONFIG_ADXL362_STREAM /* (2^31 / 2^8(shift) */ #define ADXL362_TEMP_QSCALE 8388608 #define ADXL362_TEMP_LSB_PER_C 15 #define ADXL362_COMPLEMENT 0xf000 static const uint32_t accel_period_ns[] = { [ADXL362_ODR_12_5_HZ] = UINT32_C(10000000000) / 125, [ADXL362_ODR_25_HZ] = UINT32_C(1000000000) / 25, [ADXL362_ODR_50_HZ] = UINT32_C(1000000000) / 50, [ADXL362_ODR_100_HZ] = UINT32_C(1000000000) / 100, [ADXL362_ODR_200_HZ] = UINT32_C(1000000000) / 200, [ADXL362_ODR_400_HZ] = UINT32_C(1000000000) / 400, }; static const uint32_t range_to_shift[] = { [ADXL362_RANGE_2G] = 5, [ADXL362_RANGE_4G] = 6, [ADXL362_RANGE_8G] = 7, }; /* (1 / sensitivity) * (pow(2,31) / pow(2,shift)) * (unit_scaler) */ static const uint32_t qscale_factor[] = { /* (1.0 / ADXL362_ACCEL_2G_LSB_PER_G) * (2^31 / 2^5) * SENSOR_G / 1000000 */ [ADXL362_RANGE_2G] = UINT32_C(658338), /* (1.0 / ADXL362_ACCEL_4G_LSB_PER_G) * (2^31 / 2^6) * SENSOR_G / 1000000 */ [ADXL362_RANGE_4G] = UINT32_C(658338), /* (1.0 / ADXL362_ACCEL_8G_LSB_PER_G) * (2^31 / 2^7) ) * SENSOR_G / 1000000 */ [ADXL362_RANGE_8G] = UINT32_C(700360), }; static inline void adxl362_temp_convert_q31(q31_t *out, int16_t data_in) { /* See sensitivity and bias specifications in table 1 of datasheet */ data_in &= 0xFFF; if (data_in & BIT(11)) { data_in |= ADXL362_COMPLEMENT; } *out = ((data_in - ADXL362_TEMP_BIAS_LSB) / ADXL362_TEMP_LSB_PER_C + ADXL362_TEMP_BIAS_TEST_CONDITION) * ADXL362_TEMP_QSCALE; } static inline void adxl362_accel_convert_q31(q31_t *out, int16_t data_in, int32_t range) { data_in &= 0xFFF; if (data_in & BIT(11)) { data_in |= ADXL362_COMPLEMENT; } *out = data_in * qscale_factor[range]; } static int adxl362_decode_stream(const uint8_t *buffer, struct sensor_chan_spec chan_spec, uint32_t *fit, uint16_t max_count, void *data_out) { const struct adxl362_fifo_data *enc_data = (const struct adxl362_fifo_data *)buffer; const uint8_t *buffer_end = buffer + sizeof(struct adxl362_fifo_data) + enc_data->fifo_byte_count; int count = 0; uint8_t sample_num = 0; int16_t data_in; if ((uintptr_t)buffer_end <= *fit || chan_spec.chan_idx != 0) { return 0; } buffer += sizeof(struct adxl362_fifo_data); uint8_t sample_set_size = 6; if (enc_data->has_tmp) { sample_set_size = 8; } uint64_t period_ns = accel_period_ns[enc_data->accel_odr]; /* Calculate which sample is decoded. */ if ((uint8_t *)*fit >= buffer) { sample_num = ((uint8_t *)*fit - buffer) / sample_set_size; } while (count < max_count && buffer < buffer_end) { const uint8_t *sample_end = buffer; sample_end += sample_set_size; if ((uintptr_t)buffer < *fit) { /* This frame was already decoded, move on to the next frame */ buffer = sample_end; continue; } if (chan_spec.chan_type == SENSOR_CHAN_DIE_TEMP) { if (enc_data->has_tmp) { struct sensor_q31_data *data = (struct sensor_q31_data *)data_out; memset(data, 0, sizeof(struct sensor_three_axis_data)); data->header.base_timestamp_ns = enc_data->timestamp; data->header.reading_count = 1; data->shift = 8; data->readings[count].timestamp_delta = period_ns * sample_num; data_in = sys_le16_to_cpu(*((int16_t *)(buffer + 6))); /* Check if this sample contains temperature value. */ if (ADXL362_FIFO_HDR_CHECK_TEMP(data_in)) { adxl362_temp_convert_q31(&data->readings[count].temperature, data_in); } } } else { struct sensor_three_axis_data *data = (struct sensor_three_axis_data *)data_out; memset(data, 0, sizeof(struct sensor_three_axis_data)); data->header.base_timestamp_ns = enc_data->timestamp; data->header.reading_count = 1; data->shift = range_to_shift[enc_data->selected_range]; switch (chan_spec.chan_type) { case SENSOR_CHAN_ACCEL_X: data->readings[count].timestamp_delta = sample_num * period_ns; /* Convert received data into signeg integer. */ data_in = sys_le16_to_cpu(*((int16_t *)buffer)); /* Check if this sample contains X value. */ if (ADXL362_FIFO_HDR_CHECK_ACCEL_X(data_in)) { adxl362_accel_convert_q31(&data->readings[count].x, data_in, enc_data->selected_range); } break; case SENSOR_CHAN_ACCEL_Y: data->readings[count].timestamp_delta = sample_num * period_ns; /* Convert received data into signeg integer. */ data_in = sys_le16_to_cpu(*((int16_t *)(buffer + 2))); /* Check if this sample contains Y value. */ if (ADXL362_FIFO_HDR_CHECK_ACCEL_Y(data_in)) { adxl362_accel_convert_q31(&data->readings[count].y, data_in, enc_data->selected_range); } break; case SENSOR_CHAN_ACCEL_Z: data->readings[count].timestamp_delta = sample_num * period_ns; /* Convert received data into signeg integer. */ data_in = sys_le16_to_cpu(*((int16_t *)(buffer + 4))); /* Check if this sample contains Y value. */ if (ADXL362_FIFO_HDR_CHECK_ACCEL_Z(data_in)) { adxl362_accel_convert_q31(&data->readings[count].z, data_in, enc_data->selected_range); } break; case SENSOR_CHAN_ACCEL_XYZ: data->readings[count].timestamp_delta = sample_num * period_ns; /* Convert received data into signeg integer. */ data_in = sys_le16_to_cpu(*((int16_t *)buffer)); /* Check if this sample contains X value. */ if (ADXL362_FIFO_HDR_CHECK_ACCEL_X(data_in)) { adxl362_accel_convert_q31(&data->readings[count].x, data_in, enc_data->selected_range); } /* Convert received data into signeg integer. */ data_in = sys_le16_to_cpu(*((int16_t *)(buffer + 2))); /* Check if this sample contains Y value. */ if (ADXL362_FIFO_HDR_CHECK_ACCEL_Y(data_in)) { adxl362_accel_convert_q31(&data->readings[count].y, data_in, enc_data->selected_range); } /* Convert received data into signeg integer. */ data_in = sys_le16_to_cpu(*((int16_t *)(buffer + 4))); /* Check if this sample contains Z value. */ if (ADXL362_FIFO_HDR_CHECK_ACCEL_Z(data_in)) { adxl362_accel_convert_q31(&data->readings[count].z, data_in, enc_data->selected_range); } break; default: return -ENOTSUP; } } buffer = sample_end; *fit = (uintptr_t)sample_end; count++; } return count; } #endif /* CONFIG_ADXL362_STREAM */ static int adxl362_decoder_get_frame_count(const uint8_t *buffer, struct sensor_chan_spec chan_spec, uint16_t *frame_count) { int32_t ret = -ENOTSUP; if (chan_spec.chan_idx != 0) { return ret; } #ifdef CONFIG_ADXL362_STREAM const struct adxl362_fifo_data *data = (const struct adxl362_fifo_data *)buffer; if (!data->is_fifo) { #endif /* CONFIG_ADXL362_STREAM */ 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: *frame_count = 1; ret = 0; break; default: break; } #ifdef CONFIG_ADXL362_STREAM } else { if (data->fifo_byte_count == 0) { *frame_count = 0; ret = 0; } else { 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: if (data->has_tmp) { /*6 bytes for XYZ and 2 bytes for TEMP*/ *frame_count = data->fifo_byte_count / 8; ret = 0; } else { *frame_count = data->fifo_byte_count / 6; ret = 0; } break; case SENSOR_CHAN_DIE_TEMP: if (data->has_tmp) { /*6 bytes for XYZ and 2 bytes for TEMP*/ *frame_count = data->fifo_byte_count / 8; ret = 0; } break; default: break; } } } #endif /* CONFIG_ADXL362_STREAM */ return ret; } static int adxl362_decode_sample(const struct adxl362_sample_data *data, struct sensor_chan_spec chan_spec, uint32_t *fit, uint16_t max_count, void *data_out) { struct sensor_value *out = (struct sensor_value *) data_out; if (*fit > 0) { return -ENOTSUP; } switch (chan_spec.chan_type) { case SENSOR_CHAN_ACCEL_X: /* Acceleration on the X axis, in m/s^2. */ adxl362_accel_convert(out, data->acc_x, data->selected_range); break; case SENSOR_CHAN_ACCEL_Y: /* Acceleration on the Y axis, in m/s^2. */ adxl362_accel_convert(out, data->acc_y, data->selected_range); break; case SENSOR_CHAN_ACCEL_Z: /* Acceleration on the Z axis, in m/s^2. */ adxl362_accel_convert(out, data->acc_z, data->selected_range); break; case SENSOR_CHAN_ACCEL_XYZ: /* Acceleration on the XYZ axis, in m/s^2. */ adxl362_accel_convert(out++, data->acc_x, data->selected_range); adxl362_accel_convert(out++, data->acc_y, data->selected_range); adxl362_accel_convert(out, data->acc_z, data->selected_range); break; case SENSOR_CHAN_DIE_TEMP: /* Temperature in degrees Celsius. */ adxl362_temp_convert(out, data->temp); break; default: return -ENOTSUP; } *fit = 1; return 0; } static int adxl362_decoder_decode(const uint8_t *buffer, struct sensor_chan_spec chan_spec, uint32_t *fit, uint16_t max_count, void *data_out) { const struct adxl362_sample_data *data = (const struct adxl362_sample_data *)buffer; #ifdef CONFIG_ADXL362_STREAM if (data->is_fifo) { return adxl362_decode_stream(buffer, chan_spec, fit, max_count, data_out); } #endif /* CONFIG_ADXL362_STREAM */ return adxl362_decode_sample(data, chan_spec, fit, max_count, data_out); } static bool adxl362_decoder_has_trigger(const uint8_t *buffer, enum sensor_trigger_type trigger) { const struct adxl362_fifo_data *data = (const struct adxl362_fifo_data *)buffer; if (!data->is_fifo) { return false; } switch (trigger) { case SENSOR_TRIG_DATA_READY: return ADXL362_STATUS_CHECK_DATA_READY(data->int_status); case SENSOR_TRIG_FIFO_WATERMARK: return ADXL362_STATUS_CHECK_FIFO_WTR(data->int_status); case SENSOR_TRIG_FIFO_FULL: return ADXL362_STATUS_CHECK_FIFO_OVR(data->int_status); default: return false; } } SENSOR_DECODER_API_DT_DEFINE() = { .get_frame_count = adxl362_decoder_get_frame_count, .decode = adxl362_decoder_decode, .has_trigger = adxl362_decoder_has_trigger, }; int adxl362_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder) { ARG_UNUSED(dev); *decoder = &SENSOR_DECODER_NAME(); return 0; }