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

commit 7a2343f1be23eb1bfc029c844d41cb738f53fa62
Author: Niccolò Maggioni <nicco.maggi...@gmail.com>
AuthorDate: Mon Aug 11 14:22:16 2025 +0200

    drivers/analog/ads7046: Add support for ADS7046 ADC
    
    Add support for the Texas Instruments ADS7046 12-bit, SPI-compatible ADC.
    
    Signed-off-by: Niccolò Maggioni <nicco.maggioni+nu...@gmail.com>
---
 drivers/analog/CMakeLists.txt  |   4 +
 drivers/analog/Kconfig         |  25 +++-
 drivers/analog/Make.defs       |   4 +
 drivers/analog/ads7046.c       | 282 +++++++++++++++++++++++++++++++++++++++++
 include/nuttx/analog/ads7046.h |  79 ++++++++++++
 include/nuttx/analog/ioctl.h   |   5 +
 6 files changed, 395 insertions(+), 4 deletions(-)

diff --git a/drivers/analog/CMakeLists.txt b/drivers/analog/CMakeLists.txt
index 61aab12b1db..47abb53ad82 100644
--- a/drivers/analog/CMakeLists.txt
+++ b/drivers/analog/CMakeLists.txt
@@ -109,6 +109,10 @@ if(CONFIG_ADC)
   if(CONFIG_ADC_ADS1115)
     list(APPEND SRCS ads1115.c)
   endif()
+
+  if(CONFIG_ADC_ADS7046)
+    list(APPEND SRCS ads7046.c)
+  endif()
 endif()
 
 if(CONFIG_LMP92001)
diff --git a/drivers/analog/Kconfig b/drivers/analog/Kconfig
index 80135f3d703..c4f588fdf10 100644
--- a/drivers/analog/Kconfig
+++ b/drivers/analog/Kconfig
@@ -257,6 +257,23 @@ config ADC_MCP3008_DIFFERENTIAL
 
 endif # ADC_MCP3008
 
+config ADC_ADS7046
+       bool "TI ADS7046 support"
+       default n
+       select SPI
+       ---help---
+               Enable driver support for the Texas Instruments ADS7046 12-bit 
ADC.
+
+if ADC_ADS7046
+
+config ADS7046_FREQUENCY
+       int "TI ADS7046 SPI frequency"
+       default 1000000
+       ---help---
+               ADS7046 SPI frequency. Maximum is 60 MHz.
+
+endif # ADC_ADS7046
+
 config ADC_ADS1115
        bool "ADS1115 support"
        default n
@@ -264,15 +281,15 @@ config ADC_ADS1115
        ---help---
                Enable driver support for the Texas Instruments ADS1115 16-bit 
ADC.
                Currently support is experimental, especially the comparator 
and ALRT/RDY
-               pin functions. 
+               pin functions.
 
-if ADC_ADS1115 
+if ADC_ADS1115
 
 config ADC_ADS1115_I2C_FREQUENCY
        int "ADS1115 I2C frequency"
        default 100000
        ---help---
-               ADS1115 I2C frequency. 
+               ADS1115 I2C frequency.
 
 config ADC_ADS1115_ADDR
        hex "ADS1115 I2C address"
@@ -320,7 +337,7 @@ config ADC_ADS1115_COMP_MODE
        bool "ADS1115 window comparator mode"
        default n
        ---help---
-               ADS1115 window comparator mode. 
+               ADS1115 window comparator mode.
 
 config ADC_ADS1115_COMP_POL
        bool "ADS1115 comparator polarity"
diff --git a/drivers/analog/Make.defs b/drivers/analog/Make.defs
index bdd5f013290..02a420cdbff 100644
--- a/drivers/analog/Make.defs
+++ b/drivers/analog/Make.defs
@@ -121,6 +121,10 @@ endif
 ifeq ($(CONFIG_ADC_ADS1115),y)
   CSRCS += ads1115.c 
 endif
+
+ifeq ($(CONFIG_ADC_ADS7046),y)
+  CSRCS += ads7046.c
+endif
 endif
 
 ifeq ($(CONFIG_LMP92001),y)
