comedilib/doc/tutorial.xml
Ian Abbott 6373588120 doc: include the full tut3.c program listing
Include the full tut3.c program listing in the document as it's not much
bigger than tut3_part.c and it's a pain maintaining both of them.
2016-05-13 17:55:39 +01:00

254 lines
10 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
<!ENTITY % comedilib_entities SYSTEM "comedilib.ent">
%comedilib_entities;
]>
<section id="writingprograms" xmlns:xi="http://www.w3.org/2001/XInclude">
<title>
Writing &comedi; programs
</title>
<para>
This section describes how &comedi;
can be used in an application, to communicate data with a set
of &comedi; devices.
<xref linkend="acquisitionfunctions"/> gives more details about
the various acquisition functions with which the application
programmer can perform data acquisition in &comedi;.
</para>
<para>
Also don't forget to take a good look at the
<filename class="directory">demo</filename>
directory of the Comedilib source code. It contains lots of examples
for the basic functionalities of &comedi;.
</para>
<section id="firstprogram">
<title>
Your first &comedi; program
</title>
<para>
This example requires a card that has analog or digital input. This
progam opens the device, gets the data, and prints it out:
<programlisting>
<xi:include href="../demo/tut1.c" parse="text"/>
</programlisting>
</para>
<para>
The source code file for the above program can be found in Comedilib,
at <filename>demo/tut1.c</filename>. You can compile the program using
</para>
<screen>
cc tut1.c -lcomedi -lm -o tut1
</screen>
<para>
The
<function>
<link linkend="func-ref-comedi-open">comedi_open</link>
</function> call can only be successful if the
<filename>comedi0</filename> device file is configured with a
valid &comedi; driver. <xref linkend="cardconfiguration"/> explains
how this driver is linked to the <quote>device file</quote>.
</para>
<para>
The <parameter class="function">range</parameter> 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 <literal>0</literal>, because it won't cause errors. Likewise
with <parameter class="function">aref</parameter>, which determines the
analog reference used.
</para>
</section>
<section id="convertingsamples">
<title>
Converting between integer data and physical units
</title>
<para>
If you selected an analog input subdevice, you probably noticed
that the output of <command>tut1</command> is an unsigned
number, for example between <literal>0</literal>
and <literal>65535</literal> for a 16 bit analog input. &comedi;
samples are unsigned, with <literal>0</literal> representing the
lowest voltage of the ADC, and a hardware-dependent maximum
value representing the highest voltage. &comedi; compensates
for anything else the manual for your device says (for example,
many boards represent bipolar analog input voltages as signed
integers). However, you probably prefer to have this number
translated to a voltage. Naturally, as a good programmer, your
first question is: <quote>How do I do this in a
device-independent manner?</quote>
</para>
<para>
The functions
<function><link linkend="func-ref-comedi-to-physical">comedi_to_physical</link></function>, <function><link linkend="func-ref-comedi-to-phys">comedi_to_phys</link></function>,
<function><link linkend="func-ref-comedi-from-physical">comedi_from_physical</link></function> and <function><link linkend="func-ref-comedi-from-phys">comedi_from_phys</link></function>
are used to convert between &comedi;'s integer data and floating point numbers corresponding
to physical values (voltages, etc.).
</para>
</section>
<section id="secondprogram">
<title>
Your second &comedi; program
</title>
<para>
Actually, this is the first &comedi; program again, except
we've added code to convert the integer data value to physical units.
</para>
<programlisting>
<xi:include href="../demo/tut2.c" parse="text"/>
</programlisting>
<para>
The source code file for the above program can be found in
the Comedilib source at <filename>demo/tut2.c</filename> and if
installed as a package usually at
<filename>/usr/share/doc/libcomedi-dev/demo/</filename> with all the
other tutorial/demo files.
</para>
</section>
<section id="asyncprogram">
<title>
Asynchronous acquisition
</title>
<para>
Of special importance is the so called
"asynchronous data acquisition" where &comedi; is sampling
in the background at a given sample rate. The
user can retrieve the data whenever it is convenient.
&comedi; stores the data in a ring-buffer so that
programs can perform other tasks in the foreground, for example
plotting data or interacting with the user.
This technique is used in programs such
as <command>ktimetrace</command> or <command>comedirecord</command>.
</para>
<para>
There are two different ways how a sequence of channels is
measured during asynchronous acquisition (see also the Figure in
the introduction):
<itemizedlist>
<listitem>
The channels are measured with the help
of a multiplexer which switches to the next channel after each measurement.
This means that the sampling rate is divided by the number
of channels.
</listitem>
<listitem>
The channels are all measured at the same time, for example
when every channel has its own converter. In this case the
sampling rate need not to be divided by the number of channels.
</listitem>
</itemizedlist>
How your &comedi; device handles the asynchronous acquisition can be found out
with the command <command>comedi_board_info -v</command>.
</para>
<para>
The program <filename>demo/tut3.c</filename> demonstrates the
asynchronous acquisition. The general strategy is always
the same: first, we tell &comedi; all sampling parameters such as
the sampling rate,
the number of channels and anything it needs to know
so that it can run independently in the background.
Then &comedi; checks our request and it might
modify it. For example we might want to have a sampling rate of
16kHz but we only get 1kHz. Finally we can start
the asynchronous acquisition. Once it has been started we
need to check periodically if data is available and
request it from &comedi; so that its internal buffer
won't overrun.
</para>
<para>
In summary the asynchonous acquisition is performed in the following
way:
</para>
<itemizedlist>
<listitem>
Create a command structure of type <type><link linkend="ref-type-comedi-cmd">comedi_cmd</link></type>
</listitem>
<listitem>
Call the function <function><link linkend="func-ref-comedi-get-cmd-generic-timed">comedi_get_cmd_generic_timed</link></function>
to fill the command structure with your comedi device,
subdevice, sampling rate and number of channels.
</listitem>
<listitem>
Create a channel-list and store it in the command structure. This
tells comedi which channels should be sampled in the background.
</listitem>
<listitem>
Call <function><link linkend="func-ref-comedi-command-test">comedi_command_test</link></function> with your command structure. Comedi might modify your requested sampling rate and channels.
</listitem>
<listitem>
Call <function><link linkend="func-ref-comedi-command-test">comedi_command_test</link></function> again which now should return zero for success.
</listitem>
<listitem>
Call <function><link linkend="func-ref-comedi-command">comedi_command</link></function> to start the asynchronous acquisition. From now on the kernel ringbuffer will be filled at the specified sampling rate.
</listitem>
<listitem>
Call periodically the standard
function <function>read</function> and receive the data. The
result should always be non zero as long as the acquisition
is running.
</listitem>
<listitem>
Convert the received data either into
<type><link linkend="ref-type-lsampl-t">lsampl_t</link></type> or
<type><link linkend="ref-type-sampl-t">sampl_t</link></type> depending
on the subdevice flag <constant>SDF_LSAMPL</constant>.
</listitem>
<listitem>
Poll for data with <function>read</function> as long as it returns
a positive result or until the program terminates.
</listitem>
</itemizedlist>
<para>
The program below is a stripped down version of the
program <filename>cmd.c</filename> in the demo directory.
It requests data from two channels at
a sampling rate of 1kHz and a total of 10000 samples.
which are then printed to stdout. You can pipe the data
into a file and plot it with gnuplot. As mentioned above, central in this
program is the loop using the standard C <function>read</function> command
which receives the buffer contents.
</para>
<programlisting>
<xi:include href="../demo/tut3.c" parse="text"/>
</programlisting>
<para>
The source code file for the above program can be found in Comedilib,
at <filename>demo/tut3.c</filename>. You can compile the program using
</para>
<screen>
cc tut3.c -lcomedi -lm -o tut3
</screen>
<para>
For advanced programmers the
function <function><link linkend="func-ref-comedi-get-buffer-contents">comedi_get_buffer_contents</link></function>
is useful to check if there is actually data in the ringbuffer
so that a call of <function>read</function> can be avoided for example
when the data readout is called by a timer call-back function.
</para>
</section>
<section>
<title>Further examples</title>
<para>
See the <filename>demo</filename> subdirectory of Comedilib
for more example programs. The directory contains a
<filename>README</filename> file with descriptions of the various
demo programs.
</para>
</section>
</section>