📄 dt2801.c
字号:
/* * Device Driver for DataTranslation DT2801 * *//*Driver: dt2801.oDescription: Data Translation DT2801 series and DT01-EZAuthor: dsStatus: worksDevices: [Data Translation] DT2801 (dt2801), DT2801-A, DT2801/5716A, DT2805, DT2805/5716A, DT2808, DT2818, DT2809, DT01-EZThis driver can autoprobe the type of board.Configuration options: [0] - I/O port base address [1] - unused [2] - A/D reference 0=differential, 1=single-ended [3] - A/D range 0 = [-10,10] 1 = [0,10] [4] - D/A 0 range 0 = [-10,10] 1 = [-5,5] 2 = [-2.5,2.5] 3 = [0,10] 4 = [0,5] [5] - D/A 1 range (same choices)*/#include <linux/comedidev.h>#include <linux/delay.h>#include <linux/ioport.h>#define DT2801_TIMEOUT 1000/* Hardware Configuration *//* ====================== */#define DT2801_MAX_DMA_SIZE (64 * 1024)/* Ports */#define DT2801_IOSIZE 2/* define's & typedef's *//* ====================== *//* Commands */#define DT_C_RESET 0x0#define DT_C_CLEAR_ERR 0x1#define DT_C_READ_ERRREG 0x2#define DT_C_SET_CLOCK 0x3#define DT_C_TEST 0xb#define DT_C_STOP 0xf#define DT_C_SET_DIGIN 0x4#define DT_C_SET_DIGOUT 0x5#define DT_C_READ_DIG 0x6#define DT_C_WRITE_DIG 0x7#define DT_C_WRITE_DAIM 0x8#define DT_C_SET_DA 0x9#define DT_C_WRITE_DA 0xa#define DT_C_READ_ADIM 0xc#define DT_C_SET_AD 0xd#define DT_C_READ_AD 0xe/* Command modifiers (only used with read/write), EXTTRIG can be used with some other commands.*/#define DT_MOD_DMA (1<<4)#define DT_MOD_CONT (1<<5)#define DT_MOD_EXTCLK (1<<6)#define DT_MOD_EXTTRIG (1<<7)/* Bits in status register */#define DT_S_DATA_OUT_READY (1<<0)#define DT_S_DATA_IN_FULL (1<<1)#define DT_S_READY (1<<2)#define DT_S_COMMAND (1<<3)#define DT_S_COMPOSITE_ERROR (1<<7)/* registers */#define DT2801_DATA 0#define DT2801_STATUS 1#define DT2801_CMD 1static int dt2801_attach(comedi_device *dev,comedi_devconfig *it);static int dt2801_detach(comedi_device *dev);static comedi_driver driver_dt2801={ driver_name: "dt2801", module: THIS_MODULE, attach: dt2801_attach, detach: dt2801_detach,};COMEDI_INITCLEANUP(driver_dt2801);#if 0// ignore 'defined but not used' warningstatic comedi_lrange range_dt2801_ai_pgh_bipolar={ 4, { RANGE( -10, 10 ), RANGE( -5, 5 ), RANGE( -2.5, 2.5 ), RANGE( -1.25, 1.25 ),}};#endifstatic comedi_lrange range_dt2801_ai_pgl_bipolar={ 4, { RANGE( -10, 10 ), RANGE( -1, 1 ), RANGE( -0.1, 0.1 ), RANGE( -0.02, 0.02 ),}};#if 0// ignore 'defined but not used' warningstatic comedi_lrange range_dt2801_ai_pgh_unipolar={ 4, { RANGE( 0, 10 ), RANGE( 0, 5 ), RANGE( 0, 2.5 ), RANGE( 0, 1.25 ),}};#endifstatic comedi_lrange range_dt2801_ai_pgl_unipolar={ 4, { RANGE( 0, 10 ), RANGE( 0, 1 ), RANGE( 0, 0.1 ), RANGE( 0, 0.02 ),}};typedef struct{ char *name; int boardcode; int ad_diff; int ad_chan; int adbits; int adrangetype; int dabits;} boardtype_t;/* Typeid's for the different boards of the DT2801-series (taken from the test-software, that comes with the board) */static boardtype_t boardtypes[] ={ { name: "dt2801", boardcode: 0x09, ad_diff: 2, ad_chan: 16, adbits: 12, adrangetype: 0, dabits: 12 }, { name: "dt2801-a", boardcode: 0x52, ad_diff: 2, ad_chan: 16, adbits: 12, adrangetype: 0, dabits: 12 }, { name: "dt2801/5716a", boardcode: 0x82, ad_diff: 1, ad_chan: 16, adbits: 16, adrangetype: 1, dabits: 12 }, { name: "dt2805", boardcode: 0x12, ad_diff: 1, ad_chan: 16, adbits: 12, adrangetype: 0, dabits: 12 }, { name: "dt2805/5716a", boardcode: 0x92, ad_diff: 1, ad_chan: 16, adbits: 16, adrangetype: 1, dabits: 12 }, { name: "dt2808", boardcode: 0x20, ad_diff: 0, ad_chan: 16, adbits: 12, adrangetype: 2, dabits: 8 }, { name: "dt2818", boardcode: 0xa2, ad_diff: 0, ad_chan: 4, adbits: 12, adrangetype: 0, dabits: 12 }, { name: "dt2809", boardcode: 0xb0, ad_diff: 0, ad_chan: 8, adbits: 12, adrangetype: 1, dabits: 12 },};#define n_boardtypes ((sizeof(boardtypes))/(sizeof(boardtypes[0])))#define boardtype (*(boardtype_t *)dev->board_ptr)typedef struct{ comedi_lrange *dac_range_types[2]; lsampl_t ao_readback[2];}dt2801_private;#define devpriv ((dt2801_private *)dev->private)static int dt2801_ai_insn_read(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data);static int dt2801_ao_insn_read(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data);static int dt2801_ao_insn_write(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data);static int dt2801_dio_insn_bits(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data);static int dt2801_dio_insn_config(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data);/* These are the low-level routines: writecommand: write a command to the board writedata: write data byte readdata: read data byte *//* Only checks DataOutReady-flag, not the Ready-flag as it is done in the examples of the manual. I don't see why this should be necessary. */static int dt2801_readdata(comedi_device *dev, int* data){ int stat = 0; int timeout = DT2801_TIMEOUT; do{ stat = inb_p(dev->iobase+DT2801_STATUS); if (stat & (DT_S_COMPOSITE_ERROR | DT_S_READY)) { return stat; } if(stat & DT_S_DATA_OUT_READY){ *data = inb_p(dev->iobase+DT2801_DATA); return 0; } }while(--timeout>0); return -ETIME;}static int dt2801_readdata2(comedi_device *dev, int *data){ int lb, hb; int ret; ret=dt2801_readdata(dev, &lb); if(ret)return ret; ret=dt2801_readdata(dev, &hb); if(ret)return ret; *data = (hb<<8)+lb; return 0;}static int dt2801_writedata(comedi_device *dev, unsigned int data){ int stat = 0; int timeout = DT2801_TIMEOUT; do{ stat = inb_p(dev->iobase+DT2801_STATUS); if (stat & DT_S_COMPOSITE_ERROR) { return stat; } if (!(stat & DT_S_DATA_IN_FULL)) { outb_p(data & 0xff, dev->iobase+DT2801_DATA); return 0; }#if 0 if(stat & DT_S_READY){ printk("dt2801: ready flag set (bad!) in dt2801_writedata()\n"); return -EIO; }#endif }while(--timeout>0); return -ETIME;}static int dt2801_writedata2(comedi_device *dev, unsigned int data){ int ret; ret=dt2801_writedata(dev, data & 0xff); if(ret<0)return ret; ret=dt2801_writedata(dev, (data >> 8) ); if(ret<0)return ret; return 0;}static int dt2801_wait_for_ready(comedi_device *dev){ int timeout = DT2801_TIMEOUT; int stat; stat = inb_p(dev->iobase+DT2801_STATUS); if(stat & DT_S_READY){ return 0; } do{ stat = inb_p(dev->iobase+DT2801_STATUS); if (stat & DT_S_COMPOSITE_ERROR) { return stat; } if(stat & DT_S_READY){ return 0; } }while(--timeout>0); return -ETIME;}static int dt2801_writecmd(comedi_device * dev, int command){ int stat; dt2801_wait_for_ready(dev); stat = inb_p(dev->iobase+DT2801_STATUS); if (stat & DT_S_COMPOSITE_ERROR) { printk("dt2801: composite-error in dt2801_writecmd(), ignoring\n"); } if (!(stat & DT_S_READY)) { printk("dt2801: !ready in dt2801_writecmd(), ignoring\n"); } outb_p(command, dev->iobase+DT2801_CMD); return 0;}static int dt2801_reset(comedi_device *dev){ int board_code=0; unsigned int stat; int timeout; DPRINTK("dt2801: resetting board...\n"); DPRINTK("fingerprint: 0x%02x 0x%02x\n",inb_p(dev->iobase),inb_p(dev->iobase+1)); /* pull random data from data port */ inb_p(dev->iobase+DT2801_DATA); inb_p(dev->iobase+DT2801_DATA); inb_p(dev->iobase+DT2801_DATA); inb_p(dev->iobase+DT2801_DATA); DPRINTK("dt2801: stop\n"); //dt2801_writecmd(dev,DT_C_STOP); outb_p(DT_C_STOP, dev->iobase+DT2801_CMD); //dt2801_wait_for_ready(dev); comedi_udelay(100); timeout=10000; do{ stat = inb_p(dev->iobase+DT2801_STATUS); if(stat & DT_S_READY)break; }while(timeout--); if(!timeout){ printk("dt2801: timeout 1 status=0x%02x\n",stat); } //printk("dt2801: reading dummy\n"); //dt2801_readdata(dev,&board_code); DPRINTK("dt2801: reset\n"); outb_p(DT_C_RESET, dev->iobase+DT2801_CMD); //dt2801_writecmd(dev,DT_C_RESET); comedi_udelay(100); timeout=10000; do{ stat = inb_p(dev->iobase+DT2801_STATUS); if(stat & DT_S_READY)break; }while(timeout--); if(!timeout){ printk("dt2801: timeout 2 status=0x%02x\n",stat); } DPRINTK("dt2801: reading code\n"); dt2801_readdata(dev,&board_code); DPRINTK("dt2801: ok. code=0x%02x\n",board_code); return board_code;}static int probe_number_of_ai_chans(comedi_device *dev){ int n_chans; int stat; int data; for(n_chans=0;n_chans<16;n_chans++){ stat = dt2801_writecmd(dev, DT_C_READ_ADIM); dt2801_writedata(dev, 0); dt2801_writedata(dev, n_chans); stat = dt2801_readdata2(dev, &data); if(stat)break; } dt2801_reset(dev); dt2801_reset(dev); return n_chans;}static comedi_lrange *dac_range_table[]={ &range_bipolar10, &range_bipolar5, &range_bipolar2_5, &range_unipolar10, &range_unipolar5};static comedi_lrange *dac_range_lkup(int opt){ if(opt<0 || opt>5)return &range_unknown; return dac_range_table[opt];}static comedi_lrange *ai_range_lkup(int type,int opt){ switch(type){ case 0: return (opt)? &range_dt2801_ai_pgl_unipolar: &range_dt2801_ai_pgl_bipolar; case 1: return (opt)? &range_unipolar10: &range_bipolar10; case 2: return &range_unipolar5; } return &range_unknown;}/* options: [0] - i/o base [1] - unused [2] - a/d 0=differential, 1=single-ended [3] - a/d range 0=[-10,10], 1=[0,10] [4] - dac0 range 0=[-10,10], 1=[-5,5], 2=[-2.5,2.5] 3=[0,10], 4=[0,5] [5] - dac1 range 0=[-10,10], 1=[-5,5], 2=[-2.5,2.5] 3=[0,10], 4=[0,5]*/static int dt2801_attach(comedi_device *dev,comedi_devconfig *it){ comedi_subdevice *s; int iobase; int board_code,type; int ret=0; int n_ai_chans; iobase=it->options[0]; if(check_region(iobase,DT2801_IOSIZE)<0){ comedi_error(dev,"I/O port conflict"); return -EIO; } request_region(iobase, DT2801_IOSIZE, "dt2801"); dev->iobase=iobase; /* do some checking */ board_code=dt2801_reset(dev); /* heh. if it didn't work, try it again. */ if(!board_code)board_code=dt2801_reset(dev); for(type=0;type<n_boardtypes;type++){ if(boardtypes[type].boardcode==board_code) goto havetype; } printk("dt2801: unrecognized board code=0x%02x, contact author\n",board_code); type=0;havetype: dev->board_ptr = boardtypes+type; printk("dt2801: %s at port 0x%x",boardtype.name,iobase); n_ai_chans=probe_number_of_ai_chans(dev); printk(" (ai channels = %d)",n_ai_chans); if((ret=alloc_subdevices(dev, 4))<0) goto out; if((ret=alloc_private(dev,sizeof(dt2801_private)))<0) goto out; dev->board_name = boardtype.name; s=dev->subdevices+0; /* ai subdevice */ s->type=COMEDI_SUBD_AI; s->subdev_flags=SDF_READABLE|SDF_GROUND;#if 1 s->n_chan=n_ai_chans;#else if(it->options[2])s->n_chan=boardtype.ad_chan; else s->n_chan=boardtype.ad_chan/2;#endif s->maxdata=(1<<boardtype.adbits)-1; s->range_table=ai_range_lkup(boardtype.adrangetype,it->options[3]); s->insn_read=dt2801_ai_insn_read; s++; /* ao subdevice */ s->type=COMEDI_SUBD_AO; s->subdev_flags=SDF_WRITABLE; s->n_chan=2; s->maxdata=(1<<boardtype.dabits)-1; s->range_table_list=devpriv->dac_range_types; devpriv->dac_range_types[0]=dac_range_lkup(it->options[4]); devpriv->dac_range_types[1]=dac_range_lkup(it->options[5]); s->insn_read=dt2801_ao_insn_read; s->insn_write=dt2801_ao_insn_write; s++; /* 1st digital subdevice */ s->type=COMEDI_SUBD_DIO; s->subdev_flags=SDF_READABLE|SDF_WRITABLE; s->n_chan=8; s->maxdata=1; s->range_table=&range_digital; s->insn_bits=dt2801_dio_insn_bits; s->insn_config=dt2801_dio_insn_config; s++; /* 2nd digital subdevice */ s->type=COMEDI_SUBD_DIO; s->subdev_flags=SDF_READABLE|SDF_WRITABLE; s->n_chan=8; s->maxdata=1; s->range_table=&range_digital; s->insn_bits=dt2801_dio_insn_bits; s->insn_config=dt2801_dio_insn_config; ret = 0;out: printk("\n"); return ret;}static int dt2801_detach(comedi_device *dev){ if(dev->iobase) release_region(dev->iobase,DT2801_IOSIZE); return 0;}static int dt2801_error(comedi_device *dev,int stat){ if(stat<0){ if(stat==-ETIME){ printk("dt2801: timeout\n"); }else{ printk("dt2801: error %d\n",stat); } return stat; } printk("dt2801: error status 0x%02x, resetting...\n",stat); dt2801_reset(dev); dt2801_reset(dev); return -EIO;}static int dt2801_ai_insn_read(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data){ int d; int stat; int i; for(i=0;i<insn->n;i++){ stat = dt2801_writecmd(dev, DT_C_READ_ADIM); dt2801_writedata(dev, CR_RANGE(insn->chanspec)); dt2801_writedata(dev, CR_CHAN(insn->chanspec)); stat = dt2801_readdata2(dev, &d); if (stat != 0) return dt2801_error(dev,stat); data[i]=d; } return i;}static int dt2801_ao_insn_read(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data){ data[0]=devpriv->ao_readback[CR_CHAN(insn->chanspec)]; return 1;}static int dt2801_ao_insn_write(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data){ dt2801_writecmd(dev, DT_C_WRITE_DAIM); dt2801_writedata(dev, CR_CHAN(insn->chanspec)); dt2801_writedata2(dev, data[0]); devpriv->ao_readback[CR_CHAN(insn->chanspec)]=data[0]; return 1;}static int dt2801_dio_insn_bits(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data){ int which=0; if(s==dev->subdevices+4)which=1; if(insn->n!=2)return -EINVAL; if(data[0]){ s->state &= ~data[0]; s->state |= (data[0]&data[1]); dt2801_writecmd(dev, DT_C_WRITE_DIG); dt2801_writedata(dev, which); dt2801_writedata(dev, s->state); } dt2801_writecmd(dev, DT_C_READ_DIG); dt2801_writedata(dev, which); dt2801_readdata(dev, data+1); return 2;}static int dt2801_dio_insn_config(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data){ int which=0; if(s==dev->subdevices+4)which=1; /* configure */ if(data[0]){ s->io_bits=0xff; dt2801_writecmd(dev, DT_C_SET_DIGOUT); }else{ s->io_bits=0; dt2801_writecmd(dev, DT_C_SET_DIGIN); } dt2801_writedata(dev, which); return 1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -