2 * Asynchronous Analog Output Example
5 * Copyright (c) 1999,2000 David A. Schleef <ds@schleef.org>
7 * This file may be freely modified, distributed, and combined with
8 * other software, as long as proper attribution is given in the
13 * Requirements: Analog output device capable of
14 * asynchronous commands.
16 * This demo uses an analog output subdevice with an
17 * asynchronous command to generate a waveform. The
18 * demo hijacks for -n option to select a waveform from
19 * a predefined list. The default waveform is a sine
20 * wave (surprise!). Other waveforms include sawtooth,
21 * square, triangle and cycloid.
23 * The function generation algorithm is the same as
24 * what is typically used in digital function generators.
25 * A 32-bit accumulator is incremented by a phase factor,
26 * which is the amount (in radians) that the generator
27 * advances each time step. The accumulator is then
28 * shifted right by 20 bits, to get a 12 bit offset into
29 * a lookup table. The value in the lookup table at
30 * that offset is then put into a buffer for output to
33 * [ Actually, the accumulator is only 26 bits, for some
34 * reason. I'll fix this sometime. ]
39 #include <comedilib.h>
51 /* frequency of the sine wave to output */
52 double waveform_frequency = 10.0;
54 /* peak-to-peak amplitude, in DAC units (i.e., 0-4095) */
55 double amplitude = 4000;
57 /* offset, in DAC units */
60 /* This is the size of chunks we deal with when creating and
61 outputting data. This *could* be 1, but that would be
63 #define BUF_LEN 0x8000
65 int external_trigger_number = 0;
67 sampl_t data[BUF_LEN];
69 void dds_output(sampl_t *buf,int n);
70 void dds_init(double waveform_frequency, double update_frequency, int fn);
72 void dds_init_sine(void);
73 void dds_init_pseudocycloid(void);
74 void dds_init_cycloid(void);
75 void dds_init_ramp_up(void);
76 void dds_init_ramp_down(void);
77 void dds_init_triangle(void);
78 void dds_init_square(void);
79 void dds_init_blancmange(void);
81 static void (* const dds_init_function[])(void) = {
91 #define NUMFUNCS (sizeof(dds_init_function)/sizeof(dds_init_function[0]))
93 int main(int argc, char *argv[])
100 unsigned int chanlist[16];
101 unsigned int maxdata;
104 struct parsed_options options;
107 init_parsed_options(&options);
108 options.subdevice = -1;
109 options.n_chan = 0; /* default waveform */
110 parse_options(&options, argc, argv);
112 /* Use n_chan to select waveform (cheat!) */
114 if(fn < 0 || fn >= NUMFUNCS){
118 /* Force n_chan to be 1 */
122 waveform_frequency = options.value;
125 dev = comedi_open(options.filename);
127 fprintf(stderr, "error opening %s\n", options.filename);
130 if(options.subdevice < 0)
131 options.subdevice = comedi_find_subdevice_by_type(dev, COMEDI_SUBD_AO, 0);
133 maxdata = comedi_get_maxdata(dev, options.subdevice, options.channel);
134 rng = comedi_get_range(dev, options.subdevice, options.channel, options.range);
136 offset = (double)comedi_from_phys(0.0, rng, maxdata);
137 amplitude = (double)comedi_from_phys(1.0, rng, maxdata) - offset;
139 memset(&cmd,0,sizeof(cmd));
140 cmd.subdev = options.subdevice;
141 cmd.flags = CMDF_WRITE;
142 cmd.start_src = TRIG_INT;
144 cmd.scan_begin_src = TRIG_TIMER;
145 cmd.scan_begin_arg = 1e9 / options.freq;
146 cmd.convert_src = TRIG_NOW;
148 cmd.scan_end_src = TRIG_COUNT;
149 cmd.scan_end_arg = options.n_chan;
150 cmd.stop_src = TRIG_NONE;
153 cmd.chanlist = chanlist;
154 cmd.chanlist_len = options.n_chan;
156 chanlist[0] = CR_PACK(options.channel, options.range, options.aref);
157 //chanlist[1] = CR_PACK(options.channel + 1, options.range, options.aref);
159 dds_init(waveform_frequency, options.freq, fn);
161 dump_cmd(stdout,&cmd);
163 err = comedi_command_test(dev, &cmd);
165 comedi_perror("comedi_command_test");
169 err = comedi_command_test(dev, &cmd);
171 comedi_perror("comedi_command_test");
175 if ((err = comedi_command(dev, &cmd)) < 0) {
176 comedi_perror("comedi_command");
180 dds_output(data,BUF_LEN);
181 n = BUF_LEN * sizeof(sampl_t);
182 m = write(comedi_fileno(dev), (void *)data, n);
188 fprintf(stderr, "failed to preload output buffer with %i bytes, is it too small?\n"
189 "See the --write-buffer option of comedi_config\n", n);
194 ret = comedi_internal_trigger(dev, options.subdevice, 0);
196 perror("comedi_internal_trigger\n");
201 dds_output(data,BUF_LEN);
202 n=BUF_LEN*sizeof(sampl_t);
204 m=write(comedi_fileno(dev),(void *)data+(BUF_LEN*sizeof(sampl_t)-n),n);
213 //printf("%d\n",total);
221 #define WAVEFORM_SHIFT 16
222 #define WAVEFORM_LEN (1<<WAVEFORM_SHIFT)
223 #define WAVEFORM_MASK (WAVEFORM_LEN-1)
226 sampl_t waveform[WAVEFORM_LEN];
231 void dds_init(double waveform_frequency, double update_frequency, int fn)
233 adder = waveform_frequency / update_frequency * (1 << 16) * (1 << WAVEFORM_SHIFT);
235 (*dds_init_function[fn])();
238 void dds_output(sampl_t *buf,int n)
244 *p=waveform[(acc>>16)&WAVEFORM_MASK];
250 /* Defined for x in [0,1] */
251 static inline double triangle(double x)
253 return (x > 0.5) ? 1.0 - x : x;
256 void dds_init_sine(void)
260 double amp = 0.5 * amplitude;
263 /* Probably a unipolar range. Bump up the offset. */
266 for(i=0;i<WAVEFORM_LEN;i++){
267 waveform[i]=rint(ofs+amp*cos(i*2*M_PI/WAVEFORM_LEN));
271 /* Yes, I know this is not the proper equation for a
273 void dds_init_pseudocycloid(void)
278 for(i=0;i<WAVEFORM_LEN/2;i++){
279 t=2*((double)i)/WAVEFORM_LEN;
280 waveform[i]=rint(offset+amplitude*sqrt(1-4*t*t));
282 for(i=WAVEFORM_LEN/2;i<WAVEFORM_LEN;i++){
283 t=2*(1-((double)i)/WAVEFORM_LEN);
284 waveform[i]=rint(offset+amplitude*sqrt(1-t*t));
288 void dds_init_cycloid(void)
290 enum { SUBSCALE = 2 }; /* Needs to be >= 2. */
295 for (h = 0; h < WAVEFORM_LEN * SUBSCALE; h++){
296 t = (h * (2 * M_PI)) / (WAVEFORM_LEN * SUBSCALE);
298 ni = (int)floor((x * WAVEFORM_LEN) / (2 * M_PI));
302 waveform[i] = rint(offset + (amplitude * y / 2));
307 void dds_init_ramp_up(void)
311 for(i=0;i<WAVEFORM_LEN;i++){
312 waveform[i]=rint(offset+amplitude*((double)i)/WAVEFORM_LEN);
316 void dds_init_ramp_down(void)
320 for(i=0;i<WAVEFORM_LEN;i++){
321 waveform[i]=rint(offset+amplitude*((double)(WAVEFORM_LEN-1-i))/WAVEFORM_LEN);
325 void dds_init_triangle(void)
329 for (i = 0; i < WAVEFORM_LEN; i++) {
330 waveform[i] = rint(offset + amplitude * 2 * triangle((double)i / WAVEFORM_LEN));
334 void dds_init_square(void)
338 for (i = 0; i < WAVEFORM_LEN / 2; i++) {
339 waveform[i] = rint(offset);
341 for ( ; i < WAVEFORM_LEN; i++) {
342 waveform[i] = rint(offset + amplitude);
346 void dds_init_blancmange(void)
351 for (i = 0; i < WAVEFORM_LEN; i++) {
353 for (n = 0; n < 16; n++) {
354 x = (double)i / WAVEFORM_LEN;
357 b += triangle(x) / (1 << n);
359 waveform[i] = rint(offset + amplitude * 1.5 * b);