Removed MSR example; adapted RTAI example; README files for examples.

This commit is contained in:
Florian Pose 2008-07-02 12:16:48 +00:00
parent 8265fb115f
commit 93a66a3573
13 changed files with 276 additions and 850 deletions

11
TODO
View File

@ -8,22 +8,22 @@ $Id$
Version 1.4.0:
* Race in jiffies frame timeout.
* Move EC_NUM_SYNCS define to ecrt.h.
* Read Pdo mapping for unknown Pdos before configuring it.
* Read Pdo mapping for unknown Pdos before configuring them.
* Attach Pdo names from SII or Coe dictioary to Pdos read via CoE.
* Make scanning and configuration run parallel (each).
* Adapt remaining examples.
* READMEs for examples.
* Update documentation.
* Add -a and -p switches for 'ethercat config' command.
* Race in jiffies frame timeout.
* Add a -n (numeric) switch to ethercat command.
* Make verbose and quite flags a master property.
* File access over EtherCAT (FoE).
* Allow master requesting when in ORPHANED phase
* Get original driver for r8169.
* Distributed clocks.
Future issues:
* Distributed clocks.
* Move master threads, slave handlers and state machines into a user
space daemon.
* Implement user space realtime interface via cdev.
@ -35,7 +35,6 @@ Future issues:
* Optimize alignment of process data.
* Redundancy with 2 network adapters.
* Interface/buffers for asynchronous domain IO.
* Add a -n (numeric) switch to ethercat command.
Smaller issues:

View File

@ -383,36 +383,6 @@ fi
AC_SUBST(RTAI_DIR,[$rtaidir])
#------------------------------------------------------------------------------
# MSR path (optional)
#------------------------------------------------------------------------------
AC_ARG_WITH([msr-dir],
AC_HELP_STRING(
[--with-msr-dir=<DIR>],
[MSR path (only for MSR example)]
),
[
msrdir=[$withval]
],
[
msrdir=""
]
)
AC_MSG_CHECKING([for MSR path])
if test -z "${msrdir}"; then
AC_MSG_RESULT([not specified.])
else
if test \! -r ${msrdir}/include/msr.h; then
AC_MSG_ERROR([no MSR installation found in ${msrdir}!])
fi
AC_MSG_RESULT([$msrdir])
fi
AC_SUBST(MSR_DIR,[$msrdir])
#------------------------------------------------------------------------------
# Debug interface
#------------------------------------------------------------------------------
@ -526,8 +496,6 @@ AC_CONFIG_FILES([
examples/Makefile
examples/mini/Kbuild
examples/mini/Makefile
examples/msr/Kbuild
examples/msr/Makefile
examples/rtai/Kbuild
examples/rtai/Makefile
include/Makefile

View File

@ -31,6 +31,6 @@
#
#------------------------------------------------------------------------------
DIST_SUBDIRS = mini rtai msr
DIST_SUBDIRS = mini rtai
#------------------------------------------------------------------------------

28
examples/mini/README Normal file
View File

@ -0,0 +1,28 @@
-------------------------------------------------------------------------------
$Id$
-------------------------------------------------------------------------------
This is a minimal example module for the use of the EtherCAT master realtime
interface. It uses a kernel timer to generate a cyclic task.
Most probably you'll have different EtherCAT slaves present. Try adjusting the
section "process data" in mini.c to your bus configuration.
There are some features that can be disabled by commenting out the respective
defines at the head of mini.c.
---
To build the example module, call:
make modules
To run it, call:
insmod ec_mini.ko
...and watch the system logs for the outputs.
-------------------------------------------------------------------------------

View File

@ -43,16 +43,13 @@
// Module parameters
#define FREQUENCY 100
// Optional features
// Optional features (comment to disable)
#define CONFIGURE_PDOS
#define EXTERNAL_MEMORY
#define SDO_ACCESS
#define PFX "ec_mini: "
#define AnaInPos 0, 1
#define DigOutPos 0, 3
/*****************************************************************************/
// EtherCAT
@ -66,28 +63,32 @@ static ec_domain_state_t domain1_state = {};
static ec_slave_config_t *sc_ana_in = NULL;
static ec_slave_config_state_t sc_ana_in_state = {};
// Timer
static struct timer_list timer;
static unsigned int counter = 0;
/*****************************************************************************/
// process data
static uint8_t *domain1_pd; // process data memory
static unsigned int off_ana_in; // offsets for Pdo entries
static unsigned int off_dig_out;
static unsigned int blink = 0;
#define AnaInSlavePos 0, 1
#define DigOutSlavePos 0, 3
#define Beckhoff_EL2004 0x00000002, 0x07D43052
#define Beckhoff_EL3162 0x00000002, 0x0C5A3052
static unsigned int off_ana_in; // offsets for Pdo entries
static unsigned int off_dig_out;
const static ec_pdo_entry_reg_t domain1_regs[] = {
{AnaInPos, Beckhoff_EL3162, 0x3101, 2, &off_ana_in},
{DigOutPos, Beckhoff_EL2004, 0x3001, 1, &off_dig_out},
{AnaInSlavePos, Beckhoff_EL3162, 0x3101, 2, &off_ana_in},
{DigOutSlavePos, Beckhoff_EL2004, 0x3001, 1, &off_dig_out},
{}
};
static unsigned int counter = 0;
static unsigned int blink = 0;
/*****************************************************************************/
#ifdef CONFIGURE_PDOS
@ -312,7 +313,7 @@ int __init init_mini_module(void)
}
if (!(sc_ana_in = ecrt_master_slave_config(
master, AnaInPos, Beckhoff_EL3162))) {
master, AnaInSlavePos, Beckhoff_EL3162))) {
printk(KERN_ERR PFX "Failed to get slave configuration.\n");
goto out_release_master;
}
@ -324,7 +325,8 @@ int __init init_mini_module(void)
goto out_release_master;
}
if (!(sc = ecrt_master_slave_config(master, DigOutPos, Beckhoff_EL2004))) {
if (!(sc = ecrt_master_slave_config(
master, DigOutSlavePos, Beckhoff_EL2004))) {
printk(KERN_ERR PFX "Failed to get slave configuration.\n");
goto out_release_master;
}

View File

@ -1,61 +0,0 @@
#------------------------------------------------------------------------------
#
# $Id$
#
# Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
#
# This file is part of the IgH EtherCAT Master.
#
# The IgH EtherCAT Master is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# The IgH EtherCAT Master is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with the IgH EtherCAT Master; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# The right to use EtherCAT Technology is granted and comes free of
# charge under condition of compatibility of product made by
# Licensee. People intending to distribute/sell products based on the
# code, have to sign an agreement to guarantee that products using
# software based on IgH EtherCAT master stay compatible with the actual
# EtherCAT specification (which are released themselves as an open
# standard) as the (only) precondition to have the right to use EtherCAT
# Technology, IP and trade marks.
#
# vi: syntax=make
#
#------------------------------------------------------------------------------
MODULE := ec_msr_sample
obj-m := $(MODULE).o
$(MODULE)-objs := \
msr_sample.o \
rt_lib/msr-core/msr_lists.o \
rt_lib/msr-core/msr_main.o \
rt_lib/msr-core/msr_charbuf.o \
rt_lib/msr-core/msr_reg.o \
rt_lib/msr-core/msr_interpreter.o \
rt_lib/msr-core/msr_messages.o \
rt_lib/msr-core/msr_proc.o \
rt_lib/msr-core/msr_error_reg.o \
rt_lib/msr-utils/msr_utils.o \
rt_lib/msr-utils/msr_time.o \
rt_lib/msr-math/msr_base64.o \
rt_lib/msr-math/msr_hex_bin.o \
libm.o
EXTRA_CFLAGS := \
-I@MSR_DIR@/include \
-I@RTAI_DIR@/include \
-D_SIMULATION -mhard-float
#------------------------------------------------------------------------------

View File

@ -1,54 +0,0 @@
#------------------------------------------------------------------------------
#
# $Id$
#
# Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
#
# This file is part of the IgH EtherCAT Master.
#
# The IgH EtherCAT Master is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# The IgH EtherCAT Master is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with the IgH EtherCAT Master; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# The right to use EtherCAT Technology is granted and comes free of
# charge under condition of compatibility of product made by
# Licensee. People intending to distribute/sell products based on the
# code, have to sign an agreement to guarantee that products using
# software based on IgH EtherCAT master stay compatible with the actual
# EtherCAT specification (which are released themselves as an open
# standard) as the (only) precondition to have the right to use EtherCAT
# Technology, IP and trade marks.
#
#------------------------------------------------------------------------------
EXTRA_DIST = \
Kbuild.in \
libm.o_shipped \
msr_sample.c \
msrserv.pl \
init.sh
BUILT_SOURCES = \
Kbuild
modules:
$(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_srcdir@" modules
modules_install:
mkdir -p $(DESTDIR)$(LINUX_MOD_PATH)
cp $(srcdir)/ec_msr_sample.ko $(DESTDIR)$(LINUX_MOD_PATH)
clean-local:
$(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_srcdir@" clean
#------------------------------------------------------------------------------

View File

@ -1,129 +0,0 @@
#!/bin/sh
#------------------------------------------------------------------------------
#
# MSR Init Script
#
# $Id$
#
#------------------------------------------------------------------------------
### BEGIN INIT INFO
# Provides: msr
# Required-Start: $local_fs $syslog $network
# Should-Start: $time ntp ethercat
# Required-Stop: $local_fs $syslog $network
# Should-Stop: $time ntp ethercat
# Default-Start: 3 5
# Default-Stop: 0 1 2 6
# Short-Description: MSR module
# Description:
### END INIT INFO
#------------------------------------------------------------------------------
# <Customizing>
NAME="MSR EtherCAT sample"
BASE=/opt/etherlab
MSR_SERVER=$BASE/bin/msrserv.pl
MODULE=ec_msr_sample
RTAI_PATH=/usr/realtime
RTAI_MODULES="hal up" # sem math
DEVICE=msr
DEVMASK=664
GROUP=users
# </Customizing>
#------------------------------------------------------------------------------
. /etc/rc.status
rc_reset
#------------------------------------------------------------------------------
case "$1" in
start)
echo -n Starting $NAME" "
# Insert RTAI modules
for mod in $RTAI_MODULES; do
if ! lsmod | grep -q "^rtai_$mod"; then
if ! insmod $RTAI_PATH/modules/rtai_$mod.ko; then
/bin/false
rc_status -v
rc_exit
fi
fi
done
# Insert realtime module
if ! modprobe $MODULE; then
/bin/false
rc_status -v
rc_exit
fi
#sleep 2
# Create MSR device
MAJOR=`cat /proc/devices | awk "\\$2==\"$DEVICE\" {print \\$1}"`
rm -f /dev/${DEVICE}
mknod /dev/${DEVICE} c $MAJOR 0
chgrp $GROUP /dev/${DEVICE}
chmod $DEVMASK /dev/${DEVICE}
#sleep 1
# Start MSR-Server
startproc $MSR_SERVER 1>/dev/null 2>/dev/null
rc_status -v
;;
stop)
echo -n Shutting down $NAME" "
if ! killproc $MSR_SERVER; then
/bin/false
rc_status -v
rc_exit
fi
if ! /sbin/rmmod $MODULE; then
/bin/false
rc_status -v
rc_exit
fi
# Remove stale nodes
rm -f /dev/${DEVICE} /dev/${DEVICE}0
rc_status -v
;;
restart)
$0 stop || exit 1
sleep 1
$0 start
rc_status
;;
status)
echo -n "Checking for MSR module: "
/sbin/lsmod | grep -q "^$MODULE"
rc_status -v
echo -n "Checking for MSR server: "
checkproc $MSR_SERVER
rc_status -v
;;
*)
echo "Usage: $0 {start|stop|status|restart}"
exit 1
;;
esac
rc_exit

