Makefile.am: Remove 'debian' from EXTRA_DIST.
[comedilib.git] / demo / ao_waveform.c
index 6aeb4eef585da33479ca8e56cf76c75f5b236d66..fef1d2c208cf8cd1bc72bfd60e828e0523c9ec73 100644 (file)
@@ -1,36 +1,38 @@
 /*
-   This demo uses an analog output subdevice in timed
-   mode (mode 2) to generate a waveform.  The waveform
-   in this example is a sine wave (surprise!), but this
-   can be easily changed to make a general function
-   generator.
-
-   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. ]
-
-   On the comedi side of things, the setup for mode 2
-   is similar to analog input, except for the TRIG_WRITE
-   flag.  Once you have issued the command, comedi then
-   expects you to keep the buffer full of data to output
-   to the DAC.  This is done by write().  Since there
-   may be a delay between the ioctl() and a subsequent
-   write(), you should fill the buffer using write() before
-   you call ioctl(), as is done here.
-
-   Also NOTE!  The lseek() to offset 1 is used to tell
-   comedi that you want to write to subdevice 1.  This
-   is not needed for analog input, since AI is usually on
-   subdevice 0.
+ * Asynchronous Analog Output Example
+ * Part of Comedilib
+ *
+ * Copyright (c) 1999,2000 David A. Schleef <ds@schleef.org>
+ *
+ * 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 <stdio.h>
 #include <fcntl.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <sys/ioctl.h>
 #include <errno.h>
 #include <getopt.h>
 #include <ctype.h>
 #include <math.h>
-
-#define dds_init dds_init_pseudocycloid
+#include <string.h>
+#include "examples.h"
 
 
 /* frequency of the sine wave to output */
-double waveform_frequency      = 100.0;
-
-/* update rate for the DAC, typically much higher than
-   the frequency of the sine wave. */
-double update_frequency                = 50000.0;
+double waveform_frequency      = 10.0;
 
 /* peak-to-peak amplitude, in DAC units (i.e., 0-4095) */
 double amplitude               = 4000;
@@ -63,78 +60,143 @@ 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                4096
+#define BUF_LEN 0x8000
 
-
-#define N_SCANS                0
-#define N_CHANS                1
-
-int subdevice;
-int channels[] = { 0 };
-int range = 0;
-int aref = AREF_GROUND;
 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[])
 {
-       char *fn = NULL;
-       comedi_trig it;
+       comedi_cmd cmd;
        int err;
-       int n,m,i;
+       int n,m;
        int total=0;
        comedi_t *dev;
-       double actual_freq;
-       unsigned int chan[N_CHANS];
-
-       if(argc>=2){
-               waveform_frequency=atof(argv[1]);
+       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 = 0;     /* default waveform */
+       parse_options(&options, argc, argv);
+
+       /* Use n_chan to select waveform (cheat!) */
+       fn = options.n_chan;
+       if(fn < 0 || fn >= NUMFUNCS){
+               fn = 0;
        }
 
-       fn = "/dev/comedi0";
+       /* Force n_chan to be 1 */
+       options.n_chan = 1;
 
-       dev = comedi_open(fn);
-
-       subdevice = comedi_find_subdevice_by_type(dev,COMEDI_SUBD_AO,0);
-
-       it.subdev = subdevice;
-       it.mode = 2;
-       it.flags = TRIG_WRITE;
-       it.n_chan = N_CHANS;
-       it.chanlist = chan;
-       it.data = NULL;
-       it.n = N_SCANS;
-       it.trigsrc = 0;
+       if(options.value){
+               waveform_frequency = options.value;
+       }
 
-       /* convert the frequency into a timer value */
-       comedi_get_timer(dev,subdevice,update_frequency,&it.trigvar,&actual_freq);
-       fprintf(stderr,"primary actual frequency=%g timer value=%d\n",actual_freq,it.trigvar);
+       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);
+
+       dump_cmd(stdout,&cmd);
+
+       err = comedi_command_test(dev, &cmd);
+       if (err < 0) {
+               comedi_perror("comedi_command_test");
+               exit(1);
+       }
 
-       /* pack the channel list */
-       for(i=0;i<N_CHANS;i++){
-               chan[i] = CR_PACK(channels[i], range, aref);
+       err = comedi_command_test(dev, &cmd);
+       if (err < 0) {
+               comedi_perror("comedi_command_test");
+               exit(1);
        }
 
-       dds_init();
+       if ((err = comedi_command(dev, &cmd)) < 0) {
+               comedi_perror("comedi_command");
+               exit(1);
+       }
 
        dds_output(data,BUF_LEN);
-       dds_output(data,BUF_LEN);
-
-       lseek(comedi_fileno(dev),subdevice,SEEK_SET);
-       m=write(comedi_fileno(dev),data,BUF_LEN*sizeof(sampl_t));
-       perror("write");
+       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);
+       }
        printf("m=%d\n",m);
 
-
-       if ((err = comedi_trigger(dev, &it)) < 0) {
-               perror("ioctl");
+       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);
@@ -144,7 +206,7 @@ int main(int argc, char *argv[])
                                perror("write");
                                exit(0);
                        }
-                       //printf("m=%d\n",m);
+                       printf("m=%d\n",m);
                        n-=m;
                }
                total+=BUF_LEN;
@@ -166,34 +228,49 @@ sampl_t waveform[WAVEFORM_LEN];
 unsigned int acc;
 unsigned int adder;
 
-void dds_init(void)
+void dds_init(double waveform_frequency, double update_frequency, int fn)
 {
-       int i;
+       adder = waveform_frequency / update_frequency * (1 << 16) * (1 << WAVEFORM_SHIFT);
 
-       adder=waveform_frequency/update_frequency*(1<<16)*(1<<WAVEFORM_SHIFT);
+       (*dds_init_function[fn])();
+}
 
-       dds_init_sine();
+void dds_output(sampl_t *buf,int n)
+{
+       int i;
+       sampl_t *p=buf;
 
-       /* this is due to a bug in the NI-E driver */
-       if(range){
-               for(i=0;i<WAVEFORM_LEN;i++){
-                       waveform[i]^=0x800;
-               }
+       for(i=0;i<n;i++){
+               *p=waveform[(acc>>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<WAVEFORM_LEN;i++){
-               waveform[i]=rint(offset+0.5*amplitude*cos(i*2*M_PI/WAVEFORM_LEN));
+               waveform[i]=rint(ofs+amp*cos(i*2*M_PI/WAVEFORM_LEN));
        }
 }
 
 /* Yes, I know this is not the proper equation for a
    cycloid.  Fix it. */
-void dds_init_cycloid(void)
+void dds_init_pseudocycloid(void)
 {
        int i;
        double t;
@@ -208,7 +285,26 @@ void dds_init_cycloid(void)
        }
 }
 
-void dds_init_sawtooth(void)
+void dds_init_cycloid(void)
+{
+       enum { SUBSCALE = 2 };  /* Needs to be >= 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;
 
@@ -217,15 +313,49 @@ void dds_init_sawtooth(void)
        }
 }
 
-void dds_output(sampl_t *buf,int n)
+void dds_init_ramp_down(void)
 {
        int i;
-       sampl_t *p=buf;
 
-       for(i=0;i<n;i++){
-               *p=waveform[(acc>>16)&WAVEFORM_MASK];
-               p++;
-               acc+=adder;
+       for(i=0;i<WAVEFORM_LEN;i++){
+               waveform[i]=rint(offset+amplitude*((double)(WAVEFORM_LEN-1-i))/WAVEFORM_LEN);
        }
 }
 
+void dds_init_triangle(void)
+{
+       int i;
+
+       for (i = 0; i < WAVEFORM_LEN; i++) {
+               waveform[i] = rint(offset + amplitude * 2 * triangle((double)i / WAVEFORM_LEN));
+       }
+}
+
+void dds_init_square(void)
+{
+       int i;
+
+       for (i = 0; i < WAVEFORM_LEN / 2; i++) {
+               waveform[i] = rint(offset);
+       }
+       for ( ; i < WAVEFORM_LEN; i++) {
+               waveform[i] = rint(offset + amplitude);
+       }
+}
+
+void dds_init_blancmange(void)
+{
+       int i, n;
+       double b, x;
+
+       for (i = 0; i < WAVEFORM_LEN; i++) {
+               b = 0;
+               for (n = 0; n < 16; n++) {
+                       x = (double)i / WAVEFORM_LEN;
+                       x *= (1 << n);
+                       x -= floor(x);
+                       b += triangle(x) / (1 << n);
+               }
+               waveform[i] = rint(offset + amplitude * 1.5 * b);
+       }
+}