⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ni_at_a2150.c

📁 rtlinux-3.2-pre3.tar.bz2 rtlinux3.2-pre3的源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/*    ni_at_a2150.c driver for National Instruments AT-A2150 boards    Copyright (C) 2001, 2002 Frank Mori Hess <fmhess@users.sourceforge.net>    COMEDI - Linux Control and Measurement Device Interface    Copyright (C) 2000 David A. Schleef <ds@schleef.org>    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; either version 2 of the License, or    (at your option) any later version.    This program is distributed in the hope that it will be useful,    but WITHOUT ANY WARRANTY; without even the implied warranty of    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    GNU General Public License for more details.    You should have received a copy of the GNU General Public License    along with this program; if not, write to the Free Software    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*************************************************************************//*Driver: ni_at_a2150.oDescription: National Instruments AT-A2150Author: Frank Mori HessStatus: worksDevices: [National Instruments] AT-A2150C (at_a2150c), AT-2150S (at_a2150s)If you want to ac couple the board's inputs, use AREF_OTHER.Configuration options:  [0] - I/O port base address  [1] - IRQ (optional, required for timed conversions)  [2] - DMA (optional, required for timed conversions)*//*Yet another driver for obsolete hardware brought to you by Frank Hess.Testing and debugging help provided by Dave Andruczyk.This driver supports the boards:AT-A2150CAT-A2150SThe only difference is their master clock frequencies.Options:	[0] - base io address	[1] - irq	[2] - dma channelReferences (from ftp://ftp.natinst.com/support/manuals):	   320360.pdf  AT-A2150 User ManualTODO:analog level triggeringTRIG_WAKE_EOS*/#include <linux/comedidev.h>#include <linux/ioport.h>#include <asm/dma.h>#include "8253.h"#include "comedi_fc.h"#define A2150_SIZE           28#define A2150_DMA_BUFFER_SIZE	0xff00	// size in bytes of dma buffer//#define A2150_DEBUG	// enable debugging code#undef A2150_DEBUG	// disable debugging code/* Registers and bits */#define CONFIG_REG		0x0#define   CHANNEL_BITS(x)		((x) & 0x7)#define   CHANNEL_MASK		0x7#define   CLOCK_SELECT_BITS(x)		(((x) & 0x3) << 3)#define   CLOCK_DIVISOR_BITS(x)		(((x) & 0x3) << 5)#define   CLOCK_MASK		(0xf << 3)#define   ENABLE0_BIT		0x80	// enable (don't internally ground) channels 0 and 1#define   ENABLE1_BIT		0x100	// enable (don't internally ground) channels 2 and 3#define   AC0_BIT		0x200	// ac couple channels 0,1#define   AC1_BIT		0x400	// ac couple channels 2,3#define   APD_BIT		0x800	// analog power down#define   DPD_BIT		0x1000	// digital power down#define TRIGGER_REG		0x2	// trigger config register#define   POST_TRIGGER_BITS		0x2#define   DELAY_TRIGGER_BITS		0x3#define   HW_TRIG_EN		0x10	// enable hardware trigger#define FIFO_START_REG		0x6	// software start aquistion trigger#define FIFO_RESET_REG		0x8	// clears fifo + fifo flags#define FIFO_DATA_REG		0xa	// read data#define DMA_TC_CLEAR_REG		0xe	// clear dma terminal count interrupt#define STATUS_REG		0x12	// read only#define   FNE_BIT		0x1	// fifo not empty#define   OVFL_BIT		0x8	// fifo overflow#define   EDAQ_BIT		0x10	// end of aquisition interrupt#define   DCAL_BIT		0x20	// offset calibration in progress#define   INTR_BIT		0x40	// interrupt has occured#define   DMA_TC_BIT		0x80	// dma terminal count interrupt has occured#define   ID_BITS(x)	(((x) >> 8) & 0x3)#define IRQ_DMA_CNTRL_REG		0x12	// write only#define   DMA_CHAN_BITS(x)		((x) & 0x7)	// sets dma channel#define   DMA_EN_BIT		0x8	// enables dma#define   IRQ_LVL_BITS(x)		(((x) & 0xf) << 4)	// sets irq level#define   FIFO_INTR_EN_BIT		0x100	// enable fifo interrupts#define   FIFO_INTR_FHF_BIT		0x200	// interrupt fifo half full#define   DMA_INTR_EN_BIT 		0x800	// enable interrupt on dma terminal count#define   DMA_DEM_EN_BIT	0x1000	// enables demand mode dma#define I8253_BASE_REG		0x14#define I8253_MODE_REG		0x17#define   HW_COUNT_DISABLE		0x30	// disable hardware counting of conversionstypedef struct a2150_board_struct{	char *name;	int clock[4];	// master clock periods, in nanoseconds	int num_clocks;	// number of available master clock speeds	int ai_speed;	// maximum conversion rate in nanoseconds}a2150_board;//analog input rangestatic comedi_lrange range_a2150 = {	1,	{		RANGE( -2.828, 2.828 ),	}};// enum must match board indicesenum{a2150_c, a2150_s};static a2150_board a2150_boards[] ={	{		name:	"at-a2150c",		clock:	{31250,  22676, 20833, 19531},		num_clocks:	4,		ai_speed:	19531,	},	{		name:	"at-a2150s",		clock:	{62500, 50000, 41667, 0},		num_clocks:	3,		ai_speed:	41667,	},};/* * Useful for shorthand access to the particular board structure */#define thisboard ((a2150_board *)dev->board_ptr)typedef struct{	volatile unsigned int count;  /* number of data points left to be taken */	unsigned int dma;	// dma channel	s16 *dma_buffer;	// dma buffer	unsigned int dma_transfer_size;	// size in bytes of dma transfers	int irq_dma_bits;	// irq/dma register bits	int config_bits;	// config register bits}a2150_private;#define devpriv ((a2150_private *)dev->private)static int a2150_attach(comedi_device *dev,comedi_devconfig *it);static int a2150_detach(comedi_device *dev);static int a2150_cancel(comedi_device *dev, comedi_subdevice *s);static comedi_driver driver_a2150={	driver_name:	"ni_at_a2150",	module:		THIS_MODULE,	attach:		a2150_attach,	detach:		a2150_detach,};static void a2150_interrupt(int irq, void *d, struct pt_regs *regs);static int a2150_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd);static int a2150_ai_cmd(comedi_device *dev, comedi_subdevice *s);static int a2150_ai_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);static int a2150_get_timing(comedi_device *dev, unsigned int *period, int flags);static int a2150_probe(comedi_device *dev);static int a2150_set_chanlist(comedi_device *dev, unsigned int start_channel, unsigned int num_channels);/* * A convenient macro that defines init_module() and cleanup_module(), * as necessary. */COMEDI_INITCLEANUP(driver_a2150);#ifdef A2150_DEBUGstatic void ni_dump_regs(comedi_device *dev){	rt_printk("config bits 0x%x\n", devpriv->config_bits);	rt_printk("irq dma bits 0x%x\n", devpriv->irq_dma_bits);	rt_printk("status bits 0x%x\n", inw(dev->iobase + STATUS_REG));}#endif/* interrupt service routine */static void a2150_interrupt(int irq, void *d, struct pt_regs *regs){	int i;	int status;	unsigned long flags;	comedi_device *dev = d;	comedi_subdevice *s = dev->read_subdev;	comedi_async *async;	comedi_cmd *cmd;	unsigned int max_points, num_points, residue, leftover;	sampl_t dpnt;	static const int sample_size = sizeof(devpriv->dma_buffer[0]);	if(dev->attached == 0)	{		comedi_error(dev, "premature interrupt");		return;	}	// initialize async here to make sure s is not NULL	async = s->async;	async->events = 0;	cmd = &async->cmd;	status = inw(dev->iobase + STATUS_REG);	if((status & INTR_BIT ) == 0)	{		comedi_error(dev, "spurious interrupt");		return;	}	if(status & OVFL_BIT)	{		comedi_error(dev, "fifo overflow");		a2150_cancel(dev, s);		async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;	}	if((status & DMA_TC_BIT) == 0)	{		comedi_error(dev, "caught non-dma interrupt?  Aborting.");		a2150_cancel(dev, s);		async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;		comedi_event(dev, s, async->events);		return;	}	flags = claim_dma_lock();	disable_dma(devpriv->dma);	/* clear flip-flop to make sure 2-byte registers for	 * count and address get set correctly */	clear_dma_ff(devpriv->dma);	// figure out how many points to read	max_points = devpriv->dma_transfer_size  / sample_size;	/* residue is the number of points left to be done on the dma	 * transfer.  It should always be zero at this point unless	 * the stop_src is set to external triggering.	 */	residue = get_dma_residue(devpriv->dma) / sample_size;	num_points = max_points - residue;	if(devpriv->count < num_points &&		cmd->stop_src == TRIG_COUNT)		num_points = devpriv->count;	// figure out how many points will be stored next time	leftover = 0;	if(cmd->stop_src == TRIG_NONE)	{		leftover = devpriv->dma_transfer_size / sample_size;	}else if(devpriv->count > max_points)	{		leftover = devpriv->count - max_points;		if(leftover > max_points)			leftover = max_points;	}	/* there should only be a residue if collection was stopped by having	 * the stop_src set to an external trigger, in which case there	 * will be no more data	 */	if(residue)		leftover = 0;	for(i = 0; i < num_points; i++)	{		/* write data point to comedi buffer */		dpnt = devpriv->dma_buffer[i];		// convert from 2's complement to unsigned coding		dpnt ^= 0x8000;		cfc_write_to_buffer( s, dpnt );		if(cmd->stop_src == TRIG_COUNT)		{			if(--devpriv->count == 0)			{	/* end of acquisition */				a2150_cancel(dev, s);				async->events |= COMEDI_CB_EOA;				break;			}		}	}	// re-enable  dma	if(leftover)	{		set_dma_addr(devpriv->dma, virt_to_bus(devpriv->dma_buffer));		set_dma_count(devpriv->dma, leftover * sample_size);		enable_dma(devpriv->dma);	}	release_dma_lock(flags);	async->events |= COMEDI_CB_BLOCK;	comedi_event(dev, s, async->events);	/* clear interrupt */	outw(0x00, dev->iobase + DMA_TC_CLEAR_REG);	return;}// probes board type, returns offsetstatic int a2150_probe(comedi_device *dev){	int status = inw(dev->iobase + STATUS_REG);	return ID_BITS(status);}static int a2150_attach(comedi_device *dev, comedi_devconfig *it){	comedi_subdevice *s;	int iobase = it->options[0];	int irq = it->options[1];	int dma = it->options[2];	static const int timeout = 2000;	int i;	printk("comedi%d: %s: io 0x%x", dev->minor, driver_a2150.driver_name, iobase);	if(irq)	{		printk(", irq %i", irq);	}else	{		printk(", no irq");	}	if(dma)	{		printk(", dma %i", dma);	}else	{		printk(", no dma");	}	printk("\n");	/* allocate and initialize dev->private */	if(alloc_private(dev, sizeof(a2150_private)) < 0)		return -ENOMEM;	if(iobase == 0)	{		printk(" io base address required\n");		return -EINVAL;	}	/* check if io addresses are available */	if(check_region(iobase, A2150_SIZE) < 0)	{		printk(" I/O port conflict\n");		return -EIO;	}	request_region(iobase, A2150_SIZE, driver_a2150.driver_name);	dev->iobase = iobase;	/* grab our IRQ */	if(irq)	{		// check that irq is supported		if(irq < 3 || irq == 8 || irq == 13 || irq > 15)		{			printk(" invalid irq line\n");			return -EINVAL;		}		if(comedi_request_irq( irq, a2150_interrupt, 0, driver_a2150.driver_name, dev ))		{			printk( "unable to allocate irq %d\n", irq);			return -EINVAL;		}		devpriv->irq_dma_bits |= IRQ_LVL_BITS(irq);		dev->irq = irq;	}	// initialize dma	if(dma)	{		if(dma < 0 || dma == 4 || dma > 7)		{			printk(" invalid dma channel %i\n", dma);			return -EINVAL;		}		if(request_dma(dma, driver_a2150.driver_name))		{			printk(" failed to allocate dma channel %i\n", dma);			return -EINVAL;		}		devpriv->dma = dma;		devpriv->dma_buffer = kmalloc(A2150_DMA_BUFFER_SIZE, GFP_KERNEL | GFP_DMA);		if(devpriv->dma_buffer == NULL)			return -ENOMEM;		disable_dma(dma);		set_dma_mode(dma, DMA_MODE_READ);		devpriv->irq_dma_bits |= DMA_CHAN_BITS(dma);	}	dev->board_ptr = a2150_boards + a2150_probe(dev);	dev->board_name = thisboard->name;	if(alloc_subdevices(dev, 1) < 0)		return -ENOMEM;	/* analog input subdevice */	s = dev->subdevices + 0;	dev->read_subdev = s;	s->type = COMEDI_SUBD_AI;	s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_OTHER;	s->n_chan = 4;	s->len_chanlist = 4;	s->maxdata = 0xffff;	s->range_table = &range_a2150;	s->do_cmd = a2150_ai_cmd;	s->do_cmdtest = a2150_ai_cmdtest;	s->insn_read = a2150_ai_rinsn;	s->cancel = a2150_cancel;	/* need to do this for software counting of completed conversions, to	 * prevent hardware count from stopping aquisition */	outw(HW_COUNT_DISABLE, dev->iobase + I8253_MODE_REG);	// set card's irq and dma levels	outw(devpriv->irq_dma_bits, dev->iobase + IRQ_DMA_CNTRL_REG);	// reset and sync adc clock circuitry	outw_p(DPD_BIT | APD_BIT, dev->iobase + CONFIG_REG);	outw_p(DPD_BIT, dev->iobase + CONFIG_REG);	// initialize configuration register	devpriv->config_bits = 0;	outw(devpriv->config_bits, dev->iobase + CONFIG_REG);	// wait until offset calibration is done, then enable analog inputs	for(i = 0; i < timeout; i++)	{		if((DCAL_BIT & inw(dev->iobase + STATUS_REG)) == 0)			break;		comedi_udelay(1000);	}	if(i == timeout)	{		printk(" timed out waiting for offset calibration to complete\n");		return -ETIME;	}	devpriv->config_bits |= ENABLE0_BIT | ENABLE1_BIT;	outw(devpriv->config_bits, dev->iobase + CONFIG_REG);	return 0;};static int a2150_detach(comedi_device *dev){	printk("comedi%d: %s: remove\n", dev->minor, driver_a2150.driver_name);	/* only free stuff if it has been allocated by _attach */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -