Skip to content

Commit bf3eba1

Browse files
Kampicfriedt
authored andcommitted
drivers: sensor: APDS9306: Add lux conversion
- Add lux conversion to APDS-9306 driver - Change settings of gain, resolution and frequency to index-based settings - Add Device Tree overlay sample for APDS-9306 - Fix wrong board name in light_polling README - Add value checks for the attribute set API call - Remove the reading of the sensor attributes from the sensor and use buffered values instead - Rename `frequency` property to `measurement period` Closes #91104 Signed-off-by: Daniel Kampert <[email protected]>
1 parent 995ae9f commit bf3eba1

File tree

5 files changed

+158
-94
lines changed

5 files changed

+158
-94
lines changed

drivers/sensor/apds9306/apds9306.c

Lines changed: 113 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* Copyright (c) 2024 Daniel Kampert
2-
* Author: Daniel Kampert <DanielKampert@kampis-Elektroecke.de>
2+
* Author: Daniel Kampert <DanielKampert@kampis-elektroecke.de>
33
*/
44

55
#include <zephyr/device.h>
@@ -45,42 +45,47 @@
4545

4646
LOG_MODULE_REGISTER(avago_apds9306, CONFIG_SENSOR_LOG_LEVEL);
4747

48+
/* Array length for the measurement period values. Aligned with avago,apds9306.yaml */
49+
static const uint8_t AVAGO_APDS_9306_MEASUREMENT_PERIOD_ARRAY_LENGTH = 7;
50+
51+
/* Array length for the resolution values. Aligned with avago,apds9306.yaml */
52+
static const uint8_t AVAGO_APDS_9306_RESOLUTION_ARRAY_LENGTH = 6;
53+
54+
/* See datasheet for the values. Aligned with avago,apds9306.yaml */
55+
static const uint8_t avago_apds9306_gain[] = {1, 3, 6, 9, 18};
56+
static const uint8_t AVAGO_APDS_9306_GAIN_ARRAY_LENGTH = ARRAY_SIZE(avago_apds9306_gain);
57+
58+
/* See datasheet for the values. */
59+
/* Last value is rounded up to prevent floating point operations. */
60+
static const uint16_t avago_apds9306_integration_time[] = {400, 200, 100, 50, 25, 4};
61+
62+
/* These values represent the gain based on the integration time. */
63+
/* A gain of 1 is used for a time of 3.125 ms (13 bits). */
64+
/* This results in a gain of 8 (2^3) for a time if 25 ms (16 bits), etc. */
65+
static const uint16_t avago_apds9306_integration_time_gain[] = {128, 64, 32, 16, 8, 1};
66+
4867
struct apds9306_data {
4968
uint32_t light;
69+
uint8_t measurement_period_idx; /* This field holds the index of the current */
70+
/* period measurement */
71+
uint8_t gain_idx; /* This field holds the index of the current sampling gain. */
72+
uint8_t resolution_idx; /* This field holds the index of the current sampling */
73+
/* resolution.*/
74+
uint8_t chip_id;
5075
};
5176

5277
struct apds9306_config {
5378
struct i2c_dt_spec i2c;
54-
uint8_t resolution;
55-
uint16_t frequency;
56-
uint8_t gain;
79+
uint8_t resolution_idx;
80+
uint8_t measurement_period_idx;
81+
uint8_t gain_idx;
5782
};
5883

5984
struct apds9306_worker_item_t {
6085
struct k_work_delayable dwork;
6186
const struct device *dev;
6287
} apds9306_worker_item;
6388

64-
static uint32_t apds9306_get_time_for_resolution(uint8_t value)
65-
{
66-
switch (value) {
67-
case 0:
68-
return 400;
69-
case 1:
70-
return 200;
71-
case 2:
72-
return 100;
73-
case 3:
74-
return 50;
75-
case 4:
76-
return 25;
77-
case 5:
78-
return 4;
79-
default:
80-
return 100;
81-
}
82-
}
83-
8489
static int apds9306_enable(const struct device *dev)
8590
{
8691
const struct apds9306_config *config = dev->config;
@@ -128,7 +133,41 @@ static void apds9306_worker(struct k_work *p_work)
128133
}
129134

