# HG changeset patch # User Florian Pose # Date 1215001008 0 # Node ID 088a613069306d1ec85d40b4ff57f001cc07ddfc # Parent ef1266652c4df6e61f23000dbe0bf41b623baa19 Removed MSR example; adapted RTAI example; README files for examples. diff -r ef1266652c4d -r 088a61306930 TODO --- a/TODO Wed Jul 02 11:26:51 2008 +0000 +++ b/TODO Wed Jul 02 12:16:48 2008 +0000 @@ -8,22 +8,22 @@ 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 @@ * 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: diff -r ef1266652c4d -r 088a61306930 configure.ac --- a/configure.ac Wed Jul 02 11:26:51 2008 +0000 +++ b/configure.ac Wed Jul 02 12:16:48 2008 +0000 @@ -384,36 +384,6 @@ AC_SUBST(RTAI_DIR,[$rtaidir]) #------------------------------------------------------------------------------ -# MSR path (optional) -#------------------------------------------------------------------------------ - -AC_ARG_WITH([msr-dir], - AC_HELP_STRING( - [--with-msr-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 @@ examples/Makefile examples/mini/Kbuild examples/mini/Makefile - examples/msr/Kbuild - examples/msr/Makefile examples/rtai/Kbuild examples/rtai/Makefile include/Makefile diff -r ef1266652c4d -r 088a61306930 examples/Makefile.am --- a/examples/Makefile.am Wed Jul 02 11:26:51 2008 +0000 +++ b/examples/Makefile.am Wed Jul 02 12:16:48 2008 +0000 @@ -31,6 +31,6 @@ # #------------------------------------------------------------------------------ -DIST_SUBDIRS = mini rtai msr +DIST_SUBDIRS = mini rtai #------------------------------------------------------------------------------ diff -r ef1266652c4d -r 088a61306930 examples/mini/README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/mini/README Wed Jul 02 12:16:48 2008 +0000 @@ -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. + +------------------------------------------------------------------------------- diff -r ef1266652c4d -r 088a61306930 examples/mini/mini.c --- a/examples/mini/mini.c Wed Jul 02 11:26:51 2008 +0000 +++ b/examples/mini/mini.c Wed Jul 02 12:16:48 2008 +0000 @@ -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_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 +#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; -#define Beckhoff_EL2004 0x00000002, 0x07D43052 -#define Beckhoff_EL3162 0x00000002, 0x0C5A3052 - -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}, - {} -}; - /*****************************************************************************/ #ifdef CONFIGURE_PDOS @@ -312,7 +313,7 @@ } 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 @@ 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; } diff -r ef1266652c4d -r 088a61306930 examples/msr/Kbuild.in --- a/examples/msr/Kbuild.in Wed Jul 02 11:26:51 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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 - -#------------------------------------------------------------------------------ diff -r ef1266652c4d -r 088a61306930 examples/msr/Makefile.am --- a/examples/msr/Makefile.am Wed Jul 02 11:26:51 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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 - -#------------------------------------------------------------------------------ diff -r ef1266652c4d -r 088a61306930 examples/msr/init.sh --- a/examples/msr/init.sh Wed Jul 02 11:26:51 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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 - -#------------------------------------------------------------------------------ - -# - -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 - -# - -#------------------------------------------------------------------------------ - -. /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 \ No newline at end of file diff -r ef1266652c4d -r 088a61306930 examples/msr/libm.o_shipped --- a/examples/msr/libm.o_shipped Wed Jul 02 11:26:51 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -/usr/lib/libm.a \ No newline at end of file diff -r ef1266652c4d -r 088a61306930 examples/msr/msr_sample.c --- a/examples/msr/msr_sample.c Wed Jul 02 11:26:51 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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 - -// RTAI -#include "rtai_sched.h" -#include "rtai_sem.h" - -// RT_lib -#include -#include -#include - -// 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 "); -MODULE_DESCRIPTION("EtherCAT RTAI MSR sample module"); - -module_init(init_mod); -module_exit(cleanup_mod); - -/*****************************************************************************/ diff -r ef1266652c4d -r 088a61306930 examples/msr/msrserv.pl --- a/examples/msr/msrserv.pl Wed Jul 02 11:26:51 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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 () { - 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 = "\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(); -} diff -r ef1266652c4d -r 088a61306930 examples/rtai/README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/rtai/README Wed Jul 02 12:16:48 2008 +0000 @@ -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. + +------------------------------------------------------------------------------- diff -r ef1266652c4d -r 088a61306930 examples/rtai/rtai_sample.c --- a/examples/rtai/rtai_sample.c Wed Jul 02 11:26:51 2008 +0000 +++ b/examples/rtai/rtai_sample.c Wed Jul 02 12:16:48 2008 +0000 @@ -37,100 +37,210 @@ #include // RTAI -#include "rtai_sched.h" -#include "rtai_sem.h" +#include +#include // 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; - -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}, +/*****************************************************************************/ + +// process data +static uint8_t *domain1_pd; // process data memory + +#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 @@ 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 @@ 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 @@ 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);