1
0
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:
Francisco Molina 2021-12-16 15:19:44 +01:00
parent 6efee528e0
commit fe38284a3c
13 changed files with 811 additions and 0 deletions

View File

@ -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"

View File

@ -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
View 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
View File

@ -0,0 +1,7 @@
SRC := hm330x.c
ifneq (,$(filter saul,$(USEMODULE)))
SRC += hm330x_saul.c
endif
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,2 @@
FEATURES_REQUIRED += periph_gpio
FEATURES_REQUIRED += periph_i2c

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

View 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

View 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 */
/** @} */

View 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
View 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 */
/** @} */

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

View File

@ -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();