Skip to content

drivers: add driver for APDS99XX ambient light and proximity sensors#10420

Merged
aabadie merged 4 commits intoRIOT-OS:masterfrom
gschorcht:drivers_apds99xx
Mar 11, 2020
Merged

drivers: add driver for APDS99XX ambient light and proximity sensors#10420
aabadie merged 4 commits intoRIOT-OS:masterfrom
gschorcht:drivers_apds99xx

Conversation

@gschorcht
Copy link
Copy Markdown
Contributor

Contribution description

This PR adds a driver for Broadcom APDS99XX ambient light and proximity sensor series. It supports the following sensors:

Sensor Driver variant Proximitiy [counts] Ambient light [counts] RGB [counts] Illuminance [Lux]
APDS9900 apds9900 x x - x
APDS9901 apds9901 x x - x
APDS9930 apds9930 x x - x
APDS9950 apds9950 x x x -
APDS9960 apds9960 x x x -

The driver variants for the various sensors are defined as pseudomodules. To use the driver, the sensor used must be specified by the application by one of these pseudomodules, e.g.,

USEMODULE += apds9900

The driver functionality is divided in two parts:

  • Basic driver apds99xx
    The base driver apds99xx only supports a basic set of functions and has therefore a smaller size. The procedure for retrieving new data is polling. Ambient light and proximity sensing are supported.

  • Fully functional driver apds99xx_full
    The fully functional driver apds99xx_full supports all the features supported by the base driver, as well as all other sensor features, including interrupts and their configuration. Data-ready interrupts can be used to retrieve data. In addition, threshold interrupts can be used and configured.

For each of the two driver variants there is a corresponding test application: tests/driver_apds99xx and tests/driver_apds99xx_full.

The basic driver apds99xx is automatically enabled when the driver variant for the used sensor is specicied.

Testing procedure

To test the driver, the test application can be used without interrupts as following

USEMODULE=apds9960 make flash -C ```tests/driver_apds99xx``` BOARD=...

or with interrupt

USEMODULE=apds9960 make flash -C ```tests/driver_apds99xx``` BOARD=...

where the sensor used has be specified accordingly in the USEMODULE statement.

Issues/PRs references

Fixes issue #10164

@gschorcht gschorcht added Type: new feature The issue requests / The PR implemements a new feature for RIOT Area: drivers Area: Device drivers labels Nov 17, 2018
@gschorcht gschorcht added the Area: SAUL Area: Sensor/Actuator Uber Layer label Nov 18, 2018
@aabadie aabadie added this to the Release 2019.01 milestone Dec 3, 2018
Copy link
Copy Markdown
Contributor

@leandrolanzieri leandrolanzieri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! Here are some comments, I'm still reviewing it though, it's a pretty big driver. I will also try to get a sensor to test this.

#else
EXEC_RET(_reg_write(dev, APDS99XX_REG_PPCOUNT, &dev->params.prx_pulses, 1));
EXEC_RET(_update_reg(dev, APDS99XX_REG_CONTROL, APDS99XX_REG_PDIODE, 2));
#endif
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and above, please add the comment of the corresponding tag (/* MODULE_APDS9960 */)

__func__, d->params.dev, APDS99XX_I2C_ADDRESS, ## __VA_ARGS__);

#define EXEC_RET(f) { \
int _r; \
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better not to assign the value to _r inside of the if statement. Also, is the intermediate variable really needed here and below?

Copy link
Copy Markdown
Contributor Author

@gschorcht gschorcht Dec 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your review.

It would be better not to assign the value to _r inside of the if statement.

I'm wondering why? What is the reason.

Also, is the intermediate variable really needed here and below?

Yes, if we want to print out the error code in the debug message.
But more important, we have to return the error code we got from executed function in case of error.

Copy link
Copy Markdown
Contributor

@leandrolanzieri leandrolanzieri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some little comments, otherwise looks good. I don't think I will be getting the sensor in time for testing this for the feature freeze. @aabadie any chances you could?

@obgm
Copy link
Copy Markdown
Contributor

obgm commented Dec 20, 2018

FWIW: I am currently running the provided tests with an APDS 9960. Both, the basic (polling) version as well as the int-controlled version seem to work as expected. (Did not check the color values, though.)

@gschorcht
Copy link
Copy Markdown
Contributor Author

@leandrolanzieri Thanks for reviewing.

@ArtaFakhari
Copy link
Copy Markdown

Hello Everyone
Does the APDS-9960 work reliably on RIOT-OS?

double ga = 0.49; /* glas or lens attenuation factor */
double b = 1.862;
double c = 0.746;
double d = 1.291;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you multiply them all by 1000, you wouldn't need to use double here, no?

@aabadie
Copy link
Copy Markdown
Contributor

aabadie commented Feb 3, 2020

Please rebase, I have the hardware to test this PR (apds9960) :)

@gschorcht gschorcht added the CI: needs squashing Commits in this PR need to be squashed; If set, CI systems will mark this PR as unmergable label Feb 21, 2020
apds99xx_t dev;

/* save the reference to the main thread */
t_main = (thread_t*)sched_threads[sched_active_pid];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better use t_main = thread_getpid(); here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reference to the main thread is required in apds99xx_isr. Using thread_get(pid) doesn't work since it replaces the const attribute.

I changed the interrupt handling to a mutex based approach.

#endif

/* wait for 6 ms after power on reset */
xtimer_usleep(6 * US_PER_MS);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be nice to get rid of xtimer usage, especially as this is the only one. Isn't there a state register or gpio that we could poll (while(...) {} style) instead?

Copy link
Copy Markdown
Contributor Author

