drivers/sensors: add Microchip TC74 temperature sensor driver

Add support for the Microchip TC74 digital temperature sensor using the
Sensor Driver Framework (uORB). The TC74 is an 8-bit I2C temperature
sensor with a measurement range from -40C to +125C and a resolution
of 1C.

The driver registers as a uORB topic (/dev/uorb/sensor_temp<n>) and
polls on the low-priority work queue. It supports dynamic interval
configuration and automatically enters low-power standby mode when
the topic is deactivated.

Validated against a real TC74A5-3.3 on a custom STM32H743BI board.

Assisted-by: Gemini:gemini-3.8-pro
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
This commit is contained in:
Daniel P. Carvalho 2026-09-17 07:00:57 -03:00 • committed by Xiang Xiao
parent 86159f3353
commit 1b172fb8d2
8 changed files with 580 additions and 0 deletions

View file

@ -37,6 +37,7 @@ tool for monitoring sensor activity at runtime.
sensors/nau7802.rst
sensors/qmi8658.rst
sensors/sht4x.rst
sensors/tc74.rst
sensors/tmp112.rst
sensors/lsm6dso32.rst
sensors/lsm6ds3trc.rst

View file

@ -557,6 +557,7 @@ Implemented Drivers
- :doc:`nau7802`
- :doc:`qmi8658`
- :doc:`sht4x`
- :doc:`tc74`
- :doc:`tmp112`
- :doc:`lsm6dso32`
- :doc:`lsm6ds3trc`

View file

@ -0,0 +1,46 @@
====
TC74
====
The TC74 is a Microchip serial digital thermal sensor, specified from
-40°C to +125°C, with an eight-bit reading of 1°C a count. Factory-programmed
I2C addresses range from 0x48 through 0x4f (suffixes A0 through A7), allowing
up to eight parts on the same bus.
This driver uses the :doc:`uorb
</components/drivers/special/sensors/sensors_uorb>` interface, so the reading
appears as a topic the common sensor tools can read.
Application Programming Interface
=================================
The header file for the TC74 driver interface can be included using:
.. code-block:: c
#include <nuttx/sensors/tc74.h>
Registering the driver creates one topic, ``sensor_temp<n>``, where ``n`` is
the ``devno`` passed in:
.. code-block:: c
int tc74_register(int devno, FAR struct i2c_master_s *i2c, uint8_t addr);
For example, a part with address 0x4d (TC74A5) on I2C bus 0, published as
``sensor_temp0``:
.. code-block:: c
FAR struct i2c_master_s *i2c = board_i2cbus_initialize(0);
tc74_register(0, i2c, TC74_ADDR_A5);
Reading
=======
The driver polls on the low priority work queue at whatever interval a
consumer asks for. When activated (for example when an application subscribes
to the sensor topic), the driver wakes the part up from standby mode. When
deactivated (when all subscribers close the topic), it places the part back
into low-power standby mode to save energy.

View file

@ -360,6 +360,10 @@ if(CONFIG_SENSORS)
endif()
endif()
if(CONFIG_SENSORS_TC74)
list(APPEND SRCS tc74.c)
endif()
# QMI8658 6-axis IMU
if(CONFIG_SENSORS_QMI8658)

View file

@ -2354,6 +2354,24 @@ config SENSORS_TMP112_UORB
endif #SENSORS_TMP112
config SENSORS_TC74
bool "Microchip TC74 Temperature Sensor support"
default n
select I2C
---help---
Enable driver support for the Microchip TC74 digital temperature sensor.
if SENSORS_TC74
config TC74_I2C_FREQUENCY
int "TC74 I2C frequency"
default 100000
range 1 100000
---help---
I2C clock frequency for the TC74 sensor. Default is 100 kHz.
endif # SENSORS_TC74
config SENSORS_QMI8658
bool "QST QMI8658 6-Axis IMU Sensor support"
default n

View file

@ -354,6 +354,10 @@ else
endif
endif
ifeq ($(CONFIG_SENSORS_TC74),y)
CSRCS += tc74.c
endif
endif # CONFIG_I2C
# These drivers depend on SPI support

412
drivers/sensors/tc74.c Normal file
View file

