Sophie

Sophie

distrib > Fedora > 14 > x86_64 > by-pkgid > 02e7bc50735df2e365110343fbf39739 > files > 108

comedilib-devel-0.8.1-7.fc14.x86_64.rpm

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
<HTML
><HEAD
><TITLE
>Writing Comedi programs</TITLE
><META
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
REL="HOME"
TITLE="    Comedi
  "
HREF="index.html"><LINK
REL="PREVIOUS"
TITLE="Configuration"
HREF="x333.html"><LINK
REL="NEXT"
TITLE="Acquisition and configuration functions"
HREF="x621.html"></HEAD
><BODY
CLASS="SECTION"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>Comedi: The <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>Control and Measurement Device Interface</I
></SPAN
>
handbook
  </TH
></TR
><TR
><TD
WIDTH="10%"
ALIGN="left"
VALIGN="bottom"
><A
HREF="x333.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
></TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="x621.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="SECTION"
><H1
CLASS="SECTION"
><A
NAME="WRITINGPROGRAMS"
>3. Writing <ACRONYM
CLASS="ACRONYM"
>Comedi</ACRONYM
> programs</A
></H1
><P
>This Section describes how a well-installed and configured <ACRONYM
CLASS="ACRONYM"
>Comedi</ACRONYM
>
package can be used in an application, to communicate data with a set
of <ACRONYM
CLASS="ACRONYM"
>Comedi</ACRONYM
> devices.
<A
HREF="x621.html"
>Section 4</A
> gives more details about
the various acquisition functions with which the application
programmer can perform data acquisition in <ACRONYM
CLASS="ACRONYM"
>Comedi</ACRONYM
>.</P
><P
>Also don't forget to take a good look at the
<TT
CLASS="FILENAME"
>demo</TT
>
directory of the Comedilib source code. It contains lots of examples
for the basic functionalities of <ACRONYM
CLASS="ACRONYM"
>Comedi</ACRONYM
>.</P
><DIV
CLASS="SECTION"
><H2
CLASS="SECTION"
><A
NAME="FIRSTPROGRAM"
>3.1. Your first <ACRONYM
CLASS="ACRONYM"
>Comedi</ACRONYM
> program</A
></H2
><P
>This example requires a card that has analog or digital input. This
progam opens the device, gets the data, and prints it out:
<PRE
CLASS="PROGRAMLISTING"
>#include &#60;stdio.h&#62;   /* for printf() */
#include &#60;<A
HREF="x4629.html#COMEDI-COMEDILIB-H"
>comedilib.h</A
>&#62;

int subdev = 0;         /* change this to your input subdevice */
int chan = 0;           /* change this to your channel */
int range = 0;          /* more on this later */
int aref = <A
HREF="x4629.html#AREF-GROUND"
>AREF_GROUND</A
>; /* more on this later */