View File

@ -1 +0,0 @@
/usr/lib/libm.a

View File

@ -1,230 +0,0 @@
/******************************************************************************
*
* $Id$
*
* Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
*
* This file is part of the IgH EtherCAT Master.
*
* The IgH EtherCAT Master is free software; you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* The IgH EtherCAT Master is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the IgH EtherCAT Master; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* The right to use EtherCAT Technology is granted and comes free of
* charge under condition of compatibility of product made by
* Licensee. People intending to distribute/sell products based on the
* code, have to sign an agreement to guarantee that products using
* software based on IgH EtherCAT master stay compatible with the actual
* EtherCAT specification (which are released themselves as an open
* standard) as the (only) precondition to have the right to use EtherCAT
* Technology, IP and trade marks.
*
*****************************************************************************/
// Linux
#include <linux/module.h>
// RTAI
#include "rtai_sched.h"
#include "rtai_sem.h"
// RT_lib
#include <msr_main.h>
#include <msr_reg.h>
#include <msr_time.h>
// EtherCAT
#include "../../include/ecrt.h"
#include "../../include/ecdb.h"
#define MSR_ABTASTFREQUENZ 1000
#define HZREDUCTION (MSR_ABTASTFREQUENZ / HZ)
#define TIMERTICKS (1000000000 / MSR_ABTASTFREQUENZ)
/*****************************************************************************/
// RTAI
RT_TASK task;
SEM master_sem;
cycles_t t_start = 0, t_critical;
// EtherCAT
ec_master_t *master = NULL;
ec_domain_t *domain1 = NULL;
// raw process data
void *r_ana_out;
// channels
double k_ana_out;
ec_pdo_reg_t domain1_pdos[] = {
{"3", Beckhoff_EL4132_Output1, &r_ana_out},
{}
};
/*****************************************************************************/
void msr_controller_run(void)
{
// receive
rt_sem_wait(&master_sem);
ecrt_master_receive(master);
ecrt_domain_process(domain1);
rt_sem_signal(&master_sem);
// Process data
EC_WRITE_S16(r_ana_out, k_ana_out / 10.0 * 0x7FFF);
// Send
rt_sem_wait(&master_sem);
ecrt_domain_queue(domain1);
ecrt_master_run(master);
ecrt_master_send(master);
rt_sem_signal(&master_sem);
msr_write_kanal_list();
}
/*****************************************************************************/
void msr_run(long data)
{
while (1) {
t_start = get_cycles();
MSR_RTAITHREAD_CODE(msr_controller_run(););
rt_task_wait_period();
}
}
/*****************************************************************************/
int msr_reg(void)
{
msr_reg_kanal("/ana_out", "", &k_ana_out, TDBL);
return 0;
}
/*****************************************************************************/
int request_lock(void *data)
{
// too close to the next RT cycle: deny access...
if (get_cycles() - t_start > t_critical) return -1;
// allow access
rt_sem_wait(&master_sem);
return 0;
}
/*****************************************************************************/
void release_lock(void *data)
{
rt_sem_signal(&master_sem);
}
/*****************************************************************************/
int __init init_mod(void)
{
RTIME ticks;
printk(KERN_INFO "=== Starting EtherCAT RTAI MSR sample module... ===\n");
rt_sem_init(&master_sem, 1);
t_critical = cpu_khz * 800 / MSR_ABTASTFREQUENZ; // ticks for 80%
if (msr_rtlib_init(1, MSR_ABTASTFREQUENZ, 10, &msr_reg) < 0) {
printk(KERN_ERR "Failed to initialize rtlib!\n");
goto out_return;
}
if (!(master = ecrt_request_master(0))) {
printk(KERN_ERR "Failed to request master 0!\n");
goto out_msr_cleanup;
}
ecrt_master_callbacks(master, request_lock, release_lock, NULL);
printk(KERN_INFO "Creating domains...\n");
if (!(domain1 = ecrt_master_create_domain(master))) {
printk(KERN_ERR "Failed to create domains!\n");
goto out_release_master;
}
printk(KERN_INFO "Registering Pdos...\n");
if (ecrt_domain_register_pdo_list(domain1, domain1_pdos)) {
printk(KERN_ERR "Failed to register Pdos.\n");
goto out_release_master;
}
printk(KERN_INFO "Activating master...\n");
if (ecrt_master_activate(master)) {
printk(KERN_ERR "Could not activate master!\n");
goto out_release_master;
}
printk("Starting cyclic sample thread...\n");
ticks = start_rt_timer(nano2count(TIMERTICKS));
if (rt_task_init(&task, msr_run, 0, 2000, 0, 1, NULL)) {
printk(KERN_ERR "Failed to init RTAI task!\n");
goto out_stop_timer;
}
if (rt_task_make_periodic(&task, rt_get_time() + ticks, ticks)) {
printk(KERN_ERR "Failed to run RTAI task!\n");
goto out_stop_task;
}
printk(KERN_INFO "=== EtherCAT RTAI MSR sample module started. ===\n");
return 0;
out_stop_task:
rt_task_delete(&task);
out_stop_timer:
stop_rt_timer();
out_release_master:
ecrt_release_master(master);
out_msr_cleanup:
msr_rtlib_cleanup();
out_return:
rt_sem_delete(&master_sem);
return -1;
}
/*****************************************************************************/
void __exit cleanup_mod(void)
{
printk(KERN_INFO "=== Unloading EtherCAT RTAI MSR sample module... ===\n");
rt_task_delete(&task);
stop_rt_timer();
ecrt_release_master(master);
rt_sem_delete(&master_sem);
msr_rtlib_cleanup();
printk(KERN_INFO "=== EtherCAT RTAI MSR sample module unloaded. ===\n");
}
/*****************************************************************************/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Florian Pose <fp@igh-essen.com>");
MODULE_DESCRIPTION("EtherCAT RTAI MSR sample module");
module_init(init_mod);
module_exit(cleanup_mod);
/*****************************************************************************/

