![]() |
RTXI 1.3
|
00001 /* 00002 comedi/drivers/adq12b.c 00003 driver for MicroAxial ADQ12-B data acquisition and control card 00004 00005 COMEDI - Linux Control and Measurement Device Interface 00006 Copyright (C) 2000 David A. Schleef <ds@schleef.org> 00007 00008 This program is free software; you can redistribute it and/or modify 00009 it under the terms of the GNU General Public License as published by 00010 the Free Software Foundation; either version 2 of the License, or 00011 (at your option) any later version. 00012 00013 This program is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 GNU General Public License for more details. 00017 00018 You should have received a copy of the GNU General Public License 00019 along with this program; if not, write to the Free Software 00020 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00021 00022 */ 00023 /* 00024 Driver: adq12b 00025 Description: driver for MicroAxial ADQ12-B data acquisition and control card 00026 Devices: [MicroAxial] ADQ12-B (adq12b) 00027 Author: jeremy theler <thelerg@ib.cnea.gov.ar> 00028 Updated: Thu, 21 Feb 2008 02:56:27 -0300 00029 Status: works 00030 00031 Driver for the acquisition card ADQ12-B (without any add-on). 00032 00033 - Analog input is subdevice 0 (16 channels single-ended or 8 differential) 00034 - Digital input is subdevice 1 (5 channels) 00035 - Digital output is subdevice 1 (8 channels) 00036 - The PACER is not supported in this version 00037 00038 If you do not specify any options, they will default to 00039 00040 # comedi_config /dev/comedi0 adq12b 0x300,0,0 00041 00042 option 1: I/O base address. The following table is provided as a help 00043 of the hardware jumpers. 00044 00045 address jumper JADR 00046 0x300 1 (factory default) 00047 0x320 2 00048 0x340 3 00049 0x360 4 00050 0x380 5 00051 0x3A0 6 00052 00053 option 2: unipolar/bipolar ADC selection: 0 -> bipolar, 1 -> unipolar 00054 00055 selection comedi_config option JUB 00056 bipolar 0 2-3 (factory default) 00057 unipolar 1 1-2 00058 00059 option 3: single-ended/differential AI selection: 0 -> SE, 1 -> differential 00060 00061 selection comedi_config option JCHA JCHB 00062 single-ended 0 1-2 1-2 (factory default) 00063 differential 1 2-3 2-3 00064 00065 00066 written by jeremy theler <thelerg@ib.cnea.gov.ar> 00067 00068 instituto balseiro 00069 comision nacional de energia atomica 00070 universidad nacional de cuyo 00071 argentina 00072 00073 21-feb-2008 00074 + changed supported devices string (missused the [] and ()) 00075 00076 13-oct-2007 00077 + first try 00078 00079 00080 */ 00081 00082 #include <linux/comedidev.h> 00083 00084 // address scheme (page 2.17 of the manual) 00085 #define ADQ12B_SIZE 16 00086 00087 #define ADQ12B_CTREG 0x00 00088 #define ADQ12B_STINR 0x00 00089 #define ADQ12B_OUTBR 0x04 00090 #define ADQ12B_ADLOW 0x08 00091 #define ADQ12B_ADHIG 0x09 00092 #define ADQ12B_CONT0 0x0c 00093 #define ADQ12B_CONT1 0x0d 00094 #define ADQ12B_CONT2 0x0e 00095 #define ADQ12B_COWORD 0x0f 00096 00097 // mask of the bit at STINR to check end of conversion 00098 #define ADQ12B_EOC 0x20 00099 00100 #define TIMEOUT 20 00101 00102 // available ranges through the PGA gains 00103 static const comedi_lrange range_adq12b_ai_bipolar = { 4, { 00104 BIP_RANGE( 5 ), 00105 BIP_RANGE( 2 ), 00106 BIP_RANGE( 1 ), 00107 BIP_RANGE( 0.5 ) 00108 }}; 00109 00110 static const comedi_lrange range_adq12b_ai_unipolar = { 4, { 00111 UNI_RANGE( 5 ), 00112 UNI_RANGE( 2 ), 00113 UNI_RANGE( 1 ), 00114 UNI_RANGE( 0.5 ) 00115 }}; 00116 00117 00118 00119 typedef struct adq12b_board_struct{ 00120 const char *name; 00121 int ai_se_chans; 00122 int ai_diff_chans; 00123 int ai_bits; 00124 int di_chans; 00125 int do_chans; 00126 }adq12b_board; 00127 00128 static const adq12b_board adq12b_boards[] = { 00129 { 00130 name: "adq12b", 00131 ai_se_chans: 16, 00132 ai_diff_chans: 8, 00133 ai_bits: 12, 00134 di_chans: 5, 00135 do_chans: 8 00136 } 00137 // potentially, more adq-based deviced will be added 00138 /*, 00139 name: "adq12b", 00140 ai_chans: 16, // this is just for reference, hardcoded again later 00141 ai_bits: 12, 00142 di_chans: 8, 00143 do_chans: 5 00144 }*/ 00145 }; 00146 00147 #define thisboard ((const adq12b_board *)dev->board_ptr) 00148 00149 typedef struct{ 00150 int unipolar; /* option 2 of comedi_config (1 is iobase) */ 00151 int differential; /* option 3 of comedi_config */ 00152 int last_channel; 00153 int last_range; 00154 lsampl_t digital_state; 00155 }adq12b_private; 00156 00157 #define devpriv ((adq12b_private *)dev->private) 00158 00159 /* 00160 * The comedi_driver structure tells the Comedi core module 00161 * which functions to call to configure/deconfigure (attach/detach) 00162 * the board, and also about the kernel module that contains 00163 * the device code. 00164 */ 00165 static int adq12b_attach(comedi_device *dev,comedi_devconfig *it); 00166 static int adq12b_detach(comedi_device *dev); 00167 static comedi_driver driver_adq12b={ 00168 driver_name: "adq12b", 00169 module: THIS_MODULE, 00170 attach: adq12b_attach, 00171 detach: adq12b_detach, 00172 board_name: &adq12b_boards[0].name, 00173 offset: sizeof(adq12b_board), 00174 num_names: sizeof(adq12b_boards) / sizeof(adq12b_board), 00175 }; 00176 00177 static int adq12b_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data); 00178 static int adq12b_di_insn_bits(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data); 00179 static int adq12b_do_insn_bits(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data); 00180 00181 /* 00182 * Attach is called by the Comedi core to configure the driver 00183 * for a particular board. If you specified a board_name array 00184 * in the driver structure, dev->board_ptr contains that 00185 * address. 00186 */ 00187 static int adq12b_attach(comedi_device *dev,comedi_devconfig *it) 00188 { 00189 comedi_subdevice *s; 00190 unsigned long iobase; 00191 int unipolar, differential; 00192 00193 iobase = it->options[0]; 00194 unipolar = it->options[1]; 00195 differential = it->options[2]; 00196 00197 printk("comedi%d: adq12b called with options base=0x%03lx, %s and %s\n",dev->minor, iobase, (unipolar==1)?"unipolar":"bipolar", (differential==1)?"differential":"single-ended"); 00198 00199 /* if no address was specified, try the default 0x300 */ 00200 if (iobase == 0) { 00201 printk("comedi%d: adq12b warning: I/O base address not specified. Trying the default 0x300.\n", dev->minor); 00202 iobase = 0x300; 00203 } 00204 00205 printk("comedi%d: adq12b: 0x%04lx ", dev->minor, iobase); 00206 if (!request_region(iobase, ADQ12B_SIZE, "adq12b")) { 00207 printk("I/O port conflict\n"); 00208 return -EIO; 00209 } 00210 dev->iobase = iobase; 00211 00212 /* 00213 * Initialize dev->board_name. Note that we can use the "thisboard" 00214 * macro now, since we just initialized it in the last line. 00215 */ 00216 dev->board_name = thisboard->name; 00217 00218 /* 00219 * Allocate the private structure area. alloc_private() is a 00220 * convenient macro defined in comedidev.h. 00221 */ 00222 if(alloc_private(dev, sizeof(adq12b_private)) < 0) 00223 return -ENOMEM; 00224 00225 /* fill in devpriv structure */ 00226 devpriv->unipolar = unipolar; 00227 devpriv->differential = differential; 00228 devpriv->digital_state = 0; 00229 /* initialize channel and range to -1 so we make sure we always write 00230 at least once to the CTREG in the instruction */ 00231 devpriv->last_channel = -1; 00232 devpriv->last_range = -1; 00233 00234 00235 /* 00236 * Allocate the subdevice structures. alloc_subdevice() is a 00237 * convenient macro defined in comedidev.h. 00238 */ 00239 if(alloc_subdevices(dev, 3)<0) 00240 return -ENOMEM; 00241 00242 s = dev->subdevices+0; 00243 /* analog input subdevice */ 00244 s->type = COMEDI_SUBD_AI; 00245 if (differential) { 00246 s->subdev_flags = SDF_READABLE|SDF_GROUND|SDF_DIFF; 00247 s->n_chan = thisboard->ai_diff_chans; 00248 } else { 00249 s->subdev_flags = SDF_READABLE|SDF_GROUND; 00250 s->n_chan = thisboard->ai_se_chans; 00251 } 00252 00253 if (unipolar) { 00254 s->range_table = &range_adq12b_ai_unipolar; 00255 } else { 00256 s->range_table = &range_adq12b_ai_bipolar; 00257 } 00258 00259 s->maxdata = (1 << thisboard->ai_bits)-1; 00260 00261 00262 s->len_chanlist = 4; /* This is the maximum chanlist length that 00263 the board can handle */ 00264 s->insn_read = adq12b_ai_rinsn; 00265 00266 00267 s = dev->subdevices+1; 00268 /* digital input subdevice */ 00269 s->type = COMEDI_SUBD_DI; 00270 s->subdev_flags = SDF_READABLE; 00271 s->n_chan=thisboard->di_chans; 00272 s->maxdata = 1; 00273 s->range_table = &range_digital; 00274 s->insn_bits = adq12b_di_insn_bits; 00275 00276 s = dev->subdevices+2; 00277 /* digital output subdevice */ 00278 s->type = COMEDI_SUBD_DO; 00279 s->subdev_flags = SDF_WRITABLE; 00280 s->n_chan = thisboard->do_chans; 00281 s->maxdata = 1; 00282 s->range_table = &range_digital; 00283 s->insn_bits = adq12b_do_insn_bits; 00284 00285 00286 printk("attached\n"); 00287 00288 return 0; 00289 } 00290 00291 00292 /* 00293 * _detach is called to deconfigure a device. It should deallocate 00294 * resources. 00295 * This function is also called when _attach() fails, so it should be 00296 * careful not to release resources that were not necessarily 00297 * allocated by _attach(). dev->private and dev->subdevices are 00298 * deallocated automatically by the core. 00299 */ 00300 static int adq12b_detach(comedi_device *dev) 00301 { 00302 if (dev->iobase) 00303 release_region(dev->iobase, ADQ12B_SIZE); 00304 00305 kfree(devpriv); 00306 00307 printk("comedi%d: adq12b: removed\n",dev->minor); 00308 00309 return 0; 00310 } 00311 00312 /* 00313 * "instructions" read/write data in "one-shot" or "software-triggered" 00314 * mode. 00315 */ 00316 00317 static int adq12b_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data) 00318 { 00319 int n, i; 00320 int range, channel; 00321 unsigned char hi, lo, status; 00322 00323 /* change channel and range only if it is different from the previous */ 00324 range = CR_RANGE(insn->chanspec); 00325 channel = CR_CHAN(insn->chanspec); 00326 if (channel != devpriv->last_channel || range != devpriv->last_range) { 00327 outb((range << 4) | channel, dev->iobase + ADQ12B_CTREG); 00328 comedi_udelay(50); /* wait for the mux to settle */ 00329 } 00330 00331 /* trigger conversion */ 00332 status = inb(dev->iobase + ADQ12B_ADLOW); 00333 00334 /* convert n samples */ 00335 for(n=0; n < insn->n; n++){ 00336 00337 /* wait for end of convertion */ 00338 i = 0; 00339 do { 00340 // comedi_udelay(1); 00341 status = inb(dev->iobase + ADQ12B_STINR); 00342 status = status & ADQ12B_EOC; 00343 } while (status == 0 && ++i < TIMEOUT); 00344 // } while (++i < 10); 00345 00346 /* read data */ 00347 hi = inb(dev->iobase + ADQ12B_ADHIG); 00348 lo = inb(dev->iobase + ADQ12B_ADLOW); 00349 00350 //rt_printk("debug: chan=%d range=%d status=%d hi=%d lo=%d\n", channel, range, status, hi, lo); 00351 data[n] = (hi << 8) | lo; 00352 00353 } 00354 00355 /* return the number of samples read/written */ 00356 return n; 00357 } 00358 00359 00360 static int adq12b_di_insn_bits(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data) 00361 { 00362 00363 /* only bits 0-4 have information about digital inputs */ 00364 data[1] = (inb(dev->iobase+ADQ12B_STINR) & (0x1f)); 00365 00366 return 2; 00367 } 00368 00369 00370 static int adq12b_do_insn_bits(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data) 00371 { 00372 int channel; 00373 00374 for (channel = 0; channel < 8; channel++) 00375 if (((data[0]>>channel) & 0x01) != 0) 00376 outb((((data[1]>>channel)&0x01)<<3) | channel, dev->iobase + ADQ12B_OUTBR); 00377 00378 /* store information to retrieve when asked for reading */ 00379 if (data[0]) { 00380 devpriv->digital_state &= ~data[0]; 00381 devpriv->digital_state |= (data[0]&data[1]); 00382 } 00383 00384 data[1] = devpriv->digital_state; 00385 00386 return 2; 00387 } 00388 00389 00390 /* 00391 * A convenient macro that defines init_module() and cleanup_module(), 00392 * as necessary. 00393 */ 00394 COMEDI_INITCLEANUP(driver_adq12b);