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>
4545
4646LOG_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+
4867struct 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
5277struct 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
5984struct 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-
8489static 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
171229static 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
207251static 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