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 ****************************************************************************/