📄 ni_at_a2150.c
字号:
/* 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 + -