int main(int argc,char *argv[])
{
  <A
HREF="x4629.html#REF-TYPE-COMEDI-T"
>comedi_t</A
> *it;
  <A
HREF="x4629.html#REF-TYPE-LSAMPL-T"
>lsampl_t</A
> data;

  it=<A
HREF="r4857.html"
>comedi_open</A
>("/dev/comedi0");

  <A
HREF="r5725.html"
>comedi_data_read</A
>(it,subdev,chan,range,aref, &#38; data);

  printf("%d\n",data);

  return 0;
}</PRE
>
The
<CODE
CLASS="FUNCTION"
> <A
HREF="r4857.html"
>comedi_open()</A
></CODE
> can only be successful if the
<TT
CLASS="FILENAME"
>comedi0</TT
> device file is configured to point to a
valid <ACRONYM
CLASS="ACRONYM"
>Comedi</ACRONYM
> driver. <A
HREF="x333.html#CARDCONFIGURATION"
>Section 2.1</A
> explains
how this driver is linked to the <SPAN
CLASS="QUOTE"
>"device file"</SPAN
>.</P
><P
>The code above is basically the guts of
<TT
CLASS="FILENAME"
>demo/inp.c</TT
>, without error checking or fancy
options.  Compile the program using</P
><PRE
CLASS="SCREEN"
>cc tut1.c -lcomedi -o tut1</PRE
><P
>(Replace <TT
CLASS="LITERAL"
>cc</TT
> by your favourite C compiler command.)</P
><P
>The <CODE
CLASS="PARAMETER"
>range</CODE
> variable tells
<ACRONYM
CLASS="ACRONYM"
>Comedi</ACRONYM
> which gain to use when measuring an analog voltage.  Since we
don't know (yet) which numbers are valid, or what each means, we'll
use <TT
CLASS="LITERAL"
>0</TT
>, because it won't cause errors.  Likewise
with <CODE
CLASS="PARAMETER"
>aref</CODE
>, which determines the
analog reference used.</P
></DIV
><DIV
CLASS="SECTION"
><H2
CLASS="SECTION"
><A
NAME="CONVERTINGSAMPLES"
>3.2. Converting samples to voltages</A
></H2
><P
>If you selected an analog input subdevice, you probably noticed
that the output of <B
CLASS="COMMAND"
>tut1</B
> is a number between
<TT
CLASS="LITERAL"
>0</TT
> and <TT
CLASS="LITERAL"
>4095</TT
>, or
<TT
CLASS="LITERAL"
>0</TT
> and <TT
CLASS="LITERAL"
>65535</TT
>, depending on the
number of bits in the A/D converter. <ACRONYM
CLASS="ACRONYM"
>Comedi</ACRONYM
> samples are
<SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>always</I
></SPAN
> unsigned,
with <TT
CLASS="LITERAL"
>0</TT
>  representing the lowest voltage of the ADC,
and <TT
CLASS="LITERAL"
>4095</TT
>
the highest.  <ACRONYM
CLASS="ACRONYM"
>Comedi</ACRONYM
> compensates for anything else the manual for
your device says.  However, you probably prefer to have this number
translated to a voltage.  Naturally, as a good programmer, your first
question is: <SPAN
CLASS="QUOTE"
>"How do I do this in a device-independent
manner?"</SPAN
></P
><P
>Most devices give you a choice of gain and unipolar/bipolar
input, and <ACRONYM
CLASS="ACRONYM"
>Comedi</ACRONYM
> allows you to select which of these to use.  This
parameter is called the <SPAN
CLASS="QUOTE"
>"range parameter,"</SPAN
> since it
specifies the <SPAN
CLASS="QUOTE"
>"input range"</SPAN
> for analog input (or
<SPAN
CLASS="QUOTE"
>"output range"</SPAN
> for analog output.)  The range parameter
represents both the gain and the unipolar/bipolar aspects.</P
><P
><ACRONYM
CLASS="ACRONYM"
>Comedi</ACRONYM
> keeps the number of available ranges and the largest
sample value for each subdevice/channel combination.  (Some
devices allow different input/output ranges for different
channels in a subdevice.)</P
><P
>The largest sample value can be found using the function
<PRE
CLASS="PROGRAMLISTING"
> <A
HREF="x4629.html#REF-TYPE-LSAMPL-T"
>lsampl_t</A
> <A
HREF="r5331.html"
>comedi_get_maxdata</A
>(<A
HREF="x4629.html#REF-TYPE-COMEDI-T"
>comedi_t</A
> * device, unsigned int subdevice, unsigned int channel))</PRE
>
The number of available ranges can be found using the function:
<PRE
CLASS="PROGRAMLISTING"
> int <A
HREF="r5360.html"
>comedi_get_n_ranges</A
>(<A
HREF="x4629.html#REF-TYPE-COMEDI-T"
>comedi_t</A
> * device, unsigned int subdevice, unsigned int channel);</PRE
></P
><P
>For each value of the range parameter for a particular
subdevice/channel, you can get range information using:
<PRE
CLASS="PROGRAMLISTING"
> <A
HREF="x4629.html#REF-TYPE-COMEDI-RANGE"
>comedi_range</A
> * <A
HREF="r5383.html"
>comedi_get_range</A
>(<A
HREF="x4629.html#REF-TYPE-COMEDI-T"
>comedi_t</A
> * device,
                                 unsigned int subdevice, unsigned int channel, unsigned int range);</PRE
>
which returns a pointer to a
<A
HREF="x4629.html#REF-TYPE-COMEDI-RANGE"
>comedi_range</A
>
structure, which has the following contents:
<PRE
CLASS="PROGRAMLISTING"
>typedef struct{
        double min;
        double max;
        unsigned int unit;
}comedi_range;</PRE
>
The structure element <CODE
CLASS="PARAMETER"
>min</CODE
>
represents the voltage corresponding to
<A
HREF="r5725.html"
>comedi_data_read()</A
>
returning <TT
CLASS="LITERAL"
>0</TT
>,
and <CODE
CLASS="PARAMETER"
>max</CODE
> represents
<A
HREF="r5725.html"
>comedi_data_read()</A
>
returning <CODE
CLASS="PARAMETER"
>maxdata</CODE
>,
(i.e., <TT
CLASS="LITERAL"
>4095</TT
> for <TT
CLASS="LITERAL"
>12</TT
> bit A/C
converters, <TT
CLASS="LITERAL"
>65535</TT
> for <TT
CLASS="LITERAL"
>16</TT
> bit,
or, <TT
CLASS="LITERAL"
>1</TT
> for digital input; more on this in a bit.)
The <CODE
CLASS="PARAMETER"
>unit</CODE
> entry tells you if
<CODE
CLASS="PARAMETER"
>min</CODE
> and
<CODE
CLASS="PARAMETER"
>max</CODE
> refer to voltage, current,
or are dimensionless (e.g., for digital I/O).</P
><P
><SPAN
CLASS="QUOTE"
>"Could it get easier?"</SPAN
> you say.  Well, yes.  Use
the function <CODE
CLASS="FUNCTION"
>comedi_to_phys()</CODE
>
<A
HREF="r5619.html"
>comedi_to_phys()</A
>, which
converts data values to physical units.  Call it using something like</P
><PRE
CLASS="PROGRAMLISTING"
>volts=<A
HREF="r5619.html"
>comedi_to_phys</A
>(it,data,range,maxdata);</PRE
><P
>and the opposite</P
><PRE
CLASS="PROGRAMLISTING"
>data=<A
HREF="r5673.html"
>comedi_from_phy</A
>s(it,volts,range,maxdata);</PRE
></DIV
><DIV
CLASS="SECTION"
><H2
CLASS="SECTION"
><A
NAME="USINGFILEINTERFACE"
>3.3. Using the file interface</A
></H2
><P
>In addition to providing low level routines for data
access, the <ACRONYM
CLASS="ACRONYM"
>Comedi</ACRONYM
> library provides higher-level access,
much like the standard <ACRONYM
CLASS="ACRONYM"
>C</ACRONYM
> library provides
<CODE
CLASS="FUNCTION"
>fopen()</CODE
>, etc.  as a high-level (and portable)
alternative to the direct <ACRONYM
CLASS="ACRONYM"
>UNIX</ACRONYM
> system calls
<CODE
CLASS="FUNCTION"
>open()</CODE
>, etc.  Similarily to
<CODE
CLASS="FUNCTION"
>fopen()</CODE
>, we have
<A
HREF="r4857.html"
>comedi_open()</A
>:</P
><PRE
CLASS="PROGRAMLISTING"
>file=<A
HREF="r4857.html"
>comedi_open</A
>("/dev/comedi0");</PRE
><P
>where <CODE
CLASS="PARAMETER"
>file</CODE
> is of type
<CODE
CLASS="PARAMETER"
>(<A
HREF="x4629.html#REF-TYPE-COMEDI-T"
>comedi_t</A
> *)</CODE
>.
This function calls <CODE
CLASS="FUNCTION"
>open()</CODE
>, as done explicitly in
a previous section, but also fills the
<A
HREF="x4629.html#REF-TYPE-COMEDI-T"
>comedi_t</A
>
structure with lots of goodies; this information will be useful soon.</P
><P
>Specifically, you need to know
<CODE
CLASS="PARAMETER"
>maxdata</CODE
> for a specific
subdevice/channel. How about:

<PRE
CLASS="PROGRAMLISTING"
>maxdata=<A
HREF="r5331.html"
>comedi_get_maxdata</A
>(file,subdevice,channel);</PRE
>

Wow! How easy. And the range information?

<PRE
CLASS="PROGRAMLISTING"
><A
HREF="x4629.html#REF-TYPE-COMEDI-RANGE"
>comedi_range</A
> * <A
HREF="r5383.html"
>comedi_get_range</A
>
(<A
HREF="x4629.html#REF-TYPE-COMEDI-T"
>comedi_t</A
>comedi_t *it,unsigned int subdevice,unsigned int chan,unsigned int range);</PRE
>&#13;</P
></DIV
><DIV
CLASS="SECTION"
><H2
CLASS="SECTION"
><A
NAME="SECONDPROGRAM"
>3.4. Your second <ACRONYM
CLASS="ACRONYM"
>Comedi</ACRONYM
> program: simple acquisition</A
></H2
><P
>Actually, this is the first <ACRONYM
CLASS="ACRONYM"
>Comedi</ACRONYM
> program again, just
that we've added what we've learned.</P
><PRE
CLASS="PROGRAMLISTING"
>#include &lt;stdio.h&gt;      /* for printf() */
#include &#60;<A
HREF="x4629.html#COMEDI-COMEDILIB-H"
>comedilib.h</A
>&#62;

int subdev = 0;         /* change this to your input subdevice */
int chan = 0;           /* change this to your channel */
int range = 0;          /* more on this later */
int aref = 0;           /* more on this later */

