📄 das800.c
字号:
/* das800.c driver for Keitley das800 series boards and compatibles Copyright (C) 2000 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: das800.oDescription: Keithley Metrabyte DAS800 (& compatibles)Author: Frank Mori Hess <fmhess@users.sourceforge.net>Devices: [Keithley Metrabyte] DAS-800 (das-800), DAS-801 (das-801), DAS-802 (das-802), [Measurement Computing] CIO-DAS800 (cio-das800), CIO-DAS801 (cio-das801), CIO-DAS802 (cio-das802), CIO-DAS802/16 (cio-das802/16)Status: works, cio-das802/16 untested - email me if you have tested itConfiguration options: [0] - I/O port base address [1] - IRQ (optional, required for timed or externally triggered conversions)Notes: IRQ can be omitted, although the cmd interface will not work without it. All entries in the channel/gain list must use the same gain and be consecutive channels counting upwards in channel number (these are hardware limitations.) I've never tested the gain setting stuff since I only have a DAS-800 board with fixed gain. The cio-das802/16 does not have a fifo-empty status bit! Therefore only fifo-half-full transfers are possible with this card.*//*cmd triggers supported: start_src: TRIG_NOW | TRIG_EXT scan_begin_src: TRIG_FOLLOW scan_end_src: TRIG_COUNT convert_src: TRIG_TIMER | TRIG_EXT stop_src: TRIG_NONE | TRIG_COUNT*/#include <linux/comedidev.h>#include <linux/ioport.h>#include <linux/delay.h>#include "8253.h"#include "comedi_fc.h"#define DAS800_SIZE 8#define TIMER_BASE 1000#define N_CHAN_AI 8 // number of analog input channels/* Registers for the das800 */#define DAS800_LSB 0#define FIFO_EMPTY 0x1#define FIFO_OVF 0x2#define DAS800_MSB 1#define DAS800_CONTROL1 2#define CONTROL1_INTE 0x8#define DAS800_CONV_CONTROL 2#define ITE 0x1#define CASC 0x2#define DTEN 0x4#define IEOC 0x8#define EACS 0x10#define CONV_HCEN 0x80#define DAS800_SCAN_LIMITS 2#define DAS800_STATUS 2#define IRQ 0x8#define BUSY 0x80#define DAS800_GAIN 3#define CIO_FFOV 0x8 // fifo overflow for cio-das802/16#define CIO_ENHF 0x90 // interrupt fifo half full for cio-das802/16#define CONTROL1 0x80#define CONV_CONTROL 0xa0#define SCAN_LIMITS 0xc0#define ID 0xe0#define DAS800_8254 4#define DAS800_STATUS2 7#define STATUS2_HCEN 0x80#define STATUS2_INTE 0X20#define DAS800_ID 7typedef struct das800_board_struct{ char *name; int ai_speed; comedi_lrange *ai_range; int resolution;}das800_board;//analog input rangesstatic comedi_lrange range_das800_ai = { 1, { RANGE( -5, 5 ), }};static comedi_lrange range_das801_ai = { 9, { RANGE(-5, 5), RANGE(-10, 10), RANGE(0, 10), RANGE(-0.5, 0.5), RANGE(0, 1), RANGE(-0.05, 0.05), RANGE(0, 0.1), RANGE(-0.01, 0.01), RANGE(0, 0.02), }};static comedi_lrange range_cio_das801_ai = { 9, { RANGE(-5, 5), RANGE(-10, 10), RANGE(0, 10), RANGE(-0.5, 0.5), RANGE(0, 1), RANGE(-0.05, 0.05), RANGE(0, 0.1), RANGE(-0.005, 0.005), RANGE(0, 0.01), }};static comedi_lrange range_das802_ai = { 9, { RANGE(-5, 5), RANGE(-10, 10), RANGE(0, 10), RANGE(-2.5, 2.5), RANGE(0, 5), RANGE(-1.25, 1.25), RANGE(0, 2.5), RANGE(-0.625, 0.625), RANGE(0, 1.25), }};static comedi_lrange range_das80216_ai = { 8, { RANGE(-10, 10), RANGE(0, 10), RANGE(-5, 5), RANGE(0, 5), RANGE(-2.5, 2.5), RANGE(0, 2.5), RANGE(-1.25, 1.25), RANGE(0, 1.25), }};enum{das800, ciodas800, das801, ciodas801, das802, ciodas802, ciodas80216};static das800_board das800_boards[] ={ { name: "das-800", ai_speed: 25000, ai_range: &range_das800_ai, resolution: 12, }, { name: "cio-das800", ai_speed: 20000, ai_range: &range_das800_ai, resolution: 12, }, { name: "das-801", ai_speed: 25000, ai_range: &range_das801_ai, resolution: 12, }, { name: "cio-das801", ai_speed: 20000, ai_range: &range_cio_das801_ai, resolution: 12, }, { name: "das-802", ai_speed: 25000, ai_range: &range_das802_ai, resolution: 12, }, { name: "cio-das802", ai_speed: 20000, ai_range: &range_das802_ai, resolution: 12, }, { name: "cio-das802/16", ai_speed: 10000, ai_range: &range_das80216_ai, resolution: 16, },};/* * Useful for shorthand access to the particular board structure */#define thisboard ((das800_board *)dev->board_ptr)typedef struct{ volatile unsigned int count; /* number of data points left to be taken */ volatile int forever; /* flag indicating whether we should take data forever */ unsigned int divisor1; /* value to load into board's counter 1 for timed conversions */ unsigned int divisor2; /* value to load into board's counter 2 for timed conversions */ volatile int do_bits; /* digital output bits */}das800_private;#define devpriv ((das800_private *)dev->private)static int das800_attach(comedi_device *dev,comedi_devconfig *it);static int das800_detach(comedi_device *dev);static int das800_cancel(comedi_device *dev, comedi_subdevice *s);static comedi_driver driver_das800={ driver_name: "das800", module: THIS_MODULE, attach: das800_attach, detach: das800_detach, num_names: sizeof(das800_boards) / sizeof(das800_board), board_name: (char **)das800_boards, offset: sizeof(das800_board),};static void das800_interrupt(int irq, void *d, struct pt_regs *regs);static void enable_das800(comedi_device *dev);static void disable_das800(comedi_device *dev);static int das800_ai_do_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd);static int das800_ai_do_cmd(comedi_device *dev, comedi_subdevice *s);static int das800_ai_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);static int das800_di_rbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);static int das800_do_wbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);static int das800_probe(comedi_device *dev);static int das800_set_frequency(comedi_device *dev);/* checks and probes das-800 series board type */static int das800_probe(comedi_device *dev){ int id_bits; unsigned long irq_flags; int board; // 'comedi spin lock irqsave' disables even rt interrupts, we use them to protect indirect addressing comedi_spin_lock_irqsave(&dev->spinlock, irq_flags); outb(ID, dev->iobase + DAS800_GAIN); /* select base address + 7 to be ID register */ id_bits = inb(dev->iobase + DAS800_ID) & 0x3; /* get id bits */ comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags); board = thisboard - das800_boards; switch(id_bits) { case 0x0: if(board == das800) { printk(" Board model: DAS-800\n"); return board; } if(board == ciodas800) { printk(" Board model: CIO-DAS800\n"); return board; } printk(" Board model (probed): DAS-800\n"); return das800; break; case 0x2: if(board == das801) { printk(" Board model: DAS-801\n"); return board; } if(board == ciodas801) { printk(" Board model: CIO-DAS801\n"); return board; } printk(" Board model (probed): DAS-801\n"); return das801; break; case 0x3: if(board == das802) { printk(" Board model: DAS-802\n"); return board; } if(board == ciodas802) { printk(" Board model: CIO-DAS802\n"); return board; } if(board == ciodas80216) { printk(" Board model: CIO-DAS802/16\n"); return board; } printk(" Board model (probed): DAS-802\n"); return das802; break; default : printk(" Board model: probe returned 0x%x (unknown)\n", id_bits); return board; break; } return -1;}/* * A convenient macro that defines init_module() and cleanup_module(), * as necessary. */COMEDI_INITCLEANUP(driver_das800);/* interrupt service routine */static void das800_interrupt(int irq, void *d, struct pt_regs *regs){ short i; /* loop index */ sampl_t dataPoint = 0; comedi_device *dev = d; comedi_subdevice *s = dev->read_subdev; /* analog input subdevice */ comedi_async *async; int status; unsigned long irq_flags; static const int max_loops = 128; // half-fifo size for cio-das802/16 // flags int fifo_empty = 0; int fifo_overflow = 0; status = inb(dev->iobase + DAS800_STATUS); /* if interrupt was not generated by board or driver not attached, quit */ if(!(status & IRQ) || !(dev->attached)) { return; } /* wait until here to initialize async, since we will get null dereference * if interrupt occurs before driver is fully attached! */ async = s->async; // if hardware conversions are not enabled, then quit comedi_spin_lock_irqsave(&dev->spinlock, irq_flags); outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select base address + 7 to be STATUS2 register */ status = inb(dev->iobase + DAS800_STATUS2) & STATUS2_HCEN; /* don't release spinlock yet since we want to make sure noone else disables hardware conversions */ if(status == 0) { comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags); return; } /* loop while card's fifo is not empty (and limit to half fifo for cio-das802/16) */ for(i = 0; i < max_loops; i++) { /* read 16 bits from dev->iobase and dev->iobase + 1 */ dataPoint = inb(dev->iobase + DAS800_LSB); dataPoint += inb(dev->iobase + DAS800_MSB) << 8; if(thisboard->resolution == 12) { fifo_empty = dataPoint & FIFO_EMPTY; fifo_overflow = dataPoint & FIFO_OVF; if(fifo_overflow) break; }else { fifo_empty = 0; // cio-das802/16 has no fifo empty status bit } if(fifo_empty) { break; } /* strip off extraneous bits for 12 bit cards*/ if(thisboard->resolution == 12) dataPoint = (dataPoint >> 4) & 0xfff; /* if there are more data points to collect */ if(devpriv->count > 0 || devpriv->forever == 1) { /* write data point to buffer */ cfc_write_to_buffer( s, dataPoint); if(devpriv->count > 0) devpriv->count--; } } async->events |= COMEDI_CB_BLOCK; /* check for fifo overflow */ if(thisboard->resolution == 12) { fifo_overflow = dataPoint & FIFO_OVF; // else cio-das802/16 }else { fifo_overflow = inb(dev->iobase + DAS800_GAIN) & CIO_FFOV; } if(fifo_overflow) { comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags); comedi_error(dev, "DAS800 FIFO overflow"); das800_cancel(dev, dev->subdevices + 0); async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; comedi_event(dev, s, async->events); async->events = 0; return; } if(devpriv->count > 0 || devpriv->forever == 1) { /* Re-enable card's interrupt. * We already have spinlock, so indirect addressing is safe */ outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */ outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1); /* otherwise, stop taking data */ } else { disable_das800(dev); /* diable hardware triggered conversions */ async->events |= COMEDI_CB_EOA; } comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags); comedi_event(dev, s, async->events); async->events = 0; return;}static int das800_attach(comedi_device *dev, comedi_devconfig *it){ comedi_subdevice *s; int iobase = it->options[0];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -