/* * 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 * @} */ #include #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; }