View File

@ -1,267 +0,0 @@
#!/usr/bin/perl -w
#------------------------------------------------------------------------------
#
# Copyright (C) 2006 Ingenieurgemeinschaft IgH
#
# This file is part of the IgH EtherCAT Master.
#
# The IgH EtherCAT Master is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# The IgH EtherCAT Master is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with the IgH EtherCAT Master; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# The right to use EtherCAT Technology is granted and comes free of
# charge under condition of compatibility of product made by
# Licensee. People intending to distribute/sell products based on the
# code, have to sign an agreement to guarantee that products using
# software based on IgH EtherCAT master stay compatible with the actual
# EtherCAT specification (which are released themselves as an open
# standard) as the (only) precondition to have the right to use EtherCAT
# Technology, IP and trade marks.
#
#------------------------------------------------------------------------------
#
# Multithreaded Server
# according to the example from "Programming Perl"
# this code is improved according to the example from
# perldoc perlipc, so now safely being usable under Perl 5.8
# (see note (*))
#
# works with read/write on a device-file
#
#------------------------------------------------------------------------------
require 5.002;
use strict;
BEGIN { $ENV{PATH} = '/opt/msr/bin:/usr/bin:/bin' }
use Socket;
use Carp;
use FileHandle;
use Getopt::Std;
use Sys::Syslog qw(:DEFAULT setlogsock);
use vars qw (
$self $pid $dolog $port $dev %opts $selfbase
$len $offset $stream $written $read $log $blksize
$instdir
$authfile %authhosts
);
# Do logging to local syslogd by unix-domain socket instead of inetd
setlogsock("unix");
# Prototypes and some little Tools
sub spawn;
sub logmsg {
my ($level, $debug, @text) = @_;
syslog("daemon|$level", @text) if $debug > $dolog;
# print STDERR "daemon|$level", @text, "\n" if $dolog;
}
sub out {
my $waitpid = wait;
logmsg("notice", 2, "$waitpid exited");
unlink "$selfbase.pid";
exit 0;
}
sub help {
print "\n usage: $0 [-l og] [-h elp] [-p port] [-d device]\n";
exit;
}
# Process Options
%opts = (
"l" => 1,
"h" => 0,
"p" => 2345,
"d" => "/dev/msr"
);
getopts("lhp:d:", \%opts);
help if $opts{"h"};
( $self = $0 ) =~ s+.*/++ ;
( $selfbase = $self ) =~ s/\..*//;
$log = "$selfbase.log";
$dolog = $opts{"l"};
$port = $opts{"p"};
$dev = $opts{"d"};
$blksize = 1024; # try to write as much bytes
$instdir = "/opt/msr";
$authfile = "$instdir/etc/hosts.auth";
# Start logging
openlog($self, 'pid');
# Flush Output, dont buffer
$| = 1;
# first fork and run in background
if ($pid = fork) {
# open LOG, ">$log" if $dolog;
# close LOG;
logmsg("notice", 2, "forked process: $pid\n");
exit 0;
}
# Server tells about startup success
open (PID, ">/$instdir/var/run/$selfbase.pid");
print PID "$$\n";
close PID;
# Cleanup on exit (due to kill -TERM signal)
$SIG{TERM} = \&out;
# We use streams
my $proto = getprotobyname('tcp');
# Open Server socket
socket(Server, PF_INET, SOCK_STREAM, $proto) or die "socket: $!";
setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, pack("l", 1))
or die "setsocketopt: $!";
bind (Server, sockaddr_in($port, INADDR_ANY))
or die "bind: $!";
listen (Server, SOMAXCONN)
or die "listen: $!";
%authhosts = ();
# get authorized hosts
open (AUTH, $authfile)
or logmsg ("notice", 2, "Could not read allowed hosts file: $authfile");
while (<AUTH>) {
chomp;
my $host = lc $_;
if ($host =~ /^[\d\w]/) {
$authhosts{$_} = 1;
logmsg ("notice", 2, "Authorized host: >$host<");
}
}
close (AUTH);
# tell about open server socket
logmsg ("notice", 2, "Server started at port $port");
my $waitedpid = 0;
my $paddr;
# wait for children to return, thus avoiding zombies
# improvement (*)
use POSIX ":sys_wait_h";
sub REAPER {
my $child;
while (($waitedpid = waitpid(-1,WNOHANG)) > 0) {
logmsg ("notice", 2, "reaped $waitedpid", ($? ? " with exit $?" : ""));
}
$SIG{CHLD} = \&REAPER; # loathe sysV
}
# also all sub-processes should wait for their children
$SIG{CHLD} = \&REAPER;
# start a new server for every incoming request
# improvement (*) -- loop forever
while ( 1 ) {
for ( $waitedpid = 0;
($paddr = accept(Client,Server)) || $waitedpid;
$waitedpid = 0, close Client ) {
next if $waitedpid and not $paddr;
my ($port, $iaddr) = sockaddr_in($paddr);
my $name = lc gethostbyaddr($iaddr, AF_INET);
my $ipaddr = inet_ntoa($iaddr);
my $n = 0;
# tell about the requesting client
logmsg ("info", 2, "Connection from >$ipaddr< ($name) at port $port");
spawn sub {
my ($head, $hlen, $pos, $pegel, $typ, $siz, $nch, $nrec, $dat, $i, $j, $n, $llen);
my ($watchpegel, $shmpegel);
my ($rin, $rout, $in, $line, $data_requested, $oversample);
my (@channels);
# to use stdio on writing to Client
Client->autoflush();
# Open Device
sysopen (DEV, "$dev", O_RDWR | O_NONBLOCK, 0666) or die("can't open $dev");
# Bitmask to check for input on stdin
$rin = "";
vec($rin, fileno(Client), 1) = 1;
# check for authorized hosts
my $access = 'deny';
$access = 'allow' if $authhosts{$ipaddr};
$line = "<remote_host host=\"$ipaddr\" access=\"$access\">\n";
logmsg ("info", 2, $line);
$len = length $line;
$offset = 0;
while ($len) {
$written = syswrite (DEV, $line, $len, $offset);
$len -= $written;
$offset += $written;
}
while ( 1 ) {
$in = select ($rout=$rin, undef, undef, 0.0); # poll client
# look for any Input from Client
if ($in) {
# exit on EOF
$len = sysread (Client, $line, $blksize) or exit;
logmsg("info", 0, "got $len bytes: \"$line\"");
$offset = 0;
# copy request to device
while ($len) {
$written = syswrite (DEV, $line, $len, $offset);
$len -= $written;
$offset += $written;
}
}
# look for some output from device
if ($len = sysread DEV, $stream, $blksize) {
print Client $stream;
} else {
select undef, undef, undef, 0.1; # calm down if nothing on device
}
}
};
logmsg("info", 2, "spawned\n");
}
logmsg("info", 2, "server loop\n");
}
sub spawn {
my $coderef = shift;
unless (@_ == 0 && $coderef && ref($coderef) eq 'CODE') {
confess "usage: spawn CODEREF";
}
my $pid;
if (!defined($pid = fork)) {
logmsg ("notice", 2, "fork failed: $!");
return;
} elsif ($pid) {
logmsg ("notice", 2, "Request $pid");
return; # Parent
}
# do not use fdup as in the original example
# open (STDIN, "<&Client") or die "Can't dup client to stdin";
# open (STDOUT, ">&Client") or die "Can't dup client to stdout";
# STDOUT->autoflush();
exit &$coderef();
}