int main(int argc,char *argv[])
{
  <A
HREF="x4629.html#REF-TYPE-COMEDI-T"
>comedi_t</A
> *cf;
  int chan=0;
  <A
HREF="x4629.html#REF-TYPE-LSAMPL-T"
>lsampl_t</A
> data;
  int maxdata,rangetype;
  double volts;

  cf=<A
HREF="r4857.html"
>comedi_open</A
>("/dev/comedi0");

  maxdata=<A
HREF="r5331.html"
>comedi_get_maxdata</A
>(cf,subdev,chan);

  rangetype=comedi_get_rangetype(cf,subdev,chan);

  <A
HREF="r5725.html"
>comedi_data_read</A
>(cf-&#62;fd,subdev,chan,range,aref,&amp;data);

  volts=<A
HREF="r5619.html"
>comedi_to_phys</A
>(data,rangetype,range,maxdata);

  printf("%d %g\n",data,volts);

 return 0;
}</PRE
></DIV
><DIV
CLASS="SECTION"
><H2
CLASS="SECTION"
><A
NAME="THIRDPROGRAM"
>3.5. Your third <ACRONYM
CLASS="ACRONYM"
>Comedi</ACRONYM
> program: instructions</A
></H2
><P
>This program (taken from the set of demonstration examples that come
with <ACRONYM
CLASS="ACRONYM"
>Comedi</ACRONYM
>) shows how to use a somewhat more flexible acquisition
function, the so-called <A
HREF="x621.html#INSTRUCTIONS"
>instruction</A
>.
<PRE
CLASS="PROGRAMLISTING"
>#include &#60;stdio.h&#62;
#include &#60;<A
HREF="x4629.html#COMEDI-COMEDILIB-H"
>comedilib.h</A
>&#62;
#include &#60;fcntl.h&#62;
#include &#60;unistd.h&#62;
#include &#60;errno.h&#62;
#include &#60;sys/time.h&#62;
#include &#60;unistd.h&#62;
#include "examples.h"

/*
 * This example does 3 instructions in one system call.  It does
 * a gettimeofday() call, then reads N_SAMPLES samples from an
 * analog input, and the another gettimeofday() call.
 */

#define MAX_SAMPLES 128

<A
HREF="x4629.html#REF-TYPE-COMEDI-T"
>comedi_t</A
> *device;

int main(int argc, char *argv[])
{
  int ret,i;
  <A
HREF="x4629.html#REF-TYPE-COMEDI-INSN"
>comedi_insn</A
> insn[3];
  <A
HREF="x4629.html#REF-TYPE-COMEDI-INSNLIST"
>comedi_insnlist</A
> il;
  struct timeval t1,t2;
  <A
HREF="x4629.html#REF-TYPE-LSAMPL-T"
>lsampl_t</A
> data[MAX_SAMPLES];

  parse_options(argc,argv);


  device=<A
HREF="r4857.html"
>comedi_open</A
>(filename);
  if(!device){
    <A
HREF="r4909.html"
>comedi_perror</A
>(filename);
    exit(0);
  }

  if(verbose){
    printf("measuring device=%s subdevice=%d channel=%d range=%d analog reference=%d\n",
      filename,subdevice,channel,range,aref);
  }

  /* Set up a the "instruction list", which is just a pointer
   * to the array of instructions and the number of instructions.
   */
  il.n_insns=3;
  il.insns=insn;

  /* Instruction 0: perform a gettimeofday() */
  insn[0].insn=<A
HREF="x4629.html#INSN-GTOD"
>INSN_GTOD</A
>;
  insn[0].n=2;
  insn[0].data=(void *)&amp;t1;

  /* Instruction 1: do 10 analog input reads */
  insn[1].insn=<A
HREF="x4629.html#INSN-READ"
>INSN_READ</A
>;
  insn[1].n=n_scan;
  insn[1].data=data;
  insn[1].subdev=subdevice;
  insn[1].chanspec=<A
HREF="x4629.html#REF-MACRO-CR-PACK"
>CR_PACK</A
>(channel,range,aref);

  /* Instruction 2: perform a gettimeofday() */
  insn[2].insn=<A
HREF="x4629.html#INSN-GTOD"
>INSN_GTOD</A
>;
  insn[2].n=2;
  insn[2].data=(void *)&amp;t2;

  ret=<A
HREF="r5527.html"
>comedi_do_insnlist</A
>(device,&amp;il);
  if(ret&#60;0){
    <A
HREF="r4909.html"
>comedi_perror</A
>(filename);
    exit(0);
  }

  printf("initial time: %ld.%06ld\n",t1.tv_sec,t1.tv_usec);
  for(i=0;i&#60;n_scan;i++){
    printf("%d\n",data[i]);
  }
  printf("final time: %ld.%06ld\n",t2.tv_sec,t2.tv_usec);

  printf("difference (us): %ld\n",(t2.tv_sec-t1.tv_sec)*1000000+
      (t2.tv_usec-t1.tv_usec));

  return 0;
}</PRE
></P
></DIV
><DIV
CLASS="SECTION"
><H2
CLASS="SECTION"
><A
NAME="FOURTHPROGRAM"
>3.6. Your fourth <ACRONYM
CLASS="ACRONYM"
>Comedi</ACRONYM
> program: commands</A
></H2
><P
>This example programs an analog output subdevice with <ACRONYM
CLASS="ACRONYM"
>Comedi</ACRONYM
>'s most
powerful acquisition function, the asynchronous
<A
HREF="x621.html#COMMANDSSTREAMING"
>command</A
>, to generate a waveform.</P
><P
>The waveform in this example is a sine wave, but this can be easily
changed to make a generic function generator.</P
><P
>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.</P
><P
>Once you have
issued the command, <ACRONYM
CLASS="ACRONYM"
>Comedi</ACRONYM
> expects you to keep the buffer full of
data to output to the acquisition card.  This is done by
<CODE
CLASS="FUNCTION"
>write()</CODE
>.  Since there may be a delay between the
<A
HREF="r6164.html"
>comedi_command()</A
>
and a subsequent <CODE
CLASS="FUNCTION"
>write()</CODE
>, you
should fill the buffer using <CODE
CLASS="FUNCTION"
>write()</CODE
> before you call
<A
HREF="r6164.html"
>comedi_command()</A
>,
as is done here.
<PRE
CLASS="PROGRAMLISTING"
>#include &#60;stdio.h&#62;
#include &#60;<A
HREF="x4629.html#COMEDI-COMEDILIB-H"
>comedilib.h</A
>&#62;
#include &#60;fcntl.h&#62;
#include &#60;stdlib.h&#62;
#include &#60;unistd.h&#62;
#include &#60;errno.h&#62;
#include &#60;getopt.h&#62;
#include &#60;ctype.h&#62;
#include &#60;math.h&#62;
#include "examples.h"

double waveform_frequency = 10.0; /* frequency of the sine wave to output */
double amplitude          = 4000; /* peak-to-peak amplitude, in DAC units (i.e., 0-4095) */
double offset             = 2048; /* offset, in DAC units */

/* 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

int subdevice;
int external_trigger_number = 0;

sampl_t data[BUF_LEN];

void <A
HREF="x403.html#DDS-OUTPUT"
>dds_output</A
>(sampl_t *buf,int n);
void <A
HREF="x403.html#DDS-INIT"
>dds_init</A
>(void);

/* This define determines which waveform to use. */
#define <A
NAME="DDS-INIT-FUNCTION"
></A
>dds_init_function <A
HREF="x403.html#DDS-INIT-SINE"
>dds_init_sine</A
>

void <A
HREF="x403.html#DDS-INIT-SINE"
>dds_init_sine</A
>(void);
void <A
HREF="x403.html#DDS-INIT-PSEUDOCYCLOID"
>dds_init_pseudocycloid</A
>(void);
void <A
HREF="x403.html#DDS-INIT-SAWTOOTH"
>dds_init_sawtooth</A
>(void);

int <A
NAME="COMEDI-INTERNAL-TRIGGER"
></A
>comedi_internal_trigger(<A
HREF="x4629.html#REF-TYPE-COMEDI-T"
>comedi_t</A
> *dev, unsigned int subd, unsigned int trignum)
{
  <A
HREF="x4629.html#REF-TYPE-COMEDI-INSN"
>comedi_insn</A
> insn;
  <A
HREF="x4629.html#REF-TYPE-LSAMPL-T"
>lsampl_t</A
> data[1];

  memset(&#38;insn, 0, sizeof(<A
HREF="x4629.html#REF-TYPE-COMEDI-INSN"
>comedi_insn</A
>));
  insn.insn = <A
HREF="x621.html#INSN-INTTRIG"
>INSN_INTTRIG</A
>;
  insn.subdev = subd;
  insn.data = data;
  insn.n = 1;

  data[0] = trignum;

  return <A
HREF="r5553.html"
>comedi_do_insn</A
>(dev, &#38;insn);
}


int main(int argc, char *argv[])
{
  <A
HREF="x4629.html#REF-TYPE-COMEDI-CMD"
>comedi_cmd</A
> cmd;
  int err;
  int n,m;
  int total=0;
  <A
HREF="x4629.html#REF-TYPE-COMEDI-T"
>comedi_t</A
> *dev;
  unsigned int chanlist[16];
  unsigned int maxdata;
  <A
HREF="x4629.html#REF-TYPE-COMEDI-RANGE"
>comedi_range</A
> *rng;
  int ret;
  <A
HREF="x4629.html#REF-TYPE-LSAMPL-T"
>lsampl_t</A
> insn_data = 0;

  parse_options(argc,argv);

  /* Force n_chan to be 1 */
  n_chan = 2;

  if(value){ waveform_frequency = value; }

  dev = <A
HREF="r4857.html"
>comedi_open</A
>(filename);
  if(dev == NULL){
    fprintf(stderr, "error opening %s\n", filename);
    return -1;
  }
  subdevice = <A
HREF="r5092.html"
>comedi_find_subdevice_by_type</A
>(dev,COMEDI_SUBD_AO,0);

  maxdata   = <A
HREF="r5331.html"
>comedi_get_maxdata</A
>(dev,subdevice,0);
  rng       = <A
HREF="r5383.html"
>comedi_get_range</A
>(dev,subdevice,0,0);
  offset    = (double)<A
HREF="r5673.html"
>comedi_from_phys</A
>(0.0,rng,maxdata);
  amplitude = (double)<A
HREF="r5673.html"
>comedi_from_phys</A
>(1.0,rng,maxdata) - offset;

  memset(&#38;cmd,0,sizeof(cmd));
  /* fill in the <A
HREF="x4629.html#REF-TYPE-COMEDI-CMD"
>command data structure</A
>: */
  cmd.subdev         = subdevice;
  cmd.flags          = 0;
  cmd.start_src      = <A
HREF="x621.html#TRIG-INT-START-SRC"
>TRIG_INT</A
>;
  cmd.start_arg      = 0;
  cmd.scan_begin_src = <A
HREF="x621.html#TRIG-TIMER"
>TRIG_TIMER</A
>;
  cmd.scan_begin_arg = 1e9/freq;
  cmd.convert_src    = <A
HREF="x621.html#TRIG-NOW"
>TRIG_NOW</A
>;
  cmd.convert_arg    = 0;
  cmd.scan_end_src   = <A
HREF="x621.html#TRIG-COUNT"
>TRIG_COUNT</A
>;
  cmd.scan_end_arg   = n_chan;
  cmd.stop_src       = <A
HREF="x621.html#TRIG-NONE"
>TRIG_NONE</A
>;
  cmd.stop_arg       = 0;

  cmd.chanlist       = chanlist;
  cmd.chanlist_len   = n_chan;

  chanlist[0] = <A
HREF="x4629.html#REF-MACRO-CR-PACK"
>CR_PACK</A
>(channel,range,aref);
  chanlist[1] = <A
HREF="x4629.html#REF-MACRO-CR-PACK"
>CR_PACK</A
>(channel+1,range,aref);

  <A
HREF="x403.html#DDS-INIT"
>dds_init</A
>();

  <A
HREF="x403.html#DDS-OUTPUT"
>dds_output</A
>(data,BUF_LEN);
  <A
HREF="x403.html#DDS-OUTPUT"
>dds_output</A
>(data,BUF_LEN);

  dump_cmd(stdout,&#38;cmd);

  if ((err = <A
HREF="r6164.html"
>comedi_command</A
>(dev, &#38;cmd)) &#60; 0) {
    <A
HREF="r4909.html"
>comedi_perror</A
>("comedi_command");
    exit(1);
  }

  m=write(comedi_fileno(dev),data,BUF_LEN*sizeof(sampl_t));
  if(m&#60;0){
    perror("write");
    exit(1);
  }
  printf("m=%d\n",m);

  ret = <A
HREF="x403.html#COMEDI-INTERNAL-TRIGGER"
>comedi_internal_trigger</A
>(dev, subdevice, 0);
  if(ret&#60;0){
    perror("comedi_internal_trigger\n");
    exit(1);
  }

  while(1){
    <A
HREF="x403.html#DDS-OUTPUT"
>dds_output</A
>(data,BUF_LEN);
    n=BUF_LEN*sizeof(sampl_t);
    while(n&#62;0){
      m=write(comedi_fileno(dev),(void *)data+(BUF_LEN*sizeof(sampl_t)-n),n);
      if(m&#60;0){
        perror("write");
        exit(0);
      }
      printf("m=%d\n",m);
      n-=m;
    }
    total+=BUF_LEN;
  }

  return 0;
}

#define WAVEFORM_SHIFT 16
#define WAVEFORM_LEN (1&#60;&#60;WAVEFORM_SHIFT)
#define WAVEFORM_MASK (WAVEFORM_LEN-1)

sampl_t waveform[WAVEFORM_LEN];

unsigned int acc;
unsigned int adder;

void <A
NAME="DDS-INIT"
></A
>dds_init(void)
{
  adder=waveform_frequency/freq*(1&#60;&#60;16)*(1&#60;&#60;WAVEFORM_SHIFT);

  <A
HREF="x403.html#DDS-INIT-FUNCTION"
>dds_init_function</A
>();
}

void <A
NAME="DDS-OUTPUT"
></A
>dds_output(sampl_t *buf,int n)
{
  int i;
  sampl_t *p=buf;

  
  for(i=0;i&#60;n;i++){
  *p=waveform[(acc&#62;&#62;16)&#38;WAVEFORM_MASK];
  
  p++;
  acc+=adder;
  }
}


void <A
NAME="DDS-INIT-SINE"
></A
>dds_init_sine(void)
{
  int i;

  
  for(i=0;i&#60;WAVEFORM_LEN;i++){
  waveform[i]=rint(offset+0.5*amplitude*cos(i*2*M_PI/WAVEFORM_LEN));
  
  }
}

/* Yes, I know this is not the proper equation for a cycloid.  Fix it. */
void <A
NAME="DDS-INIT-PSEUDOCYCLOID"
></A
>dds_init_pseudocycloid(void)
{
  int i;
  double t;

  
  for(i=0;i&#60;WAVEFORM_LEN/2;i++){
  t=2*((double)i)/WAVEFORM_LEN;
  waveform[i]=rint(offset+amplitude*sqrt(1-4*t*t));
  }
  for(i=WAVEFORM_LEN/2;i&#60;WAVEFORM_LEN;i++){
  t=2*(1-((double)i)/WAVEFORM_LEN);
  waveform[i]=rint(offset+amplitude*sqrt(1-t*t));
  }
  
}

void <A
NAME="DDS-INIT-SAWTOOTH"
></A
>dds_init_sawtooth(void)
{
  int i;

  
  for(i=0;i&#60;WAVEFORM_LEN;i++){
  waveform[i]=rint(offset+amplitude*((double)i)/WAVEFORM_LEN);
  
  }
}</PRE
></P
></DIV
></DIV
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
SUMMARY="Footer navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="x333.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="index.html"
ACCESSKEY="H"
>Home</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="x621.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>Configuration</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
>&nbsp;</TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>Acquisition and configuration functions</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>