This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM3354: ADC采集时,出现很大的负数

Part Number: AM3354

你好,我是用335xd的核心板,你们的adc采集,AIN0-6都正常,AIN7在采集时,会偶尔出现很大的负值,而且每次都是固定值-550525552,请问可能是什么原因,要怎么排查

  • 请问AIN7输入信号是正常的吗?电压是多少?

    另外请贴出您使用的SDK版本以及ADC设备树部分的配置。

  • 电压范围是正常的0-1.8V ,我不清楚sdk,而且内核是linux3.2,不是设备树的,是板级文件,下面是ti_adc.c的驱动

    /*
     * TI ADC MFD driver
     *
     * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
     *
     * This program is free software; you can redistribute it and/or
     * modify it under the terms of the GNU General Public License as
     * published by the Free Software Foundation version 2.
     *
     * This program is distributed "as is" WITHOUT ANY WARRANTY of any
     * kind, whether express or implied; without even the implied warranty
     * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     */
    
    #include <linux/init.h>
    #include <linux/kernel.h>
    #include <linux/err.h>
    #include <linux/module.h>
    #include <linux/slab.h>
    #include <linux/interrupt.h>
    #include <linux/platform_device.h>
    #include <linux/io.h>
    #include <linux/sched.h>
    
    #include "../iio.h"
    #include "../sysfs.h"
    #include "../buffer_generic.h"
    #include "../ring_sw.h"
    
    #include <linux/mfd/ti_tscadc.h>
    #include <linux/platform_data/ti_adc.h>
    
    struct adc_device {
    	struct ti_tscadc_dev	*mfd_tscadc;
    	struct iio_dev		*idev;
    	struct work_struct	poll_work;
    	wait_queue_head_t	wq_data_avail;
    	int			channels;
    	int			irq;
    	bool			is_continuous_mode;
    	u16			*buffer;
    };
    
    static unsigned int adc_readl(struct adc_device *adc, unsigned int reg)
    {
    	return readl(adc->mfd_tscadc->tscadc_base + reg);
    }
    
    static void adc_writel(struct adc_device *adc, unsigned int reg,
    					unsigned int val)
    {
    	writel(val, adc->mfd_tscadc->tscadc_base + reg);
    }
    
    static void adc_step_config(struct adc_device *adc_dev, bool mode)
    {
    	unsigned int    stepconfig;
    	int i, channels = 0, steps;
    
    	/*
    	 * There are 16 configurable steps and 8 analog input
    	 * lines available which are shared between Touchscreen and ADC.
    	 *
    	 * Steps backwards i.e. from 16 towards 0 are used by ADC
    	 * depending on number of input lines needed.
    	 * Channel would represent which analog input
    	 * needs to be given to ADC to digitalize data.
    	 */
    
    	steps = TOTAL_STEPS - adc_dev->channels;
    	channels = TOTAL_CHANNELS - adc_dev->channels;
    
    	if (mode == 0)
    		stepconfig = TSCADC_STEPCONFIG_AVG_16 | TSCADC_STEPCONFIG_FIFO1;
    	else
    		stepconfig = TSCADC_STEPCONFIG_AVG_16 | TSCADC_STEPCONFIG_FIFO1
    			| TSCADC_STEPCONFIG_MODE_SWCNT;
    
    	for (i = (steps + 1); i <= TOTAL_STEPS; i++) {
    		adc_writel(adc_dev, TSCADC_REG_STEPCONFIG(i),
    				stepconfig | TSCADC_STEPCONFIG_INP(channels));
    		adc_writel(adc_dev, TSCADC_REG_STEPDELAY(i),
    				TSCADC_STEPCONFIG_OPENDLY);
    		channels++;
    	}
    }
    
    static ssize_t tiadc_show_mode(struct device *dev,
    		struct device_attribute *attr, char *buf)
    {
    	struct iio_dev *indio_dev = dev_get_drvdata(dev);
    	struct adc_device *adc_dev = iio_priv(indio_dev);
    	unsigned int tmp;
    
    	tmp = adc_readl(adc_dev, TSCADC_REG_STEPCONFIG(TOTAL_STEPS));
    	tmp &= TSCADC_STEPCONFIG_MODE(1);
    
    	if (tmp == 0x00)
    		return sprintf(buf, "oneshot\n");
    	else if (tmp == 0x01)
    		return sprintf(buf, "continuous\n");
    	else
    		return sprintf(buf, "Operation mode unknown\n");
    }
    
    static ssize_t tiadc_set_mode(struct device *dev,
    		struct device_attribute *attr, const char *buf, size_t count)
    {
    	struct iio_dev *indio_dev = dev_get_drvdata(dev);
    	struct adc_device *adc_dev = iio_priv(indio_dev);
    	unsigned int config;
    
    	config = adc_readl(adc_dev, TSCADC_REG_CTRL);
    	config &= ~(TSCADC_CNTRLREG_TSCSSENB);
    	adc_writel(adc_dev, TSCADC_REG_CTRL, config);
    
    	if (!strncmp(buf, "oneshot", 7))
    		adc_dev->is_continuous_mode = false;
    	else if (!strncmp(buf, "continuous", 10))
    		adc_dev->is_continuous_mode = true;
    	else {
    		dev_err(dev, "Operational mode unknown\n");
    		return -EINVAL;
    	}
    
    	adc_step_config(adc_dev, adc_dev->is_continuous_mode);
    
    	config = adc_readl(adc_dev, TSCADC_REG_CTRL);
    	adc_writel(adc_dev, TSCADC_REG_CTRL,
    			(config | TSCADC_CNTRLREG_TSCSSENB));
    	return count;
    }
    
    static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR, tiadc_show_mode,
    		tiadc_set_mode, 0);
    
    static struct attribute *tiadc_attributes[] = {
    	&iio_dev_attr_mode.dev_attr.attr,
    	NULL,
    };
    
    static const struct attribute_group tiadc_attribute_group = {
    	.attrs = tiadc_attributes,
    };
    
    static irqreturn_t tiadc_irq(int irq, void *private)
    {
    	struct iio_dev *idev = private;
    	struct adc_device *adc_dev = iio_priv(idev);
    	unsigned int status, config;
    
    	status = adc_readl(adc_dev, TSCADC_REG_IRQSTATUS);
    	if (status & TSCADC_IRQENB_FIFO1OVRRUN) {
    		config = adc_readl(adc_dev, TSCADC_REG_CTRL);
    		config &= ~(TSCADC_CNTRLREG_TSCSSENB);
    		adc_writel(adc_dev, TSCADC_REG_CTRL, config);
    
    		adc_writel(adc_dev, TSCADC_REG_IRQSTATUS,
    				TSCADC_IRQENB_FIFO1OVRRUN |
    				TSCADC_IRQENB_FIFO1UNDRFLW |
    				TSCADC_IRQENB_FIFO1THRES);
    
    		adc_writel(adc_dev, TSCADC_REG_CTRL,
    			(config | TSCADC_CNTRLREG_TSCSSENB));
    		return IRQ_HANDLED;
    	} else if (status & TSCADC_IRQENB_FIFO1THRES) {
    		adc_writel(adc_dev, TSCADC_REG_IRQCLR,
    				TSCADC_IRQENB_FIFO1THRES);
    
    		if (iio_buffer_enabled(idev)) {
    			if (!work_pending(&adc_dev->poll_work))
    				schedule_work(&adc_dev->poll_work);
    		} else {
    			wake_up_interruptible(&adc_dev->wq_data_avail);
    		}
    		return IRQ_HANDLED;
    	} else {
    		return IRQ_NONE;
    	}
    }
    
    static void tiadc_poll_handler(struct work_struct *work_s)
    {
    	struct adc_device *adc_dev =
    		container_of(work_s, struct adc_device, poll_work);
    	struct iio_dev *idev = iio_priv_to_dev(adc_dev);
    	struct iio_buffer *buffer = idev->buffer;
    	unsigned int fifo1count, readx1;
    	int i;
    	u32 *iBuf;
    
    	fifo1count = adc_readl(adc_dev, TSCADC_REG_FIFO1CNT);
    	if (fifo1count * sizeof(u32) <
    				buffer->access->get_bytes_per_datum(buffer)) {
    		dev_err(adc_dev->mfd_tscadc->dev, "%s: Short FIFO event\n",
    								__func__);
    		goto out;
    	}
    
    	iBuf = kmalloc(fifo1count * sizeof(u32), GFP_KERNEL);
    	if (iBuf == NULL)
    		goto out;
    
    	for (i = 0; i < fifo1count; i++) {
    		readx1 = adc_readl(adc_dev, TSCADC_REG_FIFO1);
    		readx1 &= TSCADC_FIFOREAD_DATA_MASK;
    		iBuf[i] = readx1;
    	}
    
    	buffer->access->store_to(buffer, (u8 *) iBuf, iio_get_time_ns());
    	kfree(iBuf);
    
    out:
    	adc_writel(adc_dev, TSCADC_REG_IRQSTATUS,
    				TSCADC_IRQENB_FIFO1THRES);
    	adc_writel(adc_dev, TSCADC_REG_IRQENABLE,
    				TSCADC_IRQENB_FIFO1THRES);
    }
    
    static int tiadc_buffer_preenable(struct iio_dev *idev)
    {
    	struct iio_buffer *buffer = idev->buffer;
    
    	buffer->access->set_bytes_per_datum(buffer, 16);
    	return 0;
    }
    
    static int tiadc_buffer_postenable(struct iio_dev *idev)
    {
    	struct adc_device *adc_dev = iio_priv(idev);
    	struct iio_buffer *buffer = idev->buffer;
    	unsigned int enb, config;
    	int stepnum;
    	u8 bit;
    
    	if (!adc_dev->is_continuous_mode) {
    		pr_info("Data cannot be read continuously in one shot mode\n");
    		return -EINVAL;
    	} else {
    
    		config = adc_readl(adc_dev, TSCADC_REG_CTRL);
    		adc_writel(adc_dev, TSCADC_REG_CTRL,
    					config & ~TSCADC_CNTRLREG_TSCSSENB);
    		adc_writel(adc_dev, TSCADC_REG_CTRL,
    					config | TSCADC_CNTRLREG_TSCSSENB);
    
    
    		adc_writel(adc_dev, TSCADC_REG_IRQSTATUS,
    				TSCADC_IRQENB_FIFO1THRES |
    				TSCADC_IRQENB_FIFO1OVRRUN |
    				TSCADC_IRQENB_FIFO1UNDRFLW);
    		adc_writel(adc_dev, TSCADC_REG_IRQENABLE,
    				TSCADC_IRQENB_FIFO1THRES |
    				TSCADC_IRQENB_FIFO1OVRRUN);
    
    		adc_writel(adc_dev, TSCADC_REG_SE, 0x00);
    		for_each_set_bit(bit, buffer->scan_mask,
    				adc_dev->channels) {
    			struct iio_chan_spec const *chan = idev->channels + bit;
    			/*
    			 * There are a total of 16 steps available
    			 * that are shared between ADC and touchscreen.
    			 * We start configuring from step 16 to 0 incase of
    			 * ADC. Hence the relation between input channel
    			 * and step for ADC would be as below.
    			 */
    			stepnum = chan->channel + 9;
    			enb = adc_readl(adc_dev, TSCADC_REG_SE);
    			enb |= (1 << stepnum);
    			adc_writel(adc_dev, TSCADC_REG_SE, enb);
    		}
    		return 0;
    	}
    }
    
    static int tiadc_buffer_postdisable(struct iio_dev *idev)
    {
    	struct adc_device *adc_dev = iio_priv(idev);
    
    	adc_writel(adc_dev, TSCADC_REG_IRQCLR, (TSCADC_IRQENB_FIFO1THRES |
    				TSCADC_IRQENB_FIFO1OVRRUN |
    				TSCADC_IRQENB_FIFO1UNDRFLW));
    	adc_writel(adc_dev, TSCADC_REG_SE, TSCADC_STPENB_STEPENB_TC);
    	return 0;
    }
    
    static const struct iio_buffer_setup_ops tiadc_swring_setup_ops = {
    	.preenable = &tiadc_buffer_preenable,
    	.postenable = &tiadc_buffer_postenable,
    	.postdisable = &tiadc_buffer_postdisable,
    };
    
    static int tiadc_config_sw_ring(struct iio_dev *idev)
    {
    	struct adc_device *adc_dev = iio_priv(idev);
    	int ret;
    
    	idev->buffer = iio_sw_rb_allocate(idev);
    	if (!idev->buffer)
    		ret = -ENOMEM;
    
    	idev->buffer->access = &ring_sw_access_funcs;
    	idev->buffer->setup_ops = &tiadc_swring_setup_ops;
    
    	INIT_WORK(&adc_dev->poll_work, &tiadc_poll_handler);
    
    	idev->modes |= INDIO_BUFFER_HARDWARE;
    	return 0;
    }
    
    static int tiadc_channel_init(struct iio_dev *idev, struct adc_device *adc_dev)
    {
    	struct iio_chan_spec *chan_array;
    	int i, channels;
    
    	idev->num_channels = adc_dev->channels;
    	chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec),
    					GFP_KERNEL);
    
    	if (chan_array == NULL)
    		return -ENOMEM;
    
    	channels = TOTAL_CHANNELS - adc_dev->channels;
    	for (i = 0; i < (idev->num_channels); i++) {
    		struct iio_chan_spec *chan = chan_array + i;
    		chan->type = IIO_VOLTAGE;
    		chan->indexed = 1;
    		chan->channel = channels;
    		chan->scan_index = i;
    		chan->scan_type.sign = 'u';
    		chan->scan_type.realbits = 12;
    		chan->scan_type.storagebits = 32;
    		chan->scan_type.shift = 0;
    		channels++;
    	}
    
    	idev->channels = chan_array;
    	return idev->num_channels;
    }
    
    static void tiadc_channel_remove(struct iio_dev *idev)
    {
    	kfree(idev->channels);
    }
    
    static int tiadc_read_raw(struct iio_dev *idev,
    		struct iio_chan_spec const *chan,
    		int *val, int *val2, long mask)
    {
    	struct adc_device *adc_dev = iio_priv(idev);
    	int i, map_val, tmp;
    	unsigned int fifo1count, readx1, stepid;
    	unsigned long timeout = jiffies + usecs_to_jiffies
    			(IDLE_TIMEOUT * adc_dev->channels);
    
    	if (adc_dev->is_continuous_mode) {
    		pr_info("One shot mode not enabled\n");
    		return -EINVAL;
    	} else {
    
    		if(adc_dev->channels == 4)
    			tmp  = (1 << (chan->channel + TOTAL_CHANNELS + 1)) | TSCADC_STPENB_STEPENB_TC;
    		else 
    			tmp  = (1 << (chan->channel + TOTAL_CHANNELS + 1));
    
    		adc_writel(adc_dev, TSCADC_REG_SE, tmp);
    
    		/* Wait for ADC sequencer to complete sampling */
    		while (adc_readl(adc_dev, TSCADC_REG_ADCFSM) &
    				TSCADC_SEQ_STATUS) {
    			if (time_after(jiffies, timeout))
    				return -EAGAIN;
    		}
    
    		map_val = chan->channel + TOTAL_CHANNELS;
    
    
    		*val = -1;
    		timeout = jiffies + usecs_to_jiffies
    			(IDLE_TIMEOUT * adc_dev->channels);
    
    		while(1){
    				fifo1count = adc_readl(adc_dev, TSCADC_REG_FIFO1CNT);
    				if(!fifo1count){
    						if (time_after(jiffies, timeout)){
    								printk("wait fifo timeout\n");
    								return -EAGAIN;
    						} else 
    								continue;
    				}
    				for (i = 0; i < fifo1count; i++) {
    						readx1 = adc_readl(adc_dev, TSCADC_REG_FIFO1);
    						stepid = readx1 & TSCADC_FIFOREAD_CHNLID_MASK;
    						stepid = stepid >> 0x10;
    
    						if (stepid == map_val) {
    								readx1 = readx1 & TSCADC_FIFOREAD_DATA_MASK;
    								*val = readx1;
    						}
    				}
    				if(*val != -1){
    					break;
    				}
    		}
    
    		return IIO_VAL_INT;
    	}
    }
    
    static const struct iio_info tiadc_info = {
    	.read_raw = &tiadc_read_raw,
    	.attrs = &tiadc_attribute_group,
    };
    
    static int __devinit tiadc_probe(struct platform_device *pdev)
    {
    	struct iio_dev		*idev;
    	struct adc_device	*adc_dev = NULL;
    	struct ti_tscadc_dev	*tscadc_dev = pdev->dev.platform_data;
    	struct mfd_tscadc_board	*pdata;
    	int			err;
    
    	pdata = (struct mfd_tscadc_board *)tscadc_dev->dev->platform_data;
    	if (!pdata || !pdata->adc_init)  {
    		dev_err(tscadc_dev->dev, "Could not find platform data\n");
    		return -EINVAL;
    	}
    
    	idev = iio_allocate_device(sizeof(struct adc_device));
    	if (idev == NULL) {
    		dev_err(&pdev->dev, "failed to allocate iio device.\n");
    		err = -ENOMEM;
    		goto err_ret;
    	}
    	adc_dev = iio_priv(idev);
    
    	tscadc_dev->adc = adc_dev;
    	adc_dev->mfd_tscadc = tscadc_dev;
    	adc_dev->idev = idev;
    	adc_dev->channels = pdata->adc_init->adc_channels;
    	adc_dev->irq = tscadc_dev->irq;
    
    	idev->dev.parent = &pdev->dev;
    	idev->name = dev_name(&pdev->dev);
    	idev->modes = INDIO_DIRECT_MODE;
    	idev->info = &tiadc_info;
    
    	/* by default driver comes up with oneshot mode */
    	adc_step_config(adc_dev, adc_dev->is_continuous_mode);
    
    	/* program FIFO threshold to value minus 1 */
    	adc_writel(adc_dev, TSCADC_REG_FIFO1THR, FIFO1_THRESHOLD);
    
    	err = tiadc_channel_init(idev, adc_dev);
    	if (err < 0)
    		goto err_free_device;
    
    	init_waitqueue_head(&adc_dev->wq_data_avail);
    
    	err = request_irq(adc_dev->irq, tiadc_irq, IRQF_SHARED,
    		idev->name, idev);
    	if (err)
    		goto err_cleanup_channels;
    
    	err = tiadc_config_sw_ring(idev);
    	if (err)
    		goto err_free_irq;
    
    	err = iio_buffer_register(idev,
    			idev->channels, idev->num_channels);
    	if (err < 0)
    		goto err_free_sw_rb;
    
    	err = iio_device_register(idev);
    	if (err)
    		goto err_unregister;
    
    	dev_info(&pdev->dev, "attached adc driver\n");
    	platform_set_drvdata(pdev, idev);
    
    	return 0;
    
    err_unregister:
    	iio_buffer_unregister(idev);
    err_free_sw_rb:
    	iio_sw_rb_free(idev->buffer);
    err_free_irq:
    	free_irq(adc_dev->irq, idev);
    err_cleanup_channels:
    	tiadc_channel_remove(idev);
    err_free_device:
    	iio_free_device(idev);
    err_ret:
    	return err;
    }
    
    static int __devexit tiadc_remove(struct platform_device *pdev)
    {
    	struct ti_tscadc_dev	*tscadc_dev = pdev->dev.platform_data;
    	struct adc_device	*adc_dev = tscadc_dev->adc;
    	struct iio_dev		*idev = adc_dev->idev;
    
    	free_irq(adc_dev->irq, idev);
    	iio_device_unregister(idev);
    	iio_buffer_unregister(idev);
    	iio_sw_rb_free(idev->buffer);
    	tiadc_channel_remove(idev);
    
    	tscadc_dev->adc = NULL;
    	iio_free_device(idev);
    
    	platform_set_drvdata(pdev, NULL);
    	return 0;
    }
    
    static int adc_suspend(struct platform_device *pdev, pm_message_t state)
    {
    	struct ti_tscadc_dev   *tscadc_dev = pdev->dev.platform_data;
    	struct adc_device	*adc_dev = tscadc_dev->adc;
    	unsigned int idle;
    
    	if (!device_may_wakeup(tscadc_dev->dev)) {
    		idle = adc_readl(adc_dev, TSCADC_REG_CTRL);
    		idle &= ~(TSCADC_CNTRLREG_TSCSSENB);
    		adc_writel(adc_dev, TSCADC_REG_CTRL, (idle |
    				TSCADC_CNTRLREG_POWERDOWN));
    	}
    	return 0;
    }
    
    static int adc_resume(struct platform_device *pdev)
    {
    	struct ti_tscadc_dev   *tscadc_dev = pdev->dev.platform_data;
    	struct adc_device	*adc_dev = tscadc_dev->adc;
    	unsigned int restore;
    
    	restore = adc_readl(adc_dev, TSCADC_REG_CTRL);
    	restore &= ~(TSCADC_CNTRLREG_TSCSSENB);
    	adc_writel(adc_dev, TSCADC_REG_CTRL, restore);
    
    	adc_writel(adc_dev, TSCADC_REG_FIFO1THR, FIFO1_THRESHOLD);
    	adc_step_config(adc_dev, adc_dev->is_continuous_mode);
    
    	/* Make sure ADC is powered up */
    	restore &= ~(TSCADC_CNTRLREG_POWERDOWN);
    	restore |= TSCADC_CNTRLREG_TSCSSENB;
    	adc_writel(adc_dev, TSCADC_REG_CTRL, restore);
    	return 0;
    }
    
    static struct platform_driver tiadc_driver = {
    	.driver = {
    		.name   = "tiadc",
    		.owner = THIS_MODULE,
    	},
    	.probe          = tiadc_probe,
    	.remove         = __devexit_p(tiadc_remove),
    	.suspend = adc_suspend,
    	.resume = adc_resume,
    };
    
    module_platform_driver(tiadc_driver);
    
    MODULE_DESCRIPTION("TI ADC controller driver");
    MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
    MODULE_LICENSE("GPL");
    

  • 我的内核是板级文件配置,没有使用设备树,配置如下

  • 好的,我会咨询相关工程师再给您回复。

  • Linux 内核 3.2 太旧,我们无法支持。该版本的ADC 驱动程序和 Linux 5.4 之间有很多变化(肯定的是在驱动程序的触摸屏部分有变化,不确定 tscadc 驱动程序的其他部分)。如果您可以在 Linux Processor SDK 6.3 或 Linux Processor SDK 7.3 上复现问题,我们将能够提供更好的支持。

    另外请问该现象是在一块板子上出现的还是多块板子?