![]() |
RTXI 1.3
|
00001 /* 00002 * Asynchronous Analog Output Example 00003 * Part of Comedilib 00004 * 00005 * Copyright (c) 1999,2000 David A. Schleef <ds@schleef.org> 00006 * 00007 * This file may be freely modified, distributed, and combined with 00008 * other software, as long as proper attribution is given in the 00009 * source code. 00010 */ 00011 00012 /* 00013 * Requirements: Analog output device capable of 00014 * asynchronous commands. 00015 * 00016 * This demo uses an analog output subdevice with an 00017 * asynchronous command to generate a waveform. The 00018 * demo hijacks for -n option to select a waveform from 00019 * a predefined list. The default waveform is a sine 00020 * wave (surprise!). Other waveforms include sawtooth, 00021 * square, triangle and cycloid. 00022 * 00023 * The function generation algorithm is the same as 00024 * what is typically used in digital function generators. 00025 * A 32-bit accumulator is incremented by a phase factor, 00026 * which is the amount (in radians) that the generator 00027 * advances each time step. The accumulator is then 00028 * shifted right by 20 bits, to get a 12 bit offset into 00029 * a lookup table. The value in the lookup table at 00030 * that offset is then put into a buffer for output to 00031 * the DAC. 00032 * 00033 * [ Actually, the accumulator is only 26 bits, for some 00034 * reason. I'll fix this sometime. ] 00035 * 00036 */ 00037 00038 #include <stdio.h> 00039 #include <comedilib.h> 00040 #include <fcntl.h> 00041 #include <stdlib.h> 00042 #include <unistd.h> 00043 #include <errno.h> 00044 #include <getopt.h> 00045 #include <ctype.h> 00046 #include <math.h> 00047 #include <string.h> 00048 #include "examples.h" 00049 00050 00051 /* frequency of the sine wave to output */ 00052 double waveform_frequency = 10.0; 00053 00054 /* peak-to-peak amplitude, in DAC units (i.e., 0-4095) */ 00055 double amplitude = 4000; 00056 00057 /* offset, in DAC units */ 00058 double offset = 2048; 00059 00060 /* This is the size of chunks we deal with when creating and 00061 outputting data. This *could* be 1, but that would be 00062 inefficient */ 00063 #define BUF_LEN 0x8000 00064 00065 int external_trigger_number = 0; 00066 00067 sampl_t data[BUF_LEN]; 00068 00069 void dds_output(sampl_t *buf,int n); 00070 void dds_init(double waveform_frequency, double update_frequency, int fn); 00071 00072 void dds_init_sine(void); 00073 void dds_init_pseudocycloid(void); 00074 void dds_init_cycloid(void); 00075 void dds_init_ramp_up(void); 00076 void dds_init_ramp_down(void); 00077 void dds_init_triangle(void); 00078 void dds_init_square(void); 00079 void dds_init_blancmange(void); 00080 00081 static void (* const dds_init_function[])(void) = { 00082 dds_init_sine, 00083 dds_init_ramp_up, 00084 dds_init_ramp_down, 00085 dds_init_triangle, 00086 dds_init_square, 00087 dds_init_cycloid, 00088 dds_init_blancmange, 00089 }; 00090 00091 #define NUMFUNCS (sizeof(dds_init_function)/sizeof(dds_init_function[0])) 00092 00093 int main(int argc, char *argv[]) 00094 { 00095 comedi_cmd cmd; 00096 int err; 00097 int n,m; 00098 int total=0; 00099 comedi_t *dev; 00100 unsigned int chanlist[16]; 00101 unsigned int maxdata; 00102 comedi_range *rng; 00103 int ret; 00104 struct parsed_options options; 00105 int fn; 00106 00107 init_parsed_options(&options); 00108 options.subdevice = -1; 00109 options.n_chan = 0; /* default waveform */ 00110 parse_options(&options, argc, argv); 00111 00112 /* Use n_chan to select waveform (cheat!) */ 00113 fn = options.n_chan; 00114 if(fn < 0 || fn >= NUMFUNCS){ 00115 fn = 0; 00116 } 00117 00118 /* Force n_chan to be 1 */ 00119 options.n_chan = 1; 00120 00121 if(options.value){ 00122 waveform_frequency = options.value; 00123 } 00124 00125 dev = comedi_open(options.filename); 00126 if(dev == NULL){ 00127 fprintf(stderr, "error opening %s\n", options.filename); 00128 return -1; 00129 } 00130 if(options.subdevice < 0) 00131 options.subdevice = comedi_find_subdevice_by_type(dev, COMEDI_SUBD_AO, 0); 00132 00133 maxdata = comedi_get_maxdata(dev, options.subdevice, options.channel); 00134 rng = comedi_get_range(dev, options.subdevice, options.channel, options.range); 00135 00136 offset = (double)comedi_from_phys(0.0, rng, maxdata); 00137 amplitude = (double)comedi_from_phys(1.0, rng, maxdata) - offset; 00138 00139 memset(&cmd,0,sizeof(cmd)); 00140 cmd.subdev = options.subdevice; 00141 cmd.flags = CMDF_WRITE; 00142 cmd.start_src = TRIG_INT; 00143 cmd.start_arg = 0; 00144 cmd.scan_begin_src = TRIG_TIMER; 00145 cmd.scan_begin_arg = 1e9 / options.freq; 00146 cmd.convert_src = TRIG_NOW; 00147 cmd.convert_arg = 0; 00148 cmd.scan_end_src = TRIG_COUNT; 00149 cmd.scan_end_arg = options.n_chan; 00150 cmd.stop_src = TRIG_NONE; 00151 cmd.stop_arg = 0; 00152 00153 cmd.chanlist = chanlist; 00154 cmd.chanlist_len = options.n_chan; 00155 00156 chanlist[0] = CR_PACK(options.channel, options.range, options.aref); 00157 //chanlist[1] = CR_PACK(options.channel + 1, options.range, options.aref); 00158 00159 dds_init(waveform_frequency, options.freq, fn); 00160 00161 dump_cmd(stdout,&cmd); 00162 00163 err = comedi_command_test(dev, &cmd); 00164 if (err < 0) { 00165 comedi_perror("comedi_command_test"); 00166 exit(1); 00167 } 00168 00169 err = comedi_command_test(dev, &cmd); 00170 if (err < 0) { 00171 comedi_perror("comedi_command_test"); 00172 exit(1); 00173 } 00174 00175 if ((err = comedi_command(dev, &cmd)) < 0) { 00176 comedi_perror("comedi_command"); 00177 exit(1); 00178 } 00179 00180 dds_output(data,BUF_LEN); 00181 n = BUF_LEN * sizeof(sampl_t); 00182 m = write(comedi_fileno(dev), (void *)data, n); 00183 if(m < 0){ 00184 perror("write"); 00185 exit(1); 00186 }else if(m < n) 00187 { 00188 fprintf(stderr, "failed to preload output buffer with %i bytes, is it too small?\n" 00189 "See the --write-buffer option of comedi_config\n", n); 00190 exit(1); 00191 } 00192 printf("m=%d\n",m); 00193 00194 ret = comedi_internal_trigger(dev, options.subdevice, 0); 00195 if(ret < 0){ 00196 perror("comedi_internal_trigger\n"); 00197 exit(1); 00198 } 00199 00200 while(1){ 00201 dds_output(data,BUF_LEN); 00202 n=BUF_LEN*sizeof(sampl_t); 00203 while(n>0){ 00204 m=write(comedi_fileno(dev),(void *)data+(BUF_LEN*sizeof(sampl_t)-n),n); 00205 if(m<0){ 00206 perror("write"); 00207 exit(0); 00208 } 00209 printf("m=%d\n",m); 00210 n-=m; 00211 } 00212 total+=BUF_LEN; 00213 //printf("%d\n",total); 00214 } 00215 00216 return 0; 00217 } 00218 00219 00220 00221 #define WAVEFORM_SHIFT 16 00222 #define WAVEFORM_LEN (1<<WAVEFORM_SHIFT) 00223 #define WAVEFORM_MASK (WAVEFORM_LEN-1) 00224 00225 00226 sampl_t waveform[WAVEFORM_LEN]; 00227 00228 unsigned int acc; 00229 unsigned int adder; 00230 00231 void dds_init(double waveform_frequency, double update_frequency, int fn) 00232 { 00233 adder = waveform_frequency / update_frequency * (1 << 16) * (1 << WAVEFORM_SHIFT); 00234 00235 (*dds_init_function[fn])(); 00236 } 00237 00238 void dds_output(sampl_t *buf,int n) 00239 { 00240 int i; 00241 sampl_t *p=buf; 00242 00243 for(i=0;i<n;i++){ 00244 *p=waveform[(acc>>16)&WAVEFORM_MASK]; 00245 p++; 00246 acc+=adder; 00247 } 00248 } 00249 00250 /* Defined for x in [0,1] */ 00251 static inline double triangle(double x) 00252 { 00253 return (x > 0.5) ? 1.0 - x : x; 00254 } 00255 00256 void dds_init_sine(void) 00257 { 00258 int i; 00259 double ofs = offset; 00260 double amp = 0.5 * amplitude; 00261 00262 if(ofs < amp){ 00263 /* Probably a unipolar range. Bump up the offset. */ 00264 ofs = amp; 00265 } 00266 for(i=0;i<WAVEFORM_LEN;i++){ 00267 waveform[i]=rint(ofs+amp*cos(i*2*M_PI/WAVEFORM_LEN)); 00268 } 00269 } 00270 00271 /* Yes, I know this is not the proper equation for a 00272 cycloid. Fix it. */ 00273 void dds_init_pseudocycloid(void) 00274 { 00275 int i; 00276 double t; 00277 00278 for(i=0;i<WAVEFORM_LEN/2;i++){ 00279 t=2*((double)i)/WAVEFORM_LEN; 00280 waveform[i]=rint(offset+amplitude*sqrt(1-4*t*t)); 00281 } 00282 for(i=WAVEFORM_LEN/2;i<WAVEFORM_LEN;i++){ 00283 t=2*(1-((double)i)/WAVEFORM_LEN); 00284 waveform[i]=rint(offset+amplitude*sqrt(1-t*t)); 00285 } 00286 } 00287 00288 void dds_init_cycloid(void) 00289 { 00290 enum { SUBSCALE = 2 }; /* Needs to be >= 2. */ 00291 int h, i, ni; 00292 double t, x, y; 00293 00294 i = -1; 00295 for (h = 0; h < WAVEFORM_LEN * SUBSCALE; h++){ 00296 t = (h * (2 * M_PI)) / (WAVEFORM_LEN * SUBSCALE); 00297 x = t - sin(t); 00298 ni = (int)floor((x * WAVEFORM_LEN) / (2 * M_PI)); 00299 if (ni > i) { 00300 i = ni; 00301 y = 1 - cos(t); 00302 waveform[i] = rint(offset + (amplitude * y / 2)); 00303 } 00304 } 00305 } 00306 00307 void dds_init_ramp_up(void) 00308 { 00309 int i; 00310 00311 for(i=0;i<WAVEFORM_LEN;i++){ 00312 waveform[i]=rint(offset+amplitude*((double)i)/WAVEFORM_LEN); 00313 } 00314 } 00315 00316 void dds_init_ramp_down(void) 00317 { 00318 int i; 00319 00320 for(i=0;i<WAVEFORM_LEN;i++){ 00321 waveform[i]=rint(offset+amplitude*((double)(WAVEFORM_LEN-1-i))/WAVEFORM_LEN); 00322 } 00323 } 00324 00325 void dds_init_triangle(void) 00326 { 00327 int i; 00328 00329 for (i = 0; i < WAVEFORM_LEN; i++) { 00330 waveform[i] = rint(offset + amplitude * 2 * triangle((double)i / WAVEFORM_LEN)); 00331 } 00332 } 00333 00334 void dds_init_square(void) 00335 { 00336 int i; 00337 00338 for (i = 0; i < WAVEFORM_LEN / 2; i++) { 00339 waveform[i] = rint(offset); 00340 } 00341 for ( ; i < WAVEFORM_LEN; i++) { 00342 waveform[i] = rint(offset + amplitude); 00343 } 00344 } 00345 00346 void dds_init_blancmange(void) 00347 { 00348 int i, n; 00349 double b, x; 00350 00351 for (i = 0; i < WAVEFORM_LEN; i++) { 00352 b = 0; 00353 for (n = 0; n < 16; n++) { 00354 x = (double)i / WAVEFORM_LEN; 00355 x *= (1 << n); 00356 x -= floor(x); 00357 b += triangle(x) / (1 << n); 00358 } 00359 waveform[i] = rint(offset + amplitude * 1.5 * b); 00360 } 00361 }