@ -0,0 +1,412 @@
/****************************************************************************
* drivers/sensors/tc74.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 <nuttx/config.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <debug.h>
#include <nuttx/i2c/i2c_master.h>
#include <nuttx/kmalloc.h>
#include <nuttx/sensors/sensor.h>
#include <nuttx/sensors/tc74.h>
#include <nuttx/wqueue.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifndef CONFIG_TC74_I2C_FREQUENCY
# define CONFIG_TC74_I2C_FREQUENCY 100000
#endif
/* TC74 Register Addresses */
#define TC74_TEMP_REG 0x00 /* Temperature Register (read-only) */
#define TC74_CONFIG_REG 0x01 /* Configuration Register (read/write) */
/* Configuration Register Bit Definitions */
#define TC74_CONFIG_STANDBY (1 << 7) /* Bit 7: 1=Standby mode, 0=Normal mode */
/* One reading a second by default until requested otherwise */
#define TC74_DEFAULT_INTERVAL 1000000
/****************************************************************************
* Private Types
****************************************************************************/
struct tc74_dev_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 tc74_activate(FAR struct sensor_lowerhalf_s *lower,
FAR struct file *filep, bool enable);
static int tc74_set_interval(FAR struct sensor_lowerhalf_s *lower,
FAR struct file *filep,
FAR uint32_t *period_us);
static int tc74_get_info(FAR struct sensor_lowerhalf_s *lower,
FAR struct file *filep,
FAR struct sensor_device_info_s *info);
static void tc74_worker(FAR void *arg);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct sensor_ops_s g_tc74_ops =
{
.activate = tc74_activate,
.set_interval = tc74_set_interval,
.get_info = tc74_get_info,
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: tc74_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 tc74_delay(FAR struct tc74_dev_s *priv)
{
clock_t ticks = priv->interval / USEC_PER_TICK;
return ticks > 0 ? ticks : 1;
}
/****************************************************************************
* Name: tc74_putreg8
*
* Description:
* Write an 8-bit value to a TC74 register.
*
* Input Parameters:
* priv - The driver state
* regaddr - The register address
* regval - The value to write
*
* Returned Value:
* Zero on success; a negated errno value on failure.
*
****************************************************************************/
static int tc74_putreg8(FAR struct tc74_dev_s *priv,
uint8_t regaddr, uint8_t regval)
{
struct i2c_msg_s msg;
uint8_t buffer[2];
int ret;
buffer[0] = regaddr;
buffer[1] = regval;
msg.frequency = CONFIG_TC74_I2C_FREQUENCY;
msg.addr = priv->addr;
msg.flags = 0;
msg.buffer = buffer;
msg.length = 2;
ret = I2C_TRANSFER(priv->i2c, &msg, 1);
if (ret < 0)
{
snerr("ERROR: I2C_TRANSFER failed: %d\n", ret);
}
return ret;
}
/****************************************************************************
* Name: tc74_readraw
*
* Description:
* Read the temperature register and return it as the signed 8-bit count.
*
* Input Parameters:
* priv - The driver state
* raw - Where to return the signed 8-bit temperature in degrees C
*
* Returned Value:
* Zero on success, or a negated errno on failure.
*
****************************************************************************/
static int tc74_readraw(FAR struct tc74_dev_s *priv, FAR int8_t *raw)
{
struct i2c_msg_s msg[2];
uint8_t regaddr = TC74_TEMP_REG;
uint8_t val;
int ret;
msg[0].frequency = CONFIG_TC74_I2C_FREQUENCY;
msg[0].addr = priv->addr;
msg[0].flags = 0;
msg[0].buffer = &regaddr;
msg[0].length = 1;
msg[1].frequency = CONFIG_TC74_I2C_FREQUENCY;
msg[1].addr = priv->addr;
msg[1].flags = I2C_M_READ;
msg[1].buffer = &val;
msg[1].length = 1;
ret = I2C_TRANSFER(priv->i2c, msg, 2);
if (ret < 0)
{
snerr("ERROR: cannot read temperature: %d\n", ret);
return ret;
}
*raw = (int8_t)val;
return OK;
}
/****************************************************************************
* Name: tc74_worker
*
* Description:
* Take one reading and publish it, then requeue for the next.
*
* Input Parameters:
* arg - The driver state, as passed to work_queue()
*
* Returned Value:
* None.
*
****************************************************************************/
static void tc74_worker(FAR void *arg)
{
FAR struct tc74_dev_s *priv = arg;
struct sensor_temp temp;
int8_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, tc74_worker, priv,
tc74_delay(priv));
if (tc74_readraw(priv, &raw) < 0)
{
return;
}
temp.temperature = sensor_data_itof(raw);
temp.timestamp = sensor_get_timestamp();
priv->lower.push_event(priv->lower.priv, &temp, sizeof(temp));
}
/****************************************************************************
* Name: tc74_activate
*
* Description:
* Start or stop the reading stream.
*
* 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 tc74_activate(FAR struct sensor_lowerhalf_s *lower,
FAR struct file *filep, bool enable)
{
FAR struct tc74_dev_s *priv = (FAR struct tc74_dev_s *)lower;
if (enable == priv->enabled)
{
return OK;
}
if (enable)
{
/* Wake up TC74 from standby mode */
tc74_putreg8(priv, TC74_CONFIG_REG, 0);
work_queue(LPWORK, &priv->work, tc74_worker, priv,
tc74_delay(priv));
}
else
{
work_cancel(LPWORK, &priv->work);
/* Place TC74 into low-power standby mode */
tc74_putreg8(priv, TC74_CONFIG_REG, TC74_CONFIG_STANDBY);
}
priv->enabled = enable;
return OK;
}
/****************************************************************************
* Name: tc74_set_interval
*
* Description:
* Set how often to read the part.
*
* 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 tc74_set_interval(FAR struct sensor_lowerhalf_s *lower,
FAR struct file *filep,
FAR uint32_t *period_us)
{
FAR struct tc74_dev_s *priv = (FAR struct tc74_dev_s *)lower;
priv->interval = *period_us;
return OK;
}
/****************************************************************************
* Name: tc74_get_info
*
* Description:
* Describe the part: vendor, range, and resolution.
*
* 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 tc74_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.2f; /* 200 uA operating current */
info->max_range = 125.0f; /* Specified -40C to +125C */
info->resolution = 1.0f; /* 8-bit signed, 1C step */
info->min_delay = 0;
info->max_delay = 0;
info->fifo_reserved_event_count = 0;
info->fifo_max_event_count = 0;
strlcpy(info->name, "TC74", sizeof(info->name));
strlcpy(info->vendor, "Microchip", sizeof(info->vendor));
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: tc74_register
*
* Description:
* Register the TC74 as a uORB temperature sensor.
*
* Input Parameters:
* devno - The topic number, giving /dev/uorb/sensor_temp<devno>
* i2c - The bus the part is on
* addr - The 7-bit bus address (e.g., TC74_ADDR_A0 through TC74_ADDR_A7)
*
* Returned Value:
* Zero (OK) on success; a negated errno on failure.
*
****************************************************************************/
int tc74_register(int devno, FAR struct i2c_master_s *i2c, uint8_t addr)
{
FAR struct tc74_dev_s *priv;
int ret;
DEBUGASSERT(i2c != NULL);
priv = kmm_zalloc(sizeof(struct tc74_dev_s));
if (priv == NULL)
{
return -ENOMEM;
}
priv->i2c = i2c;
priv->addr = addr;
priv->interval = TC74_DEFAULT_INTERVAL;
priv->lower.ops = &g_tc74_ops;
priv->lower.type = SENSOR_TYPE_TEMPERATURE;
/* Put sensor in standby by default until activated */
tc74_putreg8(priv, TC74_CONFIG_REG, TC74_CONFIG_STANDBY);
ret = sensor_register(&priv->lower, devno);
if (ret < 0)
{
snerr("ERROR: cannot register: %d\n", ret);
kmm_free(priv);
return ret;
}
sninfo("TC74 at %02x registered as sensor_temp%d\n", addr, devno);
return OK;
}

