diff --git a/Documentation/components/drivers/special/sensors.rst b/Documentation/components/drivers/special/sensors.rst index a8c91a0ce3b..08b1122b181 100644 --- a/Documentation/components/drivers/special/sensors.rst +++ b/Documentation/components/drivers/special/sensors.rst @@ -37,6 +37,7 @@ tool for monitoring sensor activity at runtime. sensors/nau7802.rst sensors/qmi8658.rst sensors/sht4x.rst + sensors/tmp112.rst sensors/lsm6dso32.rst sensors/lis2mdl.rst sensors/l86xxx.rst diff --git a/Documentation/components/drivers/special/sensors/sensors_uorb.rst b/Documentation/components/drivers/special/sensors/sensors_uorb.rst index da61b475c10..9e6a2fea858 100644 --- a/Documentation/components/drivers/special/sensors/sensors_uorb.rst +++ b/Documentation/components/drivers/special/sensors/sensors_uorb.rst @@ -557,5 +557,6 @@ Implemented Drivers - :doc:`nau7802` - :doc:`qmi8658` - :doc:`sht4x` +- :doc:`tmp112` - :doc:`lsm6dso32` - wtgahrs2 diff --git a/Documentation/components/drivers/special/sensors/tmp112.rst b/Documentation/components/drivers/special/sensors/tmp112.rst new file mode 100644 index 00000000000..a1315867848 --- /dev/null +++ b/Documentation/components/drivers/special/sensors/tmp112.rst @@ -0,0 +1,59 @@ +====== +TMP112 +====== + +The TMP112 is a Texas Instruments I2C temperature sensor, specified from +-40°C to +125°C, with a twelve bit reading of 0.0625°C a count. Two address +pins select one of four bus addresses, 0x48 through 0x4b, so four parts can +share a bus. + +Two drivers exist for this part. This one uses the :doc:`uorb +` interface, so the reading +appears as a topic the common sensor tools can read. The older driver presents +a character device instead. Only one of the two is built: +``CONFIG_SENSORS_TMP112_UORB`` selects this one, and the board must then call +``tmp112_register_uorb()`` rather than ``tmp112_register()``, since the +registration interfaces differ. + +The uORB driver sign extends the reading. The character device driver does +not, so it reports anything below freezing as a large positive number, and +this part is specified down to -40°C. + +Application Programming Interface +================================= + +The header file for the TMP112 driver interface can be included using: + +.. code-block:: c + + #include + +Registering the driver creates one topic, ``sensor_temp``, where ``n`` is +the ``devno`` passed in: + +.. code-block:: c + + int tmp112_register_uorb(int devno, FAR struct i2c_master_s *i2c, + uint8_t addr); + +For example, a part strapped to 0x48 on I2C bus 0, published as +``sensor_temp0``: + +.. code-block:: c + + struct i2c_master_s *i2c = board_i2cbus_initialize(0); + + tmp112_register_uorb(0, i2c, 0x48); + +Reading +======= + +The part converts continuously out of reset, so the driver configures nothing +and the temperature register always holds the last completed conversion. A +read is therefore one bus transaction with no wait, and the driver polls on +the low priority work queue at whatever interval a consumer asks for. + +Asking for samples faster than the part produces them returns the same value +again, so the driver reports back the interval it will really use rather than +the one requested. A consumer that wants to know what it is getting should +read the interval back after setting it. diff --git a/drivers/sensors/CMakeLists.txt b/drivers/sensors/CMakeLists.txt index bfcf3614c25..616af9d301d 100644 --- a/drivers/sensors/CMakeLists.txt +++ b/drivers/sensors/CMakeLists.txt @@ -349,7 +349,11 @@ if(CONFIG_SENSORS) endif() if(CONFIG_SENSORS_TMP112) - list(APPEND SRCS tmp112.c) + if(CONFIG_SENSORS_TMP112_UORB) + list(APPEND SRCS tmp112_uorb.c) + else() + list(APPEND SRCS tmp112.c) + endif() endif() # QMI8658 6-axis IMU diff --git a/drivers/sensors/Kconfig b/drivers/sensors/Kconfig index f10ef20d0c1..43f4a441ded 100644 --- a/drivers/sensors/Kconfig +++ b/drivers/sensors/Kconfig @@ -2262,6 +2262,24 @@ config TMP112_I2C_FREQUENCY int "TMP112 I2C frequency" default 400000 +config SENSORS_TMP112_UORB + bool "TMP112 UORB Interface" + default n + depends on UORB + ---help--- + Build the sensor framework version of this driver rather than + the character device one. The part then appears as a uORB + temperature topic, which the common sensor tools can read, + instead of as a node returning a bare float. + + The two cannot be built together, and the register interface + differs, so a board choosing this must call + tmp112_register_uorb() rather than tmp112_register(). + + This version also sign extends the reading, which the + character device one does not, so temperatures below freezing + are reported correctly rather than as large positive ones. + endif #SENSORS_TMP112 config SENSORS_QMI8658 diff --git a/drivers/sensors/Make.defs b/drivers/sensors/Make.defs index 13cd2adebea..6cfcd615bd4 100644 --- a/drivers/sensors/Make.defs +++ b/drivers/sensors/Make.defs @@ -343,8 +343,12 @@ ifeq ($(CONFIG_SENSORS_AMG88XX),y) endif ifeq ($(CONFIG_SENSORS_TMP112),y) +ifeq ($(CONFIG_SENSORS_TMP112_UORB),y) + CSRCS += tmp112_uorb.c +else CSRCS += tmp112.c endif +endif endif # CONFIG_I2C diff --git a/drivers/sensors/tmp112_uorb.c b/drivers/sensors/tmp112_uorb.c new file mode 100644 index 00000000000..ab6edb34b3f --- /dev/null +++ b/drivers/sensors/tmp112_uorb.c @@ -0,0 +1,408 @@ +/**************************************************************************** + * drivers/sensors/tmp112_uorb.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_SENSORS_TMP112) && defined(CONFIG_SENSORS_TMP112_UORB) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_TMP112_I2C_FREQUENCY +# define CONFIG_TMP112_I2C_FREQUENCY 400000 +#endif + +/* One reading a second, until something asks for another rate. This part + * measures the room rather than anything that moves quickly. + */ + +#define TMP112_DEFAULT_INTERVAL 1000000 + +/* Twelve bits, a sixteenth of a degree each. */ + +#define TMP112_SIGN_BIT 0x0800 +#define TMP112_SIGN_EXTEND 0xf000 +#define TMP112_LSB_NUM 1 +#define TMP112_LSB_DEN 16 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct tmp112_dev_uorb_s +{ + struct sensor_lowerhalf_s lower; /* Must be first */ + FAR struct i2c_master_s *i2c; + uint8_t addr; + uint32_t interval; /* Microseconds between readings */ + struct work_s work; + bool enabled; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int tmp112_uorb_activate(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, bool enable); +static int tmp112_uorb_set_interval(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, + FAR uint32_t *period_us); +static int tmp112_uorb_get_info(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, + FAR struct sensor_device_info_s *info); +static void tmp112_uorb_worker(FAR void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct sensor_ops_s g_tmp112_uorb_ops = +{ + .activate = tmp112_uorb_activate, + .set_interval = tmp112_uorb_set_interval, + .get_info = tmp112_uorb_get_info, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: tmp112_uorb_delay + * + * Description: + * The requeue delay in ticks for the interval in force, never zero: a + * zero delay would requeue the worker without it ever yielding. + * + * Input Parameters: + * priv - The driver state + * + * Returned Value: + * The delay in clock ticks, at least one. + * + ****************************************************************************/ + +static clock_t tmp112_uorb_delay(FAR struct tmp112_dev_uorb_s *priv) +{ + clock_t ticks = priv->interval / USEC_PER_TICK; + + return ticks > 0 ? ticks : 1; +} + +/**************************************************************************** + * Name: tmp112_uorb_readraw + * + * Description: + * Read the temperature register and return it as the twelve bit signed + * count the part holds. + * + * Input Parameters: + * priv - The driver state + * raw - Where to return the count, sign extended from twelve bits + * + * Returned Value: + * Zero on success, or a negated errno on failure. + * + ****************************************************************************/ + +static int tmp112_uorb_readraw(FAR struct tmp112_dev_uorb_s *priv, + FAR int16_t *raw) +{ + struct i2c_msg_s msg[2]; + uint8_t regaddr = TMP112_REG_TEMP; + uint8_t buffer[2]; + uint16_t value; + int ret; + + msg[0].frequency = CONFIG_TMP112_I2C_FREQUENCY; + msg[0].addr = priv->addr; + msg[0].flags = 0; + msg[0].buffer = ®addr; + msg[0].length = 1; + + msg[1].frequency = CONFIG_TMP112_I2C_FREQUENCY; + msg[1].addr = priv->addr; + msg[1].flags = I2C_M_READ; + msg[1].buffer = buffer; + msg[1].length = 2; + + /* The pointer and the read go out as one transaction. The part converts + * continuously and this register always holds the last completed + * conversion, so there is nothing to wait for between the two. + */ + + ret = I2C_TRANSFER(priv->i2c, msg, 2); + if (ret < 0) + { + snerr("ERROR: cannot read the temperature: %d\n", ret); + return ret; + } + + /* The part sends the high byte first, and the reading occupies the top + * twelve bits of the pair. + */ + + value = ((uint16_t)buffer[0] << 4) | (buffer[1] >> 4); + + /* Sign extend from twelve bits. The character mode driver does not do + * this, so it reads anything below freezing as a large positive number; + * the part itself is specified down to -40C. + */ + + if ((value & TMP112_SIGN_BIT) != 0) + { + value |= TMP112_SIGN_EXTEND; + } + + *raw = (int16_t)value; + return OK; +} + +/**************************************************************************** + * Name: tmp112_uorb_worker + * + * Description: + * Take one reading and publish it, then requeue for the next. The + * requeue happens first so a failed transfer costs one sample rather + * than ending the stream. + * + * Input Parameters: + * arg - The driver state, as passed to work_queue() + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void tmp112_uorb_worker(FAR void *arg) +{ + FAR struct tmp112_dev_uorb_s *priv = arg; + struct sensor_temp temp; + int16_t raw; + + DEBUGASSERT(priv != NULL); + + /* Queue the next reading first, so that a failed transfer costs one + * sample rather than the whole stream. + */ + + work_queue(LPWORK, &priv->work, tmp112_uorb_worker, priv, + tmp112_uorb_delay(priv)); + + if (tmp112_uorb_readraw(priv, &raw) < 0) + { + return; + } + + temp.temperature = sensor_data_divi(sensor_data_itof(raw), + TMP112_LSB_DEN); + temp.timestamp = sensor_get_timestamp(); + + priv->lower.push_event(priv->lower.priv, &temp, sizeof(temp)); +} + +/**************************************************************************** + * Name: tmp112_uorb_activate + * + * Description: + * Start or stop the reading stream. The part measures whether or not + * anything is listening, so this only starts and stops the work that + * collects and publishes. + * + * Input Parameters: + * lower - The sensor lower half + * filep - The file that asked, unused + * enable - True to start reading, false to stop + * + * Returned Value: + * Zero on success, or a negated errno on failure. + * + ****************************************************************************/ + +static int tmp112_uorb_activate(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, bool enable) +{ + FAR struct tmp112_dev_uorb_s *priv = + (FAR struct tmp112_dev_uorb_s *)lower; + + if (enable == priv->enabled) + { + return OK; + } + + if (enable) + { + work_queue(LPWORK, &priv->work, tmp112_uorb_worker, priv, + tmp112_uorb_delay(priv)); + } + else + { + work_cancel(LPWORK, &priv->work); + } + + priv->enabled = enable; + return OK; +} + +/**************************************************************************** + * Name: tmp112_uorb_set_interval + * + * Description: + * Set how often to read the part. + * + * The interval is taken as asked. The upper half rejects a lower half + * that hands back a longer interval than it was given, so clamping here + * would fail the request rather than grant a slower rate. Reading faster + * than the part converts repeats a value, which costs bus traffic and + * nothing else. + * + * Input Parameters: + * lower - The sensor lower half + * filep - The file that asked, unused + * period_us - The interval wanted, updated to the interval granted + * + * Returned Value: + * Zero on success, or a negated errno on failure. + * + ****************************************************************************/ + +static int tmp112_uorb_set_interval(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, + FAR uint32_t *period_us) +{ + FAR struct tmp112_dev_uorb_s *priv = + (FAR struct tmp112_dev_uorb_s *)lower; + + priv->interval = *period_us; + return OK; +} + +/**************************************************************************** + * Name: tmp112_uorb_get_info + * + * Description: + * Describe the part to a consumer that asks: what it is, who makes it, and + * the range and resolution its readings carry. Without this a consumer + * would have to know it was talking to a TMP112 to know what the numbers + * mean. + * + * Input Parameters: + * lower - The sensor lower half + * filep - The file that asked, unused + * info - Where to return the description + * + * Returned Value: + * Zero on success. + * + ****************************************************************************/ + +static int tmp112_uorb_get_info(FAR struct sensor_lowerhalf_s *lower, + FAR struct file *filep, + FAR struct sensor_device_info_s *info) +{ + info->version = 0; + info->power = 0.01f; /* 10uA quiescent */ + info->max_range = 125.0f; /* Specified -40C to +125C */ + info->resolution = 0.0625f; /* Twelve bits over 128C */ + info->min_delay = 0; + info->max_delay = 0; + info->fifo_reserved_event_count = 0; + info->fifo_max_event_count = 0; + strlcpy(info->name, "TMP112", sizeof(info->name)); + strlcpy(info->vendor, "Texas Instruments", sizeof(info->vendor)); + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: tmp112_register_uorb + * + * Description: + * Register the TMP112 as a uORB temperature sensor. + * + * Input Parameters: + * devno - The topic number, giving /dev/uorb/sensor_temp + * i2c - The bus the part is on + * addr - The bus address, 0x48 to 0x4b as the pin straps say + * + * Returned Value: + * Zero (OK) on success; a negated errno on failure. + * + ****************************************************************************/ + +int tmp112_register_uorb(int devno, FAR struct i2c_master_s *i2c, + uint8_t addr) +{ + FAR struct tmp112_dev_uorb_s *priv; + int ret; + + DEBUGASSERT(i2c != NULL); + + priv = kmm_zalloc(sizeof(struct tmp112_dev_uorb_s)); + if (priv == NULL) + { + return -ENOMEM; + } + + priv->i2c = i2c; + priv->addr = addr; + priv->interval = TMP112_DEFAULT_INTERVAL; + priv->lower.ops = &g_tmp112_uorb_ops; + priv->lower.type = SENSOR_TYPE_TEMPERATURE; + + /* The part measures continuously out of reset, so there is nothing to + * configure before it will answer. + */ + + ret = sensor_register(&priv->lower, devno); + if (ret < 0) + { + snerr("ERROR: cannot register: %d\n", ret); + kmm_free(priv); + return ret; + } + + sninfo("TMP112 at %02x registered as sensor_temp%d\n", addr, devno); + return OK; +} + +#endif /* CONFIG_SENSORS_TMP112 && CONFIG_SENSORS_TMP112_UORB */ diff --git a/include/nuttx/sensors/tmp112.h b/include/nuttx/sensors/tmp112.h index f6821686146..a8f57242220 100644 --- a/include/nuttx/sensors/tmp112.h +++ b/include/nuttx/sensors/tmp112.h @@ -105,8 +105,30 @@ extern "C" * ****************************************************************************/ +#ifndef CONFIG_SENSORS_TMP112_UORB int tmp112_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, uint8_t addr); +#else +/**************************************************************************** + * Name: tmp112_register_uorb + * + * Description: + * Register the part as a uORB temperature sensor, appearing as + * /dev/uorb/sensor_temp. + * + * Input Parameters: + * devno - The topic number + * i2c - The bus the part is on + * addr - The bus address, 0x48 to 0x4b as the pin straps say + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int tmp112_register_uorb(int devno, FAR struct i2c_master_s *i2c, + uint8_t addr); +#endif /* CONFIG_SENSORS_TMP112_UORB */ #undef EXTERN #ifdef __cplusplus