[previous]
[index]
[next]
Example 4: FIFO Communication Between RTL and Linux
This example shows how to set up a first-in, first out (FIFO) data
queue for sharing data between
real-time tasks and user-level applications. FIFOs are often used to
connect a non-realtime human-machine interface to a real-time
application, or to log messages to disk files. These FIFOs are
analogous to normal Unix FIFOs, i.e., those that use a fifo special
file created with the Unix command 'mkfifo'.
FIFOs are simple queues
of raw bytes. Typically, fixed-sized data structures are written to a
FIFO, so that the reader doesn't need to worry about message
boundaries. In some cases variable-sized messages are written, in
which case the reader must search for the message boundary, often a
special character. In this example we used fixed-size messages.
Refer to the commented real-time
source code and the commented application
source code for the details.
Principle of Operation
- The RT task creates
two FIFOs, one for commands in from the user process and one for
status back to the user process
- There
are three commands: turn the speaker on, turn it off, and set the
frequency
- The status contains an echo of the command number, the frequency,
and a heartbeat that increments each task cycle.
-
The periodic task just toggles the speaker port and increments
the heartbeat.
- Most of the work is done by a "handler" task that
is called when the command FIFO is written by the user process.
- In the handler, we enable/disable the speaker using a shared global
variable, and reset the period of the speaker task if requested.
- The handler also echoes the command and the heartbeat back to
the user process via the status FIFO.
- The user task prints a prompt and reads typed input signifying
requests to turn the speaker on, off or set the frequency. When a
command is sent, the status FIFO is read and the contents printed.
Creating a FIFO
- On the real-time side, a FIFO is created by calling
int rtf_create (unsigned int fifo, int size);
'rtf_create()' creates FIFO number 'fifo', of initial size
'size' bytes. 'fifo' is an
integer, 0 to RTF_NO (currently 63), that identifies the fifo on further
operations. 'fifo' may refer an existing FIFO. In this case the
size is adjusted if necessary.
- On the Linux side, FIFO numbers 0 to 63 are associated with
character devices /dev/rtf0 through /dev/rtf63. These device files are
created when you install RTAI, so they are always visible. To "create"
a FIFO on the Linux side, use the standard Unix function 'open()', e.g.,
file_descriptor = open("/dev/rtf0", O_RDONLY);
The integer 'file_descriptor' is returned to you, and used to identify the
FIFO during subsequent read or write operations. 'O_RDONLY' is a
constant signifying that you'll only read the FIFO. Other
possibilities are O_WRONLY and O_RDWR, for write-only and read-write,
respectively. These are from standard Unix.
Accessing a FIFO
- On the real-time side, reading and writing are done using the
'rtf_get()' and 'rtf_put()' functions, e.g.,
num_read = rtf_get(0, &buffer_in, sizeof(buffer_in));
num_written = rtf_put(1, &buffer_out, sizeof(buffer_out));
You can check the return values for the actual number of bytes read or
written, where negative numbers signify an error.
- On the Linux side, reading and writing are done using the standard
Unix 'read()' and 'write()' functions, e.g.,
num_read = read(read_descriptor, &buffer_in, sizeof(buffer_in));
num_written = write(write_descriptor, &buffer_out,
sizeof(buffer_out));
where 'read_descriptor' and 'write_descriptor' are the file
descriptors returned to you on the previous call to 'open()'. Negative
return values signify an error.
FIFO Handlers
- One optional feature of FIFOs is the "handler," a function on the
real-time side that is called whenever a FIFO is read or written. To
install a handler, call 'rtf_create_handler()' with the FIFO number
and the name of the handler function.
- A handler is often used in conjunction with 'rtf_get()' to process
data acquired asynchronously from a Linux process. The installed
handler calls 'rtf_get()' when data is present. Because the handler is
only executed when there is activity on the FIFO, polling is not
necessary.
Blocking v. Non-Blocking Reads
- In Unix, devices can be opened for either blocking- or
non-blocking reads.
- With a blocking read, the 'read()' function does not return if
there is no data to be read. The calling process goes to sleep
("blocks") until
the 'read()' function returns later when some data is available. This
is the default behavior.
- In some situations this blocking behavior is not desirable, and
the device can be configured so that reads are non-blocking. In this
case, 'read()' returns 0 immediately if no characters are available.
- In a real-time application, blocking is typically not desired.
'rtf_get()' returns immediately with a 0 return value if no characters
are available.
FIFO Uses
FIFOs can be used for all communication between real-time tasks and
Linux processes, but their queued nature makes them best suited for
ordered streams of data, such as
- messages or error diagnostics, where the message can include a
timestamp for later analysis;
- data logs for performance metrics;
- or transfer of configuration information at startup, e.g., the
contents of initialization files.
FIFOs are not well suited for repeatedly transferring large data
structures whose contents only change sparsely. Shared memory is a
better solution.
Running the Demo
To run the demo, change to the 'ex04_fifo' subdirectory of the
top-level tutorial directory, and run the 'run' script by typing
./run
Alternatively, change to the top-level tutorial directory and run the
'runall' script there by typing
./runall
and selecting the "FIFOs" button.
You'll hear a continuously varying tone for about 10 seconds.
See the Real-Time Task Code
See the Linux Application Code
Next: Example 5, Interrupt Service Routines
Back: Example 3, Variable-Period Scheduling of a Single Task