📄 das16m1.c
字号:
/* comedi/drivers/das16m1.c CIO-DAS16/M1 driver Author: Frank Mori Hess, based on code from the das16 driver. Copyright (C) 2001 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: das16m1.oDescription: CIO-DAS16/M1Author: Frank Mori Hess <fmhess@users.sourceforge.net>Devices: [MeasurementComputing] CIO-DAS16/M1 (cio-das16/m1)Status: worksThis driver supports a single board - the CIO-DAS16/M1.As far as I know, there are no other boards that havethe same register layout. Even the CIO-DAS16/M1/16 issignificantly different.I was _barely_ able to reach the full 1 MHz capabilityof this board, using a hard real-time interrupt(set the TRIG_RT flag in your comedi_cmd and usertlinux or RTAI). The board can't do dma, so the bottleneck ispulling the data across the ISA bus. I timed the interrupthandler, and it took my computer ~470 microseconds to pull 512samples from the board. So at 1 Mhz sampling rate,expect your CPU to be spending almost all of itstime in the interrupt handler.This board has some unusual restrictions for its channel/gain list. If thelist has 2 or more channels in it, then two conditions must be satisfied:(1) - even/odd channels must appear at even/odd indices in the list(2) - the list must have an even number of entries.Options: [0] - base io address [1] - irq (optional, but you probably want it)irq can be omitted, although the cmd interface will not work without it.*/#include <linux/comedidev.h>#include <linux/ioport.h>#include "8255.h"#include "8253.h"#include "comedi_fc.h"#define DAS16M1_SIZE 16#define DAS16M1_SIZE2 8#define DAS16M1_XTAL 100 //10 MHz master clock#define FIFO_SIZE 1024 // 1024 sample fifo/* CIO-DAS16_M1.pdf "cio-das16/m1" 0 a/d bits 0-3, mux start 12 bit 1 a/d bits 4-11 unused 2 status control 3 di 4 bit do 4 bit 4 unused clear interrupt 5 interrupt, pacer 6 channel/gain queue address 7 channel/gain queue data 89ab 8254 cdef 8254 400 8255 404-407 8254*/#define DAS16M1_AI 0 // 16-bit wide register#define AI_CHAN(x) ((x) & 0xf)#define DAS16M1_CS 2#define EXT_TRIG_BIT 0x1#define OVRUN 0x20#define IRQDATA 0x80#define DAS16M1_DIO 3#define DAS16M1_CLEAR_INTR 4#define DAS16M1_INTR_CONTROL 5#define EXT_PACER 0x2#define INT_PACER 0x3#define PACER_MASK 0x3#define INTE 0x80#define DAS16M1_QUEUE_ADDR 6#define DAS16M1_QUEUE_DATA 7#define Q_CHAN(x) ((x) & 0x7)#define Q_RANGE(x) (((x) & 0xf) << 4)#define UNIPOLAR 0x40#define DAS16M1_8254_FIRST 0x8#define DAS16M1_8254_FIRST_CNTRL 0xb#define TOTAL_CLEAR 0x30#define DAS16M1_8254_SECOND 0xc#define DAS16M1_82C55 0x400#define DAS16M1_8254_THIRD 0x404static comedi_lrange range_das16m1 ={ 9, { BIP_RANGE( 5 ), BIP_RANGE( 2.5 ), BIP_RANGE( 1.25 ), BIP_RANGE( 0.625 ), UNI_RANGE(10), UNI_RANGE(5), UNI_RANGE(2.5), UNI_RANGE(1.25), BIP_RANGE(10), }};static int das16m1_do_wbits(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);static int das16m1_di_rbits(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);static int das16m1_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);static int das16m1_cmd_test(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd);static int das16m1_cmd_exec(comedi_device *dev,comedi_subdevice *s);static int das16m1_cancel(comedi_device *dev, comedi_subdevice *s);static int das16m1_poll(comedi_device *dev, comedi_subdevice *s);static void das16m1_interrupt(int irq, void *d, struct pt_regs *regs);static void das16m1_handler(comedi_device *dev, unsigned int status);static unsigned int das16m1_set_pacer(comedi_device *dev, unsigned int ns, int round_flag);static int das16m1_irq_bits(unsigned int irq);typedef struct das16m1_board_struct{ char *name; unsigned int ai_speed;}das16m1_board;static das16m1_board das16m1_boards[]={ { name: "cio-das16/m1", // CIO-DAS16_M1.pdf ai_speed: 1000, // 1MHz max speed },};#define das16m1_num_boards ((sizeof(das16m1_boards)) / (sizeof(das16m1_boards[0])))static int das16m1_attach(comedi_device *dev, comedi_devconfig *it);static int das16m1_detach(comedi_device *dev);static comedi_driver driver_das16m1={ driver_name: "das16m1", module: THIS_MODULE, attach: das16m1_attach, detach: das16m1_detach, board_name: das16m1_boards, num_names: das16m1_num_boards, offset: sizeof(das16m1_boards[0]),};struct das16m1_private_struct { unsigned int control_state; volatile unsigned int adc_count; // number of samples completed /* initial value in lower half of hardware conversion counter, * needed to keep track of whether new count has been loaded into * counter yet (loaded by first sample conversion) */ u16 initial_hw_count; sampl_t ai_buffer[ FIFO_SIZE ]; unsigned int do_bits; // saves status of digital output bits unsigned int divisor1; // divides master clock to obtain conversion speed unsigned int divisor2; // divides master clock to obtain conversion speed};#define devpriv ((struct das16m1_private_struct *)(dev->private))#define thisboard ((struct das16m1_board_struct *)(dev->board_ptr))COMEDI_INITCLEANUP(driver_das16m1);static inline sampl_t munge_sample( sampl_t data ){ return ( data >> 4 ) & 0xfff;}static int das16m1_cmd_test(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd){ unsigned int err=0, tmp, i; /* make sure triggers are valid */ tmp=cmd->start_src; cmd->start_src &= TRIG_NOW | TRIG_EXT; if(!cmd->start_src || tmp!=cmd->start_src) err++; tmp=cmd->scan_begin_src; cmd->scan_begin_src &= TRIG_FOLLOW; if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src) err++; tmp=cmd->convert_src; cmd->convert_src &= TRIG_TIMER | TRIG_EXT; if(!cmd->convert_src || tmp!=cmd->convert_src) err++; tmp=cmd->scan_end_src; cmd->scan_end_src &= TRIG_COUNT; if(!cmd->scan_end_src || tmp!=cmd->scan_end_src) err++; tmp=cmd->stop_src; cmd->stop_src &= TRIG_COUNT | TRIG_NONE; if(!cmd->stop_src || tmp!=cmd->stop_src) err++; if(err)return 1; /* step 2: make sure trigger sources are unique and mutually compatible */ if(cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE) err++; if(cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT) err++; if(cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT) err++; if(err)return 2; /* step 3: make sure arguments are trivially compatible */ if(cmd->start_arg != 0){ cmd->start_arg = 0; err++; } if(cmd->scan_begin_src == TRIG_FOLLOW){ /* internal trigger */ if(cmd->scan_begin_arg != 0){ cmd->scan_begin_arg = 0; err++; } } if(cmd->convert_src == TRIG_TIMER) { if(cmd->convert_arg < thisboard->ai_speed) { cmd->convert_arg = thisboard->ai_speed; err++; } } if(cmd->scan_end_arg != cmd->chanlist_len){ cmd->scan_end_arg = cmd->chanlist_len; err++; } if(cmd->stop_src==TRIG_COUNT){ /* any count is allowed */ }else{ /* TRIG_NONE */ if(cmd->stop_arg!=0){ cmd->stop_arg=0; err++; } } if(err) return 3; /* step 4: fix up arguments */ if(cmd->convert_src == TRIG_TIMER) { tmp = cmd->convert_arg; /* calculate counter values that give desired timing */ i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL, &(devpriv->divisor1), &(devpriv->divisor2), &(cmd->convert_arg), cmd->flags & TRIG_ROUND_MASK); if(tmp != cmd->convert_arg) err++; } if(err) return 4; // check chanlist against board's peculiarities if(cmd->chanlist && cmd->chanlist_len > 1) { for(i = 0; i < cmd->chanlist_len; i++) { // even/odd channels must go into even/odd queue addresses if((i % 2) != (CR_CHAN(cmd->chanlist[i]) % 2)) { comedi_error(dev, "bad chanlist:\n" " even/odd channels must go have even/odd chanlist indices"); err++; } } if((cmd->chanlist_len % 2) != 0) { comedi_error(dev, "chanlist must be of even length or length 1"); err++; } } if(err) return 5; return 0;}static int das16m1_cmd_exec(comedi_device *dev,comedi_subdevice *s){ comedi_async *async = s->async; comedi_cmd *cmd = &async->cmd; unsigned int byte, i; if(dev->irq == 0) { comedi_error(dev, "irq required to execute comedi_cmd"); return -1; } /* disable interrupts and internal pacer */ devpriv->control_state &= ~INTE & ~PACER_MASK; outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL); // set software count devpriv->adc_count = 0; /* Initialize lower half of hardware counter, used to determine how * many samples are in fifo. Value doesn't actually load into counter * until counter's next clock (the next a/d conversion) */ i8254_load(dev->iobase + DAS16M1_8254_FIRST, 1, 0, 2); /* remember current reading of counter so we know when counter has * actually been loaded */ devpriv->initial_hw_count = i8254_read(dev->iobase + DAS16M1_8254_FIRST, 1); /* setup channel/gain queue */ for(i = 0; i < cmd->chanlist_len; i++) { outb(i, dev->iobase + DAS16M1_QUEUE_ADDR); byte = Q_CHAN(CR_CHAN(cmd->chanlist[i])) | Q_RANGE(CR_RANGE(cmd->chanlist[i])); outb(byte, dev->iobase + DAS16M1_QUEUE_DATA); } /* set counter mode and counts */ cmd->convert_arg = das16m1_set_pacer(dev, cmd->convert_arg, cmd->flags & TRIG_ROUND_MASK); // set control & status register byte = 0; /* if we are using external start trigger (also board dislikes having * both start and conversion triggers external simultaneously) */ if(cmd->start_src == TRIG_EXT && cmd->convert_src != TRIG_EXT) { byte |= EXT_TRIG_BIT; } outb(byte, dev->iobase + DAS16M1_CS); /* clear interrupt bit */ outb(0, dev->iobase + DAS16M1_CLEAR_INTR); /* enable interrupts and internal pacer */ devpriv->control_state &= ~PACER_MASK; if(cmd->convert_src == TRIG_TIMER) { devpriv->control_state |= INT_PACER; }else { devpriv->control_state |= EXT_PACER; } devpriv->control_state |= INTE; outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL); return 0;}static int das16m1_cancel(comedi_device *dev, comedi_subdevice *s){ devpriv->control_state &= ~INTE & ~PACER_MASK; outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL); return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -