blob: 1ac18fa0ac14a5cd942d79caadce5a07b5cf5f7d [file] [log] [blame]
/*
* Copyright (c) 2025 Croxel
* SPDX-License-Identifier: Apache-2.0
*/
#include "als31300.h"
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/logging/log.h>
#include <zephyr/dsp/types.h>
LOG_MODULE_DECLARE(als31300, CONFIG_SENSOR_LOG_LEVEL);
/**
* @brief Encode channel flags for the given sensor channel
*/
static uint8_t als31300_encode_channel(enum sensor_channel chan)
{
uint8_t encode_bmask = 0;
switch (chan) {
case SENSOR_CHAN_MAGN_X:
encode_bmask |= BIT(0);
break;
case SENSOR_CHAN_MAGN_Y:
encode_bmask |= BIT(1);
break;
case SENSOR_CHAN_MAGN_Z:
encode_bmask |= BIT(2);
break;
case SENSOR_CHAN_MAGN_XYZ:
encode_bmask |= BIT(0) | BIT(1) | BIT(2);
break;
case SENSOR_CHAN_AMBIENT_TEMP:
encode_bmask |= BIT(3);
break;
case SENSOR_CHAN_ALL:
encode_bmask |= BIT(0) | BIT(1) | BIT(2) | BIT(3);
break;
default:
break;
}
return encode_bmask;
}
/**
* @brief Convert raw magnetic field value to Q31 format
* @param raw_value Signed 12-bit magnetic field value
* @param q31_out Pointer to store Q31 value
*/
static void als31300_convert_raw_to_q31_magn(int16_t raw_value, q31_t *q31_out)
{
/* Convert to microgauss using integer arithmetic */
int32_t microgauss = als31300_convert_to_gauss(raw_value);
/* Convert to Q31 format: Q31 = (value * 2^shift) / 1000000
* For magnetic field, we use shift=16, so the full scale is ±2^(31-16) = ±32768 gauss
* This gives us good resolution for the ±500G range of the ALS31300
* microgauss * 2^16 / 1000000 = microgauss * 65536 / 1000000
*/
*q31_out = (q31_t)(((int64_t)microgauss << ALS31300_MAGN_SHIFT) / 1000000);
}
/**
* @brief Convert raw temperature value to Q31 format
* @param raw_temp 12-bit raw temperature value
* @param q31_out Pointer to store Q31 value
*/
static void als31300_convert_temp_to_q31(uint16_t raw_temp, q31_t *q31_out)
{
/* Convert to microcelsius using integer arithmetic */
int32_t microcelsius = als31300_convert_temperature(raw_temp);
/* Convert to Q31 format: Q31 = (value * 2^shift) / 1000000
* For temperature, we use shift=16, so the full scale is ±2^(31-16) = ±32768°C
* This gives us good resolution for typical temperature ranges (-40°C to +125°C)
* microcelsius * 2^16 / 1000000 = microcelsius * 65536 / 1000000
*/
*q31_out = (q31_t)(((int64_t)microcelsius << ALS31300_TEMP_SHIFT) / 1000000);
}
/**
* @brief Get frame count for decoder
*/
static int als31300_decoder_get_frame_count(const uint8_t *buffer,
struct sensor_chan_spec chan_spec,
uint16_t *frame_count)
{
const struct als31300_encoded_data *edata = (const struct als31300_encoded_data *)buffer;
if (chan_spec.chan_idx != 0) {
return -ENOTSUP;
}
uint8_t channel_request = als31300_encode_channel(chan_spec.chan_type);
/* Filter unknown channels and having no data */
if ((edata->header.channels & channel_request) != channel_request) {
return -ENODATA;
}
*frame_count = 1;
return 0;
}
/**
* @brief Get size info for decoder
*/
static int als31300_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_MAGN_X:
case SENSOR_CHAN_MAGN_Y:
case SENSOR_CHAN_MAGN_Z:
case SENSOR_CHAN_MAGN_XYZ:
*base_size = sizeof(struct sensor_three_axis_data);
*frame_size = sizeof(struct sensor_three_axis_sample_data);
return 0;
case SENSOR_CHAN_AMBIENT_TEMP:
*base_size = sizeof(struct sensor_q31_data);
*frame_size = sizeof(struct sensor_q31_sample_data);
return 0;
default:
return -ENOTSUP;
}
}
/**
* @brief Decode function for RTIO
*/
static int als31300_decoder_decode(const uint8_t *buffer, struct sensor_chan_spec chan_spec,
uint32_t *fit, uint16_t max_count, void *data_out)
{
const struct als31300_encoded_data *edata = (const struct als31300_encoded_data *)buffer;
if (*fit != 0) {
return 0;
}
/* Parse raw payload data using common helper */
struct als31300_readings readings;
als31300_parse_registers(edata->payload, &readings);
switch (chan_spec.chan_type) {
case SENSOR_CHAN_MAGN_X:
case SENSOR_CHAN_MAGN_Y:
case SENSOR_CHAN_MAGN_Z:
case SENSOR_CHAN_MAGN_XYZ: {
struct sensor_three_axis_data *out = data_out;
out->header.base_timestamp_ns = edata->header.timestamp;
out->header.reading_count = 1;
out->shift = ALS31300_MAGN_SHIFT;
/* Convert raw readings to Q31 format */
als31300_convert_raw_to_q31_magn(readings.x, &out->readings[0].x);
als31300_convert_raw_to_q31_magn(readings.y, &out->readings[0].y);
als31300_convert_raw_to_q31_magn(readings.z, &out->readings[0].z);
*fit = 1;
return 1;
}
case SENSOR_CHAN_AMBIENT_TEMP: {
struct sensor_q31_data *out = data_out;
out->header.base_timestamp_ns = edata->header.timestamp;
out->header.reading_count = 1;
out->shift = ALS31300_TEMP_SHIFT;
als31300_convert_temp_to_q31(readings.temp, &out->readings[0].temperature);
*fit = 1;
return 1;
}
default:
return -ENOTSUP;
}
}
SENSOR_DECODER_API_DT_DEFINE() = {
.get_frame_count = als31300_decoder_get_frame_count,
.get_size_info = als31300_decoder_get_size_info,
.decode = als31300_decoder_decode,
};
int als31300_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder)
{
ARG_UNUSED(dev);
*decoder = &SENSOR_DECODER_NAME();
return 0;
}