@gschorcht gschorcht Mar 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that this time is specified in the data sheet. It is the time the sensor needs for initialization after power-on to become ready for operation. Unfortunately there is no status flag.

Possible solutions:

  1. We simply assume that RIOT takes longer to load than this 6 ms before the device is accessed for the first time.

  2. We allow multiple tries of the _is_available function and use the I2C ACK to determine when the device is available. However, at an I2C speed of 400 kbps, each failed attempt on the bus would only take 0.025 ms, which could mean a maximum of 250 failed attempts for 6 ms.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have removed the dependency on xtimer with commit d909fad.

Copy link
Copy Markdown
Contributor

@aabadie aabadie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found some minor typos and small issues in the Python scripts (as reported by the CI).

Maybe also add ALS ==> ALSO in the list of codespell ignored words.

I'll give this PR a try later today.

@aabadie aabadie self-assigned this Mar 9, 2020
@aabadie
Copy link
Copy Markdown
Contributor

aabadie commented Mar 9, 2020

Also: please rebase :)

@aabadie
Copy link
Copy Markdown
Contributor

aabadie commented Mar 9, 2020

I could test this PR on the adafruit-clue board (after some rebase and fix of conflicts). This board embeds an apds9960 variant. Here is what I get:

  • tests/driver_apds99xx works like a charm
  • tests/driver_apds99xx_full doesn't seem to work: no interrupt is generated when I get my hand close to the proximity sensor. I configured the interrupt pin following the board schematic, so it should work. Any idea what could be the cause @gschorcht ?

@gschorcht
Copy link
Copy Markdown
Contributor Author

  • tests/driver_apds99xx_full doesn't seem to work: no interrupt is generated when I get my hand close to the proximity sensor. I configured the interrupt pin following the board schematic, so it should work. Any idea what could be the cause @gschorcht ?

@aabadie Do you see ALS measurements?

ambient = 639 [cnts]
red = 208 [cnts], green = 295 [cnts], blue = 263 [cnts]
+-------------------------------------+

If yes, interrupts are working since these measurements use the data ready interrupt. The proximity interrupt is triggered, when the proximity value exceeds 200 counts. This happens at a distance of about 2 cm. In that case, the output also shows the proximity value, for example:

proximity = 205 [cnts]
+-------------------------------------+
ambient = 322 [cnts]
red = 95 [cnts], green = 168 [cnts], blue = 135 [cnts]
+-------------------------------------+

@gschorcht
Copy link
Copy Markdown
Contributor Author

gschorcht commented Mar 10, 2020

@aabadie @smlng Thanks for reviewing. I have rebased the PR and hopefully addressed all your remarks.

@aabadie
Copy link
Copy Markdown
Contributor

aabadie commented Mar 10, 2020

I have rebased the PR and hopefully addressed all your remarks.

Thanks! I'll have a look and test again tomorrow.

@aabadie
Copy link
Copy Markdown
Contributor

aabadie commented Mar 11, 2020

Tested this PR again this morning on adafruit-clue and I can't have the interrupt based test application to work. If I comment out the mutex_lock, messages are displayed as expected: the proximity value is only displayed when I put my finger close to the sensor (which means the interrupt source bit is set).
So there's something wrong with the interrupt pin: I have no idea what could be the problem. I suspect something with the board: there are white leds that are on when waiting for the interrupt and they go down when an interrupt occurs.

I trust you that this test is working anyway.

@gschorcht
Copy link
Copy Markdown
Contributor Author

Tested this PR again this morning on adafruit-clue and I can't have the interrupt based test application to work. If I comment out the mutex_lock, messages are displayed as expected: the proximity value is only displayed when I put my finger close to the sensor (which means the interrupt source bit is set).
So there's something wrong with the interrupt pin: I have no idea what could be the problem. I suspect something with the board: there are white leds that are on when waiting for the interrupt and they go down when an interrupt occurs.

The Adafruit-Clue seems to be a very interesting board. According to the schematic P0.09 should be the APDS_IRQ pin. GPIO_IN_PU mode is supported according to nrf5240 product specification. Might it be a problem that the pin could also be used as NFC1 pin, something that has to be considered during initialization? The WHITE_LED signal is controlled by P0.10/NFC2. Might it be that the pins are mixed up?

@aabadie
Copy link
Copy Markdown
Contributor

aabadie commented Mar 11, 2020

Might it be that the pins are mixed up?

I had the same idea and tried to invert the pins but it didn't work either. I won't block this PR because of this anyway.

@aabadie
Copy link
Copy Markdown
Contributor

aabadie commented Mar 11, 2020

I think we are good with this one. @gschorcht, please squash!

@aabadie aabadie added CI: run tests If set, CI server will run tests on hardware for the labeled PR CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR and removed CI: needs squashing Commits in this PR need to be squashed; If set, CI systems will mark this PR as unmergable CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels Mar 11, 2020
Copy link
Copy Markdown
Contributor

@aabadie aabadie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK!

@aabadie aabadie merged commit 1e80376 into RIOT-OS:master Mar 11, 2020
@gschorcht
Copy link
Copy Markdown
Contributor Author

Thank you all for reviewing and testing.

BTW, I hope I can implement the gesture recognition function of APDS9960 in next week. I will provide it as separate PR.

@gschorcht gschorcht deleted the drivers_apds99xx branch March 11, 2020 15:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: drivers Area: Device drivers Area: SAUL Area: Sensor/Actuator Uber Layer CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR CI: run tests If set, CI server will run tests on hardware for the labeled PR Type: new feature The issue requests / The PR implemements a new feature for RIOT

Projects

None yet

Development

Successfully merging this pull request may close these issues.