diff --git a/Documentation/components/drivers/special/sensors/mcp9600.rst b/Documentation/components/drivers/special/sensors/mcp9600.rst index 1d7852b45f1f6..cabf949189195 100644 --- a/Documentation/components/drivers/special/sensors/mcp9600.rst +++ b/Documentation/components/drivers/special/sensors/mcp9600.rst @@ -1,10 +1,12 @@ +======= MCP9600 ======= Contributed by Matteo Golin. -The MCP9600 is a thermocouple EMF to temperature converter made by Microchip. It is also sold as a `breakout board module -by Adafruit `_. +The MCP9600 is a thermocouple EMF to temperature converter made by Microchip. It +is also sold as a `breakout board module by Adafruit +`_. Application Programming Interface ================================== @@ -15,26 +17,63 @@ The header file for the MCP9600 driver interface can be included using: #include -The MCP9600 registration function allows the driver to be registered as a POSIX -character driver. +The MCP9600 registration function allows the driver to be registered as a +:doc:`UORB ` sensor. + +The MCP9600 measures three types of temperature: + * Hot junction temperature + * Cold junction temperature + * Temperature delta + +Registering this sensor will create three UORB temperature topics, each with +their own unique device number. You must specify the unique device numbers for +each topic in the registration function: + +.. code-block:: c + + /* Registers sensor_temp1, sensor_temp2 and sensor_temp 3, where 1 is the + * hot junction topic, 2 is the cold junction topic and 3 is the delta + */ -The standard POSIX `read()` operation will return the device information in -plain-text, which is useful when debugging/testing the driver using `cat` from -the shell. + int err; + err = mcp9600_register(i2c_master, 0x60, 1, 2, 3); + if (err < 0) { + syslog(LOG_ERR, "Could not register MCP9600: %d\n", err); + } -The `write()` operation is not implemented for this sensor. -Specific operations the sensor offers can be performed via the POSIX `ioctl` -operation. The supported commands are: +This sensor offers some additional control commands for features that are not +accessible with the standard UORB interface. + * :c:macro:`SNIOC_SET_THERMO` * :c:macro:`SNIOC_WHO_AM_I` * :c:macro:`SNIOC_READ_RAW_DATA` * :c:macro:`SNIOC_CHECK_STATUS_REG` * :c:macro:`SNIOC_CONFIGURE` * :c:macro:`SNIOC_WRITECONF` - * :c:macro:`SNIOC_READTEMP` - * :c:macro:`SNIOC_SHUTDOWN` - * :c:macro:`SNIOC_START` + +``SNIOC_SET_THERMO`` +-------------------- + +This command configures the thermocouple type of the MCP9600. The device +supports the following thermocouple types: + + * K + * J + * T + * N + * E + * S + * B + * R + +.. code-block:: c + + int err; + err = orb_ioctl(sensor, SNIOC_SET_THERMO, SENSOR_THERMO_TYPE_J); + if (err < 0) { + syslog(LOG_ERR, "Failed to set thermocouple type: %d\n", err); + } ``SNIOC_WHO_AM_I`` ------------------ @@ -46,7 +85,7 @@ type ``struct mcp9600_devinfo_s *``. .. code-block:: c struct mcp9600_devinfo_s devinfo; - err = ioctl(sensor, SNIOC_WHO_AM_I, &devinfo); + err = orb_ioctl(sensor, SNIOC_WHO_AM_I, &devinfo); uint8_t revision_minor = MCP9600_REV_MINOR(devinfo.revision); uint8_t revision_major = MCP9600_REV_MAJOR(devinfo.revision); @@ -64,7 +103,7 @@ configured resolution; consult the data sheet. .. code-block:: c int32_t raw; - err = ioctl(sensor, SNIOC_READ_RAW_DATA, &raw); + err = orb_ioctl(sensor, SNIOC_READ_RAW_DATA, &raw); ``SNIOC_CHECK_STATUS_REG`` -------------------------- @@ -75,7 +114,7 @@ this command must be a pointer to type ``struct mcp9600_status_s``. .. code-block:: c struct mcp9600_status_s status; - err = ioctl(sensor, SNIOC_CHECK_STATUS_REG, &status); + err = orb_ioctl(sensor, SNIOC_CHECK_STATUS_REG, &status); ``SNIOC_CONFIGURE`` ------------------- @@ -93,7 +132,7 @@ mcp9600_devconf_s``. .resolution = MCP9600_ADC_RES_18, /* More fields ... */ }; - err = ioctl(sensor, SNIOC_CONFIGURE, &conf); + err = orb_ioctl(sensor, SNIOC_CONFIGURE, &conf); ``SNIOC_WRITECONF`` ------------------- @@ -111,36 +150,4 @@ mcp9600_alertconf_s``. .limit = 40 / 0.25, /* More fields ... */ }; - err = ioctl(sensor, SNIOC_WRITECONF, &conf); - -``SNIOC_READTEMP`` ------------------- - -This command lets you read the three different types of temperature that the -MCP9600 can measure. The argument to this command must be a pointer to type -``struct mcp9600_temp_s``. - -.. code-block:: c - - struct mcp9600_temp_s temps; - err = ioctl(sensor, SNIOC_READTEMP, &temps); - - printf("Temperature: %d C\n", temps.hot_junc); - -``SNIOC_SHUTDOWN`` ------------------- - -This command shuts down the sensor. It takes no arguments. - -.. code-block:: c - - err = ioctl(sensor, SNIOC_SHUTDOWN, NULL); - -``SNIOC_START`` ---------------- - -This command starts the sensor in normal mode. It takes no arguments. - -.. code-block:: c - - err = ioctl(sensor, SNIOC_START, NULL); + err = orb_ioctl(sensor, SNIOC_WRITECONF, &conf); diff --git a/boards/arm/rp2040/common/src/rp2040_common_bringup.c b/boards/arm/rp2040/common/src/rp2040_common_bringup.c index 7f43be8655ff0..8d41310b024c4 100644 --- a/boards/arm/rp2040/common/src/rp2040_common_bringup.c +++ b/boards/arm/rp2040/common/src/rp2040_common_bringup.c @@ -549,9 +549,9 @@ int rp2040_common_bringup(void) #endif #ifdef CONFIG_SENSORS_MCP9600 - /* Try to register MCP9600 device as /dev/thermo0 at I2C0. */ + /* Try to register MCP9600 device as /dev/therm0 at I2C0. */ - ret = mcp9600_register("/dev/thermo0", rp2040_i2cbus_initialize(0), 0x60); + ret = mcp9600_register(rp2040_i2cbus_initialize(0), 0x60, 1, 2, 3); if (ret < 0) { syslog(LOG_ERR, "ERROR: couldn't initialize MCP9600: %d\n", ret); diff --git a/drivers/sensors/Kconfig b/drivers/sensors/Kconfig index 47960acaccfd7..473513bd33c0d 100644 --- a/drivers/sensors/Kconfig +++ b/drivers/sensors/Kconfig @@ -965,6 +965,21 @@ config MCP9600_I2C_FREQUENCY range 10000 100000 depends on SENSORS_MCP9600 +config SENSORS_MCP9600_POLL + bool "MCP9600 polling" + default y + depends on SENSORS_MCP9600 + ---help--- + Enable the worker thread for polling the MCP9600 and collecting + measurements + +config MCP9600_THREAD_STACKSIZE + int "MCP9600 stack size" + default 1024 + depends on SENSORS_MCP9600 + ---help--- + Stack size of the worker thread polling the MCP9600 for measurements + endif # SENSORS_MCP9600 config SENSORS_MCP9844 diff --git a/drivers/sensors/Make.defs b/drivers/sensors/Make.defs index bc382e7e3acfa..95410290da27e 100644 --- a/drivers/sensors/Make.defs +++ b/drivers/sensors/Make.defs @@ -225,7 +225,7 @@ ifeq ($(CONFIG_SENSORS_MB7040),y) endif ifeq ($(CONFIG_SENSORS_MCP9600),y) - CSRCS += mcp9600.c + CSRCS += mcp9600_uorb.c endif ifeq ($(CONFIG_SENSORS_MCP9844),y) diff --git a/drivers/sensors/mcp9600.c b/drivers/sensors/mcp9600_uorb.c similarity index 55% rename from drivers/sensors/mcp9600.c rename to drivers/sensors/mcp9600_uorb.c index 4c48539dc0887..dd062fbfc9464 100644 --- a/drivers/sensors/mcp9600.c +++ b/drivers/sensors/mcp9600_uorb.c @@ -1,5 +1,5 @@ /**************************************************************************** - * drivers/sensors/mcp9600.c + * drivers/sensors/mcp9600_uorb.c * * Contributed by Matteo Golin * @@ -27,6 +27,7 @@ ****************************************************************************/ #include +#include #include #include @@ -36,7 +37,11 @@ #include #include #include +#include +#include #include +#include +#include /**************************************************************************** * Pre-processor Definitions @@ -73,84 +78,126 @@ * Private Types ****************************************************************************/ +/* Lower half driver for each of the temperature measurement types */ + +struct mcp9600_sens_s +{ + FAR struct sensor_lowerhalf_s lower; /* Lower-half driver */ + FAR struct mcp9600_dev_s *dev; /* Backward reference to parent */ + bool enabled; /* Whether this sensor is enabled */ +}; + +/* Full device */ + struct mcp9600_dev_s { - FAR struct i2c_master_s *i2c; /* I2C interface */ - uint8_t addr; /* I2C address */ - struct mcp9600_devconf_s conf; /* Device configuration */ -#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS - int16_t crefs; /* Number of open references. */ - bool unlinked; /* True, driver has been unlinked. */ -#endif - mutex_t devlock; + struct mcp9600_sens_s hot_junc; /* Hot junction lower-half driver */ + struct mcp9600_sens_s cold_junc; /* Cold-junction lower-half driver */ + struct mcp9600_sens_s delta; /* Delta lower-half driver */ + uint32_t interval; /* Measurement interval in us */ + FAR struct i2c_master_s *i2c; /* I2C interface */ + uint8_t addr; /* I2C address */ + struct mcp9600_devconf_s conf; /* Device configuration */ + sem_t run; /* Run the measurement thread */ + mutex_t devlock; /* Single access */ }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ -static int mcp9600_open(FAR struct file *filep); -static int mcp9600_close(FAR struct file *filep); -static ssize_t mcp9600_write(FAR struct file *filep, FAR const char *buffer, - size_t buflen); -static ssize_t mcp9600_read(FAR struct file *filep, FAR char *buffer, - size_t buflen); -static int mcp9600_ioctl(FAR struct file *filep, int cmd, unsigned long arg); -static int mcp9600_unlink(FAR struct inode *inode); +static int mcp9600_control(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, int cmd, + unsigned long arg); +static int mcp9600_get_info(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, + FAR struct sensor_device_info_s *info); +static int mcp9600_set_interval(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, + FAR uint32_t *period_us); +static int mcp9600_activate(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, bool enable); +#ifndef CONFIG_SENSORS_MCP9600_POLL +static int mcp9600_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_mcp9600fops = +/* Sensor operations */ + +static const struct sensor_ops_s g_sensor_ops = { -#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS - .open = mcp9600_open, - .close = mcp9600_close, + .activate = mcp9600_activate, + .set_interval = mcp9600_set_interval, + .get_info = mcp9600_get_info, + .control = mcp9600_control, +#ifndef CONFIG_SENSORS_MCP9600_POLL + .fetch = mcp9600_fetch, #else - .open = NULL, - .close = NULL, -#endif - .read = mcp9600_read, - .write = mcp9600_write, - .seek = NULL, - .ioctl = mcp9600_ioctl, - .mmap = NULL, - .truncate = NULL, - .poll = NULL, -#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS - .unlink = mcp9600_unlink, + .fetch = NULL, #endif }; +/* Thermocouple types and their max ranges in Celsius (from datasheet) */ + +static const float g_thermo_ranges[] = +{ + [SENSOR_THERMO_TYPE_K] = 1372.0f, [SENSOR_THERMO_TYPE_J] = 1200.0f, + [SENSOR_THERMO_TYPE_T] = 400.0f, [SENSOR_THERMO_TYPE_N] = 1300.0f, + [SENSOR_THERMO_TYPE_S] = 1664.0f, [SENSOR_THERMO_TYPE_E] = 1000.0f, + [SENSOR_THERMO_TYPE_B] = 1800.0f, [SENSOR_THERMO_TYPE_R] = 1664.0f, +}; + +/* Thermocouple types and their register values */ + +static const uint8_t g_thermo_types[] = +{ + [SENSOR_THERMO_TYPE_K] = 0x0, [SENSOR_THERMO_TYPE_J] = 0x1, + [SENSOR_THERMO_TYPE_T] = 0x2, [SENSOR_THERMO_TYPE_N] = 0x3, + [SENSOR_THERMO_TYPE_S] = 0x4, [SENSOR_THERMO_TYPE_E] = 0x5, + [SENSOR_THERMO_TYPE_B] = 0x6, [SENSOR_THERMO_TYPE_R] = 0x7, +}; + +/* Resolutions */ + +static const float g_resolutions[] = +{ + [MCP9600_COLDRES_0625] = 0.0625f, + [MCP9600_COLDRES_25] = 0.25f, +}; + /* Alert hysterisis registers */ static const enum mcp9600_alert_e g_alert_hysts[] = { - [MCP9600_ALERT1] = REG_ALERT1_HYST, - [MCP9600_ALERT2] = REG_ALERT2_HYST, - [MCP9600_ALERT3] = REG_ALERT3_HYST, - [MCP9600_ALERT4] = REG_ALERT4_HYST, + [MCP9600_ALERT1] = REG_ALERT1_HYST, + [MCP9600_ALERT2] = REG_ALERT2_HYST, + [MCP9600_ALERT3] = REG_ALERT3_HYST, + [MCP9600_ALERT4] = REG_ALERT4_HYST, }; /* Alert limit registers */ static const enum mcp9600_alert_e g_alert_limits[] = { - [MCP9600_ALERT1] = REG_ALERT1_TEMP, - [MCP9600_ALERT2] = REG_ALERT2_TEMP, - [MCP9600_ALERT3] = REG_ALERT3_TEMP, - [MCP9600_ALERT4] = REG_ALERT4_TEMP, + [MCP9600_ALERT1] = REG_ALERT1_TEMP, + [MCP9600_ALERT2] = REG_ALERT2_TEMP, + [MCP9600_ALERT3] = REG_ALERT3_TEMP, + [MCP9600_ALERT4] = REG_ALERT4_TEMP, }; /* Alert configuration registers */ static const enum mcp9600_alert_e g_alert_configs[] = { - [MCP9600_ALERT1] = REG_ALERT1_CONF, - [MCP9600_ALERT2] = REG_ALERT2_CONF, - [MCP9600_ALERT3] = REG_ALERT3_CONF, - [MCP9600_ALERT4] = REG_ALERT4_CONF, + [MCP9600_ALERT1] = REG_ALERT1_CONF, + [MCP9600_ALERT2] = REG_ALERT2_CONF, + [MCP9600_ALERT3] = REG_ALERT3_CONF, + [MCP9600_ALERT4] = REG_ALERT4_CONF, }; /**************************************************************************** @@ -233,9 +280,10 @@ static int mcp9600_write_reg(FAR struct mcp9600_dev_s *priv, uint8_t reg, ****************************************************************************/ static int mcp9600_read_temp(FAR struct mcp9600_dev_s *priv, uint8_t reg, - FAR int16_t *temp) + FAR struct sensor_temp *temp) { int err; + int16_t raw_temp; uint8_t raw[2]; err = mcp9600_read_reg(priv, reg, raw, sizeof(raw)); @@ -246,15 +294,60 @@ static int mcp9600_read_temp(FAR struct mcp9600_dev_s *priv, uint8_t reg, /* Positive temperature */ - *temp = (raw[0] * 16 + raw[1] / 16); + raw_temp = (raw[0] * 16 + raw[1] / 16); /* Negative temperature */ if (raw[0] & 0x80) { - *temp -= 4096; + raw_temp -= 4096; } + temp->temperature = (float)(raw_temp); + temp->timestamp = sensor_get_timestamp(); + + return err; +} + +/**************************************************************************** + * Name: mcp9600_read + * + * Description: + * Reads all thermocouple values in degrees Celsius. + * + ****************************************************************************/ + +static int mcp9600_read(FAR struct mcp9600_dev_s *priv, + FAR struct sensor_temp *hot, + FAR struct sensor_temp *cold, + FAR struct sensor_temp *delta) +{ + int err; + + /* Exclusive access */ + + err = nxmutex_lock(&priv->devlock); + if (err < 0) + { + return err; + } + + err = mcp9600_read_temp(priv, REG_JUNC_TEMP_DELTA, delta); + if (err < 0) + { + goto early_ret; + }; + + err = mcp9600_read_temp(priv, REG_THERMO_HOT_JUNC, hot); + if (err < 0) + { + goto early_ret; + }; + + err = mcp9600_read_temp(priv, REG_COLD_JUNC_TEMP, cold); + +early_ret: + nxmutex_unlock(&priv->devlock); return err; } @@ -303,6 +396,26 @@ static int mcp9600_config_alert(FAR struct mcp9600_dev_s *priv, sizeof(config_reg)); } +/**************************************************************************** + * Name: mcp9600_write_devconf + * + * Description: + * Writes configuration settings for the device configuration register. + * Returns 0 on success and negated errno value on failure. + * + ****************************************************************************/ + +static int mcp9600_write_devconf(FAR struct mcp9600_dev_s *dev) +{ + uint8_t reg = 0; + reg |= (dev->conf.mode & 0x3); + reg |= ((dev->conf.num_samples & 0x7) << 2); + reg |= ((dev->conf.resolution & 0x3) << 5); + reg |= ((dev->conf.cold_res & 0x1) << 7); + + return mcp9600_write_reg(dev, REG_DEV_CONFIG, ®, sizeof(reg)); +} + /**************************************************************************** * Name: mcp9600_validate_conf * @@ -319,12 +432,6 @@ static int mcp9600_validate_conf(FAR struct mcp9600_devconf_s *conf) return -EINVAL; } - if (conf->thermo_type < MCP9600_THERMO_TYPE_K || - conf->thermo_type > MCP9600_THERMO_TYPE_R) - { - return -EINVAL; - } - if (conf->filter_coeff > 0 || conf->filter_coeff > 8) { return -EINVAL; @@ -357,222 +464,147 @@ static int mcp9600_validate_conf(FAR struct mcp9600_devconf_s *conf) } /**************************************************************************** - * Name: mcp9600_open + * Name: mcp9600_set_interval * * Description: - * This function is called whenever the MCP9600 device is opened. + * Sets the measurement interval for the MCP9600 sensor in microseconds. * ****************************************************************************/ -#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS -static int mcp9600_open(FAR struct file *filep) +static int mcp9600_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 mcp9600_dev_s *priv = inode->i_private; - int err; - - err = nxmutex_lock(&priv->devlock); - if (err < 0) - { - return err; - } - - /* Increment the count of open references on the driver */ - - priv->crefs++; - DEBUGASSERT(priv->crefs > 0); - - nxmutex_unlock(&priv->devlock); + FAR struct mcp9600_sens_s *sens = + container_of(lower, FAR struct mcp9600_sens_s, lower); + sens->dev->interval = *period_us; return 0; } -#endif /**************************************************************************** - * Name: mcp9600_close - * - * Description: - * This routine is called when the MCP9600 device is closed. - * + * Name: mcp9600_activate ****************************************************************************/ -#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS -static int mcp9600_close(FAR struct file *filep) +static int mcp9600_activate(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, bool enable) { - FAR struct inode *inode = filep->f_inode; - FAR struct mcp9600_dev_s *priv = inode->i_private; - int err; - - err = nxmutex_lock(&priv->devlock); - if (err < 0) - { - return err; - } + bool start_thread = false; + int err = 0; + FAR struct mcp9600_sens_s *priv = + container_of(lower, FAR struct mcp9600_sens_s, lower); + FAR struct mcp9600_dev_s *dev = priv->dev; - /* Decrement the count of open references on the driver */ - - DEBUGASSERT(priv->crefs > 0); - priv->crefs--; - - /* If the count has decremented to zero and the driver has been unlinked, - * then free memory now. - */ - - if (priv->crefs <= 0 && priv->unlinked) + if (enable && !priv->enabled) { - nxmutex_destroy(&priv->devlock); - kmm_free(priv); - return 0; - } + start_thread = true; - nxmutex_unlock(&priv->devlock); - return 0; -} -#endif + /* Power on the sensor for operation */ -/**************************************************************************** - * Name: mcp9600_unlink - ****************************************************************************/ - -#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS -static int mcp9600_unlink(FAR struct inode *inode) -{ - FAR struct mcp9600_dev_s *priv; - int err; - - DEBUGASSERT(inode->i_private != NULL); - priv = inode->i_private; - - err = nxmutex_lock(&priv->devlock); - if (err < 0) - { - return err; + if (dev->conf.mode == MCP9600_MODE_SHUTDOWN) + { + dev->conf.mode = MCP9600_MODE_NORMAL; + err = mcp9600_write_devconf(dev); + } } - /* Are there open references to the driver data structure? */ - - if (priv->crefs <= 0) + else if (!enable && priv->enabled) { - nxmutex_destroy(&priv->devlock); - kmm_free(priv); - return 0; - } - - /* No. Just mark the driver as unlinked and free the resources when - * the last client closes their reference to the driver. - */ + /* Temporarily mark disabled so we can check if everything is disabled + */ - priv->unlinked = true; - nxmutex_unlock(&priv->devlock); - return OK; -} -#endif + priv->enabled = enable; -/**************************************************************************** - * Name: mcp9600_read - * - * Description: - * Character driver interface to sensor for debugging. - * - ****************************************************************************/ + /* Power off the sensor to save power only if all features are disabled + * and we're not yet shut down. + */ -static ssize_t mcp9600_read(FAR struct file *filep, FAR char *buffer, - size_t buflen) -{ - FAR struct inode *inode = filep->f_inode; - FAR struct mcp9600_dev_s *priv = inode->i_private; - int err; - int16_t hot_junc_temp; + if ((dev->conf.mode != MCP9600_MODE_SHUTDOWN) && + (!dev->hot_junc.enabled && !dev->cold_junc.enabled && + !dev->delta.enabled)) + { + /* Put back enable state in case we encounter an error and fail to + * disable + */ - /* If file position is non-zero, then we're at the end of file. */ + priv->enabled = true; - if (filep->f_pos > 0) - { - return 0; + dev->conf.mode = MCP9600_MODE_SHUTDOWN; + err = mcp9600_write_devconf(dev); + } } - /* Get exclusive access */ - - err = nxmutex_lock(&priv->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. - */ - - goto finish_unlock; - } -#endif - - err = mcp9600_read_temp(priv, REG_THERMO_HOT_JUNC, &hot_junc_temp); + priv->enabled = enable; - if (err < 0) + if (start_thread) { - goto finish_unlock; - } + /* Wake up the polling thread */ - err = snprintf(buffer, buflen, "%d C\n", hot_junc_temp); - - if (err > buflen) - { - err = buflen; + nxsem_post(&dev->run); } - filep->f_pos += err; - -finish_unlock: - nxmutex_unlock(&priv->devlock); return err; } /**************************************************************************** - * Name: mcp9600_write - * - * Description: - * Not implemented. + * Name: mcp9600_get_info ****************************************************************************/ -static ssize_t mcp9600_write(FAR struct file *filep, FAR const char *buffer, - size_t buflen) +static int mcp9600_get_info(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, + FAR struct sensor_device_info_s *info) { - return -ENOSYS; + FAR struct mcp9600_sens_s *sens = + container_of(lower, FAR struct mcp9600_sens_s, lower); + FAR struct mcp9600_dev_s *dev = sens->dev; + + info->version = 0; + info->power = 1.5f; /* 1.5mA */ + info->min_delay = 63.0f; + info->max_delay = 250.0f; + memcpy(info->name, "MCP9600", sizeof("MCP9600")); + memcpy(info->vendor, "Microchip", sizeof("Microchip")); + info->max_range = g_thermo_ranges[dev->conf.thermo_type]; + info->resolution = 0.0625f; + info->fifo_reserved_event_count = 0; + info->fifo_max_event_count = 0; + return 0; } /**************************************************************************** * Name: mcp9600_ioctl ****************************************************************************/ -static int mcp9600_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +static int mcp9600_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 mcp9600_dev_s *priv = inode->i_private; + FAR struct mcp9600_sens_s *sens = + container_of(lower, FAR struct mcp9600_sens_s, lower); + FAR struct mcp9600_dev_s *dev = sens->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) + switch (cmd) { - /* Do not allow operations on unlinked sensors. This allows - * sensor use on hot swappable I2C bus. - */ + /* Set thermocouple type */ - nxmutex_unlock(&priv->devlock); - return -ENODEV; - } -#endif + case SNIOC_SET_THERMO: + { + dev->conf.thermo_type = g_thermo_types[arg]; + err = mcp9600_write_devconf(dev); + } + break; - switch (cmd) - { /* Device ID */ case SNIOC_WHO_AM_I: @@ -585,7 +617,7 @@ static int mcp9600_ioctl(FAR struct file *filep, int cmd, unsigned long arg) break; } - err = mcp9600_read_reg(priv, REG_DEVID, devinfo, sizeof(*devinfo)); + err = mcp9600_read_reg(dev, REG_DEVID, devinfo, sizeof(*devinfo)); } break; @@ -600,7 +632,7 @@ static int mcp9600_ioctl(FAR struct file *filep, int cmd, unsigned long arg) break; } - err = mcp9600_read_reg(priv, REG_RAW_ADC, raw_data, + err = mcp9600_read_reg(dev, REG_RAW_ADC, raw_data, 3); /* Only read 24 bits */ /* Sign bit 1, set all upper bits to 1 for correct value in 32-bit @@ -623,7 +655,7 @@ static int mcp9600_ioctl(FAR struct file *filep, int cmd, unsigned long arg) break; } - err = mcp9600_read_reg(priv, REG_STATUS, &status_reg, + err = mcp9600_read_reg(dev, REG_STATUS, &status_reg, sizeof(status_reg)); if (err < 0) { @@ -643,7 +675,7 @@ static int mcp9600_ioctl(FAR struct file *filep, int cmd, unsigned long arg) /* Clear what has been read (burst & temp registers) */ status_reg &= 0x3f; - err = mcp9600_write_reg(priv, REG_STATUS, &status_reg, + err = mcp9600_write_reg(dev, REG_STATUS, &status_reg, sizeof(status_reg)); } break; @@ -684,7 +716,7 @@ static int mcp9600_ioctl(FAR struct file *filep, int cmd, unsigned long arg) * write operation. */ - err = mcp9600_write_reg(priv, REG_THERMO_SEN_CONF, registers, + err = mcp9600_write_reg(dev, REG_THERMO_SEN_CONF, registers, sizeof(registers)); if (err < 0) { @@ -693,7 +725,7 @@ static int mcp9600_ioctl(FAR struct file *filep, int cmd, unsigned long arg) /* Store this as the official configuration */ - memcpy(&priv->conf, conf, sizeof(priv->conf)); + memcpy(&dev->conf, conf, sizeof(dev->conf)); } /* Configure alerts */ @@ -708,73 +740,86 @@ static int mcp9600_ioctl(FAR struct file *filep, int cmd, unsigned long arg) break; } - err = mcp9600_config_alert(priv, conf); + err = mcp9600_config_alert(dev, conf); } - /* Read temperature data */ - - case SNIOC_READTEMP: - { - struct mcp9600_temp_s *temps = (struct mcp9600_temp_s *)(arg); - if (temps == NULL) - { - err = -EINVAL; - break; - } - - err = - mcp9600_read_temp(priv, REG_JUNC_TEMP_DELTA, &temps->temp_delta); - if (err < 0) - { - break; - }; - - err = mcp9600_read_temp(priv, REG_THERMO_HOT_JUNC, &temps->hot_junc); - if (err < 0) - { - break; - }; + default: + err = -EINVAL; + break; + } - err = mcp9600_read_temp(priv, REG_COLD_JUNC_TEMP, &temps->cold_junc); - } + nxmutex_unlock(&dev->devlock); + return err; +} - /* Shutdown the device (argument unused) */ +/**************************************************************************** + * Name: mcp9600_thread + * + * Description: Thread for performing interval measurement cycle and data + * read. + * + * Parameter: + * argc - Number of arguments + * argv - Pointer to argument list + * + ****************************************************************************/ - case SNIOC_SHUTDOWN: - { - uint8_t reg = 0; - priv->conf.mode = MCP9600_MODE_SHUTDOWN; +static int mcp9600_thread(int argc, char **argv) +{ + FAR struct mcp9600_dev_s *dev = + (FAR struct mcp9600_dev_s *)((uintptr_t)strtoul(argv[1], NULL, 16)); + int err; - reg |= (priv->conf.mode & 0x3); - reg |= ((priv->conf.num_samples & 0x7) << 2); - reg |= ((priv->conf.resolution & 0x3) << 5); - reg |= ((priv->conf.cold_res & 0x1) << 7); + struct sensor_temp hot_junc; + struct sensor_temp cold_junc; + struct sensor_temp delta; - err = mcp9600_write_reg(priv, REG_DEV_CONFIG, ®, sizeof(reg)); - } + while (true) + { + if (!dev->hot_junc.enabled && !dev->cold_junc.enabled && + !dev->delta.enabled) + { + /* Wait for one of the lower halves to be enabled and wake us up */ + + snerr("MCP9600 disabled, waiting...\n"); + err = nxsem_wait(&dev->run); + if (err < 0) + { + continue; + } + } + + err = mcp9600_read(dev, &hot_junc, &cold_junc, &delta); + if (err < 0) + { + snerr("Error reading MCP9600: %d\n", err); + continue; + } - /* Start the device again */ + if (dev->hot_junc.enabled) + { + dev->hot_junc.lower.push_event(dev->hot_junc.lower.priv, &hot_junc, + sizeof(hot_junc)); + } - case SNIOC_START: - { - uint8_t reg = 0; - priv->conf.mode = MCP9600_MODE_NORMAL; + if (dev->cold_junc.enabled) + { + dev->cold_junc.lower.push_event(dev->cold_junc.lower.priv, + &cold_junc, sizeof(cold_junc)); + } - reg |= (priv->conf.mode & 0x3); - reg |= ((priv->conf.num_samples & 0x7) << 2); - reg |= ((priv->conf.resolution & 0x3) << 5); - reg |= ((priv->conf.cold_res & 0x1) << 7); + if (dev->delta.enabled) + { + dev->delta.lower.push_event(dev->delta.lower.priv, &delta, + sizeof(delta)); + } - err = mcp9600_write_reg(priv, REG_DEV_CONFIG, ®, sizeof(reg)); - } + /* Sleep before next fetch */ - default: - err = -EINVAL; - break; + nxsig_usleep(dev->interval); } - nxmutex_unlock(&priv->devlock); - return err; + return OK; } /**************************************************************************** @@ -785,23 +830,29 @@ static int mcp9600_ioctl(FAR struct file *filep, int cmd, unsigned long arg) * Name: mcp9600_register * * Description: - * Register the MCP9600 character device as 'devpath' + * Register the MCP9600 UORB sensor. This registers 3 temperature UORB + * topics. * * 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 MCP9600 * addr - The I2C address of the MCP9600, between 0x60 and 0x67 * + * h_devno - The device number for the hot junction topic + * c_devno - The device number for the cold junction topic + * d_devno - The device number for the delta topic + * * Returned Value: * Zero (OK) on success; a negated errno value on failure. * ****************************************************************************/ -int mcp9600_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, - uint8_t addr) +int mcp9600_register(FAR struct i2c_master_s *i2c, uint8_t addr, + uint8_t h_devno, uint8_t c_devno, uint8_t d_devno) { FAR struct mcp9600_dev_s *priv; + FAR char *argv[2]; + char arg1[32]; int err; DEBUGASSERT(i2c != NULL); @@ -818,10 +869,11 @@ int mcp9600_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, priv->i2c = i2c; priv->addr = addr; + priv->interval = 1000000; /* 1s default interval */ priv->conf = (struct mcp9600_devconf_s) { - .thermo_type = MCP9600_THERMO_TYPE_T, + .thermo_type = SENSOR_THERMO_TYPE_T, .filter_coeff = 0, .resolution = MCP9600_ADC_RES_18, .num_samples = MCP9600_SAMPLE_1, @@ -829,8 +881,15 @@ int mcp9600_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, .mode = MCP9600_COLDRES_0625, }; - priv->unlinked = false; - priv->crefs = 0; + /* Initialize semaphore */ + + err = nxsem_init(&priv->run, 0, 0); + if (err < 0) + { + snerr("Failed to register MCP9600 driver: %d\n", err); + kmm_free(priv); + return err; + } /* Initialize mutex */ @@ -838,18 +897,73 @@ int mcp9600_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, if (err < 0) { snerr("ERROR: Failed to register MCP9600 driver: %d\n", err); - kmm_free(priv); - return err; + goto del_sem; } - /* Register the character driver */ + /* Cold junction lower half */ + + priv->cold_junc.enabled = false; + priv->cold_junc.dev = priv; + priv->cold_junc.lower.type = SENSOR_TYPE_TEMPERATURE; + priv->cold_junc.lower.ops = &g_sensor_ops; - err = register_driver(devpath, &g_mcp9600fops, 0666, priv); + err = sensor_register(&priv->cold_junc.lower, c_devno); if (err < 0) { - snerr("ERROR: Failed to register MCP9600 driver: %d\n", err); + snerr("Failed to register MCP9600 driver: %d\n", err); + goto del_mutex; + } + + /* Hot junction lower half */ + + priv->hot_junc.enabled = false; + priv->hot_junc.dev = priv; + priv->hot_junc.lower.type = SENSOR_TYPE_TEMPERATURE; + priv->hot_junc.lower.ops = &g_sensor_ops; + + err = sensor_register(&priv->hot_junc.lower, h_devno); + if (err < 0) + { + snerr("Failed to register MCP9600 driver: %d\n", err); + goto unreg_cold; + } + + /* Delta lower half */ + + priv->delta.enabled = false; + priv->delta.dev = priv; + priv->delta.lower.type = SENSOR_TYPE_TEMPERATURE; + priv->delta.lower.ops = &g_sensor_ops; + + err = sensor_register(&priv->delta.lower, d_devno); + if (err < 0) + { + snerr("Failed to register MCP9600 driver: %d\n", err); + goto unreg_hot; + } + + /* Start polling thread */ + + snprintf(arg1, 16, "%p", priv); + argv[0] = arg1; + argv[1] = NULL; + err = kthread_create("mcp9600_thread", SCHED_PRIORITY_DEFAULT, + CONFIG_MCP9600_THREAD_STACKSIZE, mcp9600_thread, + argv); + if (err < 0) + { + snerr("Failed to create the MCP9600 notification kthread.\n"); + sensor_unregister(&priv->delta.lower, d_devno); + unreg_hot: + sensor_unregister(&priv->hot_junc.lower, h_devno); + unreg_cold: + sensor_unregister(&priv->cold_junc.lower, c_devno); + del_mutex: nxmutex_destroy(&priv->devlock); + del_sem: + nxsem_destroy(&priv->run); kmm_free(priv); + return err; } return err; diff --git a/include/nuttx/sensors/ioctl.h b/include/nuttx/sensors/ioctl.h index a77cd19c2752f..96bf05dc7454b 100644 --- a/include/nuttx/sensors/ioctl.h +++ b/include/nuttx/sensors/ioctl.h @@ -454,7 +454,7 @@ #endif /* Command: SNIOC_FLUSH - * Description: Flush sensor harware fifo buffer. + * Description: Flush sensor hardware fifo buffer. */ #define SNIOC_FLUSH _SNIOC(0x009D) @@ -466,4 +466,31 @@ #define SNIOC_GET_EVENTS _SNIOC(0x009E) +/* Command: SNIOC_SET_THERMO + * Description: Set the thermocouple type. + * Argument: An option from `enum sensor_thermo_type_e` + */ + +#define SNIOC_SET_THERMO _SNIOC(0x009F) + +/**************************************************************************** + * Public types + ****************************************************************************/ + +/* Possible thermocouple types. Implementations should not rely on the enum's + * underlying value. + */ + +enum sensor_thermo_type_e +{ + SENSOR_THERMO_TYPE_K, + SENSOR_THERMO_TYPE_J, + SENSOR_THERMO_TYPE_T, + SENSOR_THERMO_TYPE_N, + SENSOR_THERMO_TYPE_S, + SENSOR_THERMO_TYPE_E, + SENSOR_THERMO_TYPE_B, + SENSOR_THERMO_TYPE_R, +}; + #endif /* __INCLUDE_NUTTX_SENSORS_IOCTL_H */ diff --git a/include/nuttx/sensors/mcp9600.h b/include/nuttx/sensors/mcp9600.h index e336cbaef2c85..0cc3762f7710d 100644 --- a/include/nuttx/sensors/mcp9600.h +++ b/include/nuttx/sensors/mcp9600.h @@ -56,20 +56,6 @@ enum mcp9600_alert_e MCP9600_ALERT4 = 3, /* Alert 4 */ }; -/* Thermocouple types */ - -enum mcp9600_thermocouple_e -{ - MCP9600_THERMO_TYPE_K = 0x0, - MCP9600_THERMO_TYPE_J = 0x1, - MCP9600_THERMO_TYPE_T = 0x2, - MCP9600_THERMO_TYPE_N = 0x3, - MCP9600_THERMO_TYPE_S = 0x4, - MCP9600_THERMO_TYPE_E = 0x5, - MCP9600_THERMO_TYPE_B = 0x6, - MCP9600_THERMO_TYPE_R = 0x7, -}; - /* ADC resolution */ enum mcp9600_resolution_e @@ -134,7 +120,7 @@ struct mcp9600_alert_conf_s struct mcp9600_devconf_s { - enum mcp9600_thermocouple_e thermo_type; /* Thermocouple type */ + enum sensor_thermo_type_e thermo_type; /* Thermocouple type */ uint8_t filter_coeff; /* Filter coefficient */ enum mcp9600_resolution_e resolution; /* ADC resolution */ enum mcp9600_samples_e num_samples; /* Number of samples */ @@ -161,15 +147,6 @@ struct mcp9600_status_s bool alerts[4]; /* Alert statuses for alerts 1-4 (0-3) */ }; -/* Temperature readings */ - -struct mcp9600_temp_s -{ - int16_t temp_delta; /* Temperature delta in degrees Celsius */ - int16_t hot_junc; /* Hot junction temperature in degrees Celsius */ - int16_t cold_junc; /* Cold junction temperature in degrees Celsius */ -}; - /**************************************************************************** * Public Function Prototypes ****************************************************************************/ @@ -178,20 +155,24 @@ struct mcp9600_temp_s * Name: mcp9600_register * * Description: - * Register the MCP9600 character device as 'devpath' + * Register the MCP9600 UORB sensor. This registers 3 temperature UORB + * topics. * * 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 MCP9600 * addr - The I2C address of the MCP9600, between 0x60 and 0x67 * + * h_devno - The device number for the hot junction topic + * c_devno - The device number for the cold junction topic + * d_devno - The device number for the delta topic + * * Returned Value: * Zero (OK) on success; a negated errno value on failure. * ****************************************************************************/ -int mcp9600_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, - uint8_t addr); +int mcp9600_register(FAR struct i2c_master_s *i2c, uint8_t addr, + uint8_t h_devno, uint8_t c_devno, uint8_t d_devno); #endif /* __INCLUDE_NUTTX_SENSORS_MCP9600_H */ diff --git a/include/nuttx/uorb.h b/include/nuttx/uorb.h index 8a201b2bdc5a5..2f2cfbf12b1b6 100644 --- a/include/nuttx/uorb.h +++ b/include/nuttx/uorb.h @@ -112,7 +112,7 @@ * Celsius. */ -#define SENSOR_TYPE_TEMPERAUTRE 7 +#define SENSOR_TYPE_TEMPERATURE 7 /* Proximity * The values correspond to the distance to the nearest @@ -162,7 +162,7 @@ #define SENSOR_TYPE_AMBIENT_TEMPERATURE 13 -/* Magneric Field Uncalibrated +/* Magnetic Field Uncalibrated * Similar to MAGNETIC_FIELD, all values are in micro-Tesla (uT) * and measure the geomagnetic field in X, Y and Z axis. */