mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
drivers/hms330x: initial import
This commit is contained in:
parent
6efee528e0
commit
fe38284a3c
@ -81,6 +81,7 @@ rsource "gp2y10xx/Kconfig"
|
||||
rsource "hdc1000/Kconfig"
|
||||
rsource "hih6130/Kconfig"
|
||||
rsource "hmc5883l/Kconfig"
|
||||
rsource "hm330x/Kconfig"
|
||||
rsource "hsc/Kconfig"
|
||||
rsource "hts221/Kconfig"
|
||||
rsource "ina2xx/Kconfig"
|
||||
|
@ -52,6 +52,10 @@ ifneq (,$(filter hmc5883l_%,$(USEMODULE)))
|
||||
USEMODULE += hmc5883l
|
||||
endif
|
||||
|
||||
ifneq (,$(filter hm330%,$(USEMODULE)))
|
||||
USEMODULE += hm330x
|
||||
endif
|
||||
|
||||
ifneq (,$(filter ina2%,$(USEMODULE)))
|
||||
USEMODULE += ina2xx
|
||||
endif
|
||||
|
69
drivers/hm330x/Kconfig
Normal file
69
drivers/hm330x/Kconfig
Normal file
@ -0,0 +1,69 @@
|
||||
# Copyright (c) 2021 Inria
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
menuconfig MODULE_HM330X
|
||||
bool
|
||||
prompt "HM330x Particulate Matter Sensor" if !(MODULE_SAUL_DEFAULT && HAVE_HM330X)
|
||||
default y if (MODULE_SAUL_DEFAULT && HAVE_HM330x)
|
||||
depends on HAS_PERIPH_I2C
|
||||
depends on HAS_PERIPH_GPIO
|
||||
depends on TEST_KCONFIG
|
||||
select MODULE_PERIPH_I2C
|
||||
select MODULE_PERIPH_GPIO
|
||||
help
|
||||
HM330X Particulate Matter Sensor for HM3301/HM3302. Select a model.
|
||||
|
||||
if MODULE_HM330X
|
||||
|
||||
choice
|
||||
bool "sensor variant"
|
||||
default MODULE_HM3301 if HAVE_HM3301
|
||||
default MODULE_HM3302 if HAVE_HM3302
|
||||
help
|
||||
Device driver for the HM330X Particulate Matter Sensor.
|
||||
|
||||
config MODULE_HM3301
|
||||
bool "HM3301"
|
||||
|
||||
config MODULE_HM3302
|
||||
bool "HM3302"
|
||||
|
||||
endchoice
|
||||
|
||||
endif # MODULE_HM330X
|
||||
|
||||
menuconfig KCONFIG_USEMODULE_HM330X
|
||||
bool "Configure HM330X driver"
|
||||
depends on USEMODULE_HM330X
|
||||
help
|
||||
Configure the HM330X driver using Kconfig.
|
||||
|
||||
if KCONFIG_USEMODULE_HM330X
|
||||
|
||||
config HM330X_INDOOR_ENVIRONMENT
|
||||
bool "Indoor environment calibration"
|
||||
default 500
|
||||
help
|
||||
The HM330X sensor outputs two set of PM* values, one calibrated for indoor
|
||||
environment and another one for atmospheric environment, set this value
|
||||
according to your deployment.
|
||||
endif # KCONFIG_USEMODULE_HM330X
|
||||
|
||||
config HAVE_HM330x
|
||||
bool
|
||||
|
||||
config HAVE_HM3301
|
||||
bool
|
||||
select HAVE_HM330X
|
||||
help
|
||||
Indicates that a HM3301 sensor is present.
|
||||
|
||||
config HAVE_HM3302
|
||||
bool
|
||||
select HAVE_HM330x
|
||||
help
|
||||
Indicates that a HM3302 sensor is present.
|
7
drivers/hm330x/Makefile
Normal file
7
drivers/hm330x/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
SRC := hm330x.c
|
||||
|
||||
ifneq (,$(filter saul,$(USEMODULE)))
|
||||
SRC += hm330x_saul.c
|
||||
endif
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
2
drivers/hm330x/Makefile.dep
Normal file
2
drivers/hm330x/Makefile.dep
Normal file
@ -0,0 +1,2 @@
|
||||
FEATURES_REQUIRED += periph_gpio
|
||||
FEATURES_REQUIRED += periph_i2c
|
5
drivers/hm330x/Makefile.include
Normal file
5
drivers/hm330x/Makefile.include
Normal file
@ -0,0 +1,5 @@
|
||||
USEMODULE_INCLUDES_hm330x := $(LAST_MAKEFILEDIR)/include
|
||||
USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_hm330x)
|
||||
|
||||
PSEUDOMODULES += hm3301
|
||||
PSEUDOMODULES += hm3302
|
166
drivers/hm330x/hm330x.c
Normal file
166
drivers/hm330x/hm330x.c
Normal file
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Inria
|
||||
*
|
||||
* 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_hm330x
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Device driver implementation for the HM330X Sensor Driver
|
||||
*
|
||||
* @author Francisco Molina <francois-xavier.molina@inria.fr>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "hm330x.h"
|
||||
#include "hm330x_constants.h"
|
||||
#include "hm330x_params.h"
|
||||
#include "clk.h"
|
||||
#include "timex.h"
|
||||
|
||||
#if IS_USED(MODULE_ZTIMER_USEC)
|
||||
#include "ztimer.h"
|
||||
#elif IS_USED(MODULE_XTIMER)
|
||||
#include "xtimer.h"
|
||||
#endif
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
/* pull reset pin low for ~10 us */
|
||||
#define HM330X_RESET_TIME_US (10)
|
||||
|
||||
int _set_i2c_mode(hm330x_t *dev)
|
||||
{
|
||||
i2c_acquire(dev->params.i2c);
|
||||
uint8_t cmd = HM330X_CMD_I2C_MODE;
|
||||
int ret = i2c_write_bytes(dev->params.i2c, HM330X_I2C_ADDRESS, &cmd, 1, 0);
|
||||
|
||||
i2c_release(dev->params.i2c);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int hm330x_init(hm330x_t *dev, const hm330x_params_t *params)
|
||||
{
|
||||
assert(dev && params);
|
||||
memset(dev, 0, sizeof(hm330x_t));
|
||||
dev->params = *params;
|
||||
|
||||
if (gpio_is_valid(dev->params.reset_pin)) {
|
||||
if (gpio_init(dev->params.reset_pin, GPIO_OUT)) {
|
||||
DEBUG_PUTS("[hm330x]: failed to init reset pin");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
if (gpio_is_valid(dev->params.set_pin)) {
|
||||
if (gpio_init(dev->params.set_pin, GPIO_OUT)) {
|
||||
DEBUG_PUTS("[hm330x]: failed to init set pin");
|
||||
return -EIO;
|
||||
}
|
||||
gpio_set(dev->params.set_pin);
|
||||
}
|
||||
|
||||
DEBUG_PUTS("[hm330x]: reset device if reset_pin");
|
||||
hm330x_reset(dev);
|
||||
|
||||
if (_set_i2c_mode(dev)) {
|
||||
DEBUG_PUTS("[hm330x]: failed set i2c mode");
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hm330x_read(hm330x_t *dev, hm330x_data_t *data)
|
||||
{
|
||||
i2c_acquire(dev->params.i2c);
|
||||
|
||||
uint8_t buf[HM330X_DATA_LENGTH] = { 0 };
|
||||
|
||||
if (i2c_read_bytes(dev->params.i2c, HM330X_I2C_ADDRESS, buf, HM330X_DATA_LENGTH, 0)) {
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
/* calculate crc */
|
||||
uint8_t crc = 0;
|
||||
|
||||
for (uint8_t i = 0; i < HM330X_DATA_LENGTH - 1; i++) {
|
||||
crc += buf[i];
|
||||
}
|
||||
|
||||
if (crc != buf[HM330X_DATA_LENGTH - 1]) {
|
||||
DEBUG("crc mismatch expected %02x got %02x\n", buf[HM330X_DATA_LENGTH - 1], crc);
|
||||
}
|
||||
|
||||
i2c_release(dev->params.i2c);
|
||||
|
||||
hm330x_data_t tmp = {
|
||||
.mc_pm_1 = (buf[4] << 8) | buf[5],
|
||||
.mc_pm_2p5 = (buf[6] << 8) | buf[7],
|
||||
.mc_pm_10 = (buf[8] << 8) | buf[9],
|
||||
.amc_pm_1 = (buf[10] << 8) | buf[11],
|
||||
.amc_pm_2p5 = (buf[12] << 8) | buf[13],
|
||||
.amc_pm_10 = (buf[14] << 8) | buf[15],
|
||||
#if IS_USED(MODULE_HM3302)
|
||||
.nc_pm_0p3 = (buf[16] << 8) | buf[17],
|
||||
.nc_pm_0p5 = (buf[18] << 8) | buf[19],
|
||||
.nc_pm_1 = (buf[20] << 8) | buf[21],
|
||||
.nc_pm_2p5 = (buf[22] << 8) | buf[23],
|
||||
.nc_pm_5 = (buf[24] << 8) | buf[25],
|
||||
.nc_pm_10 = (buf[26] << 8) | buf[27],
|
||||
#endif
|
||||
};
|
||||
|
||||
memcpy(data, &tmp, sizeof(hm330x_data_t));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hm330x_reset(hm330x_t *dev)
|
||||
{
|
||||
if (gpio_is_valid(dev->params.reset_pin)) {
|
||||
gpio_clear(dev->params.reset_pin);
|
||||
#if IS_USED(MODULE_ZTIMER_USEC)
|
||||
ztimer_sleep(ZTIMER_USEC, HM330X_RESET_TIME_US);
|
||||
#elif IS_USED(MODULE_XTIMER)
|
||||
xtimer_sleep(HM330X_RESET_TIME_US);
|
||||
#else
|
||||
/* each loop iteration is at least 3 instructions, so this tries
|
||||
to approximate the target time based on coreclk(), but
|
||||
a precise time is not needed here */
|
||||
for (uint32_t i = 0;
|
||||
i < HM330X_RESET_TIME_US * (coreclk() / US_PER_SEC / 3);
|
||||
i++) {
|
||||
/* Make sure for loop is not optimized out */
|
||||
__asm__ ("");
|
||||
}
|
||||
#endif
|
||||
gpio_set(dev->params.reset_pin);
|
||||
}
|
||||
}
|
||||
|
||||
void hm330x_sleep(hm330x_t *dev)
|
||||
{
|
||||
if (gpio_is_valid(dev->params.set_pin)) {
|
||||
gpio_clear(dev->params.set_pin);
|
||||
}
|
||||
}
|
||||
|
||||
void hm330x_wakeup(hm330x_t *dev)
|
||||
{
|
||||
if (gpio_is_valid(dev->params.set_pin)) {
|
||||
gpio_set(dev->params.set_pin);
|
||||
}
|
||||
}
|
175
drivers/hm330x/hm330x_saul.c
Normal file
175
drivers/hm330x/hm330x_saul.c
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Inria
|
||||
*
|
||||
* 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_hm330x
|
||||
* @{
|
||||
* @file
|
||||
* @brief SAUL adaption of the HM330X particulate matter sensor
|
||||
*
|
||||
* @author Francisco Molina <francois-xavier.molina@inria.fr>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "phydat.h"
|
||||
#include "saul.h"
|
||||
#include "hm330x.h"
|
||||
#include "hm330x_params.h"
|
||||
#include "hm330x_constants.h"
|
||||
|
||||
static int read_mc_pm_1(const void *_dev, phydat_t *data)
|
||||
{
|
||||
hm330x_t *dev = (hm330x_t *)_dev;
|
||||
|
||||
hm330x_data_t values;
|
||||
|
||||
hm330x_read(dev, &values);
|
||||
data->unit = UNIT_GPM3;
|
||||
data->scale = -6;
|
||||
uint32_t value;
|
||||
|
||||
if (IS_ACTIVE(CONFIG_HM330X_INDOOR_ENVIRONMENT)) {
|
||||
value = values.mc_pm_1;
|
||||
}
|
||||
else {
|
||||
value = values.amc_pm_1;
|
||||
}
|
||||
phydat_fit(data, (int32_t *)&value, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int read_mc_pm_2p5(const void *_dev, phydat_t *data)
|
||||
{
|
||||
hm330x_t *dev = (hm330x_t *)_dev;
|
||||
|
||||
hm330x_data_t values;
|
||||
|
||||
hm330x_read(dev, &values);
|
||||
data->unit = UNIT_GPM3;
|
||||
data->scale = -6;
|
||||
uint32_t value;
|
||||
|
||||
if (IS_ACTIVE(CONFIG_HM330X_INDOOR_ENVIRONMENT)) {
|
||||
value = values.mc_pm_2p5;
|
||||
}
|
||||
else {
|
||||
value = values.amc_pm_2p5;
|
||||
}
|
||||
phydat_fit(data, (int32_t *)&value, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int read_mc_pm_10(const void *_dev, phydat_t *data)
|
||||
{
|
||||
hm330x_t *dev = (hm330x_t *)_dev;
|
||||
|
||||
hm330x_data_t values;
|
||||
|
||||
hm330x_read(dev, &values);
|
||||
data->unit = UNIT_GPM3;
|
||||
data->scale = -6;
|
||||
uint32_t value;
|
||||
|
||||
if (IS_ACTIVE(CONFIG_HM330X_INDOOR_ENVIRONMENT)) {
|
||||
value = values.mc_pm_10;
|
||||
}
|
||||
else {
|
||||
value = values.amc_pm_10;
|
||||
}
|
||||
phydat_fit(data, (int32_t *)&value, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if IS_USED(MODULE_HM3302)
|
||||
static int read_nc_pm_1(const void *_dev, phydat_t *data)
|
||||
{
|
||||
hm330x_t *dev = (hm330x_t *)_dev;
|
||||
|
||||
hm330x_data_t values;
|
||||
|
||||
hm330x_read(dev, &values);
|
||||
data->unit = UNIT_CPM3;
|
||||
data->scale = 4;
|
||||
uint32_t value = values.nc_pm_1;
|
||||
|
||||
phydat_fit(data, (int32_t *)&value, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int read_nc_pm_2p5(const void *_dev, phydat_t *data)
|
||||
{
|
||||
hm330x_t *dev = (hm330x_t *)_dev;
|
||||
|
||||
hm330x_data_t values;
|
||||
|
||||
hm330x_read(dev, &values);
|
||||
data->unit = UNIT_CPM3;
|
||||
data->scale = 4;
|
||||
uint32_t value = values.nc_pm_2p5;
|
||||
|
||||
phydat_fit(data, (int32_t *)&value, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int read_nc_pm_10(const void *_dev, phydat_t *data)
|
||||
{
|
||||
hm330x_t *dev = (hm330x_t *)_dev;
|
||||
|
||||
hm330x_data_t values;
|
||||
|
||||
hm330x_read(dev, &values);
|
||||
data->unit = UNIT_CPM3;
|
||||
data->scale = 4;
|
||||
uint32_t value = values.nc_pm_10;
|
||||
|
||||
phydat_fit(data, (int32_t *)&value, 1);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
const saul_driver_t hm330x_saul_driver_mc_pm_1 = {
|
||||
.read = read_mc_pm_1,
|
||||
.write = saul_notsup,
|
||||
.type = SAUL_SENSE_PM
|
||||
};
|
||||
|
||||
const saul_driver_t hm330x_saul_driver_mc_pm_2p5 = {
|
||||
.read = read_mc_pm_2p5,
|
||||
.write = saul_notsup,
|
||||
.type = SAUL_SENSE_PM
|
||||
};
|
||||
|
||||
const saul_driver_t hm330x_saul_driver_mc_pm_10 = {
|
||||
.read = read_mc_pm_10,
|
||||
.write = saul_notsup,
|
||||
.type = SAUL_SENSE_PM
|
||||
};
|
||||
|
||||
#if IS_USED(MODULE_HM3302)
|
||||
const saul_driver_t hm330x_saul_driver_nc_pm_1 = {
|
||||
.read = read_nc_pm_1,
|
||||
.write = saul_notsup,
|
||||
.type = SAUL_SENSE_COUNT
|
||||
};
|
||||
|
||||
const saul_driver_t hm330x_saul_driver_nc_pm_2p5 = {
|
||||
.read = read_nc_pm_2p5,
|
||||
.write = saul_notsup,
|
||||
.type = SAUL_SENSE_COUNT
|
||||
};
|
||||
|
||||
const saul_driver_t hm330x_saul_driver_nc_pm_10 = {
|
||||
.read = read_nc_pm_10,
|
||||
.write = saul_notsup,
|
||||
.type = SAUL_SENSE_COUNT
|
||||
};
|
||||
#endif
|
46
drivers/hm330x/include/hm330x_constants.h
Normal file
46
drivers/hm330x/include/hm330x_constants.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Inria
|
||||
*
|
||||
* 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_hm330x
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Internal addresses, registers and constants
|
||||
*
|
||||
* @author Francisco Molina <francois-xavier.molina@inria.fr>
|
||||
*/
|
||||
|
||||
#ifndef HM330X_CONSTANTS_H
|
||||
#define HM330X_CONSTANTS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief HM330X default i2c address
|
||||
*/
|
||||
#define HM330X_I2C_ADDRESS (0x40U)
|
||||
|
||||
/**
|
||||
* @brief HM330X data length
|
||||
*/
|
||||
#define HM330X_DATA_LENGTH (29U)
|
||||
|
||||
/**
|
||||
* @brief HM330X cmd to switch to i2c mode
|
||||
*/
|
||||
#define HM330X_CMD_I2C_MODE (0x88U)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HM330X_CONSTANTS_H */
|
||||
/** @} */
|
102
drivers/hm330x/include/hm330x_params.h
Normal file
102
drivers/hm330x/include/hm330x_params.h
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Inria
|
||||
*
|
||||
* 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_hm330x
|
||||
*
|
||||
* @{
|
||||
* @file
|
||||
* @brief Default configuration
|
||||
*
|
||||
* @author Francisco Molina <francois-xavier.molina@inria.fr>
|
||||
*/
|
||||
|
||||
#ifndef HM330X_PARAMS_H
|
||||
#define HM330X_PARAMS_H
|
||||
|
||||
#include "board.h"
|
||||
#include "saul_reg.h"
|
||||
|
||||
#include "hm330x.h"
|
||||
#include "hm330x_constants.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name Set default configuration parameters
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief HM330X default I2C bus
|
||||
*/
|
||||
#ifndef HM330X_PARAM_I2C_DEV
|
||||
#define HM330X_PARAM_I2C_DEV I2C_DEV(0)
|
||||
#endif
|
||||
/**
|
||||
* @brief HM330X default reset pin
|
||||
*/
|
||||
#ifndef HM330X_PARAM_RESET_PIN
|
||||
#define HM330X_PARAM_RESET_PIN GPIO_UNDEF
|
||||
#endif
|
||||
/**
|
||||
* @brief HM330X default set pin
|
||||
*/
|
||||
#ifndef HM330X_PARAM_SET_PIN
|
||||
#define HM330X_PARAM_SET_PIN GPIO_UNDEF
|
||||
#endif
|
||||
/**
|
||||
* @brief HM330X default SAUL information
|
||||
*/
|
||||
#ifndef HM330X_SAUL_INFO
|
||||
#define HM330X_SAUL_INFO { .name = "hm330x" }
|
||||
#endif
|
||||
/**
|
||||
* @brief HM330X default parameters
|
||||
*/
|
||||
#ifndef HM330X_PARAMS
|
||||
#define HM330X_PARAMS { .i2c = HM330X_PARAM_I2C_DEV, \
|
||||
.reset_pin = HM330X_PARAM_RESET_PIN, \
|
||||
.set_pin = HM330X_PARAM_SET_PIN }
|
||||
#endif
|
||||
/**@}*/
|
||||
|
||||
/**
|
||||
* @brief Configuration struct
|
||||
*/
|
||||
static const hm330x_params_t hm330x_params[] =
|
||||
{
|
||||
HM330X_PARAMS
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Define the number of configured sensors
|
||||
*/
|
||||
#define HM330X_NUMOF ARRAY_SIZE(hm330x_params)
|
||||
|
||||
/**
|
||||
* @brief Additional meta information to keep in the SAUL registry
|
||||
*/
|
||||
static const saul_reg_info_t hm330x_saul_info[] =
|
||||
{
|
||||
HM330X_SAUL_INFO
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Number of saul info structs
|
||||
*/
|
||||
#define HM330X_INFO_NUM ARRAY_SIZE(hm330x_saul_info)
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HM330X_PARAMS_H */
|
||||
/** @} */
|
142
drivers/include/hm330x.h
Normal file
142
drivers/include/hm330x.h
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Inria
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup drivers_hm330x HM330X Laser Particulate Matter Sensor
|
||||
* @ingroup drivers_sensors
|
||||
* @ingroup drivers_saul
|
||||
* @brief Driver for HM330X particle matter sensor
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* About
|
||||
* =====
|
||||
*
|
||||
* This driver provides an interface for the HM-330/3600 laser dust sensor.
|
||||
* The datasheet can be found [here](https://files.seeedstudio.com/wiki/Grove-Laser_PM2.5_Sensor-HM330X/res/HM-3300%263600_V2.1.pdf)
|
||||
*
|
||||
* The device also support an UART mode, but the currently available
|
||||
* breakout from [seedstudio](https://www.seeedstudio.com/Grove-Laser-PM2-5-Sensor-HM330X.html)
|
||||
* only supports I2C.
|
||||
*
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* @author Francisco Molina <francois-xavier.molina@inria.fr>
|
||||
*/
|
||||
|
||||
#ifndef HM330X_H
|
||||
#define HM330X_H
|
||||
|
||||
#include "periph/i2c.h"
|
||||
#include "periph/gpio.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief HM330X deployment setting
|
||||
*
|
||||
* The HM330X sensor outputs two set of PM* values, one calibrated for indoor
|
||||
* environment and another one for atmospheric environment, set this value
|
||||
* according to your deployment.
|
||||
*
|
||||
*/
|
||||
#ifndef CONFIG_HM330X_INDOOR_ENVIRONMENT
|
||||
#define CONFIG_HM330X_INDOOR_ENVIRONMENT 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Set of measured particulate matter values as sent by the device
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t mc_pm_1; /**< PM1.0 ug/m3 (ultrafine particles) <= 1µm [µg/m^3] */
|
||||
uint16_t mc_pm_2p5; /**< PM2.5 ug/m3 (combustion particles, organic compounds, metals) <= 2.5µm [µg/m^3] */
|
||||
uint16_t mc_pm_10; /**< PM10 ug/m3 (dust, pollen, mould spores) <= 10µm [µg/m^3] */
|
||||
uint16_t amc_pm_1; /**< PM1.0 ug/m3 (atmospheric environment) <= 1µm [µg/m^3] */
|
||||
uint16_t amc_pm_2p5; /**< PM2.5 ug/m3 (atmospheric environment) <= 2.5µm [µg/m^3] */
|
||||
uint16_t amc_pm_10; /**< PM10 ug/m3 (atmospheric environment) <= 10µm [µg/m^3] */
|
||||
#if IS_USED(MODULE_HM3302)
|
||||
uint16_t nc_pm_0p3; /**< Number concentration of all particles <= 0.3µm [#/cm^3] */
|
||||
uint16_t nc_pm_0p5; /**< Number concentration of all particles <= 0.5µm [#/cm^3] */
|
||||
uint16_t nc_pm_1; /**< Number concentration of all particles <= 1µm [#/cm^3] */
|
||||
uint16_t nc_pm_2p5; /**< Number concentration of all particles <= 2.5µm [#/cm^3] */
|
||||
uint16_t nc_pm_5; /**< Number concentration of all particles <= 5µm [#/cm^3] */
|
||||
uint16_t nc_pm_10; /**< Number concentration of all particles <= 10µm [#/cm^3] */
|
||||
#endif
|
||||
} hm330x_data_t;
|
||||
|
||||
/**
|
||||
* @brief Device initialization parameters
|
||||
*/
|
||||
typedef struct {
|
||||
i2c_t i2c; /**< The I2C device */
|
||||
gpio_t reset_pin; /**< Reset pin, active low */
|
||||
gpio_t set_pin; /**< Set/Enable pin, active high*/
|
||||
} hm330x_params_t;
|
||||
|
||||
/**
|
||||
* @brief Device descriptor for the driver
|
||||
*/
|
||||
typedef struct {
|
||||
hm330x_params_t params; /**< parameters of the sensor device */
|
||||
} hm330x_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize the given device
|
||||
*
|
||||
* @param[inout] dev Device descriptor of the driver
|
||||
* @param[in] params Initialization parameters
|
||||
*
|
||||
* @retval 0 Success
|
||||
* @retval -EIO Failed to initialize GPIO pins
|
||||
* @retval -EPROTO Sensor did not acknowledge command
|
||||
*/
|
||||
int hm330x_init(hm330x_t *dev, const hm330x_params_t *params);
|
||||
|
||||
/**
|
||||
* @brief Read particle matter measurements
|
||||
*
|
||||
* @param[in] dev Device descriptor of the driver
|
||||
* @param[out] data Pre-allocated particle matter data
|
||||
*
|
||||
* @retval 0 Success
|
||||
* @retval -EBADMSG CRC checksum didn't match
|
||||
* @retval -EPROTO Sensor did not acknowledge command
|
||||
*/
|
||||
int hm330x_read(hm330x_t *dev, hm330x_data_t* data);
|
||||
|
||||
/**
|
||||
* @brief Reset the sensor.
|
||||
*
|
||||
* @param[in] dev Device descriptor of the driver
|
||||
*/
|
||||
void hm330x_reset(hm330x_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Set Device to Sleep
|
||||
*
|
||||
* @param[in] dev Device descriptor of the driver
|
||||
*/
|
||||
void hm330x_sleep(hm330x_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Wakeup Device
|
||||
*
|
||||
* @param[in] dev Device descriptor of the driver
|
||||
*/
|
||||
void hm330x_wakeup(hm330x_t *dev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HM330X_H */
|
||||
/** @} */
|
88
drivers/saul/init_devs/auto_init_hm3301.c
Normal file
88
drivers/saul/init_devs/auto_init_hm3301.c
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Inria
|
||||
*
|
||||
* 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 sys_auto_init_saul
|
||||
* @{
|
||||
* @file
|
||||
* @brief Auto initialization for HM330X particle matter sensor
|
||||
*
|
||||
* @author Francisco Molina <francois-xavier.molina@inria.fr>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "assert.h"
|
||||
#include "log.h"
|
||||
#include "saul_reg.h"
|
||||
#include "hm330x_params.h"
|
||||
#include "hm330x.h"
|
||||
|
||||
/**
|
||||
* @brief Allocate memory for the device descriptors
|
||||
*/
|
||||
static hm330x_t hm330x_devs[HM330X_NUMOF];
|
||||
|
||||
#if IS_ACTIVE(MODULE_SAUL)
|
||||
/**
|
||||
* @brief Number of logical saul devices per physical sensor
|
||||
*/
|
||||
#define HM330X_SAUL_DEV_NUM (6)
|
||||
|
||||
/**
|
||||
* @brief Memory for the SAUL registry entries
|
||||
*/
|
||||
static saul_reg_t saul_entries[HM330X_NUMOF * HM330X_SAUL_DEV_NUM];
|
||||
|
||||
/**
|
||||
* @brief Define the number of saul info
|
||||
*/
|
||||
#define HM330X_INFO_NUM ARRAY_SIZE(hm330x_saul_info)
|
||||
|
||||
/**
|
||||
* @name Import SAUL endpoints
|
||||
* @{
|
||||
*/
|
||||
extern const saul_driver_t hm330x_saul_driver_mc_pm_1;
|
||||
extern const saul_driver_t hm330x_saul_driver_mc_pm_2p5;
|
||||
extern const saul_driver_t hm330x_saul_driver_mc_pm_10;
|
||||
extern const saul_driver_t hm330x_saul_driver_nc_pm_1;
|
||||
extern const saul_driver_t hm330x_saul_driver_nc_pm_2p5;
|
||||
extern const saul_driver_t hm330x_saul_driver_nc_pm_10;
|
||||
/** @} */
|
||||
#endif
|
||||
|
||||
void auto_init_hm330x(void)
|
||||
{
|
||||
#if IS_ACTIVE(MODULE_SAUL)
|
||||
assert(HM330X_INFO_NUM == HM330X_NUMOF);
|
||||
#endif
|
||||
|
||||
for (unsigned int i = 0; i < HM330X_NUMOF; i++) {
|
||||
LOG_DEBUG("[auto_init_saul] initializing hm330x #%u\n", i);
|
||||
|
||||
if (hm330x_init(&hm330x_devs[i], &hm330x_params[i])) {
|
||||
LOG_ERROR("[auto_init_saul] error initializing hm330x #%u\n",
|
||||
i);
|
||||
continue;
|
||||
}
|
||||
#if IS_ACTIVE(MODULE_SAUL)
|
||||
saul_entries[(i * HM330X_SAUL_DEV_NUM)].driver = &hm330x_saul_driver_mc_pm_1;
|
||||
saul_entries[(i * HM330X_SAUL_DEV_NUM) + 1].driver = &hm330x_saul_driver_mc_pm_2p5;
|
||||
saul_entries[(i * HM330X_SAUL_DEV_NUM) + 2].driver = &hm330x_saul_driver_mc_pm_10;
|
||||
saul_entries[(i * HM330X_SAUL_DEV_NUM) + 3].driver = &hm330x_saul_driver_nc_pm_1;
|
||||
saul_entries[(i * HM330X_SAUL_DEV_NUM) + 4].driver = &hm330x_saul_driver_nc_pm_2p5;
|
||||
saul_entries[(i * HM330X_SAUL_DEV_NUM) + 5].driver = &hm330x_saul_driver_nc_pm_10;
|
||||
/* the physical device is the same for all logical SAUL instances */
|
||||
for (unsigned x = 0; x < HM330X_SAUL_DEV_NUM; x++) {
|
||||
saul_entries[i * HM330X_SAUL_DEV_NUM + x].dev = &(hm330x_devs[i]);
|
||||
saul_entries[i * HM330X_SAUL_DEV_NUM + x].name = hm330x_saul_info[i].name;
|
||||
saul_reg_add(&saul_entries[i * HM330X_SAUL_DEV_NUM + x]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
@ -123,6 +123,10 @@ void saul_init_devs(void)
|
||||
extern void auto_init_hdc1000(void);
|
||||
auto_init_hdc1000();
|
||||
}
|
||||
if (IS_USED(MODULE_HM330X)) {
|
||||
extern void auto_init_hm330x(void);
|
||||
auto_init_hm330x();
|
||||
}
|
||||
if (IS_USED(MODULE_HSC)) {
|
||||
extern void auto_init_hsc(void);
|
||||
auto_init_hsc();
|
||||
|
Loading…
Reference in New Issue
Block a user