View file

@ -0,0 +1,94 @@
/****************************************************************************
* include/nuttx/sensors/tc74.h
*
* 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.
*
****************************************************************************/
#ifndef __INCLUDE_NUTTX_SENSORS_TC74_H
#define __INCLUDE_NUTTX_SENSORS_TC74_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#if defined(CONFIG_I2C) && defined(CONFIG_SENSORS_TC74)
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* TC74 Factory I2C Addresses (7-bit)
* The address is factory programmed according to the part suffix (A0-A7).
*/
#define TC74_ADDR_A0 0x48
#define TC74_ADDR_A1 0x49
#define TC74_ADDR_A2 0x4a
#define TC74_ADDR_A3 0x4b
#define TC74_ADDR_A4 0x4c
#define TC74_ADDR_A5 0x4d
#define TC74_ADDR_A6 0x4e
#define TC74_ADDR_A7 0x4f
/****************************************************************************
* Public Types
****************************************************************************/
struct i2c_master_s;
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/****************************************************************************
* Name: tc74_register
*
* Description:
* Register the TC74 sensor as a uORB temperature sensor, appearing as
* /dev/uorb/sensor_temp<devno>.
*
* Input Parameters:
* devno - The topic number, giving /dev/uorb/sensor_temp<devno>
* i2c - An instance of the I2C interface to communicate with the TC74
* addr - The 7-bit I2C address of the TC74 (e.g., TC74_ADDR_A5)
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int tc74_register(int devno, FAR struct i2c_master_s *i2c, uint8_t addr);
#undef EXTERN
#ifdef __cplusplus
}
#endif
#endif /* CONFIG_I2C && CONFIG_SENSORS_TC74 */
#endif /* __INCLUDE_NUTTX_SENSORS_TC74_H */