1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/gp2y10xx/gp2y10xx.c
Jean Pierre Dudey e920a2e645 drivers: add gp2y10xx dust sensor
Signed-off-by: Jean Pierre Dudey <me@jeandudey.tech>
2020-11-18 09:13:56 +01:00

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;
}