31
examples/rtai/README Normal file
View File

@ -0,0 +1,31 @@
-------------------------------------------------------------------------------
$Id$
-------------------------------------------------------------------------------
This is a minimal example module for the use of the EtherCAT master realtime
interface with an RTAI thread.
The module expects an RTAI installation. Configure the master sources
specifying --with-rtai-dir to have the right include paths.
Most probably you'll have different EtherCAT slaves present. Try adjusting the
section "process data" in rtai_sample.c to your bus configuration.
There are some features that can be disabled by commenting out the respective
defines at the head of rtai_sample.c.
---
To build the example module, call:
make modules
To run it, load the appropriate RTAI modules and call:
insmod ec_rtai_sample.ko
...and watch the system logs for the outputs.
-------------------------------------------------------------------------------

View File

@ -37,100 +37,210 @@
#include <linux/module.h>
// RTAI
#include "rtai_sched.h"
#include "rtai_sem.h"
#include <rtai_sched.h>
#include <rtai_sem.h>
// EtherCAT
#include "../../include/ecrt.h"
#include "../../include/ecdb.h"
/*****************************************************************************/
// RTAI task frequency in Hz
#define FREQUENCY 2000
// Module parameters
#define FREQUENCY 2000 // task frequency in Hz
#define INHIBIT_TIME 20
#define TIMERTICKS (1000000000 / FREQUENCY)
// Optional features (comment to disable)
#define CONFIGURE_PDOS
#define PFX "ec_rtai_sample: "
/*****************************************************************************/
// EtherCAT
static ec_master_t *master = NULL;
static ec_master_state_t master_state = {};
spinlock_t master_lock = SPIN_LOCK_UNLOCKED;
static ec_domain_t *domain1 = NULL;
static ec_domain_state_t domain1_state = {};
static ec_slave_config_t *sc_ana_in = NULL;
static ec_slave_config_state_t sc_ana_in_state = {};
// RTAI
static RT_TASK task;
static SEM master_sem;
static cycles_t t_last_cycle = 0, t_critical;
// EtherCAT
static ec_master_t *master = NULL;
static ec_domain_t *domain1 = NULL;
static ec_master_status_t master_status, old_status = {};
/*****************************************************************************/
// data fields
static void *r_dig_out;
static void *r_ana_out;
static void *r_count;
//static void *r_freq;
// process data
static uint8_t *domain1_pd; // process data memory
const static ec_pdo_reg_t domain1_pdo_regs[] = {
{"2", Beckhoff_EL2004_Outputs, &r_dig_out},
{"3", Beckhoff_EL4132_Output1, &r_ana_out},
{"#888:1", Beckhoff_EL5101_Value, &r_count},
//{"4", Beckhoff_EL5101_Frequency, &r_freq},
#define AnaInSlavePos 0, 1
#define DigOutSlavePos 0, 3
#define Beckhoff_EL2004 0x00000002, 0x07D43052
#define Beckhoff_EL3162 0x00000002, 0x0C5A3052
static unsigned int off_ana_in; // offsets for Pdo entries
static unsigned int off_dig_out;
const static ec_pdo_entry_reg_t domain1_regs[] = {
{AnaInSlavePos, Beckhoff_EL3162, 0x3101, 2, &off_ana_in},
{DigOutSlavePos, Beckhoff_EL2004, 0x3001, 1, &off_dig_out},
{}
};
static unsigned int counter = 0;
static unsigned int blink = 0;
/*****************************************************************************/
#ifdef CONFIGURE_PDOS
static ec_pdo_entry_info_t el3162_channel1[] = {
{0x3101, 1, 8}, // status
{0x3101, 2, 16} // value
};
static ec_pdo_entry_info_t el3162_channel2[] = {
{0x3102, 1, 8}, // status
{0x3102, 2, 16} // value
};
static ec_pdo_info_t el3162_pdos[] = {
{0x1A00, 2, el3162_channel1},
{0x1A01, 2, el3162_channel2}
};
static ec_sync_info_t el3162_syncs[] = {
{2, EC_DIR_OUTPUT},
{3, EC_DIR_INPUT, 2, el3162_pdos},
{0xff}
};
static ec_pdo_entry_info_t el2004_channels[] = {
{0x3001, 1, 1}, // Value 1
{0x3001, 2, 1}, // Value 2
{0x3001, 3, 1}, // Value 3
{0x3001, 4, 1} // Value 4
};
static ec_pdo_info_t el2004_pdos[] = {
{0x1600, 1, &el2004_channels[0]},
{0x1601, 1, &el2004_channels[1]},
{0x1602, 1, &el2004_channels[2]},
{0x1603, 1, &el2004_channels[3]}
};
static ec_sync_info_t el2004_syncs[] = {
{0, EC_DIR_OUTPUT, 4, el2004_pdos},
{1, EC_DIR_INPUT},
{0xff}
};
#endif
/*****************************************************************************/
void check_domain1_state(void)
{
ec_domain_state_t ds;
spin_lock(&master_lock);
ecrt_domain_state(domain1, &ds);
spin_unlock(&master_lock);
if (ds.working_counter != domain1_state.working_counter)
printk(KERN_INFO PFX "Domain1: WC %u.\n", ds.working_counter);
if (ds.wc_state != domain1_state.wc_state)
printk(KERN_INFO PFX "Domain1: State %u.\n", ds.wc_state);
domain1_state = ds;
}
/*****************************************************************************/
void check_master_state(void)
{
ec_master_state_t ms;
spin_lock(&master_lock);
ecrt_master_state(master, &ms);
spin_unlock(&master_lock);
if (ms.slaves_responding != master_state.slaves_responding)
printk(KERN_INFO PFX "%u slave(s).\n", ms.slaves_responding);
if (ms.al_states != master_state.al_states)
printk(KERN_INFO PFX "AL states: 0x%02X.\n", ms.al_states);
if (ms.link_up != master_state.link_up)
printk(KERN_INFO PFX "Link is %s.\n", ms.link_up ? "up" : "down");
master_state = ms;
}
/*****************************************************************************/
void check_slave_config_states(void)
{
ec_slave_config_state_t s;
spin_lock(&master_lock);
ecrt_slave_config_state(sc_ana_in, &s);
spin_unlock(&master_lock);
if (s.al_state != sc_ana_in_state.al_state)
printk(KERN_INFO PFX "AnaIn: State 0x%02X.\n", s.al_state);
if (s.online != sc_ana_in_state.online)
printk(KERN_INFO PFX "AnaIn: %s.\n", s.online ? "online" : "offline");
if (s.operational != sc_ana_in_state.operational)
printk(KERN_INFO PFX "AnaIn: %soperational.\n",
s.operational ? "" : "Not ");
sc_ana_in_state = s;
}
/*****************************************************************************/
void run(long data)
{
static unsigned int blink = 0;
static unsigned int counter = 0;
while (1) {
t_last_cycle = get_cycles();
// receive process data
rt_sem_wait(&master_sem);
ecrt_master_receive(master);
ecrt_domain_process(domain1);
rt_sem_signal(&master_sem);
// process data
EC_WRITE_U8(r_dig_out, blink ? 0x0F : 0x00);
// check process data state (optional)
check_domain1_state();
if (counter) {
counter--;
} else { // do this at 1 Hz
counter = FREQUENCY;
// calculate new process data
blink = !blink;
// check for master state (optional)
check_master_state();
// check for islave configuration state(s) (optional)
check_slave_config_states();
}
// write process data
EC_WRITE_U8(domain1_pd + off_dig_out, blink ? 0x06 : 0x09);
rt_sem_wait(&master_sem);
ecrt_domain_queue(domain1);
ecrt_master_send(master);
rt_sem_signal(&master_sem);
if (counter) {
counter--;
}
else {
counter = FREQUENCY;
blink = !blink;
rt_sem_wait(&master_sem);
ecrt_master_get_status(master, &master_status);
rt_sem_signal(&master_sem);
if (master_status.bus_status != old_status.bus_status) {
printk(KERN_INFO PFX "bus status changed to %i.\n",
master_status.bus_status);
}
if (master_status.bus_tainted != old_status.bus_tainted) {
printk(KERN_INFO PFX "tainted flag changed to %u.\n",
master_status.bus_tainted);
}
if (master_status.slaves_responding !=
old_status.slaves_responding) {
printk(KERN_INFO PFX "slaves_responding changed to %u.\n",
master_status.slaves_responding);
}
old_status = master_status;
}
rt_task_wait_period();
}
}
@ -159,6 +269,9 @@ void release_lock(void *data)
int __init init_mod(void)
{
RTIME tick_period, requested_ticks, now;
#ifdef CONFIGURE_PDOS
ec_slave_config_t *sc;
#endif
printk(KERN_INFO PFX "Starting...\n");
@ -173,15 +286,39 @@ int __init init_mod(void)
ecrt_master_callbacks(master, request_lock, release_lock, NULL);
printk(KERN_INFO PFX "Creating domain...\n");
printk(KERN_INFO PFX "Registering domain...\n");
if (!(domain1 = ecrt_master_create_domain(master))) {
printk(KERN_ERR PFX "Domain creation failed!\n");
goto out_release_master;
}
printk(KERN_INFO PFX "Registering Pdos...\n");
if (ecrt_domain_register_pdo_list(domain1, domain1_pdo_regs)) {
printk(KERN_ERR PFX "Pdo registration failed!\n");
if (!(sc_ana_in = ecrt_master_slave_config(
master, AnaInSlavePos, Beckhoff_EL3162))) {
printk(KERN_ERR PFX "Failed to get slave configuration.\n");
goto out_release_master;
}
#ifdef CONFIGURE_PDOS
printk(KERN_INFO PFX "Configuring Pdos...\n");
if (ecrt_slave_config_sync_managers(sc_ana_in, EC_END, el3162_syncs)) {
printk(KERN_ERR PFX "Failed to configure Pdos.\n");
goto out_release_master;
}
if (!(sc = ecrt_master_slave_config(master, DigOutSlavePos, Beckhoff_EL2004))) {
printk(KERN_ERR PFX "Failed to get slave configuration.\n");
goto out_release_master;
}
if (ecrt_slave_config_sync_managers(sc, EC_END, el2004_syncs)) {
printk(KERN_ERR PFX "Failed to configure Pdos.\n");
goto out_release_master;
}
#endif
printk(KERN_INFO PFX "Registering Pdo entries...\n");
if (ecrt_domain_reg_pdo_entry_list(domain1, domain1_regs)) {
printk(KERN_ERR PFX "Pdo entry registration failed!\n");
goto out_release_master;
}
@ -191,6 +328,9 @@ int __init init_mod(void)
goto out_release_master;
}
// Get internal process data for domain
domain1_pd = ecrt_domain_data(domain1);
printk(KERN_INFO PFX "Starting cyclic sample thread...\n");
requested_ticks = nano2count(TIMERTICKS);
tick_period = start_rt_timer(requested_ticks);