130135
data->light = sys_get_le24(buffer);
136+
LOG_DBG("Last raw measurement: %u", data->light);
137+
138+
/* Based on the formula from the APDS-9309 datasheet, page 4:
139+
* https://docs.broadcom.com/doc/AV02-3689EN
140+
*
141+
* Illuminance [Lux] = Data * (1 / (Gain * Integration Time)) * Factor [Lux]
142+
*
143+
* The factor is calculated with the given values from the
144+
* APDS-9306 datasheet, page 4.
145+
* 1. Convert the E value from uW/sqcm to Lux
146+
* - 340.134 for the APDS-9306
147+
* - 293.69 for the APDS-9306-065
148+
* 2. Use the formula from the APDS-9309 datasheet to get the factor by using
149+
* - Gain = 3
150+
* - Integration time = 100 ms
151+
* Caution: The unit is ms. We need a unit without a dimension to prevent wrong
152+
* units. So it must be converted into a value without dimension. This is done by converting
153+
* it into a bit value based on the resolution gain (=32).
154+
* - ADC count = 2000
155+
* 3. Repeat it for both sensor types to get the factors (converted for integer operations)
156+
* - APDS-9306: 16
157+
* - APDS-9306-065: 14
158+
*/
159+
uint32_t gain = avago_apds9306_gain[data->gain_idx];
160+
uint32_t integration_time = avago_apds9306_integration_time_gain[data->resolution_idx];
161+
uint32_t factor = 16;
162+
163+
if (data->chip_id == APDS_9306_065_CHIP_ID) {
164+
factor = 14;
165+
}
131166

167+
data->light = (data->light * factor) / (gain * integration_time);
168+
169+
LOG_DBG("Gain: %u", gain);
170+
LOG_DBG("Integration time: %u", integration_time);
132171
LOG_DBG("Last measurement: %u", data->light);
133172
}
134173

