diff --git a/drivers/Kconfig b/drivers/Kconfig index eead0d8c2e..e34d68da35 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -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" diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 0940945c31..064c9c093e 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -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 diff --git a/drivers/hm330x/Kconfig b/drivers/hm330x/Kconfig new file mode 100644 index 0000000000..774bf1f712 --- /dev/null +++ b/drivers/hm330x/Kconfig @@ -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. diff --git a/drivers/hm330x/Makefile b/drivers/hm330x/Makefile new file mode 100644 index 0000000000..b51d9c28b9 --- /dev/null +++ b/drivers/hm330x/Makefile @@ -0,0 +1,7 @@ +SRC := hm330x.c + +ifneq (,$(filter saul,$(USEMODULE))) + SRC += hm330x_saul.c +endif + +include $(RIOTBASE)/Makefile.base diff --git a/drivers/hm330x/Makefile.dep b/drivers/hm330x/Makefile.dep new file mode 100644 index 0000000000..7fc0c52dd6 --- /dev/null +++ b/drivers/hm330x/Makefile.dep @@ -0,0 +1,2 @@ +FEATURES_REQUIRED += periph_gpio +FEATURES_REQUIRED += periph_i2c diff --git a/drivers/hm330x/Makefile.include b/drivers/hm330x/Makefile.include new file mode 100644 index 0000000000..a9aa64feca --- /dev/null +++ b/drivers/hm330x/Makefile.include @@ -0,0 +1,5 @@ +USEMODULE_INCLUDES_hm330x := $(LAST_MAKEFILEDIR)/include +USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_hm330x) + +PSEUDOMODULES += hm3301 +PSEUDOMODULES += hm3302 diff --git a/drivers/hm330x/hm330x.c b/drivers/hm330x/hm330x.c new file mode 100644 index 0000000000..42f0bd48a1 --- /dev/null +++ b/drivers/hm330x/hm330x.c @@ -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 + * + * @} + */ + +#include +#include +#include + +#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); + } +} diff --git a/drivers/hm330x/hm330x_saul.c b/drivers/hm330x/hm330x_saul.c new file mode 100644 index 0000000000..3bba7726ff --- /dev/null +++ b/drivers/hm330x/hm330x_saul.c @@ -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 + * @} + */ + +#include +#include +#include + +#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 diff --git a/drivers/hm330x/include/hm330x_constants.h b/drivers/hm330x/include/hm330x_constants.h new file mode 100644 index 0000000000..e25abb22f1 --- /dev/null +++ b/drivers/hm330x/include/hm330x_constants.h @@ -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 + */ + +#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 */ +/** @} */ diff --git a/drivers/hm330x/include/hm330x_params.h b/drivers/hm330x/include/hm330x_params.h new file mode 100644 index 0000000000..1dd53acaf2 --- /dev/null +++ b/drivers/hm330x/include/hm330x_params.h @@ -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 + */ + +#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 */ +/** @} */ diff --git a/drivers/include/hm330x.h b/drivers/include/hm330x.h new file mode 100644 index 0000000000..2230290a00 --- /dev/null +++ b/drivers/include/hm330x.h @@ -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 + */ + +#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 */ +/** @} */ diff --git a/drivers/saul/init_devs/auto_init_hm3301.c b/drivers/saul/init_devs/auto_init_hm3301.c new file mode 100644 index 0000000000..6bc5ecf955 --- /dev/null +++ b/drivers/saul/init_devs/auto_init_hm3301.c @@ -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 + * @} + */ + +#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 + } +} diff --git a/drivers/saul/init_devs/init.c b/drivers/saul/init_devs/init.c index e052c8410c..11dfae985a 100644 --- a/drivers/saul/init_devs/init.c +++ b/drivers/saul/init_devs/init.c @@ -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();