mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
e920a2e645
Signed-off-by: Jean Pierre Dudey <me@jeandudey.tech>
145 lines
3.5 KiB
C
145 lines
3.5 KiB
C
/*
|
|
* Copyright (C) 2020 Locha Inc
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @ingroup drivers_gp2y10xx
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief GP2Y10xx Compact Optical Dust Sensor device driver
|
|
*
|
|
* @author Jean Pierre Dudey <jeandudey@hotmail.com>
|
|
* @}
|
|
*/
|
|
|
|
#include <assert.h>
|
|
|
|
#include "gp2y10xx.h"
|
|
#include "gp2y10xx_params.h"
|
|
|
|
#include "periph/adc.h"
|
|
#include "periph/gpio.h"
|
|
#include "xtimer.h"
|
|
|
|
#define ENABLE_DEBUG 0
|
|
#include "debug.h"
|
|
|
|
#define ILED_PULSE_WAIT_US (280U)
|
|
#define ILED_PULSE_OFF_US (20U)
|
|
#define NO_DUST_VOLTAGE (500)
|
|
|
|
static inline void _iled_on(const gp2y10xx_t *dev)
|
|
{
|
|
gpio_write(dev->params.iled_pin,
|
|
dev->params.iled_level == GP2Y10XX_ILED_LEVEL_HIGH);
|
|
}
|
|
|
|
static inline void _iled_off(const gp2y10xx_t *dev)
|
|
{
|
|
gpio_write(dev->params.iled_pin,
|
|
dev->params.iled_level == GP2Y10XX_ILED_LEVEL_LOW);
|
|
}
|
|
|
|
static inline int _adc_resolution(adc_res_t res)
|
|
{
|
|
/* get the resolution the ADC can read */
|
|
int exp = 0;
|
|
switch (res) {
|
|
case ADC_RES_6BIT:
|
|
exp = 6;
|
|
break;
|
|
case ADC_RES_8BIT:
|
|
exp = 8;
|
|
break;
|
|
case ADC_RES_10BIT:
|
|
exp = 10;
|
|
break;
|
|
case ADC_RES_12BIT:
|
|
exp = 12;
|
|
break;
|
|
case ADC_RES_14BIT:
|
|
exp = 14;
|
|
break;
|
|
case ADC_RES_16BIT:
|
|
exp = 16;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
|
|
return exp;
|
|
}
|
|
|
|
int gp2y10xx_init(gp2y10xx_t *dev, const gp2y10xx_params_t *params)
|
|
{
|
|
assert(dev && params);
|
|
|
|
dev->params = *params;
|
|
if (adc_init(dev->params.aout) < 0) {
|
|
return GP2Y10XX_ERR_ADC;
|
|
}
|
|
|
|
if (gpio_init(dev->params.iled_pin, GPIO_OUT) < 0) {
|
|
return GP2Y10XX_ERR_ILED;
|
|
}
|
|
_iled_off(dev);
|
|
|
|
return GP2Y10XX_OK;
|
|
}
|
|
|
|
int gp2y10xx_read_density(const gp2y10xx_t *dev, uint16_t *density)
|
|
{
|
|
uint32_t adc_sum = 0;
|
|
int32_t adc_value;
|
|
uint32_t voltage;
|
|
|
|
assert(dev && density);
|
|
|
|
for (unsigned i = 0; i < CONFIG_GP2Y10XX_MAX_READINGS; i++) {
|
|
/* turn ILED on/off and wait a little bit to read the ADC */
|
|
_iled_on(dev);
|
|
xtimer_usleep(ILED_PULSE_WAIT_US);
|
|
if ((adc_value = adc_sample(dev->params.aout,
|
|
dev->params.adc_res)) < 0) {
|
|
_iled_off(dev);
|
|
return GP2Y10XX_ERR_ADC;
|
|
}
|
|
_iled_off(dev);
|
|
xtimer_usleep(ILED_PULSE_OFF_US);
|
|
DEBUG("[gp2y10xx] read: unfiltered adc_value=%"PRIi32"\n", adc_value);
|
|
|
|
adc_sum += adc_value;
|
|
}
|
|
/* calculate the average between all readings */
|
|
adc_value = adc_sum / CONFIG_GP2Y10XX_MAX_READINGS;
|
|
DEBUG("[gp2y10xx] read: filtered adc_value=%"PRIi32"\n", adc_value);
|
|
|
|
/* convert ADC reading to a voltage */
|
|
voltage = (dev->params.vref * adc_value);
|
|
voltage >>= _adc_resolution(dev->params.adc_res);
|
|
/* check if the voltage provides us meaningful data */
|
|
if (voltage <= NO_DUST_VOLTAGE) {
|
|
*density = 0;
|
|
}
|
|
else {
|
|
voltage -= NO_DUST_VOLTAGE;
|
|
}
|
|
DEBUG("[gp2y10xx] read: voltage=%"PRIi32"\n", voltage);
|
|
/* multiply by the magic eleven.
|
|
*
|
|
* XXX: find out why 11 is magic and the shenanigans behind it
|
|
*/
|
|
voltage *= 11;
|
|
|
|
/* convert "voltage" to ug/m3 */
|
|
*density = (uint16_t)((voltage * 2) / 10);
|
|
|
|
return GP2Y10XX_OK;
|
|
}
|