This is an automated email from the ASF dual-hosted git repository. xiaoxiang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push: new c15b900a88 sensors/sht4x: Converted SHT4X driver to UORB framework. c15b900a88 is described below commit c15b900a8807fdc41c1f04731cbe4eca877e9089 Author: Matteo Golin <matteo.go...@gmail.com> AuthorDate: Wed Jan 15 20:30:12 2025 -0500 sensors/sht4x: Converted SHT4X driver to UORB framework. --- .../components/drivers/special/sensors/sht4x.rst | 103 ++-- .../arm/rp2040/common/src/rp2040_common_bringup.c | 4 +- drivers/sensors/CMakeLists.txt | 2 +- drivers/sensors/Kconfig | 21 +- drivers/sensors/Make.defs | 2 +- drivers/sensors/{sht4x.c => sht4x_uorb.c} | 626 ++++++++++++--------- include/nuttx/sensors/sht4x.h | 17 +- 7 files changed, 403 insertions(+), 372 deletions(-) diff --git a/Documentation/components/drivers/special/sensors/sht4x.rst b/Documentation/components/drivers/special/sensors/sht4x.rst index 56991b3061..a2d99ad793 100644 --- a/Documentation/components/drivers/special/sensors/sht4x.rst +++ b/Documentation/components/drivers/special/sensors/sht4x.rst @@ -1,3 +1,4 @@ +===== SHT4X ===== @@ -7,7 +8,8 @@ The SHT4x is a family of temperature and humidity sensors created by Sensirion which operates over I2C. They include a small heating element. The driver provided allows interfacing with the sensor over I2C. It has been -tested against the SHT41. +tested against the SHT41. This driver uses the :doc:`uorb +</components/drivers/special/sensors/sensors_uorb>` interface. Application Programming Interface ================================= @@ -16,42 +18,48 @@ The header file for the SHT4X driver interface can be included using: .. code-block:: c - # include <nuttx/sensors/sht4x.h> + #include <nuttx/sensors/sht4x.h> + +The SHT4x registration function allows the driver to be registered as a UORB +driver. -The SHT4x registration function allows the driver to be registered as a POSIX -character driver. +The SHT4x measures both ambient temperature and humidity, so registering this +driver will cause two new UORB topics to appear: ``sensor_humi<n>`` and +``sensor_temp<n>``. -The standard POSIX `read()` operation will return the temperature and humidity -measurements in plain-text, which is useful when debugging/testing the driver -using `cat` from the shell. +.. code-block:: c -The `write()` operation is not implemented for this sensor. + int err; + err = sht4x_register(i2c_master, 0, 0x44); + if (err < 0) + { + syslog(LOG_ERR, "Couldn't register SHT4X driver at 0x44: %d\n", err); + } -Specific operations the sensor offers can be performed via the POSIX `ioctl` -operation. The supported commands are: +To debug this device, you can include the ``uorb_listener`` in your build with +debugging enabled. Running it will show the sensor measurements. - * :c:macro:`SNIOC_RESET` - * :c:macro:`SNIOC_WHO_AM_I` - * :c:macro:`SNIOC_READ_RAW_DATA` - * :c:macro:`SNIOC_MEASURE` - * :c:macro:`SNIOC_READ_CONVERT_DATA` - * :c:macro:`SNIOC_HEAT` - * :c:macro:`SNIOC_CONFIGURE` +This sensor also offers some addition control commands for using the onboard +heater and checking the serial number. These control commands can be used on +either topic (humidity or temperature), since they control the device as a +whole. -.. c:macro:: SNIOC_RESET +``SNIOC_RESET`` +---------------- - This will perform the SHT4X's soft reset command. +This will perform the SHT4X's soft reset command. .. code-block:: c - err = ioctl(sensor, SNIOC_RESET); + err = orb_ioctl(sensor, SNIOC_RESET); if (err) { fprintf(stderr, "SNIOC_RESET: %s\n", strerror(errno)); } else { puts("RESET success!"); } -.. c:macro:: SNIOC_WHO_AM_I +``SNIOC_WHO_AM_I`` +------------------ This command reads the serial number of the SHT4X sensor. The serial number is returned in the argument to the command, which must be a `uint32_t` pointer. @@ -59,49 +67,15 @@ returned in the argument to the command, which must be a `uint32_t` pointer. .. code-block:: c uint32_t serialno = 0; - err = ioctl(sensor, SNIOC_WHO_AM_I, &serialno); - -.. c:macro:: SNIOC_READ_RAW_DATA - -This command allows the caller to read the raw data returned from the sensor, -without the driver performing any calculation to convert it into familiar units -(i.e. degrees Celsius for temperature). - -The argument to this command must be a pointer to a `struct sht4x_raw_data_s` -structure. The raw data will be returned here. - -.. code-block:: c - - struct sht4x_raw_data_s raw; - err = ioctl(sensor, SNIOC_READ_RAW_DATA, &raw); - -.. c:macro:: SNIOC_MEASURE - -This command will measure temperature and humidity, and return it in familiar -units to the user. Temperature will be in degrees (Fahrenheit or Celsius depends -on the Kconfig options selected during compilation) and humidity will be %RH. - -The argument to this command must be a pointer to a `struct sht4x_conv_data_s`. -This is where the converted data will be returned. - -.. code-block:: c - - struct sht4x_conv_data_s data; - err = ioctl(sensor, SNIOC_MEASURE, &data); - -.. c:macro:: SNIOC_READ_CONVERT_DATA - -Same as `SNIOC_MEASURE`. + err = orb_ioctl(sensor, SNIOC_WHO_AM_I, &serialno); -.. c:macro:: SNIOC_HEAT +``SNIOC_HEAT`` +-------------- This command will instruct the SHT4X to turn on its heater unit for the -specified time. Afterwards, a measurement of temperature and humidity is taken, -and the converted data is returned to the caller. +specified time. -The argument to this command must be a pointer to a `struct sht4x_conv_data_s`. -This is where the converted data will be returned. The `temperature` field of -the struct must contain a value from the `enum sht4x_heater_e`, which will +The argument to this command must be of type `enum sht4x_heater_e`, which will indicate the duration the heater is on and the power used. Heating commands are not allowed more than once per second to avoid damaging the @@ -110,11 +84,10 @@ sensor. If a command is issued before this one second cool-down period is over, .. code-block:: c - struct sht4x_conv_data_s data; - data.temp = SHT4X_HEATER_200MW_1; - err = ioctl(sensor, SNIOC_HEAT, &data); + err = orb_ioctl(sensor, SNIOC_HEAT, SHT4X_HEATER_200MW_1); -.. c:macro:: SNIOC_CONFIGURE +``SNIOC_CONFIGURE`` +------------------- This command allows the caller to configure the precision of the SHT4X sensor used by subsequent measurement commands. By default, the sensor starts at high @@ -124,4 +97,4 @@ The argument to this command is one of the values in `enum sht4x_precision_e`. .. code-block:: c - err = ioctl(sensor, SNIOC_CONFIGURE, SHT4X_PREC_LOW); + err = orb_ioctl(sensor, SNIOC_CONFIGURE, SHT4X_PREC_LOW); diff --git a/boards/arm/rp2040/common/src/rp2040_common_bringup.c b/boards/arm/rp2040/common/src/rp2040_common_bringup.c index 3ee76b0bb6..7f43be8655 100644 --- a/boards/arm/rp2040/common/src/rp2040_common_bringup.c +++ b/boards/arm/rp2040/common/src/rp2040_common_bringup.c @@ -538,9 +538,9 @@ int rp2040_common_bringup(void) #ifdef CONFIG_SENSORS_SHT4X - /* Try to register SHT4X device as /dev/sht4x0 at I2C0. */ + /* Try to register SHT4X device on I2C0 */ - ret = sht4x_register("/dev/sht4x0", rp2040_i2cbus_initialize(0), + ret = sht4x_register(rp2040_i2cbus_initialize(0), 0, CONFIG_SHT4X_I2C_ADDR); if (ret < 0) { diff --git a/drivers/sensors/CMakeLists.txt b/drivers/sensors/CMakeLists.txt index e6bf460ec3..02cfd2579b 100644 --- a/drivers/sensors/CMakeLists.txt +++ b/drivers/sensors/CMakeLists.txt @@ -285,7 +285,7 @@ if(CONFIG_SENSORS) endif() if(CONFIG_SENSORS_SHT4X) - list(APPEND SRCS sht4x.c) + list(APPEND SRCS sht4x_uorb.c) endif() if(CONFIG_SENSORS_SPS30) diff --git a/drivers/sensors/Kconfig b/drivers/sensors/Kconfig index 14986498af..47960acacc 100644 --- a/drivers/sensors/Kconfig +++ b/drivers/sensors/Kconfig @@ -1627,6 +1627,12 @@ config SHT4X_I2C_FREQUENCY default 400000 range 1 400000 +config SHT4X_THREAD_STACKSIZE + int "SHT4X worker thread stack size" + default 1024 + ---help--- + The stack size for the worker thread that performs measurements + config SHT4X_I2C_ADDR hex "SHT4X I2C address" default 0x44 @@ -1634,12 +1640,6 @@ config SHT4X_I2C_ADDR ---help--- Enables debug features for the SHT4X -config SHT4X_FAHRENHEIT - bool "SHT4X use Fahrenheit" - default n - ---help--- - Configures read outputs of the SHT4X to be in Fahrenheit. Default uses Celsius. - config SHT4X_LIMIT_HUMIDITY bool "SHT4X limit humidity between 0-100%" default y @@ -1652,11 +1652,12 @@ config SHT4X_CRC_LOOKUP ---help--- Configures the SHT4X to do CRC checks with a lookup table. Default uses a bitwise CRC calculation. -config SHT4X_DEBUG - bool "SHT4X debug support" - default n +config SENSORS_SHT4X_POLL + bool "Use polling" + default y ---help--- - Enables debug features for the SHT4X + Configures the SHT4X to be polled at an interval instead of allowing user code to choose when measurements are + taken. endif # SENSORS_SHT4X diff --git a/drivers/sensors/Make.defs b/drivers/sensors/Make.defs index ac6ec09022..bc382e7e3a 100644 --- a/drivers/sensors/Make.defs +++ b/drivers/sensors/Make.defs @@ -289,7 +289,7 @@ ifeq ($(CONFIG_SENSORS_SHT3X),y) endif ifeq ($(CONFIG_SENSORS_SHT4X),y) - CSRCS += sht4x.c + CSRCS += sht4x_uorb.c endif ifeq ($(CONFIG_SENSORS_SPS30),y) diff --git a/drivers/sensors/sht4x.c b/drivers/sensors/sht4x_uorb.c similarity index 59% rename from drivers/sensors/sht4x.c rename to drivers/sensors/sht4x_uorb.c index d8cb2c9863..9303e833ec 100644 --- a/drivers/sensors/sht4x.c +++ b/drivers/sensors/sht4x_uorb.c @@ -1,5 +1,5 @@ /**************************************************************************** - * drivers/sensors/sht4x.c + * drivers/sensors/sht4x_uorb.c * * SPDX-License-Identifier: Apache-2.0 * @@ -24,35 +24,29 @@ * Included Files ****************************************************************************/ +#include <nuttx/config.h> +#include <nuttx/nuttx.h> + #include <debug.h> +#include <stdio.h> +#include <unistd.h> + #include <nuttx/clock.h> -#include <nuttx/config.h> #include <nuttx/fs/fs.h> #include <nuttx/i2c/i2c_master.h> #include <nuttx/kmalloc.h> +#include <nuttx/kthread.h> #include <nuttx/mutex.h> #include <nuttx/random.h> +#include <nuttx/semaphore.h> +#include <nuttx/sensors/sensor.h> #include <nuttx/sensors/sht4x.h> - -#include <stdio.h> -#include <unistd.h> - -#if defined(CONFIG_I2C) && defined(CONFIG_SENSORS_SHT4X) +#include <nuttx/signal.h> /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ -#ifdef CONFIG_SHT4X_DEBUG -#define sht4x_dbg(x, ...) _info(x, ##__VA_ARGS__) -#endif - -#if defined(CONFIG_SHT4X_FAHRENHEIT) -#define SHT4X_TEMP_UNIT "F" -#else -#define SHT4X_TEMP_UNIT "C" -#endif - #ifndef CONFIG_SHT4X_I2C_FREQUENCY #define CONFIG_SHT4X_I2C_FREQUENCY 400000 #endif @@ -76,19 +70,34 @@ * Private ****************************************************************************/ +/* Sensor information for the lowerhalf sensors. + * Since the SHT4X has both a relative humidity and temperature sensor, + * two lower halves are needed which will follow this structure. + */ + +struct sht4x_sensor_s +{ + FAR struct sensor_lowerhalf_s sensor_lower; /* Lower-half driver */ + bool enabled; /* If this sensor is enabled */ + FAR struct sht4x_dev_s *dev; /* Backward reference to + * device */ +}; + +/* Represents the main device, with two lower halves for both data types. */ + struct sht4x_dev_s { - FAR struct i2c_master_s *i2c; /* I2C interface. */ - uint8_t addr; /* I2C address. */ -#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS - bool unlinked; /* True, driver has been unlinked. */ -#endif + struct sht4x_sensor_s hum; /* Humidity lower-half */ + struct sht4x_sensor_s temp; /* Temperature lower-half */ + FAR struct i2c_master_s *i2c; /* I2C interface. */ + uint8_t addr; /* I2C address. */ struct timespec last_heat; /* Last time heater was active. */ enum sht4x_precision_e precision; /* The precision for read operations. */ -#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS - int16_t crefs; /* Number of open references. */ -#endif - mutex_t devlock; + sem_t run; /* Lock for the polling measurement + * cycle */ + mutex_t devlock; /* Exclusive device access */ + uint32_t interval; /* Measurement interval for polling in + * us, shared by both halves. */ }; /* Easy unpacking of serial number from I2C packet. */ @@ -111,9 +120,9 @@ union sht4x_serialno_t static const uint8_t g_precision_read[] = { - [SHT4X_PREC_LOW] = SHT4X_READ_LOW_PREC, - [SHT4X_PREC_MED] = SHT4X_READ_MED_PREC, - [SHT4X_PREC_HIGH] = SHT4X_READ_HIGH_PREC, + [SHT4X_PREC_LOW] = SHT4X_READ_LOW_PREC, + [SHT4X_PREC_MED] = SHT4X_READ_MED_PREC, + [SHT4X_PREC_HIGH] = SHT4X_READ_HIGH_PREC, }; #ifdef CONFIG_SHT4X_CRC_LOOKUP @@ -153,68 +162,67 @@ static const uint8_t g_crc_lookup[] = static const uint16_t g_measurement_times[] = { - [SHT4X_PREC_LOW] = 1600, - [SHT4X_PREC_MED] = 4500, - [SHT4X_PREC_HIGH] = 8300, + [SHT4X_PREC_LOW] = 1600, + [SHT4X_PREC_MED] = 4500, + [SHT4X_PREC_HIGH] = 8300, }; /* Commands for the various heating options. */ static const uint8_t g_heat_cmds[] = { - [SHT4X_HEATER_200MW_1] = SHT4X_HEAT_200_1, - [SHT4X_HEATER_200MW_01] = SHT4X_HEAT_200_P1, - [SHT4X_HEATER_110MW_1] = SHT4X_HEAT_110_1, - [SHT4X_HEATER_110MW_01] = SHT4X_HEAT_110_P1, - [SHT4X_HEATER_20MW_1] = SHT4X_HEAT_20_1, - [SHT4X_HEATER_20MW_01] = SHT4X_HEAT_20_P1, + [SHT4X_HEATER_200MW_1] = SHT4X_HEAT_200_1, + [SHT4X_HEATER_200MW_01] = SHT4X_HEAT_200_P1, + [SHT4X_HEATER_110MW_1] = SHT4X_HEAT_110_1, + [SHT4X_HEATER_110MW_01] = SHT4X_HEAT_110_P1, + [SHT4X_HEATER_20MW_1] = SHT4X_HEAT_20_1, + [SHT4X_HEATER_20MW_01] = SHT4X_HEAT_20_P1, }; /* Timeouts for the various heating options in microseconds. */ static const uint32_t g_heat_times[] = { - [SHT4X_HEATER_200MW_1] = 1000000, [SHT4X_HEATER_200MW_01] = 100000, - [SHT4X_HEATER_110MW_1] = 1000000, [SHT4X_HEATER_110MW_01] = 100000, - [SHT4X_HEATER_20MW_1] = 1000000, [SHT4X_HEATER_20MW_01] = 100000, + [SHT4X_HEATER_200MW_1] = 1000000, [SHT4X_HEATER_200MW_01] = 100000, + [SHT4X_HEATER_110MW_1] = 1000000, [SHT4X_HEATER_110MW_01] = 100000, + [SHT4X_HEATER_20MW_1] = 1000000, [SHT4X_HEATER_20MW_01] = 100000, }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ -static int sht4x_open(FAR struct file *filep); -static int sht4x_close(FAR struct file *filep); -static ssize_t sht4x_write(FAR struct file *filep, FAR const char *buffer, - size_t buflen); -static ssize_t sht4x_read(FAR struct file *filep, FAR char *buffer, - size_t buflen); -static int sht4x_ioctl(FAR struct file *filep, int cmd, unsigned long arg); -static int sht4x_unlink(FAR struct inode *inode); +static int sht4x_set_interval(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, + FAR uint32_t *period_us); +static int sht4x_activate(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, bool enable); +static int sht4x_get_info(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, + FAR struct sensor_device_info_s *info); +static int sht4x_control(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, int cmd, unsigned long arg); +#ifndef CONFIG_SENSORS_SHT4X_POLL +static int sht4x_fetch(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, FAR char *buffer, + size_t buflen); +#endif /**************************************************************************** * Private Data ****************************************************************************/ -static const struct file_operations g_sht4xfops = +static const struct sensor_ops_s g_sensor_ops = { -#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS - .open = sht4x_open, - .close = sht4x_close, + .activate = sht4x_activate, +#ifdef CONFIG_SENSORS_SHT4X_POLL + .fetch = NULL, #else - .open = NULL, - .close = NULL, -#endif - .read = sht4x_read, - .write = sht4x_write, - .seek = NULL, - .ioctl = sht4x_ioctl, - .mmap = NULL, - .truncate = NULL, - .poll = NULL, -#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS - .unlink = sht4x_unlink, + .fetch = sht4x_fetch, #endif + .set_interval = sht4x_set_interval, + .get_info = sht4x_get_info, + .control = sht4x_control, }; /**************************************************************************** @@ -411,19 +419,13 @@ static int sht4x_reset(FAR struct sht4x_dev_s *priv) * Name: sht4x_calc_temp * * Description: - * Calculate the temperature from the SHT4X raw data. + * Calculate the temperature in degrees Celsius from the SHT4X raw data. * ****************************************************************************/ -static int32_t sht4x_calc_temp(uint16_t temp) +static float sht4x_calc_temp(uint16_t temp) { -#ifdef CONFIG_SHT4X_FAHRENHEIT - /* Millidegrees Fahrenheit */ - - return -49000 + 315 * ((temp * 1000) / 65535); -#else - return -45000 + 175 * ((temp * 1000) / 65535); /* Millidegrees Celsius */ -#endif + return (float)(-45000 + 175 * ((temp * 1000) / 65535)) / 1000.0f; } /**************************************************************************** @@ -434,7 +436,7 @@ static int32_t sht4x_calc_temp(uint16_t temp) * ****************************************************************************/ -static int16_t sht4x_calc_hum(uint16_t humidity) +static float sht4x_calc_hum(uint16_t humidity) { int16_t hum = -600 + 125 * ((humidity * 100) / 65535); /* 0.01 %RH */ #ifdef CONFIG_SHT4X_LIMIT_HUMIDITY @@ -451,253 +453,201 @@ static int16_t sht4x_calc_hum(uint16_t humidity) } #endif - return hum; + return (float)(hum / 100.0f); } /**************************************************************************** - * Name: has_time_passed + * Name: sht4x_read * - * Description: - * Return true if curr >= start + secs_since_start + * Description: Read temperature and humidity into UORB sensor formats. + * + * Return: + * Negated error code, or 0 on success. * ****************************************************************************/ -static bool has_time_passed(struct timespec curr, struct timespec start, - unsigned int secs_since_start) +static int sht4x_read(FAR struct sht4x_dev_s *priv, + FAR struct sensor_humi *humi, + FAR struct sensor_temp *temp) { - if ((long)((start.tv_sec + secs_since_start) - curr.tv_sec) == 0) + uint16_t raw_temp; + uint16_t raw_hum; + int err; + + /* Exclusive access */ + + err = nxmutex_lock(&priv->devlock); + if (err < 0) { - return start.tv_nsec <= curr.tv_nsec; + return err; } - return (long)((start.tv_sec + secs_since_start) - curr.tv_sec) <= 0; -} + /* Get temp and humidity */ -/**************************************************************************** - * Name: sht4x_open - * - * Description: - * This function is called whenever the SHT4X device is opened. - * - ****************************************************************************/ + err = sht4x_cmd(priv, g_precision_read[priv->precision], + g_measurement_times[priv->precision], &raw_temp, &raw_hum); + if (err < 0) + { + nxmutex_unlock(&priv->devlock); + return err; + } -#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS -static int sht4x_open(FAR struct file *filep) -{ - FAR struct inode *inode = filep->f_inode; - FAR struct sht4x_dev_s *priv = inode->i_private; - int err; + /* Release mutex */ - err = nxmutex_lock(&priv->devlock); + err = nxmutex_unlock(&priv->devlock); if (err < 0) { return err; } - /* Increment the count of open references on the driver */ + /* Store results */ - priv->crefs++; - DEBUGASSERT(priv->crefs > 0); + humi->timestamp = sensor_get_timestamp(); + humi->humidity = sht4x_calc_hum(raw_hum); + temp->timestamp = humi->timestamp; + temp->temperature = sht4x_calc_temp(raw_temp); - nxmutex_unlock(&priv->devlock); - return 0; + return err; } -#endif /**************************************************************************** - * Name: sht4x_close + * Name: has_time_passed * * Description: - * This routine is called when the SHT4X device is closed. + * Return true if curr >= start + secs_since_start * ****************************************************************************/ -#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS -static int sht4x_close(FAR struct file *filep) +static bool has_time_passed(struct timespec curr, struct timespec start, + unsigned int secs_since_start) { - FAR struct inode *inode = filep->f_inode; - FAR struct sht4x_dev_s *priv = inode->i_private; - int err; - - err = nxmutex_lock(&priv->devlock); - if (err < 0) + if ((long)((start.tv_sec + secs_since_start) - curr.tv_sec) == 0) { - return err; + return start.tv_nsec <= curr.tv_nsec; } - /* Decrement the count of open references on the driver */ + return (long)((start.tv_sec + secs_since_start) - curr.tv_sec) <= 0; +} - DEBUGASSERT(priv->crefs > 0); - priv->crefs--; +/**************************************************************************** + * Name: sht4x_get_info + ****************************************************************************/ - /* If the count has decremented to zero and the driver has been unlinked, - * then free memory now. - */ +static int sht4x_get_info(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, + FAR struct sensor_device_info_s *info) +{ + /* Fill out information */ - if (priv->crefs <= 0 && priv->unlinked) + info->version = 0; + info->power = 0.0004f; /* 0.4uA */ + info->min_delay = 1300; /* 1.3ms */ + info->max_delay = 8300; /* 8.3ms */ + memcpy(info->name, "SHT4x", sizeof("SHT4x")); + memcpy(info->vendor, "Sensirion", sizeof("Sensirion")); + + /* Fill out information specific to a certain lower half */ + + if (lower->type == SENSOR_TYPE_RELATIVE_HUMIDITY) { - nxmutex_destroy(&priv->devlock); - kmm_free(priv); - return 0; + info->max_range = 100.0f; + info->resolution = 0.01f; + } + else if (lower->type == SENSOR_TYPE_AMBIENT_TEMPERATURE) + { + info->max_range = 125.0f; + info->resolution = 0.01f; } - nxmutex_unlock(&priv->devlock); + info->fifo_reserved_event_count = 0; + info->fifo_max_event_count = 0; return 0; } -#endif /**************************************************************************** - * Name: sht4x_read + * Name: sht4x_set_interval * * Description: - * Character driver interface to sensor for debugging. + * Sets the measurement interval for the SHT4X sensor in microseconds. * ****************************************************************************/ -static ssize_t sht4x_read(FAR struct file *filep, FAR char *buffer, - size_t buflen) +static int sht4x_set_interval(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, + FAR uint32_t *period_us) { - FAR struct inode *inode = filep->f_inode; - FAR struct sht4x_dev_s *priv = inode->i_private; - ssize_t length = 0; - struct sht4x_raw_data_s raw_data; - struct sht4x_conv_data_s data; - int err; - - /* If file position is non-zero, then we're at the end of file. */ + FAR struct sht4x_sensor_s *priv = + container_of(lower, FAR struct sht4x_sensor_s, sensor_lower); + FAR struct sht4x_dev_s *dev = priv->dev; + dev->interval = *period_us; + return 0; +} - if (filep->f_pos > 0) - { - return 0; - } +/**************************************************************************** + * Name: sht4x_activate + ****************************************************************************/ - /* Get exclusive access */ +static int sht4x_activate(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, bool enable) +{ + bool start_thread = false; + FAR struct sht4x_sensor_s *priv = + container_of(lower, FAR struct sht4x_sensor_s, sensor_lower); + FAR struct sht4x_dev_s *dev = priv->dev; - err = nxmutex_lock(&priv->devlock); - if (err < 0) + if (enable) { - return err; + if (!priv->enabled) + { + start_thread = true; + } } -#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS - if (priv->unlinked) - { - /* Do not allow operations on unlinked sensors. This allows - * sensor use on hot swappable I2C bus. - */ + priv->enabled = enable; - nxmutex_unlock(&priv->devlock); - return 0; - } -#endif - - err = sht4x_cmd(priv, g_precision_read[priv->precision], - g_measurement_times[priv->precision], &raw_data.raw_temp, - &raw_data.raw_hum); - if (err < 0) + if (start_thread) { -#ifdef CONFIG_SHT4X_DEBUG - sht4x_dbg("Could not read device: %d\n", err); -#endif - nxmutex_unlock(&priv->devlock); - return err; - } - - /* Convert to proper units. */ + /* Wake up the polling thread */ - data.temp = sht4x_calc_temp(raw_data.raw_temp); - data.hum = sht4x_calc_hum(raw_data.raw_hum); - - length = snprintf(buffer, buflen, "%ld m" SHT4X_TEMP_UNIT ", %d %%RH\n", - data.temp, data.hum / 100); - if (length > buflen) - { - length = buflen; + nxsem_post(&dev->run); } - filep->f_pos += length; - nxmutex_unlock(&priv->devlock); - return length; -} - -/**************************************************************************** - * Name: sht4x_write - * - * Description: - * Not implemented. - ****************************************************************************/ - -static ssize_t sht4x_write(FAR struct file *filep, FAR const char *buffer, - size_t buflen) -{ - return -ENOSYS; + return 0; } /**************************************************************************** - * Name: sht4x_ioctl + * Name: sht4x_control ****************************************************************************/ -static int sht4x_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +static int sht4x_control(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, int cmd, unsigned long arg) { - FAR struct inode *inode = filep->f_inode; - FAR struct sht4x_dev_s *priv = inode->i_private; + FAR struct sht4x_sensor_s *priv = + container_of(lower, FAR struct sht4x_sensor_s, sensor_lower); + FAR struct sht4x_dev_s *dev = priv->dev; int err; - err = nxmutex_lock(&priv->devlock); + err = nxmutex_lock(&dev->devlock); if (err < 0) { return err; } -#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS - if (priv->unlinked) - { - /* Do not allow operations on unlinked sensors. This allows - * sensor use on hot swappable I2C bus. - */ - - nxmutex_unlock(&priv->devlock); - return -ENODEV; - } -#endif - switch (cmd) { case SNIOC_RESET: - err = sht4x_reset(priv); + err = sht4x_reset(dev); break; case SNIOC_WHO_AM_I: { union sht4x_serialno_t serialno; - err = sht4x_cmd(priv, SHT4X_READ_SERIAL, 10, &serialno.halves.msb, + err = sht4x_cmd(dev, SHT4X_READ_SERIAL, 10, &serialno.halves.msb, &serialno.halves.lsb); *((FAR uint32_t *)(arg)) = serialno.full; } break; - case SNIOC_READ_RAW_DATA: - err = sht4x_cmd(priv, g_precision_read[priv->precision], - g_measurement_times[priv->precision], - &((FAR struct sht4x_raw_data_s *)(arg))->raw_temp, - &((FAR struct sht4x_raw_data_s *)(arg))->raw_hum); - break; - - case SNIOC_MEASURE: - case SNIOC_READ_CONVERT_DATA: - { - uint16_t raw_t; - uint16_t raw_h; - - err = sht4x_cmd(priv, g_precision_read[priv->precision], - g_measurement_times[priv->precision], - &raw_t, &raw_h); - ((FAR struct sht4x_conv_data_s *)(arg))->temp = - sht4x_calc_temp(raw_t); - ((FAR struct sht4x_conv_data_s *)(arg))->hum = sht4x_calc_hum(raw_h); - } - break; - case SNIOC_HEAT: { struct timespec now; @@ -705,23 +655,30 @@ static int sht4x_ioctl(FAR struct file *filep, int cmd, unsigned long arg) /* Check if it has been one second since the last heat command. */ - if (!has_time_passed(now, priv->last_heat, 1)) + if (!has_time_passed(now, dev->last_heat, 1)) { err = -EAGAIN; /* Signal to try again in some time. */ break; } - /* Caller must pass heater option in the temperature argument. */ + /* Check for invalid heater command */ + + if (0 < arg || arg >= (sizeof(g_heat_cmds) / sizeof(g_heat_cmds[0]))) + { + return -EINVAL; + } - uint16_t raw_t = ((FAR struct sht4x_conv_data_s *)(arg))->temp; - uint16_t raw_h; + /* Heat for the desired period */ + + uint16_t trash; + err = sht4x_cmd(dev, g_heat_cmds[arg], g_heat_times[arg], &trash, + &trash); + if (err) + { + break; + } - err = sht4x_cmd(priv, g_heat_cmds[raw_t], g_heat_times[raw_t], - &raw_t, &raw_h); - ((FAR struct sht4x_conv_data_s *)(arg))->temp = - sht4x_calc_temp(raw_t); - ((FAR struct sht4x_conv_data_s *)(arg))->hum = sht4x_calc_hum(raw_h); - clock_systime_timespec(&priv->last_heat); /* Update last heat time. */ + clock_systime_timespec(&dev->last_heat); /* Update last heat time. */ } break; @@ -729,11 +686,7 @@ static int sht4x_ioctl(FAR struct file *filep, int cmd, unsigned long arg) /* Caller must pass precision option as argument. */ - priv->precision = arg; -#ifdef CONFIG_SHT4X_DEBUG - sht4x_dbg("Precision set to %d\n", priv->precision); -#endif - + dev->precision = arg; break; default: @@ -741,47 +694,105 @@ static int sht4x_ioctl(FAR struct file *filep, int cmd, unsigned long arg) break; } - nxmutex_unlock(&priv->devlock); + nxmutex_unlock(&dev->devlock); return err; } /**************************************************************************** - * Name: sht4x_unlink + * Name: sht4x_fetch ****************************************************************************/ -#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS -static int sht4x_unlink(FAR struct inode *inode) +#ifndef CONFIG_SENSORS_SHT4X_POLL +static int sht4x_fetch(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, FAR char *buffer, + size_t buflen) { - FAR struct sht4x_dev_s *priv; + FAR struct sht4x_sensor_s *priv = + container_of(lower, FAR struct sht4x_sensor_s, sensor_lower); + FAR struct sht4x_dev_s *dev = priv->dev; int err; - DEBUGASSERT(inode->i_private != NULL); - priv = inode->i_private; - - err = nxmutex_lock(&priv->devlock); + err = sht4x_read(dev, &humi_data, &temp_data); if (err < 0) { return err; } - /* Are there open references to the driver data structure? */ + if (dev->hum.enabled) + { + dev->hum.sensor_lower.push_event(dev->hum.sensor_lower.priv, + &humi_data, sizeof(humi_data)); + } - if (priv->crefs <= 0) + if (dev->temp.enabled) { - nxmutex_destroy(&priv->devlock); - kmm_free(priv); - return 0; + dev->temp.sensor_lower.push_event(dev->temp.sensor_lower.priv, + &temp_data, sizeof(temp_data)); } - /* No. Just mark the driver as unlinked and free the resources when - * the last client closes their reference to the driver. - */ + return err; +} +#endif /* CONFIG_SENSORS_SHT4X_POLL */ + +/**************************************************************************** + * Name: sht4x_thread + * + * Description: Thread for performing interval measurement cycle and data + * read. + * + * Parameter: + * argc - Number of arguments + * argv - Pointer to argument list + * + ****************************************************************************/ + +static int sht4x_thread(int argc, char **argv) +{ + FAR struct sht4x_dev_s *dev = + (FAR struct sht4x_dev_s *)((uintptr_t)strtoul(argv[1], NULL, 16)); + + struct sensor_temp temp_data; + struct sensor_humi humi_data; + int err; + + while (true) + { + if (!dev->hum.enabled && !dev->temp.enabled) + { + /* Wait for one of the lower halves to be enabled and wake us up */ + + err = nxsem_wait(&dev->run); + if (err < 0) + { + continue; + } + } + + err = sht4x_read(dev, &humi_data, &temp_data); + if (err < 0) + { + continue; + } + + if (dev->hum.enabled) + { + dev->hum.sensor_lower.push_event(dev->hum.sensor_lower.priv, + &humi_data, sizeof(humi_data)); + } + + if (dev->temp.enabled) + { + dev->temp.sensor_lower.push_event(dev->temp.sensor_lower.priv, + &temp_data, sizeof(temp_data)); + } + + /* Sleep before next fetch */ + + nxsig_usleep(dev->interval); + } - priv->unlinked = true; - nxmutex_unlock(&priv->devlock); return OK; } -#endif /**************************************************************************** * Public Functions @@ -805,11 +816,12 @@ static int sht4x_unlink(FAR struct inode *inode) * ****************************************************************************/ -int sht4x_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, - uint8_t addr) +int sht4x_register(FAR struct i2c_master_s *i2c, int devno, uint8_t addr) { FAR struct sht4x_dev_s *priv; int err; + FAR char *argv[2]; + char arg1[32]; DEBUGASSERT(i2c != NULL); DEBUGASSERT(addr == 0x44 || addr == 0x45 || addr == 0x46); @@ -826,6 +838,7 @@ int sht4x_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, priv->i2c = i2c; priv->addr = addr; priv->precision = SHT4X_PREC_HIGH; + priv->interval = 1000000; /* 1s interval */ err = clock_systime_timespec(&priv->last_heat); if (err < 0) @@ -849,17 +862,74 @@ int sht4x_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, return err; } - /* Register the character driver */ + err = nxsem_init(&priv->run, 0, 0); + if (err < 0) + { + snerr("Failed to register SHT4X driver: %d\n", err); + nxmutex_destroy(&priv->devlock); + kmm_free(priv); + return err; + } + + /* Register lower half for humidity */ + + priv->hum.sensor_lower.ops = &g_sensor_ops; + priv->hum.sensor_lower.type = SENSOR_TYPE_RELATIVE_HUMIDITY; + priv->hum.sensor_lower.uncalibrated = false; + priv->hum.enabled = false; + priv->hum.dev = priv; - err = register_driver(devpath, &g_sht4xfops, 0666, priv); + /* Register UORB Sensor */ + + err = sensor_register(&priv->hum.sensor_lower, devno); if (err < 0) { - snerr("ERROR: Failed to register SHT4X driver: %d\n", err); + snerr("Failed to register SHT4X driver: %d\n", err); + nxmutex_destroy(&priv->devlock); + nxsem_destroy(&priv->run); + kmm_free(priv); + return err; + } + + /* Register lower half for temperature */ + + priv->temp.sensor_lower.ops = &g_sensor_ops; + priv->temp.sensor_lower.type = SENSOR_TYPE_AMBIENT_TEMPERATURE; + priv->temp.sensor_lower.uncalibrated = false; + priv->temp.enabled = false; + priv->temp.dev = priv; + + /* Register UORB Sensor */ + + err = sensor_register(&priv->temp.sensor_lower, devno); + if (err < 0) + { + snerr("Failed to register SHT4X driver: %d\n", err); + sensor_unregister(&priv->hum.sensor_lower, devno); nxmutex_destroy(&priv->devlock); + nxsem_destroy(&priv->run); kmm_free(priv); + return err; } + /* Polling thread */ + + snprintf(arg1, 16, "%p", priv); + argv[0] = arg1; + argv[1] = NULL; + err = kthread_create("sht4x_thread", SCHED_PRIORITY_DEFAULT, + CONFIG_SHT4X_THREAD_STACKSIZE, sht4x_thread, argv); + if (err < 0) + { + snerr("Failed to create the SHT4X notification kthread.\n"); + sensor_unregister(&priv->hum.sensor_lower, devno); + sensor_unregister(&priv->temp.sensor_lower, devno); + nxmutex_destroy(&priv->devlock); + nxsem_destroy(&priv->run); + kmm_free(priv); + return err; + } + + sninfo("Registered SHT4X driver.\n"); return err; } - -#endif /* CONFIG_I2C && CONFIG_SENSORS_SHT4X */ diff --git a/include/nuttx/sensors/sht4x.h b/include/nuttx/sensors/sht4x.h index 0c2af21daf..6fe2fd1bd0 100644 --- a/include/nuttx/sensors/sht4x.h +++ b/include/nuttx/sensors/sht4x.h @@ -35,18 +35,6 @@ struct i2c_master_s; /* Forward reference */ -struct sht4x_conv_data_s -{ - int32_t temp; /* Millidegrees Celsius or Fahrenheit (depending on config) */ - int16_t hum; /* Percentage relative humidity in units of 0.01 %RH */ -}; - -struct sht4x_raw_data_s -{ - uint16_t raw_temp; - uint16_t raw_hum; -}; - /* Precision for reading. */ enum sht4x_precision_e @@ -79,9 +67,9 @@ enum sht4x_heater_e * Register the SHT4X character device as 'devpath' * * Input Parameters: - * devpath - The full path to the driver to register. E.g., "/dev/temp0" * i2c - An instance of the I2C interface to use to communicate with * the SHT4X + * devno - The device number that this device should have. * addr - The I2C address of the SHT4X. The I2C address is one of 0x44, * 0x45 and 0x46. * @@ -90,7 +78,6 @@ enum sht4x_heater_e * ****************************************************************************/ -int sht4x_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, - uint8_t addr); +int sht4x_register(FAR struct i2c_master_s *i2c, int devno, uint8_t addr); #endif /* __INCLUDE_NUTTX_SENSORS_SHT4X_H */