![]() |
RTXI 1.3
|
00001 /*************************************************************************** 00002 cb.c - calibration support for some Measurement computing boards. 00003 ------------------- 00004 00005 begin : Sat Apr 27 2002 00006 copyright : (C) 2002,2003 by Frank Mori Hess 00007 email : fmhess@users.sourceforge.net 00008 00009 ***************************************************************************/ 00010 00011 /*************************************************************************** 00012 * * 00013 * This program is free software; you can redistribute it and/or modify * 00014 * it under the terms of the GNU Lesser General Public License as * 00015 * published by * 00016 * the Free Software Foundation; either version 2.1 of the License, or * 00017 * (at your option) any later version. * 00018 * * 00019 ***************************************************************************/ 00020 00021 #define _GNU_SOURCE 00022 00023 #include <stdio.h> 00024 #include <fcntl.h> 00025 #include <unistd.h> 00026 #include <errno.h> 00027 #include <getopt.h> 00028 #include <ctype.h> 00029 #include <math.h> 00030 #include <stdlib.h> 00031 #include <string.h> 00032 #include <stdint.h> 00033 #include <assert.h> 00034 00035 #include "calib.h" 00036 00037 00038 char cb_id[] = "$Id: cb.c,v 1.1.1.1 2006-02-05 20:53:20 fmhess Exp $"; 00039 00040 struct board_struct{ 00041 char *name; 00042 int status; 00043 int (*setup)( calibration_setup_t *setup ); 00044 }; 00045 00046 static int setup_cb_pci_1xxx( calibration_setup_t *setup ); 00047 static int setup_cb_pci_1602_16( calibration_setup_t *setup ); 00048 00049 static int cal_cb_pci_1xxx( calibration_setup_t *setup ); 00050 static int cal_cb_pci_1602_16( calibration_setup_t *setup ); 00051 00052 static int init_observables_1xxx( calibration_setup_t *setup ); 00053 00054 static struct board_struct boards[]={ 00055 { "pci-das1000", STATUS_DONE, setup_cb_pci_1xxx }, 00056 { "pci-das1001", STATUS_GUESS, setup_cb_pci_1xxx }, 00057 { "pci-das1002", STATUS_GUESS, setup_cb_pci_1xxx }, 00058 { "pci-das1200", STATUS_DONE, setup_cb_pci_1xxx }, 00059 { "pci-das1200/jr", STATUS_GUESS, setup_cb_pci_1xxx }, 00060 { "pci-das1602/12", STATUS_GUESS, setup_cb_pci_1xxx }, 00061 { "pci-das1602/16", STATUS_DONE, setup_cb_pci_1602_16 }, 00062 { "pci-das1602/16/jr", STATUS_GUESS, setup_cb_pci_1602_16 }, 00063 }; 00064 00065 static const int num_boards = ( sizeof(boards) / sizeof(boards[0]) ); 00066 00067 enum calibration_source_1xxx 00068 { 00069 CS_1XXX_GROUND = 0, 00070 CS_1XXX_7V = 1, 00071 CS_1002_3500mV = 2, 00072 CS_1002_1750mV = 3, 00073 CS_1001_88600uV = 3, 00074 CS_1XXX_875mV = 4, 00075 CS_1XXX_8600uV = 5, 00076 CS_1602_16_minus_10V = 5, 00077 CS_1XXX_DAC0 = 6, 00078 CS_1XXX_DAC1 = 7, 00079 }; 00080 static inline int CS_1XXX_DAC( unsigned int channel ) 00081 { 00082 if( channel ) 00083 return CS_1XXX_DAC1; 00084 else 00085 return CS_1XXX_DAC0; 00086 } 00087 00088 int cb_setup( calibration_setup_t *setup, const char *device_name ) 00089 { 00090 unsigned int i; 00091 00092 for( i = 0; i < num_boards; i++ ) 00093 { 00094 if( !strcmp( device_name, boards[i].name ) ) 00095 { 00096 setup->status = boards[i].status; 00097 return boards[i].setup( setup ); 00098 break; 00099 } 00100 } 00101 if( i == num_boards ) return -1; 00102 00103 return 0; 00104 } 00105 00106 static int setup_cb_pci_1xxx( calibration_setup_t *setup ) 00107 { 00108 int retval; 00109 static const int caldac_subdev = 4; 00110 static const int calpot_subdev = 5; 00111 00112 if( comedi_get_version_code( setup->dev ) <= COMEDI_VERSION_CODE( 0, 7, 66 ) ) 00113 { 00114 DPRINT(0, "WARNING: you need comedi driver version 0.7.67 or later\n" 00115 "for this calibration to work properly\n" ); 00116 } 00117 //this larger delay is definitely needed by pci-das1000, possibly not for pci-das1200 00118 setup->sv_settling_time_ns = 10000000; 00119 retval = init_observables_1xxx( setup ); 00120 if( retval < 0 ) return retval; 00121 setup_caldacs( setup, caldac_subdev ); 00122 setup_caldacs( setup, calpot_subdev ); 00123 setup->do_cal = cal_cb_pci_1xxx; 00124 return 0; 00125 } 00126 00127 static int setup_cb_pci_1602_16( calibration_setup_t *setup ) 00128 { 00129 int retval; 00130 static const int caldac_subdev = 4; 00131 static const int calpot_subdev = 5; 00132 static const int dac08_subdev = 6; 00133 00134 if( comedi_get_version_code( setup->dev ) <= COMEDI_VERSION_CODE( 0, 7, 66 ) ) 00135 { 00136 DPRINT(0, "WARNING: you need comedi driver version 0.7.67 or later\n" 00137 "for this calibration to work properly\n" ); 00138 } 00139 setup->sv_settling_time_ns = 10000000; 00140 setup->sv_order = 12; 00141 retval = init_observables_1xxx( setup ); 00142 00143 if( retval < 0 ) return retval; 00144 setup_caldacs( setup, caldac_subdev ); 00145 setup_caldacs( setup, calpot_subdev ); 00146 setup_caldacs( setup, dac08_subdev ); 00147 setup->do_cal = cal_cb_pci_1602_16; 00148 return 0; 00149 } 00150 00151 static int ai_ground_observable_1xxx( const calibration_setup_t *setup, 00152 unsigned int channel, unsigned int range ) 00153 { 00154 return 2 * range; 00155 } 00156 00157 static int ai_high_observable_1xxx( const calibration_setup_t *setup, 00158 unsigned int channel, unsigned int range ) 00159 { 00160 return ai_ground_observable_1xxx( setup, channel, range ) + 1; 00161 } 00162 00163 static int ao_ground_observable_1xxx( const calibration_setup_t *setup, 00164 unsigned int channel, unsigned int range ) 00165 { 00166 int num_ai_ranges, num_ao_ranges; 00167 00168 num_ai_ranges = comedi_get_n_ranges( setup->dev, setup->ad_subdev, 0 ); 00169 assert( num_ai_ranges > 0 ); 00170 num_ao_ranges = comedi_get_n_ranges( setup->dev, setup->da_subdev, 0 ); 00171 assert( num_ao_ranges > 0 ); 00172 00173 return 2 * num_ai_ranges + 2 * num_ao_ranges * channel + 2 * range; 00174 } 00175 00176 static int ao_high_observable_1xxx( const calibration_setup_t *setup, 00177 unsigned int channel, unsigned int range ) 00178 { 00179 return ao_ground_observable_1xxx( setup, channel, range ) + 1; 00180 } 00181 00182 static double ai_low_target_1xxx( calibration_setup_t *setup, 00183 unsigned int range ) 00184 { 00185 if( is_bipolar( setup->dev, setup->ad_subdev, 0, range ) ) 00186 return 0.0; 00187 else 00188 return very_low_target( setup->dev, setup->ad_subdev, 0, range ); 00189 } 00190 00191 static int source_eeprom_addr_1xxx( calibration_setup_t *setup, unsigned int range_index ) 00192 { 00193 enum source_eeprom_addr 00194 { 00195 EEPROM_7V_CHAN = 0x80, 00196 EEPROM_3500mV_CHAN = 0x84, 00197 EEPROM_1750mV_CHAN = 0x88, 00198 EEPROM_88600uV_CHAN_1001 = 0x88, 00199 EEPROM_875mV_CHAN = 0x8c, 00200 EEPROM_8600uV_CHAN = 0x90, 00201 }; 00202 comedi_range *range; 00203 00204 range = comedi_get_range( setup->dev, setup->ad_subdev, 0, range_index ); 00205 if( range == NULL ) return -1; 00206 00207 if( range->max > 7.0 ) 00208 return EEPROM_7V_CHAN; 00209 else if( range->max > 3.5 ) 00210 return EEPROM_3500mV_CHAN; 00211 else if( range->max > 1.750 ) 00212 return EEPROM_1750mV_CHAN; 00213 else if( range->max > 0.875 ) 00214 return EEPROM_875mV_CHAN; 00215 else if( range->max > .0886 ) 00216 return EEPROM_88600uV_CHAN_1001; 00217 else if( range->max > 0.0086 ) 00218 return EEPROM_8600uV_CHAN; 00219 00220 return -1; 00221 } 00222 00223 static int ai_high_cal_source_1xxx( calibration_setup_t *setup, unsigned int range_index ) 00224 { 00225 comedi_range *range; 00226 00227 range = comedi_get_range( setup->dev, setup->ad_subdev, 0, range_index ); 00228 if( range == NULL ) return -1; 00229 00230 if( range->max > 7.0 ) 00231 return CS_1XXX_7V; 00232 else if( range->max > 3.5 ) 00233 return CS_1002_3500mV; 00234 else if( range->max > 1.750 ) 00235 return CS_1002_1750mV; 00236 else if( range->max > 0.875 ) 00237 return CS_1XXX_875mV; 00238 else if( range->max > .0886 ) 00239 return CS_1001_88600uV; 00240 else if( range->max > 0.0086 ) 00241 return CS_1XXX_8600uV; 00242 00243 return -1; 00244 } 00245 00246 static int ao_set_high_target_1xxx( calibration_setup_t *setup, unsigned int obs, 00247 unsigned int range_index ) 00248 { 00249 double target; 00250 comedi_range *range; 00251 00252 range = comedi_get_range( setup->dev, setup->da_subdev, 0, range_index ); 00253 if( range == NULL ) return -1; 00254 00255 target = range->max * 0.9; 00256 set_target( setup, obs, target ); 00257 return 0; 00258 } 00259 00260 static int init_observables_1xxx( calibration_setup_t *setup ) 00261 { 00262 comedi_insn tmpl, po_tmpl; 00263 observable *o; 00264 int retval, range, num_ai_ranges, num_ao_ranges, 00265 channel, num_channels; 00266 float target; 00267 int ai_for_ao_range; 00268 00269 setup->n_observables = 0; 00270 00271 memset( &tmpl, 0, sizeof(tmpl) ); 00272 tmpl.insn = INSN_READ; 00273 tmpl.n = 1; 00274 tmpl.subdev = setup->ad_subdev; 00275 00276 num_ai_ranges = comedi_get_n_ranges( setup->dev, setup->ad_subdev, 0 ); 00277 if( num_ai_ranges < 0 ) return -1; 00278 00279 for( range = 0; range < num_ai_ranges; range++ ) 00280 { 00281 o = setup->observables + ai_ground_observable_1xxx( setup, 0, range ); 00282 o->reference_source = CS_1XXX_GROUND; 00283 assert( o->name == NULL ); 00284 asprintf( &o->name, "calibration source %i, range %i, ground referenced", 00285 o->reference_source, range ); 00286 o->observe_insn = tmpl; 00287 o->observe_insn.chanspec = CR_PACK( 0, range, AREF_GROUND) | 00288 CR_ALT_SOURCE | CR_ALT_FILTER; 00289 o->target = ai_low_target_1xxx( setup, range ); 00290 setup->n_observables++; 00291 00292 o = setup->observables + ai_high_observable_1xxx( setup, 0, range );; 00293 retval = ai_high_cal_source_1xxx( setup, range ); 00294 if( retval < 0 ) return -1; 00295 o->reference_source = retval; 00296 assert( o->name == NULL ); 00297 asprintf( &o->name, "calibration source %i, range %i, ground referenced", 00298 o->reference_source, range ); 00299 o->observe_insn = tmpl; 00300 o->observe_insn.chanspec = CR_PACK( 0, range, AREF_GROUND) | 00301 CR_ALT_SOURCE | CR_ALT_FILTER; 00302 retval = cb_actual_source_voltage( setup->dev, setup->eeprom_subdev, 00303 source_eeprom_addr_1xxx( setup, range ), &target ); 00304 if( retval < 0 ) return -1; 00305 o->target = target; 00306 setup->n_observables++; 00307 } 00308 00309 if( setup->da_subdev >= 0 ) 00310 { 00311 num_channels = comedi_get_n_channels( setup->dev, setup->da_subdev ); 00312 if( num_channels < 0 ) return -1; 00313 00314 num_ao_ranges = comedi_get_n_ranges( setup->dev, setup->da_subdev, 0 ); 00315 if( num_ao_ranges < 0 ) return -1; 00316 00317 memset( &po_tmpl, 0, sizeof(po_tmpl) ); 00318 po_tmpl.insn = INSN_WRITE; 00319 po_tmpl.n = 1; 00320 po_tmpl.subdev = setup->da_subdev; 00321 00322 ai_for_ao_range = get_bipolar_lowgain( setup->dev, setup->ad_subdev ); 00323 if( ai_for_ao_range < 0 ) return -1; 00324 00325 for( range = 0; range < num_ao_ranges; range++ ) 00326 { 00327 for( channel = 0; channel < num_channels; channel++ ) 00328 { 00329 o = setup->observables + ao_ground_observable_1xxx( setup, channel, range ); 00330 o->reference_source = CS_1XXX_DAC( channel ); 00331 assert( o->name == NULL ); 00332 asprintf( &o->name, "DAC ground calibration source, ch %i, range %i", 00333 channel, range ); 00334 o->preobserve_insn = po_tmpl; 00335 o->preobserve_insn.chanspec = CR_PACK( channel, range, AREF_GROUND ); 00336 o->preobserve_insn.data = o->preobserve_data; 00337 o->observe_insn = tmpl; 00338 o->observe_insn.chanspec = CR_PACK( 0, ai_for_ao_range, AREF_GROUND) | 00339 CR_ALT_SOURCE | CR_ALT_FILTER; 00340 set_target( setup, ao_ground_observable_1xxx( setup, channel, range ), 0.0 ); 00341 setup->n_observables++; 00342 00343 o = setup->observables + ao_high_observable_1xxx( setup, channel, range ); 00344 o->reference_source = CS_1XXX_DAC( channel ); 00345 assert( o->name == NULL ); 00346 asprintf( &o->name, "DAC high calibration source, ch %i, range %i", channel, 00347 range ); 00348 o->preobserve_insn = po_tmpl; 00349 o->preobserve_insn.chanspec = CR_PACK( channel , range, AREF_GROUND ); 00350 o->preobserve_insn.data = o->preobserve_data; 00351 o->observe_insn = tmpl; 00352 o->observe_insn.chanspec = CR_PACK( 0, ai_for_ao_range, AREF_GROUND) | 00353 CR_ALT_SOURCE | CR_ALT_FILTER; 00354 ao_set_high_target_1xxx( setup, ao_high_observable_1xxx( setup, channel, range ), 00355 range ); 00356 setup->n_observables++; 00357 } 00358 } 00359 } 00360 00361 return 0; 00362 } 00363 00364 enum cal_knobs_1xxx 00365 { 00366 DAC0_GAIN_FINE_1XXX = 0, 00367 DAC0_GAIN_COARSE_1XXX = 1, 00368 DAC0_OFFSET_1XXX = 2, 00369 DAC1_OFFSET_1XXX = 3, 00370 DAC1_GAIN_FINE_1XXX = 4, 00371 DAC1_GAIN_COARSE_1XXX = 5, 00372 ADC_OFFSET_COARSE_1XXX = 6, 00373 ADC_OFFSET_FINE_1XXX = 7, 00374 ADC_GAIN_1XXX = 8, 00375 }; 00376 static int adc_offset_coarse_1xxx( unsigned int channel ) 00377 { 00378 return ADC_OFFSET_COARSE_1XXX; 00379 } 00380 static int adc_offset_fine_1xxx( unsigned int channel ) 00381 { 00382 return ADC_OFFSET_FINE_1XXX; 00383 } 00384 static int adc_gain_1xxx( unsigned int channel ) 00385 { 00386 return ADC_GAIN_1XXX; 00387 } 00388 static int dac_offset_1xxx( unsigned int channel ) 00389 { 00390 if( channel ) 00391 return DAC1_OFFSET_1XXX; 00392 else 00393 return DAC0_OFFSET_1XXX; 00394 } 00395 static int dac_gain_fine_1xxx( unsigned int channel ) 00396 { 00397 if( channel ) 00398 return DAC1_GAIN_FINE_1XXX; 00399 else 00400 return DAC0_GAIN_FINE_1XXX; 00401 } 00402 static int dac_gain_coarse_1xxx( unsigned int channel ) 00403 { 00404 if( channel ) 00405 return DAC1_GAIN_COARSE_1XXX; 00406 else 00407 return DAC0_GAIN_COARSE_1XXX; 00408 } 00409 00410 static int cal_cb_pci_1xxx( calibration_setup_t *setup ) 00411 { 00412 generic_layout_t layout; 00413 00414 init_generic_layout( &layout ); 00415 layout.adc_gain = adc_gain_1xxx; 00416 layout.adc_offset = adc_offset_coarse_1xxx; 00417 layout.adc_offset_fine = adc_offset_fine_1xxx; 00418 layout.dac_gain = dac_gain_coarse_1xxx; 00419 layout.dac_gain_fine = dac_gain_fine_1xxx; 00420 layout.dac_offset = dac_offset_1xxx; 00421 layout.adc_high_observable = ai_high_observable_1xxx; 00422 layout.adc_ground_observable = ai_ground_observable_1xxx; 00423 layout.dac_high_observable = ao_high_observable_1xxx; 00424 layout.dac_ground_observable = ao_ground_observable_1xxx; 00425 layout.adc_fractional_tolerance = get_tolerance( setup, setup->ad_subdev, 1.0 ); 00426 layout.dac_fractional_tolerance = get_tolerance( setup, setup->da_subdev, 1.0 ); 00427 return generic_cal_by_range( setup, &layout ); 00428 } 00429 00430 enum cal_knobs_1602_16 00431 { 00432 DAC0_GAIN_FINE_1602_16 = 0, 00433 DAC0_GAIN_COARSE_1602_16 = 1, 00434 DAC0_OFFSET_COARSE_1602_16 = 2, 00435 DAC1_OFFSET_COARSE_1602_16 = 3, 00436 DAC1_GAIN_FINE_1602_16 = 4, 00437 DAC1_GAIN_COARSE_1602_16 = 5, 00438 DAC0_OFFSET_FINE_1602_16 = 6, 00439 DAC1_OFFSET_FINE_1602_16 = 7, 00440 ADC_GAIN_1602_16 = 8, 00441 ADC_POSTGAIN_OFFSET_1602_16 = 9, 00442 ADC_PREGAIN_OFFSET_1602_16 = 10, 00443 }; 00444 static int dac_gain_coarse_1602_16( unsigned int channel ) 00445 { 00446 if( channel ) return DAC1_GAIN_COARSE_1602_16; 00447 else return DAC0_GAIN_COARSE_1602_16; 00448 } 00449 static int dac_gain_fine_1602_16( unsigned int channel ) 00450 { 00451 if( channel ) return DAC1_GAIN_FINE_1602_16; 00452 else return DAC0_GAIN_FINE_1602_16; 00453 } 00454 static int dac_offset_coarse_1602_16( unsigned int channel ) 00455 { 00456 if( channel ) return DAC1_OFFSET_COARSE_1602_16; 00457 else return DAC0_OFFSET_COARSE_1602_16; 00458 } 00459 static int dac_offset_fine_1602_16( unsigned int channel ) 00460 { 00461 if( channel ) return DAC1_OFFSET_FINE_1602_16; 00462 else return DAC0_OFFSET_FINE_1602_16; 00463 } 00464 static int adc_gain_1602_16( unsigned int channel ) 00465 { 00466 return ADC_GAIN_1602_16; 00467 } 00468 static int adc_pregain_offset_1602_16( unsigned int channel ) 00469 { 00470 return ADC_PREGAIN_OFFSET_1602_16; 00471 } 00472 static int adc_postgain_offset_1602_16( unsigned int channel ) 00473 { 00474 return ADC_POSTGAIN_OFFSET_1602_16; 00475 } 00476 static int cal_cb_pci_1602_16( calibration_setup_t *setup ) 00477 { 00478 generic_layout_t layout; 00479 00480 init_generic_layout( &layout ); 00481 layout.adc_gain = adc_gain_1602_16; 00482 layout.adc_offset = adc_pregain_offset_1602_16; 00483 layout.adc_postgain_offset = adc_postgain_offset_1602_16; 00484 layout.dac_gain = dac_gain_coarse_1602_16; 00485 layout.dac_gain_fine = dac_gain_fine_1602_16; 00486 layout.dac_offset = dac_offset_coarse_1602_16; 00487 layout.dac_offset_fine = dac_offset_fine_1602_16; 00488 layout.adc_high_observable = ai_high_observable_1xxx; 00489 layout.adc_ground_observable = ai_ground_observable_1xxx; 00490 layout.dac_high_observable = ao_high_observable_1xxx; 00491 layout.dac_ground_observable = ao_ground_observable_1xxx; 00492 layout.adc_fractional_tolerance = get_tolerance( setup, setup->ad_subdev, 1.0 ); 00493 layout.dac_fractional_tolerance = get_tolerance( setup, setup->da_subdev, 1.0 ); 00494 /* The bipolar postgain calibration should be good for both 00495 * bipolar and unipolar ranges, so disable separate 00496 * unipolar postgain offset calibration (it will fail 00497 * horribly anyways if you try it). 00498 */ 00499 layout.do_adc_unipolar_postgain = 0; 00500 return generic_cal_by_range( setup, &layout ); 00501 } 00502 00503 // converts calibration source voltages from two 16 bit eeprom values to a floating point value 00504 static float eeprom16_to_source( uint16_t *data ) 00505 { 00506 union translator 00507 { 00508 uint32_t bits; 00509 float value; 00510 }; 00511 00512 union translator my_translator; 00513 00514 my_translator.bits = ( data[ 0 ] & 0xffff ) | ( ( data[ 1 ] << 16 ) & 0xffff0000 ); 00515 00516 return my_translator.value; 00517 } 00518 00519 static float eeprom8_to_source( uint8_t *data ) 00520 { 00521 union translator 00522 { 00523 uint32_t bits; 00524 float value; 00525 }; 00526 union translator my_translator; 00527 int i; 00528 00529 my_translator.bits = 0; 00530 for( i = 0; i < 4; i++ ) 00531 { 00532 my_translator.bits |= ( data[ i ] & 0xffff ) << ( 8 * i ); 00533 } 00534 00535 return my_translator.value; 00536 } 00537 00538 int cb_actual_source_voltage( comedi_t *dev, unsigned int subdevice, unsigned int eeprom_channel, float *voltage) 00539 { 00540 int retval; 00541 unsigned int i; 00542 lsampl_t data; 00543 int max_data; 00544 00545 max_data = comedi_get_maxdata( dev, subdevice, eeprom_channel ); 00546 00547 if( max_data == 0xffff ) 00548 { 00549 uint16_t word[ 2 ]; 00550 00551 for( i = 0; i < 2; i++ ) 00552 { 00553 retval = comedi_data_read( dev, subdevice, eeprom_channel + i, 0, 0, &data ); 00554 if( retval < 0 ) 00555 { 00556 comedi_perror( __FUNCTION__ ); 00557 return retval; 00558 } 00559 word[ i ] = data; 00560 } 00561 *voltage = eeprom16_to_source( word ); 00562 }else if( max_data == 0xff ) 00563 { 00564 uint8_t byte[ 4 ]; 00565 00566 for( i = 0; i < 4; i++ ) 00567 { 00568 retval = comedi_data_read( dev, subdevice, eeprom_channel + i, 0, 0, &data ); 00569 if( retval < 0 ) 00570 { 00571 comedi_perror( __FUNCTION__ ); 00572 return retval; 00573 } 00574 byte[ i ] = data; 00575 } 00576 *voltage = eeprom8_to_source( byte ); 00577 }else 00578 { 00579 fprintf( stderr, "%s: maxdata = 0x%x invalid for subdevice %i, channel %i\n", 00580 __FUNCTION__, max_data, subdevice, eeprom_channel); 00581 return -1; 00582 } 00583 00584 DPRINT(1, "eeprom ch 0x%x gives calibration source of %gV\n", eeprom_channel, *voltage); 00585 return 0; 00586 }