diff --git a/drivers/analog/ads7046.c b/drivers/analog/ads7046.c
new file mode 100644
index 00000000000..849014d73cc
--- /dev/null
+++ b/drivers/analog/ads7046.c
@@ -0,0 +1,282 @@
+/****************************************************************************
+ * drivers/analog/ads7046.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 <sys/types.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/analog/adc.h>
+#include <nuttx/analog/ioctl.h>
+#include <nuttx/analog/ads7046.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Device uses SPI Mode 1: CKPOL = 0, CKPHA = 1 */
+
+#define ADS7046_SPI_MODE  (SPIDEV_MODE1)
+#define ADS7046_SPI_BITS  16
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct ads7046_dev_s
+{
+  FAR struct spi_dev_s *spi;
+  int devno;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* SPI Helpers */
+
+static void ads7046_lock(FAR struct spi_dev_s *spi);
+
+static void ads7046_unlock(FAR struct spi_dev_s *spi);
+
+/* Character driver methods */
+
+static int ads7046_open(FAR struct file *filep);
+
+static int ads7046_close(FAR struct file *filep);
+
+static int ads7046_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations ads7046_fops =
+{
+  ads7046_open,     /* open */
+  ads7046_close,    /* close */
+  NULL,             /* read */
+  NULL,             /* write */
+  NULL,             /* seek */
+  ads7046_ioctl,    /* ioctl */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static void ads7046_lock(FAR struct spi_dev_s *spi)
+{
+  SPI_LOCK(spi, true);
+}
+
+static void ads7046_unlock(FAR struct spi_dev_s *spi)
+{
+  SPI_LOCK(spi, false);
+}
+
+static void ads7046_config_spi(FAR struct spi_dev_s *spi)
+{
+  SPI_SETMODE(spi, ADS7046_SPI_MODE);
+  SPI_SETBITS(spi, ADS7046_SPI_BITS);
+  SPI_HWFEATURES(spi, 0);
+  SPI_SETFREQUENCY(spi, CONFIG_ADS7046_FREQUENCY);
+}
+
+static int ads7046_open(FAR struct file *filep)
+{
+  FAR struct inode *inode = filep->f_inode;
+  FAR struct ads7046_dev_s *priv = inode->i_private;
+
+  ads7046_config_spi(priv->spi);
+  up_mdelay(100);
+
+  return OK;
+}
+
+static int ads7046_close(FAR struct file *filep)
+{
+  UNUSED(filep);
+  return OK;
+}
+
+/* As per the datasheet, the ADS7046 returns the following readings:
+ * INPUT VOLTAGE (AINP - AINM)               DESCRIPTION                 HEX
+ * --------------------------------------    ------------------------    ---
+ * <= 1 LSB                                  Negative full-scale code    000
+ * 1 LSB to 2 LSB                            -                           001
+ * V_REF / 2 to V_REF / 2 + 1 LSB            Mid code                    7FF
+ * V_REF / 2 + 1 LSB to V_REF / 2 + 2 LSB    -                           800
+ * >= V_REF - 1 LSB                          Positive full-scale code    FFF
+ */
+
+static void ads7046_read_conversion_result(
+  FAR const struct ads7046_dev_s *dev, FAR uint16_t *conversion_result,
+  bool fast_unsafe)
+{
+  *conversion_result = 0;
+
+  if (!fast_unsafe)
+    {
+      ads7046_lock(dev->spi);
+      ads7046_config_spi(dev->spi);
+    }
+
+  SPI_SELECT(dev->spi, SPIDEV_ADC(dev->devno), true);
+
+  SPI_RECVBLOCK(dev->spi, conversion_result, 1);
+
+  SPI_SELECT(dev->spi, SPIDEV_ADC(dev->devno), false);
+  if (!fast_unsafe)
+    {
+      ads7046_unlock(dev->spi);
+    }
+
+  *conversion_result = (*conversion_result & 0b0111111111111000) >> 3;
+}
+
+static void ads7046_offset_calibration(FAR const struct ads7046_dev_s *dev)
+{
+  ads7046_lock(dev->spi);
+  ads7046_config_spi(dev->spi);
+  SPI_SELECT(dev->spi, SPIDEV_ADC(dev->devno), true);
+
+  SPI_SNDBLOCK(dev->spi, 0, 4);
+
+  SPI_SELECT(dev->spi, SPIDEV_ADC(dev->devno), false);
+  ads7046_unlock(dev->spi);
+
+  ainfo("requested offset calibration\n");
+}
+
+static int ads7046_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+  FAR struct inode *inode = filep->f_inode;
+  FAR struct ads7046_dev_s *priv = inode->i_private;
+  int ret = OK;
+
+  switch (cmd)
+    {
+      /* Read the result of an analog conversion */
+
+      case ANIOC_ADS7046_READ:
+        {
+          FAR uint16_t *data = (FAR uint16_t *)((uintptr_t)arg);
+          ads7046_read_conversion_result(priv, data, false);
+          break;
+        }
+
+      /* Read the result of an analog conversion (skip SPI locking &
+       * reconfiguring for faster transfers - HIC SUNT DRACONES!)
+       */
+
+      case ANIOC_ADS7046_READ_FASTUNSAFE:
+        {
+          FAR uint16_t *data = (FAR uint16_t *)((uintptr_t)arg);
+          ads7046_read_conversion_result(priv, data, true);
+          break;
+        }
+
+      /* Start an offset calibration */
+
+      case ANIOC_ADS7046_OFFCAL:
+        {
+          ads7046_offset_calibration(priv);
+          break;
+        }
+
+      /* Command was not recognized */
+
+      default:
+        {
+          aerr("ERROR: Unrecognized cmd: %d\n", cmd);
+          ret = -ENOTTY;
+          break;
+        }
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ads7046_register
+ *
+ * Description:
+ *   Register the ADS7046 character device as 'devpath'
+ *
+ * Input Parameters:
+ *   devpath  - The full path to the driver to register. E.g., "/dev/adc0"
+ *   spi      - An instance of the SPI interface to use.
+ *   devno    - SPI device number
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int ads7046_register(FAR const char *devpath, FAR struct spi_dev_s *spi,
+                     unsigned int devno)
+{
+  /* Sanity check */
+
+  DEBUGASSERT(spi != NULL);
+
+  /* Initialize the ADS7046 device structure */
+
+  struct ads7046_dev_s *priv = kmm_malloc(sizeof(struct ads7046_dev_s));
+  if (priv == NULL)
+    {
+      aerr("ERROR: Failed to allocate instance %s (%d)\n", strerror(errno),
+           errno);
+      return -ENOMEM;
+    }
+
+  priv->spi = spi;
+  priv->devno = devno;
+
+  /* Register the character driver */
+
+  int ret = register_driver(devpath, &ads7046_fops, 0666, priv);
+  if (ret < 0)
+    {
+      aerr("ERROR: Failed to register driver: %s (%d)\n", strerror(-ret),
+           ret);
+      kmm_free(priv);
+    }
+
+  return ret;
+}
diff --git a/include/nuttx/analog/ads7046.h b/include/nuttx/analog/ads7046.h
new file mode 100644
index 00000000000..8539d0f2e62
--- /dev/null
+++ b/include/nuttx/analog/ads7046.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+ * include/nuttx/analog/ads7046.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_ANALOG_ADS7046_H
+#define __INCLUDE_NUTTX_ANALOG_ADS7046_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/spi/spi.h>
+#include <stdint.h>
+
+#if defined(CONFIG_ADC_ADS7046)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* IOCTL Commands ***********************************************************/
+
+/* Cmd: ANIOC_ADS7046_READ                    Arg: uint16_t *value
+ * Cmd: ANIOC_ADS7046_READ_FASTUNSAFE         Arg: uint16_t *value
+ * Cmd: ANIOC_ADS7046_OFFCAL                  Arg: N/A
+ */
+
+#define ANIOC_ADS7046_READ                    _ANIOC(AN_ADS7046_FIRST + 0)
+#define ANIOC_ADS7046_READ_FASTUNSAFE         _ANIOC(AN_ADS7046_FIRST + 1)
+#define ANIOC_ADS7046_OFFCAL                  _ANIOC(AN_ADS7046_FIRST + 2)
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ads7046_register
+ *
+ * Description:
+ *   Register the ADS7046 character device as 'devpath'
+ *
+ * Input Parameters:
+ *   devpath  - The full path to the driver to register. E.g., "/dev/adc0"
+ *   spi      - An instance of the SPI interface to use.
+ *   devno    - SPI device number.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int ads7046_register(FAR const char *devpath, FAR struct spi_dev_s *spi,
+                     unsigned int devno);
+
+#endif /* CONFIG_ADC_ADS7046 */
+#endif /* __INCLUDE_NUTTX_ANALOG_ADS7046_H */
diff --git a/include/nuttx/analog/ioctl.h b/include/nuttx/analog/ioctl.h
index 24cd2f5cf32..25e759e26c0 100644
--- a/include/nuttx/analog/ioctl.h
+++ b/include/nuttx/analog/ioctl.h
@@ -126,6 +126,11 @@
 #define AN_MCP47X6_FIRST (AN_ADS1115_FIRST + AN_ADS1115_NCMDS)
 #define AN_MCP47X6_NCMDS 3
 
+/* See include/nuttx/analog/ads7046.h */
+
+#define AN_ADS7046_FIRST  (AN_MCP47X6_FIRST + AN_MCP47X6_NCMDS)
+#define AN_ADS7046_NCMDS  3
+
 /****************************************************************************
  * Public Function Prototypes
  ****************************************************************************/

Reply via email to