Removed examples; fixed undefines references; preliminary version.
This commit is contained in:
parent
9d60587c6b
commit
ed50f17cb8
|
|
@ -82,14 +82,13 @@
|
|||
|
||||
{\Huge\bf IgH \includegraphics[height=2.4ex]{images/ethercat}
|
||||
Master \masterversion\\[1ex]
|
||||
Documentation}
|
||||
Preliminary Documentation}
|
||||
|
||||
\vspace{1ex}
|
||||
\rule{\textwidth}{1.5mm}
|
||||
|
||||
\vspace{\fill}
|
||||
{\Large Florian Pose, \url{fp@igh-essen.com}\\[1ex]
|
||||
Ingenieurgemeinschaft \IgH}
|
||||
\vspace{\fill} {\Large Dipl.-Ing. (FH) Florian Pose,
|
||||
\url{fp@igh-essen.com}\\[1ex] Ingenieurgemeinschaft \IgH}
|
||||
|
||||
\vspace{\fill}
|
||||
{\Large Essen, \SVNDate\\[1ex]
|
||||
|
|
@ -102,7 +101,7 @@
|
|||
\tableofcontents
|
||||
\listoftables
|
||||
\listoffigures
|
||||
\lstlistoflistings
|
||||
%\lstlistoflistings
|
||||
|
||||
%------------------------------------------------------------------------------
|
||||
|
||||
|
|
@ -199,7 +198,7 @@ independent architecture.
|
|||
|
||||
\end{itemize}
|
||||
|
||||
\item Common ``realtime interface'' for applications, that want to use
|
||||
\item Common ``Application Interface'' for applications, that want to use
|
||||
EtherCAT functionality (see section~\ref{sec:ecrt}).
|
||||
|
||||
\item \textit{Domains} are introduced, to allow grouping of process
|
||||
|
|
@ -332,7 +331,7 @@ Figure~\ref{fig:arch} gives a general overview of the master architecture.
|
|||
|
||||
Kernel module containing one or more EtherCAT master instances (see
|
||||
section~\ref{sec:mastermod}), the ``Device Interface'' (see
|
||||
section~\ref{sec:ecdev}) and the ``Realtime Interface'' (see
|
||||
section~\ref{sec:ecdev}) and the ``Application Interface'' (see
|
||||
section~\ref{sec:ecrt}).
|
||||
|
||||
\paragraph{Device Modules}
|
||||
|
|
@ -352,11 +351,11 @@ connected to the kernel's network stack as usual.
|
|||
Kernel modules, that use the EtherCAT master (usually for cyclic exchange of
|
||||
process data with EtherCAT slaves). These modules are not part of the EtherCAT
|
||||
master code\footnote{Although there are some examples provided in the
|
||||
\textit{examples} directory, see chapter~\ref{chapter:examples}}, but have to
|
||||
be generated or written by the user. An application module can ``request'' a
|
||||
master through the realtime interface (see section~\ref{sec:ecrt}). If this
|
||||
succeeds, the module has the control over the master: It can provide a bus
|
||||
configuration and exchange process data.
|
||||
\textit{examples/} directory.}, but have to be generated or written by the
|
||||
user. An application module can ``request'' a master through the application
|
||||
interface (see section~\ref{sec:ecrt}). If this succeeds, the module has the
|
||||
control over the master: It can provide a bus configuration and exchange
|
||||
process data.
|
||||
|
||||
%------------------------------------------------------------------------------
|
||||
|
||||
|
|
@ -428,7 +427,7 @@ MAC addresses for multiple masters have to be separated by commas:
|
|||
|
||||
The two masters can be addressed by their indices 0 and 1 respectively (see
|
||||
figure~\ref{fig:masters}). The master index is needed for the
|
||||
\lstinline+ecrt_master_request()+ function of the realtime interface (see
|
||||
\lstinline+ecrt_master_request()+ function of the application interface (see
|
||||
section~\ref{sec:ecrt}) and the \lstinline+--master+ option of the
|
||||
\textit{ethercat} command-line tool (see section~\ref{sec:ethercat}), which
|
||||
defaults to $0$.
|
||||
|
|
@ -593,12 +592,17 @@ section~\ref{sec:cyclic}).
|
|||
|
||||
\end{description}
|
||||
|
||||
\paragraph{Example Applications} \index{Example Applications} There are a few
|
||||
example applications in the \textit{examples/} subdirectory of the master
|
||||
code. They are documented in the source code.
|
||||
|
||||
%------------------------------------------------------------------------------
|
||||
|
||||
\section{Master Configuration}
|
||||
\label{sec:masterconfig}
|
||||
|
||||
\ldots
|
||||
% FIXME Attaching
|
||||
|
||||
\begin{figure}[htbp]
|
||||
\centering
|
||||
|
|
@ -646,12 +650,13 @@ considers it to be awkward at the moment.
|
|||
\label{fig:locks}
|
||||
\end{figure}
|
||||
|
||||
Figure~\ref{fig:locks} exemplary shows, how two processes share one master: The
|
||||
application's cyclic task uses the master for process data exchange, while the
|
||||
master-internal EoE process uses it to communicate with EoE-capable slaves.
|
||||
Both have to acquire the master lock before access: The application task can
|
||||
access the lock natively, while the EoE process has to use the callbacks.
|
||||
Section~\ref{sec:concurrency} gives an example, of how to implement this.
|
||||
Figure~\ref{fig:locks} exemplary shows, how two processes share one master:
|
||||
The application's cyclic task uses the master for process data exchange, while
|
||||
the master-internal EoE process uses it to communicate with EoE-capable
|
||||
slaves. Both have to acquire the master lock before access: The application
|
||||
task can access the lock natively, while the EoE process has to use the
|
||||
callbacks. See the application interface documentation
|
||||
(section~\ref{sec:ecrt} of how to use the locking callbacks.
|
||||
|
||||
%------------------------------------------------------------------------------
|
||||
|
||||
|
|
@ -1441,8 +1446,8 @@ is skipped.
|
|||
\item[PREOP] The state change FSM is used to bring the slave to PREOP state.
|
||||
If this is the requested state, the state machine is finished.
|
||||
|
||||
\item[Sdo Configuration] If there is a slave configuration attached
|
||||
(see section~\ref{sec:attach}), and there are any Sdo configurations are
|
||||
\item[Sdo Configuration] If there is a slave configuration attached (see
|
||||
section~\ref{sec:masterconfig}), and there are any Sdo configurations are
|
||||
provided by the application, these are sent to the slave.
|
||||
|
||||
\item[Pdo Configuration] The Pdo configuration state machine is executed to
|
||||
|
|
@ -1577,7 +1582,7 @@ section~\ref{sec:coeimp}), so the slave must support the CoE mailbox protocol.
|
|||
\paragraph{Pdo Reading FSM} This state machine (fig.~\ref{fig:fsm-pdo-read})
|
||||
has the purpose to read the complete Pdo configuration of a slave. It reads
|
||||
the Pdo assignment for each Sync Manager and uses the Pdo Entry Reading FSM
|
||||
(fig.~\ref{fig:fsm_pdo_entry_read}) to read the mapping for each assigned Pdo.
|
||||
(fig.~\ref{fig:fsm-pdo-entry-read}) to read the mapping for each assigned Pdo.
|
||||
|
||||
\begin{figure}[htbp]
|
||||
\centering
|
||||
|
|
@ -1593,8 +1598,8 @@ the assigned Pdo's indices. When a Pdo index is read, the Pdo Entry Reading
|
|||
FSM is executed to read the Pdo's mapped Pdo entries.
|
||||
|
||||
\paragraph{Pdo Entry Reading FSM} This state machine
|
||||
(fig.~\ref{fig:fsm_pdo_entry_reading}) reads the Pdo mapping (the Pdo entries)
|
||||
of a Pdo. It reads the respective mapping Sdo (\lstinline+0x1600+ -
|
||||
(fig.~\ref{fig:fsm-pdo-entry-read}) reads the Pdo mapping (the Pdo entries) of
|
||||
a Pdo. It reads the respective mapping Sdo (\lstinline+0x1600+ -
|
||||
\lstinline+0x17ff+, or \lstinline+0x1a00+ - \lstinline+0x1bff+) for the given
|
||||
Pdo by reading first the subindex zero (number of elements) to determine the
|
||||
number of mapped Pdo entries. After that, each subindex is read to get the
|
||||
|
|
@ -1604,21 +1609,21 @@ mapped Pdo entry index, subindex and bit size.
|
|||
\centering
|
||||
\includegraphics[width=.4\textwidth]{graphs/fsm_pdo_entry_read}
|
||||
\caption{Transition Diagram of the Pdo Entry Reading State Machine}
|
||||
\label{fig:fsm-pdo-read}
|
||||
\label{fig:fsm-pdo-entry-read}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[htbp]
|
||||
\centering
|
||||
\includegraphics[width=.9\textwidth]{graphs/fsm_pdo_conf}
|
||||
\caption{Transition Diagram of the Pdo Configuration State Machine}
|
||||
\label{fig:fsm-pdo-read}
|
||||
\label{fig:fsm-pdo-conf}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[htbp]
|
||||
\centering
|
||||
\includegraphics[width=.4\textwidth]{graphs/fsm_pdo_entry_conf}
|
||||
\caption{Transition Diagram of the Pdo Entry Configuration State Machine}
|
||||
\label{fig:fsm-pdo-read}
|
||||
\label{fig:fsm-pdo-entry-conf}
|
||||
\end{figure}
|
||||
|
||||
%------------------------------------------------------------------------------
|
||||
|
|
@ -1780,7 +1785,6 @@ execute the EoE state machines. For that, the master owns a kernel timer, that
|
|||
is executed each timer interrupt. This guarantees a constant bandwidth, but
|
||||
poses the new problem of concurrent access to the master. The locking
|
||||
mechanisms needed for this are introduced in section~\ref{sec:concurr}.
|
||||
Section~\ref{sec:concurrency} gives practical implementation examples.
|
||||
|
||||
\paragraph{Automatic Configuration}
|
||||
|
||||
|
|
@ -1801,7 +1805,7 @@ configure slaves and exchange data objects on application level.
|
|||
%
|
||||
% Download / Upload
|
||||
% Expedited / Normal
|
||||
% Segmentung
|
||||
% Segmenting
|
||||
% Sdo Info Services
|
||||
%
|
||||
|
||||
|
|
@ -1916,6 +1920,7 @@ master.
|
|||
%------------------------------------------------------------------------------
|
||||
|
||||
\subsection{Setting Alias Addresses}
|
||||
\label{sec:alias} % FIXME
|
||||
|
||||
\lstinputlisting[basicstyle=\ttfamily\footnotesize]{external/ethercat_alias}
|
||||
|
||||
|
|
@ -2069,9 +2074,9 @@ The EtherCAT master init script conforms to the requirements of the ``Linux
|
|||
Standard Base'' (LSB\index{LSB}, \cite{lsb}). The script is installed to
|
||||
\textit{etc/init.d/ethercat} below the installation prefix and has to be
|
||||
copied (or better: linked) to the appropriate location (see
|
||||
section~\ref{sec:install}), before the master can be inserted as a service.
|
||||
Please note, that the init script depends on the sysconfig file described
|
||||
below.
|
||||
section~\ref{sec:installation}), before the master can be inserted as a
|
||||
service. Please note, that the init script depends on the sysconfig file
|
||||
described below.
|
||||
|
||||
To provide service dependencies (i.~e. which services have to be started before
|
||||
others) inside the init script code, LSB defines a special comment block.
|
||||
|
|
@ -2156,10 +2161,11 @@ are rare, there are a few aspects that can (and should be) dealt with.
|
|||
|
||||
\subsection{Application Interface Profiling}
|
||||
\label{sec:timing-profile}
|
||||
\index{Realtime!Profiling}
|
||||
\index{Profiling}
|
||||
% FIXME
|
||||
|
||||
One of the most important timing aspects are the execution times of the
|
||||
realtime interface functions, that are called in cyclic context. These
|
||||
application interface functions, that are called in cyclic context. These
|
||||
functions make up an important part of the overall timing of the application.
|
||||
To measure the timing of the functions, the following code was used:
|
||||
|
||||
|
|
@ -2187,7 +2193,7 @@ results were averaged. These can be seen in table~\ref{tab:profile}.
|
|||
|
||||
\begin{table}[htpb]
|
||||
\centering
|
||||
\caption{Profiling of a Realtime Cycle on a \unit{2.0}{\giga\hertz}
|
||||
\caption{Profiling of an Application Cycle on a \unit{2.0}{\giga\hertz}
|
||||
Processor}
|
||||
\label{tab:profile}
|
||||
\vspace{2mm}
|
||||
|
|
@ -2256,11 +2262,11 @@ reason is, that even with interrupts enabled, the time from the event
|
|||
to the notification is unknown. Therefore the only way to confidently
|
||||
determine the bus cycle time is an electrical measuring.
|
||||
|
||||
Anyway, the bus cycle time is an important factor when designing realtime code,
|
||||
because it limits the maximum frequency for the cyclic task of the application.
|
||||
In practice, these timing parameters are highly dependent on the hardware and
|
||||
often a trial and error method must be used to determine the limits of the
|
||||
system.
|
||||
Anyway, the bus cycle time is an important factor when designing realtime
|
||||
code, because it limits the maximum frequency for the cyclic task of the
|
||||
application. In practice, these timing parameters are highly dependent on the
|
||||
hardware and often a trial and error method must be used to determine the
|
||||
limits of the system.
|
||||
|
||||
The central question is: What happens, if the cycle frequency is too high? The
|
||||
answer is, that the EtherCAT frames that have been sent at the end of the cycle
|
||||
|
|
@ -2380,7 +2386,7 @@ will generate the documents in the subdirectory \textit{doxygen-output}:
|
|||
$ `\textbf{make doc}`
|
||||
\end{lstlisting}
|
||||
|
||||
The interface documentation can be viewed by pointing a browser to
|
||||
The interface documentation can be viewed by pointing a browser to the file
|
||||
\textit{doxygen-output/html/index.html}.
|
||||
|
||||
\section{Installing the Software}
|
||||
|
|
@ -2481,625 +2487,6 @@ state machine and begins scanning the bus.
|
|||
|
||||
%------------------------------------------------------------------------------
|
||||
|
||||
\chapter{Application Examples}
|
||||
\label{chapter:examples}
|
||||
|
||||
This chapter will give practical examples of how to use the EtherCAT master
|
||||
via the realtime interface by writing an application module.
|
||||
|
||||
% FIXME remove examples?
|
||||
|
||||
%------------------------------------------------------------------------------
|
||||
|
||||
\section{Minimal Example}
|
||||
\label{sec:mini}
|
||||
\index{Examples!Minimal}
|
||||
|
||||
This section will explain the use of the EtherCAT master from a minimal kernel
|
||||
module. The complete module code is obtainable as a part of the EtherCAT master
|
||||
code release (see~\cite{etherlab}, file \textit{examples/mini/mini.c}).
|
||||
|
||||
The minimal example uses a kernel timer (software interrupt) to generate a
|
||||
cyclic task. After the timer function is executed, it re-adds itself with a
|
||||
delay of one \textit{jiffy}\index{jiffies}, which results in a timer frequency
|
||||
of \textit{HZ}\nomenclature{HZ}{Kernel macro containing the timer interrupt
|
||||
frequency}
|
||||
|
||||
The module-global variables, needed to operate the master can be seen
|
||||
in listing~\ref{lst:minivar}.
|
||||
|
||||
\begin{lstlisting}[gobble=2,language=C,numbers=left,caption={Minimal
|
||||
variables},label=lst:minivar]
|
||||
struct timer_list timer;
|
||||
|
||||
ec_master_t *master = NULL;
|
||||
ec_domain_t *domain1 = NULL;
|
||||
|
||||
void *r_dig_in, *r_ana_out;
|
||||
|
||||
ec_pdo_reg_t domain1_pdos[] = {
|
||||
{"1", Beckhoff_EL1014_Inputs, &r_dig_in},
|
||||
{"2", Beckhoff_EL4132_Ouput1, &r_ana_out},
|
||||
{}
|
||||
};
|
||||
\end{lstlisting}
|
||||
|
||||
\begin{description}
|
||||
\item[\linenum{1}] There is a timer object
|
||||
declared, that is needed to tell the kernel to install a timer and
|
||||
execute a certain function, if it runs out. This is done by a
|
||||
variable of the \textit{timer\_list} structure.
|
||||
\item[\linenum{3} -- \linenum{4}] There
|
||||
is a pointer declared, that will later point to a requested EtherCAT
|
||||
master. Additionally there is a pointer to a domain object needed,
|
||||
that will manage process data IO.
|
||||
\item[\linenum{6}] The pointers \textit{r\_*}
|
||||
will later point to the \underline{r}aw process data values inside
|
||||
the domain memory. The addresses they point to will be set during a
|
||||
call to \textit{ec\_\-master\_\-activate()}, that will create the
|
||||
domain memory and configure the mapped process data image.
|
||||
\item[\linenum{8} -- \linenum{12}] The
|
||||
configuration of the mapping of certain Pdos in a domain can easily
|
||||
be done with the help of an initialization array of the
|
||||
\textit{ec\_pdo\_reg\_t} type, defined as part of the realtime
|
||||
interface. Each record must contain the ASCII bus-address of the
|
||||
slave (see section~\ref{sec:addr}), the slave's vendor ID and
|
||||
product code, and the index and subindex of the Pdo to map (these
|
||||
four fields can be specified in junction, by using one of the
|
||||
defines out of the \textit{include/ecdb.h} header). The last field
|
||||
has to be the address of the process data pointer, so it can later
|
||||
be redirected appropriately. Attention: The initialization array
|
||||
must end with an empty record (\textit{\{\}})!
|
||||
\end{description}
|
||||
|
||||
The initialization of the minimal application is done by the ``Minimal init
|
||||
function'' in listing~\ref{lst:miniinit}.
|
||||
|
||||
\begin{lstlisting}[gobble=2,language=C,numbers=left,caption={Minimal init
|
||||
function},label={lst:miniinit}]
|
||||
int __init init_mini_module(void)
|
||||
{
|
||||
if (!(master = ecrt_request_master(0))) {
|
||||
goto out_return;
|
||||
}
|
||||
|
||||
if (!(domain1 = ecrt_master_create_domain(master))) {
|
||||
goto out_release_master;
|
||||
}
|
||||
|
||||
if (ecrt_domain_register_pdo_list(domain1,
|
||||
domain1_pdos)) {
|
||||
goto out_release_master;
|
||||
}
|
||||
|
||||
if (ecrt_master_activate(master)) {
|
||||
goto out_release_master;
|
||||
}
|
||||
|
||||
ecrt_master_prepare(master);
|
||||
|
||||
init_timer(&timer);
|
||||
timer.function = run;
|
||||
timer.expires = jiffies + 10;
|
||||
add_timer(&timer);
|
||||
|
||||
return 0;
|
||||
|
||||
out_release_master:
|
||||
ecrt_release_master(master);
|
||||
out_return:
|
||||
return -1;
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
||||
\begin{description}
|
||||
\item[\linenum{3}] It is tried to request the
|
||||
first EtherCAT master (index 0). On success, the
|
||||
\textit{ecrt\_\-request\_\-master()} function returns a pointer to
|
||||
the reserved master, that can be used as an object to following
|
||||
functions calls. On failure, the function returns \textit{NULL}.
|
||||
\item[\linenum{7}] In order to exchange process
|
||||
data, a domain object has to be created. The
|
||||
\textit{ecrt\_\-master\_\-create\_domain()} function also returns a
|
||||
pointer to the created domain, or \textit{NULL} in error case.
|
||||
\item[\linenum{11}] The registration of domain
|
||||
Pdos with an initialization array results in a single function call.
|
||||
Alternatively the data fields could be registered with individual
|
||||
calls of \textit{ecrt\_domain\_register\_pdo()}.
|
||||
\item[\linenum{16}] After the configuration of
|
||||
process data mapping, the master can be activated for cyclic
|
||||
operation. This will configure all slaves and bring them into
|
||||
OP state.
|
||||
\item[\linenum{20}] This call is needed to avoid
|
||||
a case differentiation in cyclic operation: The first operation in
|
||||
cyclic mode is a receive call. Due to the fact, that there is
|
||||
nothing to receive during the first cycle, there had to be an
|
||||
\textit{if}-statement to avoid a warning. A call to
|
||||
\textit{ec\_master\_prepare()} sends a first datagram containing a
|
||||
process data exchange datagram, so that the first receive call will
|
||||
not fail.
|
||||
\item[\linenum{22} -- \linenum{25}] The
|
||||
master is now ready for cyclic operation. The kernel timer that
|
||||
cyclically executes the \textit{run()} function is initialized and
|
||||
started.
|
||||
\end{description}
|
||||
|
||||
The coding of a cleanup function fo the minimal module can be seen in
|
||||
listing~\ref{lst:miniclean}.
|
||||
|
||||
\begin{lstlisting}[gobble=2,language=C,numbers=left,caption={Minimal cleanup
|
||||
function},label={lst:miniclean}]
|
||||
void __exit cleanup_mini_module(void)
|
||||
{
|
||||
del_timer_sync(&timer);
|
||||
ecrt_master_deactivate(master);
|
||||
ecrt_release_master(master);
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
||||
\begin{description}
|
||||
\item[\linenum{3}] To cleanup the module, it it
|
||||
necessary to stop the cyclic processing. This is done by a call to
|
||||
\textit{del\_timer\_sync()} which safely removes a queued timer
|
||||
object. It is assured, that no cyclic work will be done after this
|
||||
call returns.
|
||||
\item[\linenum{4}] This call deactivates the
|
||||
master, which results in all slaves being brought to their INIT
|
||||
state again.
|
||||
\item[\linenum{5}] This call releases the master,
|
||||
removes any existing configuration and silently starts the idle
|
||||
mode. The value of the master pointer is invalid after this call and
|
||||
the module can be safely unloaded.
|
||||
\end{description}
|
||||
|
||||
The final part of the minimal module is that for the cyclic work. Its
|
||||
coding can be seen in listing~\ref{lst:minirun}.
|
||||
|
||||
\begin{lstlisting}[gobble=2,language=C,numbers=left,caption={Minimal cyclic
|
||||
function},label={lst:minirun}]
|
||||
void run(unsigned long data)
|
||||
{
|
||||
static uint8_t dig_in_0;
|
||||
|
||||
ecrt_master_receive(master);
|
||||
ecrt_domain_process(domain1);
|
||||
|
||||
dig_in_0 = EC_READ_BIT(r_dig_in, 0);
|
||||
EC_WRITE_S16(r_ana_out, dig_in_0 * 0x3FFF);
|
||||
|
||||
ecrt_master_run(master);
|
||||
ecrt_master_send(master);
|
||||
|
||||
timer.expires += 1; // frequency = HZ
|
||||
add_timer(&timer);
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
||||
\begin{description}
|
||||
|
||||
\item[\linenum{5}] The cyclic processing starts with receiving datagrams, that
|
||||
were sent in the last cycle. The frames containing these datagrams have to be
|
||||
received by the network interface card prior to this call.
|
||||
|
||||
\item[\linenum{6}] The process data of domain 1 has been automatically copied
|
||||
into domain memory while datagram reception. This call checks the working
|
||||
counter for changes and re-queues the domain's datagram for sending.
|
||||
|
||||
\item[\linenum{8}] This is an example for reading out a bit-oriented process
|
||||
data value (i.~e. bit 0) via the \textit{EC\_READ\_BIT()} macro. See
|
||||
section~\ref{sec:macros} for more information about those macros.
|
||||
|
||||
\item[\linenum{9}] This line shows how to write a signed, 16-bit process data
|
||||
value. In this case, the slave is able to output voltages of
|
||||
\unit{-10--+10}{\volt} with a resolution of \unit{16}{bit}. This write command
|
||||
outputs either \unit{0}{\volt} or \unit{+5}{\volt}, depending of the value of
|
||||
\textit{dig\_in\_0}.
|
||||
|
||||
\item[\linenum{11}] This call runs the master's operation state machine (see
|
||||
section~\ref{sec:fsm-op}). A single state is processed, and datagrams are
|
||||
queued. Mainly bus observation is done: The bus state is determined and in case
|
||||
of slaves that lost their configuration, reconfiguration is tried.
|
||||
|
||||
\item[\linenum{12}] This method sends all queued datagrams, in this case the
|
||||
domain's datagram and one of the master state machine. In best case, all
|
||||
datagrams fit into one frame.
|
||||
|
||||
\item[\linenum{14} -- \linenum{15}] Kernel timers are implemented as
|
||||
``one-shot'' timers, so they have to be re-added after each execution. The time
|
||||
of the next execution is specified in \textit{jiffies} and will happen at the
|
||||
time of the next system timer interrupt. This results in the \textit{run()}
|
||||
function being executed with a frequency of \textit{HZ}.
|
||||
|
||||
\end{description}
|
||||
|
||||
%------------------------------------------------------------------------------
|
||||
|
||||
\section{RTAI Example}
|
||||
\label{sec:rtai}
|
||||
\index{Examples!RTAI}
|
||||
|
||||
The whole code can be seen in the EtherCAT master code release
|
||||
(see~\cite{etherlab}, file \textit{examples/rtai/rtai\_sample.c}).
|
||||
|
||||
Listing~\ref{lst:rtaivar} shows the defines and global variables
|
||||
needed for a minimal RTAI module with EtherCAT processing.
|
||||
|
||||
\begin{lstlisting}[gobble=2,language=C,numbers=left,caption={RTAI task
|
||||
declaration},label={lst:rtaivar}]
|
||||
#define FREQUENCY 10000
|
||||
#define TIMERTICKS (1000000000 / FREQUENCY)
|
||||
|
||||
RT_TASK task;
|
||||
\end{lstlisting}
|
||||
|
||||
\begin{description}
|
||||
\item[\linenum{1} -- \linenum{2}] RTAI
|
||||
takes the cycle period as nanoseconds, so the easiest way is to
|
||||
define a frequency and convert it to a cycle time in nanoseconds.
|
||||
\item[\linenum{4}] The \textit{task} variable
|
||||
later contains information about the running RTAI task.
|
||||
\end{description}
|
||||
|
||||
Listing~\ref{lst:rtaiinit} shows the module init function for the RTAI
|
||||
module. Most lines are the same as in listing~\ref{lst:miniinit},
|
||||
differences come up when starting the cyclic code.
|
||||
|
||||
\begin{lstlisting}[gobble=2,language=C,numbers=left,caption={RTAI module init
|
||||
function},label={lst:rtaiinit}]
|
||||
int __init init_mod(void)
|
||||
{
|
||||
RTIME requested_ticks, tick_period, now;
|
||||
|
||||
if (!(master = ecrt_request_master(0))) {
|
||||
goto out_return;
|
||||
}
|
||||
|
||||
if (!(domain1 = ecrt_master_create_domain(master))) {
|
||||
goto out_release_master;
|
||||
}
|
||||
|
||||
if (ecrt_domain_register_pdo_list(domain1,
|
||||
domain1_pdos)) {
|
||||
goto out_release_master;
|
||||
}
|
||||
|
||||
if (ecrt_master_activate(master)) {
|
||||
goto out_release_master;
|
||||
}
|
||||
|
||||
ecrt_master_prepare(master);
|
||||
|
||||
requested_ticks = nano2count(TIMERTICKS);
|
||||
tick_period = start_rt_timer(requested_ticks);
|
||||
|
||||
if (rt_task_init(&task, run, 0, 2000, 0, 1, NULL)) {
|
||||
goto out_stop_timer;
|
||||
}
|
||||
|
||||
now = rt_get_time();
|
||||
if (rt_task_make_periodic(&task, now + tick_period,
|
||||
tick_period)) {
|
||||
goto out_stop_task;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_stop_task:
|
||||
rt_task_delete(&task);
|
||||
out_stop_timer:
|
||||
stop_rt_timer();
|
||||
out_deactivate:
|
||||
ecrt_master_deactivate(master);
|
||||
out_release_master:
|
||||
ecrt_release_master(master);
|
||||
out_return:
|
||||
return -1;
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
||||
\begin{description}
|
||||
\item[\linenum{24} -- \linenum{25}] The
|
||||
nanoseconds are converted to RTAI timer ticks and an RTAI timer is
|
||||
started. \textit{tick\_period} will be the ``real'' number of ticks
|
||||
used for the timer period (which can be different to the requested
|
||||
one).
|
||||
\item[\linenum{27}] The RTAI task is initialized
|
||||
by specifying the cyclic function, the parameter to hand over, the
|
||||
stack size, priority, a flag that tells, if the function will use
|
||||
floating point operations and a signal handler.
|
||||
\item[\linenum{32}] The task is made periodic by
|
||||
specifying a start time and a period.
|
||||
\end{description}
|
||||
|
||||
The cleanup function of the RTAI module in listing~\ref{lst:rtaiclean}
|
||||
is nearly as simple as that of the minimal module.
|
||||
|
||||
\begin{lstlisting}[gobble=2,language=C,numbers=left,caption={RTAI module
|
||||
cleanup function},label={lst:rtaiclean}]
|
||||
void __exit cleanup_mod(void)
|
||||
{
|
||||
rt_task_delete(&task);
|
||||
stop_rt_timer();
|
||||
ecrt_master_deactivate(master);
|
||||
ecrt_release_master(master);
|
||||
rt_sem_delete(&master_sem);
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
||||
\begin{description}
|
||||
\item[\linenum{2}] The RTAI task will be stopped
|
||||
and deleted.
|
||||
\item[\linenum{3}] After that, the RTAI timer can
|
||||
be stopped.
|
||||
\end{description}
|
||||
|
||||
The rest is the same as for the minimal module.
|
||||
|
||||
Worth to mention is, that the cyclic function of the RTAI module
|
||||
(listing~\ref{lst:rtairun}) has a slightly different architecture. The
|
||||
function is not executed until returning for every cycle, but has an
|
||||
infinite loop in it, that is placed in a waiting state for the rest of
|
||||
each cycle.
|
||||
|
||||
\begin{lstlisting}[gobble=2,language=C,numbers=left,caption={RTAI module cyclic
|
||||
function},label={lst:rtairun}]
|
||||
void run(long data)
|
||||
{
|
||||
while (1) {
|
||||
ecrt_master_receive(master);
|
||||
ecrt_domain_process(domain1);
|
||||
|
||||
k_pos = EC_READ_U32(r_ssi_input);
|
||||
|
||||
ecrt_master_run(master);
|
||||
ecrt_master_send(master);
|
||||
|
||||
rt_task_wait_period();
|
||||
}
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
||||
\begin{description}
|
||||
\item[\linenum{3}] The \textit{while (1)} loop
|
||||
executes for the lifetime of the RTAI task.
|
||||
\item[\linenum{12}] The
|
||||
\textit{rt\_task\_wait\_period()} function sets the process into a
|
||||
sleeping state until the beginning of the next cycle. It also
|
||||
checks, if the cyclic function has to be terminated.
|
||||
\end{description}
|
||||
|
||||
%------------------------------------------------------------------------------
|
||||
|
||||
\section{Concurrency Example}
|
||||
\label{sec:concurrency}
|
||||
\index{Examples!Concurrency}
|
||||
|
||||
As mentioned before, there can be concurrent access to the EtherCAT master. The
|
||||
application and a EoE\index{EoE} process can compete for master access, for
|
||||
example. In this case, the module has to provide the locking mechanism, because
|
||||
it depends on the module's architecture which lock has to be used. The module
|
||||
makes this locking mechanism available to the master through the master's
|
||||
locking callbacks.
|
||||
|
||||
In case of RTAI, the lock can be an RTAI semaphore, as shown in
|
||||
listing~\ref{lst:convar}. A normal Linux semaphore would not be appropriate,
|
||||
because it could not block the RTAI task due to RTAI running in a higher domain
|
||||
than the Linux kernel (see~\cite{rtai}).
|
||||
|
||||
\begin{lstlisting}[gobble=2,language=C,numbers=left,caption={RTAI semaphore for
|
||||
concurrent access},label={lst:convar}]
|
||||
SEM master_sem;
|
||||
\end{lstlisting}
|
||||
|
||||
The module has to implement the two callbacks for requesting and
|
||||
releasing the master lock. An exemplary coding can be seen in
|
||||
listing~\ref{lst:conlock}.
|
||||
|
||||
\begin{lstlisting}[gobble=2,language=C,numbers=left,caption={RTAI locking
|
||||
callbacks for concurrent access},label={lst:conlock}]
|
||||
int request_lock(void *data)
|
||||
{
|
||||
rt_sem_wait(&master_sem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void release_lock(void *data)
|
||||
{
|
||||
rt_sem_signal(&master_sem);
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
||||
\begin{description}
|
||||
\item[\linenum{1}] The \textit{request\_lock()}
|
||||
function has a data parameter. The master always passes the value,
|
||||
that was specified when registering the callback function. This can
|
||||
be used for handing the master pointer. Notice, that it has an
|
||||
integer return value (see line 4).
|
||||
\item[\linenum{3}] The call to
|
||||
\textit{rt\_sem\_wait()} either returns at once, when the semaphore
|
||||
was free, or blocks until the semaphore is freed again. In any case,
|
||||
the semaphore finally is reserved for the process calling the
|
||||
request function.
|
||||
\item[\linenum{4}] When the lock was requested
|
||||
successfully, the function should return 0. The module can prohibit
|
||||
requesting the lock by returning non-zero (see paragraph ``Tuning
|
||||
the jitter'' below).
|
||||
\item[\linenum{7}] The \textit{release\_lock()}
|
||||
function gets the same argument passed, but has a void return value,
|
||||
because is always succeeds.
|
||||
\item[\linenum{9}] The \textit{rt\_sem\_signal()}
|
||||
function frees the semaphore, that was prior reserved with
|
||||
\textit{rt\_sem\_wait()}.
|
||||
\end{description}
|
||||
|
||||
In the module's init function, the semaphore must be initialized, and
|
||||
the callbacks must be passed to the EtherCAT master:
|
||||
|
||||
\begin{lstlisting}[gobble=2,language=C,numbers=left,caption={Module init
|
||||
function for concurrent access},label={lst:coninit}]
|
||||
int __init init_mod(void)
|
||||
{
|
||||
RTIME tick_period, requested_ticks, now;
|
||||
|
||||
rt_sem_init(&master_sem, 1);
|
||||
|
||||
if (!(master = ecrt_request_master(0))) {
|
||||
goto out_return;
|
||||
}
|
||||
|
||||
ecrt_master_callbacks(master, request_lock,
|
||||
release_lock, NULL);
|
||||
// ...
|
||||
\end{lstlisting}
|
||||
|
||||
\begin{description}
|
||||
\item[\linenum{5}] The call to
|
||||
\textit{rt\_sem\_init()} initializes the semaphore and sets its
|
||||
value to 1, meaning that only one process can reserve the semaphore
|
||||
without blocking.
|
||||
\item[\linenum{11}] The callbacks are passed to
|
||||
the master with a call to \textit{ecrt\_master\_callbacks()}. The
|
||||
last parameter is the argument, that the master should pass with
|
||||
each call to a callback function. Here it is not used and set to
|
||||
\textit{NULL}.
|
||||
\end{description}
|
||||
|
||||
For the cyclic function being only one competitor for master access,
|
||||
it has to request the lock like any other process. There is no need to
|
||||
use the callbacks (which are meant for processes of lower priority),
|
||||
so it can access the semaphore directly:
|
||||
|
||||
\begin{lstlisting}[gobble=2,language=C,numbers=left,caption={RTAI cyclic
|
||||
function for concurrent access},label={lst:conrun}]
|
||||
void run(long data)
|
||||
{
|
||||
while (1) {
|
||||
rt_sem_wait(&master_sem);
|
||||
|
||||
ecrt_master_receive(master);
|
||||
ecrt_domain_process(domain1);
|
||||
|
||||
k_pos = EC_READ_U32(r_ssi_input);
|
||||
|
||||
ecrt_master_run(master);
|
||||
ecrt_master_send(master);
|
||||
|
||||
rt_sem_signal(&master_sem);
|
||||
rt_task_wait_period();
|
||||
}
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
||||
\begin{description}
|
||||
|
||||
\item[\linenum{4}] Every access to the master has to be preceded by a call to
|
||||
\textit{rt\_sem\_wait()}, because another instance might currently access the
|
||||
master.
|
||||
|
||||
\item[\linenum{14}] When cyclic processing finished, the semaphore has to be
|
||||
freed again, so that other processes have the possibility to access the master.
|
||||
|
||||
\end{description}
|
||||
|
||||
A little change has to be made to the cleanup function in case of
|
||||
concurrent master access.
|
||||
|
||||
\begin{lstlisting}[gobble=2,language=C,numbers=left,caption={RTAI module
|
||||
cleanup function for concurrent access},label={lst:conclean}]
|
||||
void __exit cleanup_mod(void)
|
||||
{
|
||||
rt_task_delete(&task);
|
||||
stop_rt_timer();
|
||||
ecrt_master_deactivate(master);
|
||||
ecrt_release_master(master);
|
||||
rt_sem_delete(&master_sem);
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
||||
\begin{description}
|
||||
\item[\linenum{7}] Upon module cleanup, the
|
||||
semaphore has to be deleted, so that memory can be freed.
|
||||
\end{description}
|
||||
|
||||
\paragraph{Tuning the Jitter}
|
||||
\index{Jitter}
|
||||
|
||||
Concurrent access leads to higher jitter for the application task, because
|
||||
there are situations, in which the task has to wait for a process of lower
|
||||
priority to finish accessing the master. In most cases this is acceptable,
|
||||
because a master access cycle (receive/process/send) only takes
|
||||
\unit{10-20}{\micro\second} on recent systems, what would be the maximum
|
||||
additional jitter. However some applications demand a minimum jitter. For this
|
||||
reason the master access can be prohibited by the application: If the time,
|
||||
another process wants to access the master, is to close to the beginning of the
|
||||
next application cycle, the module can disallow, that the lock is taken. In
|
||||
this case, the request callback has to return $1$, meaning that the lock has
|
||||
not been taken. The foreign process must abort its master access and try again
|
||||
next time.
|
||||
|
||||
This measure helps to significantly reducing the jitter produced by concurrent
|
||||
master access. Below are excerpts of an example coding:
|
||||
|
||||
\begin{lstlisting}[gobble=2,language=C,numbers=left,caption={Variables for
|
||||
jitter reduction},label={lst:redvar}]
|
||||
#define FREQUENCY 10000 // RTAI task frequency in Hz
|
||||
// ...
|
||||
cycles_t t_last_cycle = 0;
|
||||
const cycles_t t_critical = cpu_khz * 1000 / FREQUENCY
|
||||
- cpu_khz * 30 / 1000;
|
||||
\end{lstlisting}
|
||||
|
||||
\begin{description}
|
||||
|
||||
\item[\linenum{3}] The variable \textit{t\_last\_cycle} holds the timer ticks
|
||||
at the beginning of the last realtime cycle.
|
||||
|
||||
\item[\linenum{4}] \textit{t\_critical} contains the number of ticks, that may
|
||||
have passed since the beginning of the last cycle, until there is no more
|
||||
foreign access possible. It is calculated by subtracting the ticks for
|
||||
\unit{30}{\micro\second} from the ticks for a complete cycle.
|
||||
|
||||
\end{description}
|
||||
|
||||
\begin{lstlisting}[gobble=2,language=C,numbers=left,caption={Cyclic function
|
||||
with reduced jitter},label={lst:redrun}]
|
||||
void run(long data)
|
||||
{
|
||||
while (1) {
|
||||
t_last_cycle = get_cycles();
|
||||
rt_sem_wait(&master_sem);
|
||||
// ...
|
||||
\end{lstlisting}
|
||||
|
||||
\begin{description}
|
||||
\item[\linenum{4}] The ticks of the beginning of
|
||||
the current realtime cycle are taken before reserving the semaphore.
|
||||
\end{description}
|
||||
|
||||
\begin{lstlisting}[gobble=2,language=C,numbers=left,caption={Request callback
|
||||
for reduced jitter},label={lst:redreq}]
|
||||
int request_lock(void *data)
|
||||
{
|
||||
// too close to the next RT cycle: deny access.
|
||||
if (get_cycles() - t_last_cycle > t_critical)
|
||||
return -1;
|
||||
|
||||
// allow access
|
||||
rt_sem_wait(&master_sem);
|
||||
return 0;
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
||||
\begin{description}
|
||||
|
||||
\item[\linenum{4}] If the time of request is too close to the next realtime
|
||||
cycle (here: \unit{<30}{\micro\second} before the estimated beginning), the
|
||||
locking is denied. The requesting process must abort its cycle.
|
||||
|
||||
\end{description}
|
||||
|
||||
%------------------------------------------------------------------------------
|
||||
|
||||
\begin{thebibliography}{99}
|
||||
|
||||
\bibitem{etherlab} Ingenieurgemeinschaft IgH: EtherLab -- Open Source Toolkit
|
||||
|
|
|
|||
Loading…
Reference in New Issue