@@ -139,20 +178,30 @@ static int apds9306_attr_set(const struct device *dev, enum sensor_channel chann
139178
uint8_t mask;
140179
uint8_t temp;
141180
const struct apds9306_config *config = dev->config;
181+
struct apds9306_data *data = dev->data;
142182

143-
if (channel != SENSOR_CHAN_LIGHT) {
183+
if ((channel != SENSOR_CHAN_ALL) && (channel != SENSOR_CHAN_LIGHT)) {
144184
return -ENOTSUP;
145185
}
146186

147187
if (attribute == SENSOR_ATTR_SAMPLING_FREQUENCY) {
188+
if (value->val1 > (AVAGO_APDS_9306_MEASUREMENT_PERIOD_ARRAY_LENGTH - 1)) {
189+
return -EINVAL;
190+
}
148191
reg = APDS9306_REGISTER_ALS_MEAS_RATE;
149192
mask = GENMASK(2, 0);
150193
temp = FIELD_PREP(0x07, value->val1);
151194
} else if (attribute == SENSOR_ATTR_GAIN) {
195+
if (value->val1 > (AVAGO_APDS_9306_GAIN_ARRAY_LENGTH - 1)) {
196+
return -EINVAL;
197+
}
152198
reg = APDS9306_REGISTER_ALS_GAIN;
153199
mask = GENMASK(2, 0);
154200
temp = FIELD_PREP(0x07, value->val1);
155201
} else if (attribute == SENSOR_ATTR_RESOLUTION) {
202+
if (value->val1 > (AVAGO_APDS_9306_RESOLUTION_ARRAY_LENGTH - 1)) {
203+
return -EINVAL;
204+
}
156205
reg = APDS9306_REGISTER_ALS_MEAS_RATE;
157206
mask = GENMASK(7, 4);
158207
temp = FIELD_PREP(0x07, value->val1) << 0x04;
@@ -165,51 +214,44 @@ static int apds9306_attr_set(const struct device *dev, enum sensor_channel chann
165214
return -EFAULT;
166215
}
167216

217+
/* We only save the new values when no error occurs to prevent invalid settings. */
218+
if (attribute == SENSOR_ATTR_SAMPLING_FREQUENCY) {
219+
data->measurement_period_idx = value->val1;
220+
} else if (attribute == SENSOR_ATTR_GAIN) {
221+
data->gain_idx = value->val1;
222+
} else if (attribute == SENSOR_ATTR_RESOLUTION) {
223+
data->resolution_idx = value->val1;
224+
}
225+
168226
return 0;
169227
}
170228

171229
static int apds9306_attr_get(const struct device *dev, enum sensor_channel channel,
172230
enum sensor_attribute attribute, struct sensor_value *value)
173231
{
174-
uint8_t mask;
175-
uint8_t temp;
176-
uint8_t reg;
177-
const struct apds9306_config *config = dev->config;
232+
struct apds9306_data *data = dev->data;
178233

179-
if (channel != SENSOR_CHAN_LIGHT) {
234+
if ((channel != SENSOR_CHAN_ALL) && (channel != SENSOR_CHAN_LIGHT)) {
180235
return -ENOTSUP;
181236
}
182237

183238
if (attribute == SENSOR_ATTR_SAMPLING_FREQUENCY) {
184-
reg = APDS9306_REGISTER_ALS_MEAS_RATE;
185-
mask = 0x00;
239+
value->val1 = data->measurement_period_idx;
186240
} else if (attribute == SENSOR_ATTR_GAIN) {
187-
reg = APDS9306_REGISTER_ALS_GAIN;
188-
mask = 0x00;
241+
value->val1 = data->gain_idx;
189242
} else if (attribute == SENSOR_ATTR_RESOLUTION) {
190-
reg = APDS9306_REGISTER_ALS_MEAS_RATE;
191-
mask = 0x04;
243+
value->val1 = data->resolution_idx;
192244
} else {
193245
return -ENOTSUP;
194246
}
195247

196-
if (i2c_reg_read_byte_dt(&config->i2c, reg, &temp)) {
197-
LOG_ERR("Failed to read sensor attribute!");
198-
return -EFAULT;
199-
}
200-
201-
value->val1 = (temp >> mask) & 0x07;
202-
value->val2 = 0;
203-
204248
return 0;
205249
}
206250

207251
static int apds9306_sample_fetch(const struct device *dev, enum sensor_channel channel)
208252
{
209-
uint8_t buffer;
210-
uint8_t resolution;
211253
uint16_t delay;
212-
const struct apds9306_config *config = dev->config;
254+
struct apds9306_data *data = dev->data;
213255

214256
if ((channel != SENSOR_CHAN_ALL) && (channel != SENSOR_CHAN_LIGHT)) {
215257
return -ENOTSUP;
@@ -221,17 +263,10 @@ static int apds9306_sample_fetch(const struct device *dev, enum sensor_channel c
221263
return -EFAULT;
222264
}
223265

224-
/* Get the measurement resolution. */
225-
if (i2c_reg_read_byte_dt(&config->i2c, APDS9306_REGISTER_ALS_MEAS_RATE, &buffer)) {
226-
LOG_ERR("Failed reading resolution");
227-
return -EFAULT;
228-
}
229-
230266
/* Convert the resolution into a delay time and wait for the result. */
231-
resolution = (buffer >> 4) & 0x07;
232-
delay = apds9306_get_time_for_resolution(resolution);
233-
LOG_DBG("Measurement resolution: %u", resolution);
234-
LOG_DBG("Wait for %u ms", delay);
267+
delay = avago_apds9306_integration_time[data->resolution_idx];
268+
LOG_DBG("Measurement resolution index: %u", data->resolution_idx);
269+
LOG_DBG("Wait for %d ms", delay);
235270

236271
/* We add a bit more delay to cover the startup time etc. */
237272
if (!k_work_delayable_is_pending(&apds9306_worker_item.dwork)) {
@@ -252,13 +287,15 @@ static int apds9306_channel_get(const struct device *dev, enum sensor_channel ch
252287
{
253288
struct apds9306_data *data = dev->data;
254289

255-
if (channel != SENSOR_CHAN_LIGHT) {
290+
switch (channel) {
291+
case SENSOR_CHAN_LIGHT:
292+
value->val1 = data->light;
293+
value->val2 = 0;
294+
break;
295+
default:
256296
return -ENOTSUP;
257297
}
258298

259-
value->val1 = data->light;
260-
value->val2 = 0;
261-
262299
return 0;
263300
}
264301

@@ -267,6 +304,7 @@ static int apds9306_sensor_setup(const struct device *dev)
267304
uint32_t now;
268305
uint8_t temp;
269306
const struct apds9306_config *config = dev->config;
307+
struct apds9306_data *data = dev->data;
270308

271309
/* Wait for the device to become ready after a possible power cycle. */
272310
now = k_uptime_get_32();
@@ -285,19 +323,19 @@ static int apds9306_sensor_setup(const struct device *dev)
285323
k_msleep(10);
286324
} while (temp & APDS9306_BIT_POWER_ON_STATUS);
287325

288-
if (i2c_reg_read_byte_dt(&config->i2c, APDS9306_REGISTER_PART_ID, &temp)) {
326+
if (i2c_reg_read_byte_dt(&config->i2c, APDS9306_REGISTER_PART_ID, &data->chip_id)) {
289327
LOG_ERR("Failed reading chip id!");
290328
return -EFAULT;
291329
}
292330

293-
if ((temp != APDS_9306_CHIP_ID) && (temp != APDS_9306_065_CHIP_ID)) {
294-
LOG_ERR("Invalid chip id! Found 0x%X!", temp);
331+
if ((data->chip_id != APDS_9306_CHIP_ID) && (data->chip_id != APDS_9306_065_CHIP_ID)) {
332+
LOG_ERR("Invalid chip id! Found 0x%X!", data->chip_id);
295333
return -EFAULT;
296334
}
297335

298-
if (temp == APDS_9306_CHIP_ID) {
336+
if (data->chip_id == APDS_9306_CHIP_ID) {
299337
LOG_DBG("APDS-9306 found!");
300-
} else if (temp == APDS_9306_065_CHIP_ID) {
338+
} else if (data->chip_id == APDS_9306_065_CHIP_ID) {
301339
LOG_DBG("APDS-9306-065 found!");
302340
}
303341

@@ -316,6 +354,7 @@ static int apds9306_init(const struct device *dev)
316354
{
317355
uint8_t value;
318356
const struct apds9306_config *config = dev->config;
357+
struct apds9306_data *data = dev->data;
319358

320359
LOG_DBG("Start to initialize APDS9306...");
321360

@@ -329,16 +368,18 @@ static int apds9306_init(const struct device *dev)
329368
return -EFAULT;
330369
}
331370

332-
value = ((config->resolution & 0x07) << 4) | (config->frequency & 0x0F);
371+
data->measurement_period_idx = config->measurement_period_idx;
372+
data->resolution_idx = config->resolution_idx;
373+
value = ((data->resolution_idx & 0x07) << 4) | (data->measurement_period_idx & 0x07);
333374
LOG_DBG("Write configuration 0x%x to register 0x%x", value,
334375
APDS9306_REGISTER_ALS_MEAS_RATE);
335376
if (i2c_reg_write_byte_dt(&config->i2c, APDS9306_REGISTER_ALS_MEAS_RATE, value)) {
336377
return -EFAULT;
337378
}
338379

339-
value = config->gain;
380+
data->gain_idx = config->gain_idx;
340381
LOG_DBG("Write configuration 0x%x to register 0x%x", value, APDS9306_REGISTER_ALS_GAIN);
341-
if (i2c_reg_write_byte_dt(&config->i2c, APDS9306_REGISTER_ALS_GAIN, value)) {
382+
if (i2c_reg_write_byte_dt(&config->i2c, APDS9306_REGISTER_ALS_GAIN, data->gain_idx)) {
342383
return -EFAULT;
343384
}
344385

@@ -358,9 +399,9 @@ static DEVICE_API(sensor, apds9306_driver_api) = {
358399
static struct apds9306_data apds9306_data_##inst; \
359400
static const struct apds9306_config apds9306_config_##inst = { \
360401
.i2c = I2C_DT_SPEC_INST_GET(inst), \
361-
.resolution = DT_INST_PROP(inst, resolution), \
362-
.gain = DT_INST_PROP(inst, gain), \
363-
.frequency = DT_INST_PROP(inst, frequency), \
402+
.resolution_idx = DT_INST_ENUM_IDX(inst, resolution), \
403+
.gain_idx = DT_INST_ENUM_IDX(inst, gain), \
404+
.measurement_period_idx = DT_INST_ENUM_IDX(inst, measurement_period), \
364405
}; \
365406
\
366407
SENSOR_DEVICE_DT_INST_DEFINE(inst, apds9306_init, NULL, &apds9306_data_##inst, \

0 commit comments

Comments
 (0)