comedilib/doc/html/comedilib-4.html
2000-02-02 05:14:23 +00:00

251 lines
7.4 KiB
HTML

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<HTML>
<HEAD>
<META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9">
<TITLE>Comedi Documentation: Writing programs that use comedi and comedilib</TITLE>
<LINK HREF="comedilib-5.html" REL=next>
<LINK HREF="comedilib-3.html" REL=previous>
<LINK HREF="comedilib.html#toc4" REL=contents>
</HEAD>
<BODY>
<A HREF="comedilib-5.html">Next</A>
<A HREF="comedilib-3.html">Previous</A>
<A HREF="comedilib.html#toc4">Contents</A>
<HR>
<H2><A NAME="s4">4. Writing programs that use comedi and comedilib</A></H2>
<P>
<P>
<H2><A NAME="ss4.1">4.1 Your first comedi program</A>
</H2>
<P>
<P>This example requires a card that has analog or
digital input. Right to the source:
<P>
<BLOCKQUOTE><CODE>
<PRE>
#include &lt;stdio.h> /* for printf() */
#include &lt;comedilib.h>
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 = AREF_GROUND; /* more on this later */
int main(int argc,char *argv[])
{
comedi_t *it;
int chan=0;
lsampl_t data;
it=comedi_open("/dev/comedi0");
comedi_data_read(it,subdev,chan,range,aref,&amp; data);
printf("%d\n",data);
return 0;
}
</PRE>
</CODE></BLOCKQUOTE>
<P>
<P>Should be understandable. Open the device, get the data,
print it out. This is basically the guts of <CODE>demo/inp.c</CODE>,
without error checking or fancy options. Including all
the appropriate headers is sometimes a little tricky.
Compile it using
<P>
<BLOCKQUOTE><CODE>
<PRE>
cc tut1.c -lcomedi -o tut1
</PRE>
</CODE></BLOCKQUOTE>
<P>Hopefully it works.
<P>A few notes: The range variable tells comedi 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 0, because it won't cause errors. Likewise with
aref, which determines the analog reference used.
<P>
<P>
<P>
<H2><A NAME="ss4.2">4.2 Converting samples to voltages</A>
</H2>
<P>
<P>If you selected an analog input subdevice, you should notice
that the output of <CODE>tut1</CODE> is a number between
0 and 4095, or 0 and 65535, depending on the number of bits
in the A/D converter. Comedi samples are <B>always</B> unsigned,
with 0 representing the lowest voltage of the ADC, and 4095
the highest. Comedi 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: "How do I do this in a device-independent
manner?"
<P>For each subdevice, the comedi kernel module keeps a
'range_type' variable. This variable contains the number
of available ranges (i.e., gains) that you can select,
along with an offset in a list of range information
structures. If you know the range_type variable, you
can use these macros:
<P>RANGE_OFFSET(range_type)
RANGE_LENGTH(range_type)
<P>to extract such information. However, you want the
actual voltage information, not some integer offset
in a table. Rather than messing with the library
internals, use the function
<P>ptr=comedi_get_range(comedi_file,subdevice,channel,
range)
<P>which returns a pointer to a comedi_range structure.
The comedi_range structure looks like
<P>
<P>
<BLOCKQUOTE><CODE>
<PRE>
typedef struct{
double min;
double max;
unsigned int unit;
}comedi_range;
</PRE>
</CODE></BLOCKQUOTE>
<P>As you might expect, ptr[range] is for range 'range',
which you provided to comedi_data_read() above. 'min' represents
the voltage corresponding to comedi_data_read() returning 0,
and 'max' represents comedi_data_read() returning 'maxdata',
(i.e., 4095 for 12 bit A/C converters, 65535 for 16 bit,
or, 1 for digital input -- more on this in a bit.) The
'unit' entry tells you if min and
max refer to voltage, current, etc.
<P>"Could it get easier?", you say. Well, yes. Use
the function comedi_to_phys(), which converts data
values to physical units. Call it using something like
<P>
<BLOCKQUOTE><CODE>
<PRE>
volts=comedi_to_phys(it,data,range,maxdata);
</PRE>
</CODE></BLOCKQUOTE>
<P>and the opposite
<P>
<BLOCKQUOTE><CODE>
<PRE>
data=comedi_from_phys(it,volts,range,maxdata);
</PRE>
</CODE></BLOCKQUOTE>
<P>You probably noticed (and were worried) that we haven't
discussed how to determine maxdata and range_type. Well,
you could ask the kernel this information each time you need
it, but since there are other variables, special cases,
and several subdevices to worry about, it would be nice
if the library could take care of this... (read on...)
<P>
<P>
<P>
<H2><A NAME="ss4.3">4.3 Another section</A>
</H2>
<P>
<P>
<P>In addition to providing low level routines for data
access, the comedi library provides higher-level access,
much like the standard C library provides fopen(), etc.
as a high-level (and portable) alternative to the direct
UNIX system calls open(), etc. Similarily to fopen(),
we have comedi_open():
<P>
<P>
<BLOCKQUOTE><CODE>
<PRE>
file=comedi_open("/dev/comedi0");
</PRE>
</CODE></BLOCKQUOTE>
<P>where file is of type <CODE>(comedi_t *)</CODE>. This function
calls <CODE>open()</CODE>, like we did explicitly in a previous
section, but also fills the <CODE>comedi_t</CODE> structure with
lots of goodies -- information that we will need to use
soon.
<P>Specifically, we needed to know maxdata for a specific
subdevice/channel. How about:
<P>
<BLOCKQUOTE><CODE>
<PRE>
maxdata=comedi_get_maxdata(file,subdevice,channel);
</PRE>
</CODE></BLOCKQUOTE>
<P>Wow. How easy. And the range type?
<P>
<BLOCKQUOTE><CODE>
<PRE>
range_type=comedi_get_rangetype(file,subdevice,channel);
</PRE>
</CODE></BLOCKQUOTE>
<P>Cool. Other information you need to know about a channel
can be gotten in a similar way.
<P>
<P>
<P>
<H2><A NAME="ss4.4">4.4 Your second comedi program</A>
</H2>
<P>
<P>
<P>Actually, this is the first comedi program again, just
that we've added what we've learned.
<P>
<P>
<BLOCKQUOTE><CODE>
<PRE>
#include &lt;stdio.h> /* for printf() */
#include &lt;comedi.h> /* also included by comedilib.h */
#include &lt;comedilib.h> /* for comedi_get() */
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[])
{
comedi_t *cf;
int chan=0;
int data;
int maxdata,rangetype;
double volts;
cf=comedi_open("/dev/comedi0");
maxdata=comedi_get_maxdata(cf,subdev,chan);
rangetype=comedi_get_rangetype(cf,subdev,chan);
data=comedi_get(cf->fd,subdev,chan,range,aref);
volts=comedi_to_phys(data,rangetype,range,maxdata);
printf("%d %g\n",data,volts);
return 0;
}
</PRE>
</CODE></BLOCKQUOTE>
<P>
<P>By now, the <CODE>comedi_read_data()</CODE> line looks a little archaic, using
the UNIX file descriptor cf->fd instead of just cf. (By the
way, somewhere in the heart of <CODE>comedi_open()</CODE> is the line
<CODE>cf->fd=open(filename,O_RDWR)</CODE>.) Well, there isn't one good
replacement, since it highly depends on your application
what additional features you might want in a <CODE>comedi_get()</CODE>
replacement. But this is the topic of a different section.
<P>
<P>
<P>
<HR>
<A HREF="comedilib-5.html">Next</A>
<A HREF="comedilib-3.html">Previous</A>
<A HREF="comedilib.html#toc4">Contents</A>
</BODY>
</HTML>