1 /*
2 * Copyright (c) 2025 Croxel
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #include "als31300.h"
7
8 #include <zephyr/device.h>
9 #include <zephyr/drivers/sensor.h>
10 #include <zephyr/logging/log.h>
11 #include <zephyr/dsp/types.h>
12
13 LOG_MODULE_DECLARE(als31300, CONFIG_SENSOR_LOG_LEVEL);
14
15 /**
16 * @brief Encode channel flags for the given sensor channel
17 */
als31300_encode_channel(enum sensor_channel chan)18 static uint8_t als31300_encode_channel(enum sensor_channel chan)
19 {
20 uint8_t encode_bmask = 0;
21
22 switch (chan) {
23 case SENSOR_CHAN_MAGN_X:
24 encode_bmask |= BIT(0);
25 break;
26 case SENSOR_CHAN_MAGN_Y:
27 encode_bmask |= BIT(1);
28 break;
29 case SENSOR_CHAN_MAGN_Z:
30 encode_bmask |= BIT(2);
31 break;
32 case SENSOR_CHAN_MAGN_XYZ:
33 encode_bmask |= BIT(0) | BIT(1) | BIT(2);
34 break;
35 case SENSOR_CHAN_AMBIENT_TEMP:
36 encode_bmask |= BIT(3);
37 break;
38 case SENSOR_CHAN_ALL:
39 encode_bmask |= BIT(0) | BIT(1) | BIT(2) | BIT(3);
40 break;
41 default:
42 break;
43 }
44
45 return encode_bmask;
46 }
47
48 /**
49 * @brief Convert raw magnetic field value to Q31 format
50 * @param raw_value Signed 12-bit magnetic field value
51 * @param q31_out Pointer to store Q31 value
52 */
als31300_convert_raw_to_q31_magn(int16_t raw_value,q31_t * q31_out)53 static void als31300_convert_raw_to_q31_magn(int16_t raw_value, q31_t *q31_out)
54 {
55 /* Convert to microgauss using integer arithmetic */
56 int32_t microgauss = als31300_convert_to_gauss(raw_value);
57
58 /* Convert to Q31 format: Q31 = (value * 2^shift) / 1000000
59 * For magnetic field, we use shift=16, so the full scale is ±2^(31-16) = ±32768 gauss
60 * This gives us good resolution for the ±500G range of the ALS31300
61 * microgauss * 2^16 / 1000000 = microgauss * 65536 / 1000000
62 */
63 *q31_out = (q31_t)(((int64_t)microgauss << ALS31300_MAGN_SHIFT) / 1000000);
64 }
65
66 /**
67 * @brief Convert raw temperature value to Q31 format
68 * @param raw_temp 12-bit raw temperature value
69 * @param q31_out Pointer to store Q31 value
70 */
als31300_convert_temp_to_q31(uint16_t raw_temp,q31_t * q31_out)71 static void als31300_convert_temp_to_q31(uint16_t raw_temp, q31_t *q31_out)
72 {
73 /* Convert to microcelsius using integer arithmetic */
74 int32_t microcelsius = als31300_convert_temperature(raw_temp);
75
76 /* Convert to Q31 format: Q31 = (value * 2^shift) / 1000000
77 * For temperature, we use shift=16, so the full scale is ±2^(31-16) = ±32768°C
78 * This gives us good resolution for typical temperature ranges (-40°C to +125°C)
79 * microcelsius * 2^16 / 1000000 = microcelsius * 65536 / 1000000
80 */
81 *q31_out = (q31_t)(((int64_t)microcelsius << ALS31300_TEMP_SHIFT) / 1000000);
82 }
83
84 /**
85 * @brief Get frame count for decoder
86 */
als31300_decoder_get_frame_count(const uint8_t * buffer,struct sensor_chan_spec chan_spec,uint16_t * frame_count)87 static int als31300_decoder_get_frame_count(const uint8_t *buffer,
88 struct sensor_chan_spec chan_spec,
89 uint16_t *frame_count)
90 {
91 const struct als31300_encoded_data *edata = (const struct als31300_encoded_data *)buffer;
92
93 if (chan_spec.chan_idx != 0) {
94 return -ENOTSUP;
95 }
96
97 uint8_t channel_request = als31300_encode_channel(chan_spec.chan_type);
98
99 /* Filter unknown channels and having no data */
100 if ((edata->header.channels & channel_request) != channel_request) {
101 return -ENODATA;
102 }
103
104 *frame_count = 1;
105 return 0;
106 }
107
108 /**
109 * @brief Get size info for decoder
110 */
als31300_decoder_get_size_info(struct sensor_chan_spec chan_spec,size_t * base_size,size_t * frame_size)111 static int als31300_decoder_get_size_info(struct sensor_chan_spec chan_spec, size_t *base_size,
112 size_t *frame_size)
113 {
114 switch (chan_spec.chan_type) {
115 case SENSOR_CHAN_MAGN_X:
116 case SENSOR_CHAN_MAGN_Y:
117 case SENSOR_CHAN_MAGN_Z:
118 case SENSOR_CHAN_MAGN_XYZ:
119 *base_size = sizeof(struct sensor_three_axis_data);
120 *frame_size = sizeof(struct sensor_three_axis_sample_data);
121 return 0;
122 case SENSOR_CHAN_AMBIENT_TEMP:
123 *base_size = sizeof(struct sensor_q31_data);
124 *frame_size = sizeof(struct sensor_q31_sample_data);
125 return 0;
126 default:
127 return -ENOTSUP;
128 }
129 }
130
131 /**
132 * @brief Decode function for RTIO
133 */
als31300_decoder_decode(const uint8_t * buffer,struct sensor_chan_spec chan_spec,uint32_t * fit,uint16_t max_count,void * data_out)134 static int als31300_decoder_decode(const uint8_t *buffer, struct sensor_chan_spec chan_spec,
135 uint32_t *fit, uint16_t max_count, void *data_out)
136 {
137 const struct als31300_encoded_data *edata = (const struct als31300_encoded_data *)buffer;
138
139 if (*fit != 0) {
140 return 0;
141 }
142
143 /* Parse raw payload data using common helper */
144 struct als31300_readings readings;
145
146 als31300_parse_registers(edata->payload, &readings);
147
148 switch (chan_spec.chan_type) {
149 case SENSOR_CHAN_MAGN_X:
150 case SENSOR_CHAN_MAGN_Y:
151 case SENSOR_CHAN_MAGN_Z:
152 case SENSOR_CHAN_MAGN_XYZ: {
153 struct sensor_three_axis_data *out = data_out;
154
155 out->header.base_timestamp_ns = edata->header.timestamp;
156 out->header.reading_count = 1;
157 out->shift = ALS31300_MAGN_SHIFT;
158
159 /* Convert raw readings to Q31 format */
160 als31300_convert_raw_to_q31_magn(readings.x, &out->readings[0].x);
161 als31300_convert_raw_to_q31_magn(readings.y, &out->readings[0].y);
162 als31300_convert_raw_to_q31_magn(readings.z, &out->readings[0].z);
163 *fit = 1;
164 return 1;
165 }
166 case SENSOR_CHAN_AMBIENT_TEMP: {
167 struct sensor_q31_data *out = data_out;
168
169 out->header.base_timestamp_ns = edata->header.timestamp;
170 out->header.reading_count = 1;
171 out->shift = ALS31300_TEMP_SHIFT;
172
173 als31300_convert_temp_to_q31(readings.temp, &out->readings[0].temperature);
174 *fit = 1;
175 return 1;
176 }
177 default:
178 return -ENOTSUP;
179 }
180 }
181
182 SENSOR_DECODER_API_DT_DEFINE() = {
183 .get_frame_count = als31300_decoder_get_frame_count,
184 .get_size_info = als31300_decoder_get_size_info,
185 .decode = als31300_decoder_decode,
186 };
187
als31300_get_decoder(const struct device * dev,const struct sensor_decoder_api ** decoder)188 int als31300_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder)
189 {
190 ARG_UNUSED(dev);
191 *decoder = &SENSOR_DECODER_NAME();
192 return 0;
193 }
194