<!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 <stdio.h> /* for printf() */ #include <<A HREF="x4629.html#COMEDI-COMEDILIB-H" >comedilib.h</A >> 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, & 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 > </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 <stdio.h> /* for printf() */ #include <<A HREF="x4629.html#COMEDI-COMEDILIB-H" >comedilib.h</A >> 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->fd,subdev,chan,range,aref,&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 <stdio.h> #include <<A HREF="x4629.html#COMEDI-COMEDILIB-H" >comedilib.h</A >> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <sys/time.h> #include <unistd.h> #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 *)&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 *)&t2; ret=<A HREF="r5527.html" >comedi_do_insnlist</A >(device,&il); if(ret<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<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 <stdio.h> #include <<A HREF="x4629.html#COMEDI-COMEDILIB-H" >comedilib.h</A >> #include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <getopt.h> #include <ctype.h> #include <math.h> #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(&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, &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(&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,&cmd); if ((err = <A HREF="r6164.html" >comedi_command</A >(dev, &cmd)) < 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<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<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>0){ m=write(comedi_fileno(dev),(void *)data+(BUF_LEN*sizeof(sampl_t)-n),n); if(m<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<<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<<16)*(1<<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<n;i++){ *p=waveform[(acc>>16)&WAVEFORM_MASK]; p++; acc+=adder; } } void <A NAME="DDS-INIT-SINE" ></A >dds_init_sine(void) { int i; for(i=0;i<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<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<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<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" > </TD ><TD WIDTH="33%" ALIGN="right" VALIGN="top" >Acquisition and configuration functions</TD ></TR ></TABLE ></DIV ></BODY ></HTML >