/* * Asynchronous Analog Output Example * Part of Comedilib * * Copyright (c) 1999,2000 David A. Schleef * * This file may be freely modified, distributed, and combined with * other software, as long as proper attribution is given in the * source code. */ /* * Requirements: Analog output device capable of * asynchronous commands. * * This demo uses an analog output subdevice with an * asynchronous command to generate a waveform. The * demo hijacks for -n option to select a waveform from * a predefined list. The default waveform is a sine * wave (surprise!). Other waveforms include sawtooth, * square, triangle and cycloid. * * The function generation algorithm is the same as * what is typically used in digital function generators. * A 32-bit accumulator is incremented by a phase factor, * which is the amount (in radians) that the generator * advances each time step. The accumulator is then * shifted right by 20 bits, to get a 12 bit offset into * a lookup table. The value in the lookup table at * that offset is then put into a buffer for output to * the DAC. * * [ Actually, the accumulator is only 26 bits, for some * reason. I'll fix this sometime. ] * */ #include #include #include #include #include #include #include #include #include #include #include "examples.h" /* frequency of the sine wave to output */ double waveform_frequency = 10.0; /* peak-to-peak amplitude, in DAC units (i.e., 0-4095) */ double amplitude = 4000; /* offset, in DAC units */ double offset = 2048; /* This is the size of chunks we deal with when creating and outputting data. This *could* be 1, but that would be inefficient */ #define BUF_LEN 0x8000 int external_trigger_number = 0; sampl_t data[BUF_LEN]; void dds_output(sampl_t *buf,int n); void dds_init(double waveform_frequency, double update_frequency, int fn); void dds_init_sine(void); void dds_init_pseudocycloid(void); void dds_init_cycloid(void); void dds_init_ramp_up(void); void dds_init_ramp_down(void); void dds_init_triangle(void); void dds_init_square(void); void dds_init_blancmange(void); static void (* const dds_init_function[])(void) = { dds_init_sine, dds_init_ramp_up, dds_init_ramp_down, dds_init_triangle, dds_init_square, dds_init_cycloid, dds_init_blancmange, }; #define NUMFUNCS (sizeof(dds_init_function)/sizeof(dds_init_function[0])) int main(int argc, char *argv[]) { comedi_cmd cmd; int err; int n,m; int total=0; comedi_t *dev; unsigned int chanlist[16]; unsigned int maxdata; comedi_range *rng; int ret; struct parsed_options options; int fn; init_parsed_options(&options); options.subdevice = -1; options.n_chan = -1; /* waveform */ parse_options(&options, argc, argv); /* Use n_chan to select waveform (cheat!) */ fn = options.n_chan; if(fn < 0 || fn >= NUMFUNCS){ fprintf(stderr,"Use the option '-n' to select another waveform.\n"); fn = 0; } /* Force n_chan to be 1 */ options.n_chan = 1; if(options.value){ waveform_frequency = options.value; } dev = comedi_open(options.filename); if(dev == NULL){ fprintf(stderr, "error opening %s\n", options.filename); return -1; } if(options.subdevice < 0) options.subdevice = comedi_find_subdevice_by_type(dev, COMEDI_SUBD_AO, 0); maxdata = comedi_get_maxdata(dev, options.subdevice, options.channel); rng = comedi_get_range(dev, options.subdevice, options.channel, options.range); offset = (double)comedi_from_phys(0.0, rng, maxdata); amplitude = (double)comedi_from_phys(1.0, rng, maxdata) - offset; memset(&cmd,0,sizeof(cmd)); cmd.subdev = options.subdevice; cmd.flags = CMDF_WRITE; cmd.start_src = TRIG_INT; cmd.start_arg = 0; cmd.scan_begin_src = TRIG_TIMER; cmd.scan_begin_arg = 1e9 / options.freq; cmd.convert_src = TRIG_NOW; cmd.convert_arg = 0; cmd.scan_end_src = TRIG_COUNT; cmd.scan_end_arg = options.n_chan; cmd.stop_src = TRIG_NONE; cmd.stop_arg = 0; cmd.chanlist = chanlist; cmd.chanlist_len = options.n_chan; chanlist[0] = CR_PACK(options.channel, options.range, options.aref); //chanlist[1] = CR_PACK(options.channel + 1, options.range, options.aref); dds_init(waveform_frequency, options.freq, fn); if (options.verbose) dump_cmd(stdout,&cmd); err = comedi_command_test(dev, &cmd); if (err < 0) { comedi_perror("comedi_command_test"); exit(1); } err = comedi_command_test(dev, &cmd); if (err < 0) { comedi_perror("comedi_command_test"); exit(1); } comedi_set_write_subdevice(dev, cmd.subdev); ret = comedi_get_write_subdevice(dev); if (ret < 0 || ret != cmd.subdev) { fprintf(stderr, "failed to change 'write' subdevice from %d to %d\n", ret, cmd.subdev); exit(1); } if ((err = comedi_command(dev, &cmd)) < 0) { comedi_perror("comedi_command"); exit(1); } dds_output(data,BUF_LEN); n = BUF_LEN * sizeof(sampl_t); m = write(comedi_fileno(dev), (void *)data, n); if(m < 0){ perror("write"); exit(1); }else if(m < n) { fprintf(stderr, "failed to preload output buffer with %i bytes, is it too small?\n" "See the --write-buffer option of comedi_config\n", n); exit(1); } if (options.verbose) printf("m=%d\n",m); ret = comedi_internal_trigger(dev, options.subdevice, 0); if(ret < 0){ perror("comedi_internal_trigger\n"); exit(1); } while(1){ dds_output(data,BUF_LEN); n=BUF_LEN*sizeof(sampl_t); while(n>0){ m=write(comedi_fileno(dev),(void *)data+(BUF_LEN*sizeof(sampl_t)-n),n); if(m<0){ perror("write"); exit(0); } if (options.verbose) printf("m=%d\n",m); n-=m; } total+=BUF_LEN; //printf("%d\n",total); } return 0; } #define WAVEFORM_SHIFT 16 #define WAVEFORM_LEN (1<>16)&WAVEFORM_MASK]; p++; acc+=adder; } } /* Defined for x in [0,1] */ static inline double triangle(double x) { return (x > 0.5) ? 1.0 - x : x; } void dds_init_sine(void) { int i; double ofs = offset; double amp = 0.5 * amplitude; if(ofs < amp){ /* Probably a unipolar range. Bump up the offset. */ ofs = amp; } for(i=0;i= 2. */ int h, i, ni; double t, x, y; i = -1; for (h = 0; h < WAVEFORM_LEN * SUBSCALE; h++){ t = (h * (2 * M_PI)) / (WAVEFORM_LEN * SUBSCALE); x = t - sin(t); ni = (int)floor((x * WAVEFORM_LEN) / (2 * M_PI)); if (ni > i) { i = ni; y = 1 - cos(t); waveform[i] = rint(offset + (amplitude * y / 2)); } } } void dds_init_ramp_up(void) { int i; for(i=0;i