Hi everyone,
I've a machine with 25 Hiwin servos working usign linuxcnc and Sittner HAL
etherCAT component <https://github.com/sittner/linuxcnc-ethercat>
My problem is I am suffering some random desynchronizations. Dmesg output:
(full text as attachment dmesgoutput.txt)
Jan 22 12:01:16 kernel: [11788.586944] EtherCAT 0: Domain 0: 6 working
counter changes - now 75/75.
at 12:01:16 the machine is working ok and everything is well but suddenly
(most of times without doing anything on the application)
Jan 22 13:04:31 kernel: [15578.066190] EtherCAT 0: Domain 0: Working
counter changed to 27/75.
Jan 22 13:04:31 kernel: [15578.074179] EtherCAT 0: 9 slave(s) responding on
main device.
Jan 22 13:04:31 kernel: [15578.074181] EtherCAT 0: Slave states on main
device: OP.
Jan 22 13:04:31 kernel: [15578.077148] EtherCAT 0: Scanning bus.
and so on... until it gets to sync everything again.
I've reading a lot of the etherlab users mailing list and I've found
something I think could help me with the problem.
The application that I'm running (what most of people of linuxcnc run) is
using the first method that Graeme Foot describes here
<http://lists.etherlab.org/pipermail/etherlab-users/2016/003013.html>
In the file lcec_main.c
<https://github.com/sittner/linuxcnc-ethercat/blob/master/src/lcec_main.c>
from line 1119 you can see how he makes it.
I've tried to modify it in many ways but I have not been able to make it
works propertly.
With this version
<https://github.com/narogon/linuxcnc-ethercat/commit/e4ab86ba6167ced532e49904059df580062b2d97#diff-059a684a933530837771b5a249433ff3>
(also as attachment lcec_main.c) I get the servos sync and OP but it seems
that the PDO doesn't arrive for some of the slaves (no idea why).
Could you help me with this problem? Could be the DC method the origin of
the desynchronizations or must be something different? How could I get the
application working with the 2nd mode that Foot described?
Many thanks,
Best Regards
Nacho Rosales
Jan 22 12:01:16 kernel: [11788.586944] EtherCAT 0: Domain 0: 6 working counter
changes - now 75/75.
Jan 22 12:10:17 kernel: [12329.376472] [Hardware Error]: Machine check events
logged
Jan 22 12:22:32 kernel: [13063.594366] [Hardware Error]: Machine check events
logged
Jan 22 12:36:27 kernel: [13896.896685] [Hardware Error]: Machine check events
logged
Jan 22 12:50:51 kernel: [14759.573840] [Hardware Error]: Machine check events
logged
Jan 22 13:04:31 kernel: [15578.066190] EtherCAT 0: Domain 0: Working counter
changed to 27/75.
Jan 22 13:04:31 kernel: [15578.074179] EtherCAT 0: 9 slave(s) responding on
main device.
Jan 22 13:04:31 kernel: [15578.074181] EtherCAT 0: Slave states on main device:
OP.
Jan 22 13:04:31 kernel: [15578.077148] EtherCAT 0: Scanning bus.
Jan 22 13:04:32 kernel: [15579.680451] EtherCAT 0: Domain 0: Working counter
changed to 28/75.
Jan 22 13:04:33 kernel: [15580.274450] EtherCAT WARNING: Datagram f69602cc
(domain0-0-main) was SKIPPED 1 time.
Jan 22 13:04:33 kernel: [15580.621858] EtherCAT WARNING 0: 1 datagram TIMED OUT!
Jan 22 13:04:33 kernel: [15580.681762] EtherCAT 0: Domain 0: 2 working counter
changes - now 42/75.
Jan 22 13:04:33 kernel: [15580.955298] EtherCAT 0: Bus scanning completed in
2884 ms.
Jan 22 13:04:33 kernel: [15580.955300] EtherCAT 0: Using slave 0 as DC
reference clock.
Jan 22 13:04:33 kernel: [15580.955302] EtherCAT ERROR 0: Failed to calculate
bus topology.
Jan 22 13:04:33 kernel: [15580.977276] EtherCAT 0: 37 slave(s) responding on
main device.
Jan 22 13:04:33 kernel: [15580.977277] EtherCAT 0: Slave states on main device:
INIT, PREOP, SAFEOP, OP + ERROR.
Jan 22 13:04:33 kernel: [15580.980253] EtherCAT 0: Scanning bus.
Jan 22 13:04:36 kernel: [15583.850415] EtherCAT WARNING 0-9: Slave has state
error bit set (SAFEOP + ERROR)!
Jan 22 13:04:37 kernel: [15584.157896] EtherCAT WARNING 0-10: Slave has state
error bit set (INIT + ERROR)!
Jan 22 13:04:37 kernel: [15584.475361] EtherCAT WARNING 0-11: Slave has state
error bit set (SAFEOP + ERROR)!
Jan 22 13:04:37 kernel: [15584.785838] EtherCAT WARNING 0-12: Slave has state
error bit set (SAFEOP + ERROR)!
Jan 22 13:04:38 kernel: [15585.106297] EtherCAT WARNING 0-13: Slave has state
error bit set (SAFEOP + ERROR)!
Jan 22 13:04:38 kernel: [15585.425758] EtherCAT WARNING 0-14: Slave has state
error bit set (SAFEOP + ERROR)!
Jan 22 13:04:38 kernel: [15585.746218] EtherCAT WARNING 0-15: Slave has state
error bit set (SAFEOP + ERROR)!
Jan 22 13:04:39 kernel: [15586.067676] EtherCAT WARNING 0-16: Slave has state
error bit set (SAFEOP + ERROR)!
Jan 22 13:04:39 kernel: [15586.376157] EtherCAT WARNING 0-17: Slave has state
error bit set (SAFEOP + ERROR)!
Jan 22 13:04:39 kernel: [15586.683638] EtherCAT WARNING 0-18: Slave has state
error bit set (SAFEOP + ERROR)!
Jan 22 13:04:39 kernel: [15586.994114] EtherCAT WARNING 0-19: Slave has state
error bit set (SAFEOP + ERROR)!
Jan 22 13:04:40 kernel: [15587.315572] EtherCAT WARNING 0-20: Slave has state
error bit set (SAFEOP + ERROR)!
Jan 22 13:04:40 kernel: [15587.636033] EtherCAT WARNING 0-21: Slave has state
error bit set (SAFEOP + ERROR)!
Jan 22 13:04:40 kernel: [15587.944511] EtherCAT WARNING 0-22: Slave has state
error bit set (SAFEOP + ERROR)!
Jan 22 13:04:41 kernel: [15588.253990] EtherCAT WARNING 0-23: Slave has state
error bit set (SAFEOP + ERROR)!
Jan 22 13:04:41 kernel: [15588.574449] EtherCAT WARNING 0-24: Slave has state
error bit set (SAFEOP + ERROR)!
Jan 22 13:04:45 kernel: [15592.503824] EtherCAT 0: Bus scanning completed in
11540 ms.
Jan 22 13:04:45 kernel: [15592.503827] EtherCAT 0: Using slave 0 as DC
reference clock.
Jan 22 13:04:45 kernel: [15592.580710] EtherCAT 0: Slave states on main device:
PREOP, SAFEOP, OP + ERROR.
Jan 22 13:04:45 kernel: [15592.621625] EtherCAT ERROR 0-9: AL status message
0x001B: "Sync manager watchdog".
Jan 22 13:04:45 kernel: [15592.624622] EtherCAT 0-9: Acknowledged state SAFEOP.
Jan 22 13:04:45 kernel: [15592.624626] EtherCAT 0: Domain 0: Working counter
changed to 44/75.
Jan 22 13:04:46 kernel: [15593.565035] EtherCAT ERROR 0-11: AL status message
0x001B: "Sync manager watchdog".
Jan 22 13:04:46 kernel: [15593.568032] EtherCAT 0-11: Acknowledged state SAFEOP.
Jan 22 13:04:46 kernel: [15593.627934] EtherCAT 0: Domain 0: 6 working counter
changes - now 45/75.
Jan 22 13:04:46 kernel: [15593.680840] EtherCAT ERROR 0-12: AL status message
0x001B: "Sync manager watchdog".
Jan 22 13:04:46 kernel: [15593.683837] EtherCAT 0-12: Acknowledged state SAFEOP.
Jan 22 13:04:46 kernel: [15593.885497] EtherCAT ERROR 0-13: AL status message
0x001B: "Sync manager watchdog".
Jan 22 13:04:46 kernel: [15593.889490] EtherCAT 0-13: Acknowledged state SAFEOP.
Jan 22 13:04:47 kernel: [15594.105128] EtherCAT ERROR 0-14: AL status message
0x001B: "Sync manager watchdog".
Jan 22 13:04:47 kernel: [15594.109120] EtherCAT 0-14: Acknowledged state SAFEOP.
Jan 22 13:04:47 kernel: [15594.324757] EtherCAT ERROR 0-15: AL status message
0x001B: "Sync manager watchdog".
Jan 22 13:04:47 kernel: [15594.328750] EtherCAT 0-15: Acknowledged state SAFEOP.
Jan 22 13:04:47 kernel: [15594.544387] EtherCAT ERROR 0-16: AL status message
0x001B: "Sync manager watchdog".
Jan 22 13:04:47 kernel: [15594.548379] EtherCAT 0-16: Acknowledged state SAFEOP.
Jan 22 13:04:47 kernel: [15594.631241] EtherCAT 0: Domain 0: 21 working counter
changes - now 56/75.
Jan 22 13:04:47 kernel: [15594.764017] EtherCAT ERROR 0-17: AL status message
0x001B: "Sync manager watchdog".
Jan 22 13:04:47 kernel: [15594.768009] EtherCAT 0-17: Acknowledged state SAFEOP.
Jan 22 13:04:47 kernel: [15594.983647] EtherCAT ERROR 0-18: AL status message
0x001B: "Sync manager watchdog".
Jan 22 13:04:47 kernel: [15594.987639] EtherCAT 0-18: Acknowledged state SAFEOP.
Jan 22 13:04:48 kernel: [15595.203276] EtherCAT ERROR 0-19: AL status message
0x001B: "Sync manager watchdog".
Jan 22 13:04:48 kernel: [15595.207269] EtherCAT 0-19: Acknowledged state SAFEOP.
Jan 22 13:04:48 kernel: [15595.422915] EtherCAT ERROR 0-20: AL status message
0x001B: "Sync manager watchdog".
Jan 22 13:04:48 kernel: [15595.426911] EtherCAT 0-20: Acknowledged state SAFEOP.
Jan 22 13:04:48 kernel: [15595.633551] EtherCAT 0: Domain 0: 17 working counter
changes - now 66/75.
Jan 22 13:04:48 kernel: [15595.642535] EtherCAT ERROR 0-21: AL status message
0x001B: "Sync manager watchdog".
Jan 22 13:04:48 kernel: [15595.646528] EtherCAT 0-21: Acknowledged state SAFEOP.
Jan 22 13:04:48 kernel: [15595.862166] EtherCAT ERROR 0-22: AL status message
0x001B: "Sync manager watchdog".
Jan 22 13:04:48 kernel: [15595.866158] EtherCAT 0-22: Acknowledged state SAFEOP.
Jan 22 13:04:49 kernel: [15596.081795] EtherCAT ERROR 0-23: AL status message
0x001B: "Sync manager watchdog".
Jan 22 13:04:49 kernel: [15596.085788] EtherCAT 0-23: Acknowledged state SAFEOP.
Jan 22 13:04:49 kernel: [15596.301425] EtherCAT ERROR 0-24: AL status message
0x001B: "Sync manager watchdog".
Jan 22 13:04:49 kernel: [15596.305418] EtherCAT 0-24: Acknowledged state SAFEOP.
Jan 22 13:04:49 kernel: [15596.572968] EtherCAT ERROR 0-0: AL status message
0x001A: "Synchronization error".
Jan 22 13:04:49 kernel: [15596.576959] EtherCAT 0-0: Acknowledged state SAFEOP.
Jan 22 13:04:49 kernel: [15596.635861] EtherCAT 0: Domain 0: 18 working counter
changes - now 72/75.
Jan 22 13:04:49 kernel: [15596.796591] EtherCAT ERROR 0-2: AL status message
0x001A: "Synchronization error".
Jan 22 13:04:49 kernel: [15596.800583] EtherCAT 0-2: Acknowledged state SAFEOP.
Jan 22 13:04:49 kernel: [15597.020213] EtherCAT ERROR 0-4: AL status message
0x001A: "Synchronization error".
Jan 22 13:04:49 kernel: [15597.024206] EtherCAT 0-4: Acknowledged state SAFEOP.
Jan 22 13:04:50 kernel: [15597.244834] EtherCAT ERROR 0-6: AL status message
0x001A: "Synchronization error".
Jan 22 13:04:50 kernel: [15597.248827] EtherCAT 0-6: Acknowledged state SAFEOP.
Jan 22 13:04:50 kernel: [15597.468458] EtherCAT ERROR 0-8: AL status message
0x001A: "Synchronization error".
Jan 22 13:04:50 kernel: [15597.472450] EtherCAT 0-8: Acknowledged state SAFEOP.
Jan 22 13:04:50 kernel: [15597.639169] EtherCAT 0: Domain 0: 12 working counter
changes - now 72/75.
Jan 22 13:04:50 kernel: [15597.797923] EtherCAT 0: Slave states on main device:
PREOP, OP.
Jan 22 13:04:51 kernel: [15598.640501] EtherCAT 0: Domain 0: 2 working counter
changes - now 75/75.
//
// Copyright (C) 2012 Sascha Ittner <[email protected]>
//
// This program 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.
//
// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//
#include "lcec.h"
#include "lcec_generic.h"
#include "lcec_ek1100.h"
#include "lcec_el1xxx.h"
#include "lcec_el1252.h"
#include "lcec_el2xxx.h"
#include "lcec_el2202.h"
#include "lcec_el31x2.h"
#include "lcec_el3255.h"
#include "lcec_el40x1.h"
#include "lcec_el40x2.h"
#include "lcec_el41x2.h"
#include "lcec_el5101.h"
#include "lcec_el5151.h"
#include "lcec_el5152.h"
#include "lcec_el2521.h"
#include "lcec_el7041_1000.h"
#include "lcec_el7342.h"
#include "lcec_el95xx.h"
#include "lcec_em7004.h"
#include "lcec_stmds5k.h"
#include "lcec_deasda.h"
#include "rtapi_app.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sascha Ittner <[email protected]>");
MODULE_DESCRIPTION("Driver for EtherCAT devices");
typedef struct lcec_typelist {
LCEC_SLAVE_TYPE_T type;
uint32_t vid;
uint32_t pid;
int pdo_entry_count;
lcec_slave_init_t proc_init;
} lcec_typelist_t;
static const lcec_typelist_t types[] = {
// bus coupler
{ lcecSlaveTypeEK1100, LCEC_EK1100_VID, LCEC_EK1100_PID, LCEC_EK1100_PDOS, NULL},
// digital in
{ lcecSlaveTypeEL1002, LCEC_EL1xxx_VID, LCEC_EL1002_PID, LCEC_EL1002_PDOS, lcec_el1xxx_init},
{ lcecSlaveTypeEL1004, LCEC_EL1xxx_VID, LCEC_EL1004_PID, LCEC_EL1004_PDOS, lcec_el1xxx_init},
{ lcecSlaveTypeEL1008, LCEC_EL1xxx_VID, LCEC_EL1008_PID, LCEC_EL1008_PDOS, lcec_el1xxx_init},
{ lcecSlaveTypeEL1012, LCEC_EL1xxx_VID, LCEC_EL1012_PID, LCEC_EL1012_PDOS, lcec_el1xxx_init},
{ lcecSlaveTypeEL1014, LCEC_EL1xxx_VID, LCEC_EL1014_PID, LCEC_EL1014_PDOS, lcec_el1xxx_init},
{ lcecSlaveTypeEL1018, LCEC_EL1xxx_VID, LCEC_EL1018_PID, LCEC_EL1018_PDOS, lcec_el1xxx_init},
{ lcecSlaveTypeEL1024, LCEC_EL1xxx_VID, LCEC_EL1024_PID, LCEC_EL1024_PDOS, lcec_el1xxx_init},
{ lcecSlaveTypeEL1034, LCEC_EL1xxx_VID, LCEC_EL1034_PID, LCEC_EL1034_PDOS, lcec_el1xxx_init},
{ lcecSlaveTypeEL1084, LCEC_EL1xxx_VID, LCEC_EL1084_PID, LCEC_EL1084_PDOS, lcec_el1xxx_init},
{ lcecSlaveTypeEL1088, LCEC_EL1xxx_VID, LCEC_EL1088_PID, LCEC_EL1088_PDOS, lcec_el1xxx_init},
{ lcecSlaveTypeEL1094, LCEC_EL1xxx_VID, LCEC_EL1094_PID, LCEC_EL1094_PDOS, lcec_el1xxx_init},
{ lcecSlaveTypeEL1098, LCEC_EL1xxx_VID, LCEC_EL1098_PID, LCEC_EL1098_PDOS, lcec_el1xxx_init},
{ lcecSlaveTypeEL1104, LCEC_EL1xxx_VID, LCEC_EL1104_PID, LCEC_EL1104_PDOS, lcec_el1xxx_init},
{ lcecSlaveTypeEL1114, LCEC_EL1xxx_VID, LCEC_EL1114_PID, LCEC_EL1114_PDOS, lcec_el1xxx_init},
{ lcecSlaveTypeEL1124, LCEC_EL1xxx_VID, LCEC_EL1124_PID, LCEC_EL1124_PDOS, lcec_el1xxx_init},
{ lcecSlaveTypeEL1134, LCEC_EL1xxx_VID, LCEC_EL1134_PID, LCEC_EL1134_PDOS, lcec_el1xxx_init},
{ lcecSlaveTypeEL1144, LCEC_EL1xxx_VID, LCEC_EL1144_PID, LCEC_EL1144_PDOS, lcec_el1xxx_init},
{ lcecSlaveTypeEL1252, LCEC_EL1252_VID, LCEC_EL1252_PID, LCEC_EL1252_PDOS, lcec_el1252_init}, // 2 fast channels with timestamp
{ lcecSlaveTypeEL1808, LCEC_EL1xxx_VID, LCEC_EL1808_PID, LCEC_EL1808_PDOS, lcec_el1xxx_init},
{ lcecSlaveTypeEL1809, LCEC_EL1xxx_VID, LCEC_EL1809_PID, LCEC_EL1809_PDOS, lcec_el1xxx_init},
// digital out
{ lcecSlaveTypeEL2002, LCEC_EL2xxx_VID, LCEC_EL2002_PID, LCEC_EL2002_PDOS, lcec_el2xxx_init},
{ lcecSlaveTypeEL2004, LCEC_EL2xxx_VID, LCEC_EL2004_PID, LCEC_EL2004_PDOS, lcec_el2xxx_init},
{ lcecSlaveTypeEL2008, LCEC_EL2xxx_VID, LCEC_EL2008_PID, LCEC_EL2008_PDOS, lcec_el2xxx_init},
{ lcecSlaveTypeEL2022, LCEC_EL2xxx_VID, LCEC_EL2022_PID, LCEC_EL2022_PDOS, lcec_el2xxx_init},
{ lcecSlaveTypeEL2024, LCEC_EL2xxx_VID, LCEC_EL2024_PID, LCEC_EL2024_PDOS, lcec_el2xxx_init},
{ lcecSlaveTypeEL2032, LCEC_EL2xxx_VID, LCEC_EL2032_PID, LCEC_EL2032_PDOS, lcec_el2xxx_init},
{ lcecSlaveTypeEL2034, LCEC_EL2xxx_VID, LCEC_EL2034_PID, LCEC_EL2034_PDOS, lcec_el2xxx_init},
{ lcecSlaveTypeEL2042, LCEC_EL2xxx_VID, LCEC_EL2042_PID, LCEC_EL2042_PDOS, lcec_el2xxx_init},
{ lcecSlaveTypeEL2084, LCEC_EL2xxx_VID, LCEC_EL2084_PID, LCEC_EL2084_PDOS, lcec_el2xxx_init},
{ lcecSlaveTypeEL2088, LCEC_EL2xxx_VID, LCEC_EL2088_PID, LCEC_EL2088_PDOS, lcec_el2xxx_init},
{ lcecSlaveTypeEL2124, LCEC_EL2xxx_VID, LCEC_EL2124_PID, LCEC_EL2124_PDOS, lcec_el2xxx_init},
{ lcecSlaveTypeEL2202, LCEC_EL2202_VID, LCEC_EL2202_PID, LCEC_EL2202_PDOS, lcec_el2202_init}, // 2 fast channels with tristate
{ lcecSlaveTypeEL2622, LCEC_EL2xxx_VID, LCEC_EL2622_PID, LCEC_EL2622_PDOS, lcec_el2xxx_init},
{ lcecSlaveTypeEL2808, LCEC_EL2xxx_VID, LCEC_EL2808_PID, LCEC_EL2808_PDOS, lcec_el2xxx_init},
{ lcecSlaveTypeEL2798, LCEC_EL2xxx_VID, LCEC_EL2798_PID, LCEC_EL2798_PDOS, lcec_el2xxx_init},
{ lcecSlaveTypeEL2809, LCEC_EL2xxx_VID, LCEC_EL2809_PID, LCEC_EL2809_PDOS, lcec_el2xxx_init},
{ lcecSlaveTypeEP2028, LCEC_EL2xxx_VID, LCEC_EP2028_PID, LCEC_EP2028_PDOS, lcec_el2xxx_init},
// analog in, 2ch, 16 bits
{ lcecSlaveTypeEL3102, LCEC_EL31x2_VID, LCEC_EL3102_PID, LCEC_EL31x2_PDOS, lcec_el31x2_init},
{ lcecSlaveTypeEL3112, LCEC_EL31x2_VID, LCEC_EL3112_PID, LCEC_EL31x2_PDOS, lcec_el31x2_init},
{ lcecSlaveTypeEL3122, LCEC_EL31x2_VID, LCEC_EL3122_PID, LCEC_EL31x2_PDOS, lcec_el31x2_init},
{ lcecSlaveTypeEL3142, LCEC_EL31x2_VID, LCEC_EL3142_PID, LCEC_EL31x2_PDOS, lcec_el31x2_init},
{ lcecSlaveTypeEL3152, LCEC_EL31x2_VID, LCEC_EL3152_PID, LCEC_EL31x2_PDOS, lcec_el31x2_init},
{ lcecSlaveTypeEL3162, LCEC_EL31x2_VID, LCEC_EL3162_PID, LCEC_EL31x2_PDOS, lcec_el31x2_init},
// analog in, 5ch, 16 bits
{ lcecSlaveTypeEL3255, LCEC_EL3255_VID, LCEC_EL3255_PID, LCEC_EL3255_PDOS, lcec_el3255_init},
// analog out, 1ch, 12 bits
{ lcecSlaveTypeEL4001, LCEC_EL40x1_VID, LCEC_EL4001_PID, LCEC_EL40x1_PDOS, lcec_el40x1_init},
{ lcecSlaveTypeEL4011, LCEC_EL40x1_VID, LCEC_EL4011_PID, LCEC_EL40x1_PDOS, lcec_el40x1_init},
{ lcecSlaveTypeEL4021, LCEC_EL40x1_VID, LCEC_EL4021_PID, LCEC_EL40x1_PDOS, lcec_el40x1_init},
{ lcecSlaveTypeEL4031, LCEC_EL40x1_VID, LCEC_EL4031_PID, LCEC_EL40x1_PDOS, lcec_el40x1_init},
// analog out, 2ch, 12 bits
{ lcecSlaveTypeEL4002, LCEC_EL40x2_VID, LCEC_EL4002_PID, LCEC_EL40x2_PDOS, lcec_el40x2_init},
{ lcecSlaveTypeEL4012, LCEC_EL40x2_VID, LCEC_EL4012_PID, LCEC_EL40x2_PDOS, lcec_el40x2_init},
{ lcecSlaveTypeEL4022, LCEC_EL40x2_VID, LCEC_EL4022_PID, LCEC_EL40x2_PDOS, lcec_el40x2_init},
{ lcecSlaveTypeEL4032, LCEC_EL40x2_VID, LCEC_EL4032_PID, LCEC_EL40x2_PDOS, lcec_el40x2_init},
// analog out, 2ch, 16 bits
{ lcecSlaveTypeEL4102, LCEC_EL41x2_VID, LCEC_EL4102_PID, LCEC_EL41x2_PDOS, lcec_el41x2_init},
{ lcecSlaveTypeEL4112, LCEC_EL41x2_VID, LCEC_EL4112_PID, LCEC_EL41x2_PDOS, lcec_el41x2_init},
{ lcecSlaveTypeEL4122, LCEC_EL41x2_VID, LCEC_EL4122_PID, LCEC_EL41x2_PDOS, lcec_el41x2_init},
{ lcecSlaveTypeEL4132, LCEC_EL41x2_VID, LCEC_EL4132_PID, LCEC_EL41x2_PDOS, lcec_el41x2_init},
// encoder inputs
{ lcecSlaveTypeEL5101, LCEC_EL5101_VID, LCEC_EL5101_PID, LCEC_EL5101_PDOS, lcec_el5101_init},
{ lcecSlaveTypeEL5151, LCEC_EL5151_VID, LCEC_EL5151_PID, LCEC_EL5151_PDOS, lcec_el5151_init},
{ lcecSlaveTypeEL5152, LCEC_EL5152_VID, LCEC_EL5152_PID, LCEC_EL5152_PDOS, lcec_el5152_init},
// pulse train (stepper) output
{ lcecSlaveTypeEL2521, LCEC_EL2521_VID, LCEC_EL2521_PID, LCEC_EL2521_PDOS, lcec_el2521_init},
// stepper
{ lcecSlaveTypeEL7041_1000, LCEC_EL7041_1000_VID, LCEC_EL7041_1000_PID, LCEC_EL7041_1000_PDOS, lcec_el7041_1000_init},
// dc servo
{ lcecSlaveTypeEL7342, LCEC_EL7342_VID, LCEC_EL7342_PID, LCEC_EL7342_PDOS, lcec_el7342_init},
// power supply
{ lcecSlaveTypeEL9505, LCEC_EL95xx_VID, LCEC_EL9505_PID, LCEC_EL95xx_PDOS, lcec_el95xx_init},
{ lcecSlaveTypeEL9508, LCEC_EL95xx_VID, LCEC_EL9508_PID, LCEC_EL95xx_PDOS, lcec_el95xx_init},
{ lcecSlaveTypeEL9510, LCEC_EL95xx_VID, LCEC_EL9510_PID, LCEC_EL95xx_PDOS, lcec_el95xx_init},
{ lcecSlaveTypeEL9512, LCEC_EL95xx_VID, LCEC_EL9512_PID, LCEC_EL95xx_PDOS, lcec_el95xx_init},
{ lcecSlaveTypeEL9515, LCEC_EL95xx_VID, LCEC_EL9515_PID, LCEC_EL95xx_PDOS, lcec_el95xx_init},
// multi axis interface
{ lcecSlaveTypeEM7004, LCEC_EM7004_VID, LCEC_EM7004_PID, LCEC_EM7004_PDOS, lcec_em7004_init},
// stoeber MDS5000 series
{ lcecSlaveTypeStMDS5k, LCEC_STMDS5K_VID, LCEC_STMDS5K_PID, LCEC_STMDS5K_PDOS, lcec_stmds5k_init},
// Delta ASDA series
{ lcecSlaveTypeDeASDA, LCEC_DEASDA_VID, LCEC_DEASDA_PID, LCEC_DEASDA_PDOS, lcec_deasda_init},
{ lcecSlaveTypeInvalid }
};
static lcec_master_t *first_master = NULL;
static lcec_master_t *last_master = NULL;
static int comp_id = -1;
static lcec_master_data_t *global_hal_data;
static ec_master_state_t global_ms;
int lcec_parse_config(void);
void lcec_clear_config(void);
void lcec_request_lock(void *data);
void lcec_release_lock(void *data);
lcec_master_data_t *lcec_init_master_hal(const char *pfx);
lcec_slave_state_t *lcec_init_slave_state_hal(char *master_name, char *slave_name);
void lcec_update_master_hal(lcec_master_data_t *hal_data, ec_master_state_t *ms);
void lcec_update_slave_state_hal(lcec_slave_state_t *hal_data, ec_slave_config_state_t *ss);
void lcec_read_all(void *arg, long period);
void lcec_write_all(void *arg, long period);
void lcec_read_master(void *arg, long period);
void lcec_write_master(void *arg, long period);
int rtapi_app_main(void) {
int slave_count;
lcec_master_t *master;
lcec_slave_t *slave;
char name[HAL_NAME_LEN + 1];
ec_pdo_entry_reg_t *pdo_entry_regs;
lcec_slave_sdoconf_t *sdo_config;
struct timeval tv;
// connect to the HAL
if ((comp_id = hal_init (LCEC_MODULE_NAME)) < 0) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "hal_init() failed\n");
goto fail0;
}
// parse configuration
if ((slave_count = lcec_parse_config()) < 0) {
goto fail1;
}
// init global hal data
if ((global_hal_data = lcec_init_master_hal(LCEC_MODULE_NAME)) == NULL) {
goto fail2;
}
// initialize masters
for (master = first_master; master != NULL; master = master->next) {
// request ethercat master
if (!(master->master = ecrt_request_master(master->index))) {
rtapi_print_msg (RTAPI_MSG_ERR, LCEC_MSG_PFX "requesting master %s (index %d) failed\n", master->name, master->index);
goto fail2;
}
#ifdef __KERNEL__
// register callbacks
ecrt_master_callbacks(master->master, lcec_request_lock, lcec_release_lock, master);
#endif
// create domain
if (!(master->domain = ecrt_master_create_domain(master->master))) {
rtapi_print_msg (RTAPI_MSG_ERR, LCEC_MSG_PFX "master %s domain creation failed\n", master->name);
goto fail2;
}
// initialize slaves
pdo_entry_regs = master->pdo_entry_regs;
for (slave = master->first_slave; slave != NULL; slave = slave->next) {
// read slave config
if (!(slave->config = ecrt_master_slave_config(master->master, 0, slave->index, slave->vid, slave->pid))) {
rtapi_print_msg (RTAPI_MSG_ERR, LCEC_MSG_PFX "fail to read slave %s.%s configuration\n", master->name, slave->name);
goto fail2;
}
// initialize sdos
if (slave->sdo_config != NULL) {
for (sdo_config = slave->sdo_config; sdo_config->index != 0xffff; sdo_config = (lcec_slave_sdoconf_t *) &sdo_config->data[sdo_config->length]) {
if (sdo_config->subindex == LCEC_CONF_SDO_COMPLETE_SUBIDX) {
if (ecrt_slave_config_complete_sdo(slave->config, sdo_config->index, &sdo_config->data[0], sdo_config->length) != 0) {
rtapi_print_msg (RTAPI_MSG_ERR, LCEC_MSG_PFX "fail to configure slave %s.%s sdo %04x (complete)\n", master->name, slave->name, sdo_config->index);
}
} else {
if (ecrt_slave_config_sdo(slave->config, sdo_config->index, sdo_config->subindex, &sdo_config->data[0], sdo_config->length) != 0) {
rtapi_print_msg (RTAPI_MSG_ERR, LCEC_MSG_PFX "fail to configure slave %s.%s sdo %04x:%02x\n", master->name, slave->name, sdo_config->index, sdo_config->subindex);
}
}
}
}
// setup pdos
if (slave->proc_init != NULL) {
if ((slave->proc_init(comp_id, slave, pdo_entry_regs)) != 0) {
goto fail2;
}
}
pdo_entry_regs += slave->pdo_entry_count;
// configure dc for this slave
if (slave->dc_conf != NULL) {
ecrt_slave_config_dc(slave->config, slave->dc_conf->assignActivate,
slave->dc_conf->sync0Cycle, slave->dc_conf->sync0Shift,
slave->dc_conf->sync1Cycle, slave->dc_conf->sync1Shift);
rtapi_print_msg (RTAPI_MSG_DBG, LCEC_MSG_PFX "configuring DC for slave %s.%s: assignActivate=x%x sync0Cycle=%d sync0Shift=%d sync1Cycle=%d sync1Shift=%d\n",
master->name, slave->name, slave->dc_conf->assignActivate,
slave->dc_conf->sync0Cycle, slave->dc_conf->sync0Shift,
slave->dc_conf->sync1Cycle, slave->dc_conf->sync1Shift);
}
// Configure the slave's watchdog times.
if (slave->wd_conf != NULL) {
ecrt_slave_config_watchdog(slave->config, slave->wd_conf->divider, slave->wd_conf->intervals);
}
// configure slave
if (slave->sync_info != NULL) {
if (ecrt_slave_config_pdos(slave->config, EC_END, slave->sync_info)) {
rtapi_print_msg (RTAPI_MSG_ERR, LCEC_MSG_PFX "fail to configure slave %s.%s\n", master->name, slave->name);
goto fail2;
}
}
// export state pins
if ((slave->hal_state_data = lcec_init_slave_state_hal(master->name, slave->name)) == NULL) {
goto fail2;
}
}
// terminate POD entries
pdo_entry_regs->index = 0;
// register PDO entries
if (ecrt_domain_reg_pdo_entry_list(master->domain, master->pdo_entry_regs)) {
rtapi_print_msg (RTAPI_MSG_ERR, LCEC_MSG_PFX "master %s PDO entry registration failed\n", master->name);
goto fail2;
}
// initialize application time
lcec_gettimeofday(&tv);
master->app_time = EC_TIMEVAL2NANO(tv);
// activating master
if (ecrt_master_activate(master->master)) {
rtapi_print_msg (RTAPI_MSG_ERR, LCEC_MSG_PFX "failed to activate master %s\n", master->name);
goto fail2;
}
// Get internal process data for domain
master->process_data = ecrt_domain_data(master->domain);
master->process_data_len = ecrt_domain_size(master->domain);
// init hal data
rtapi_snprintf(name, HAL_NAME_LEN, "%s.%s", LCEC_MODULE_NAME, master->name);
if ((master->hal_data = lcec_init_master_hal(name)) == NULL) {
goto fail2;
}
// export read function
rtapi_snprintf(name, HAL_NAME_LEN, "%s.%s.read", LCEC_MODULE_NAME, master->name);
if (hal_export_funct(name, lcec_read_master, master, 0, 0, comp_id) != 0) {
rtapi_print_msg (RTAPI_MSG_ERR, LCEC_MSG_PFX "master %s read funct export failed\n", master->name);
goto fail2;
}
// export write function
rtapi_snprintf(name, HAL_NAME_LEN, "%s.%s.write", LCEC_MODULE_NAME, master->name);
if (hal_export_funct(name, lcec_write_master, master, 0, 0, comp_id) != 0) {
rtapi_print_msg (RTAPI_MSG_ERR, LCEC_MSG_PFX "master %s write funct export failed\n", master->name);
goto fail2;
}
}
// export read-all function
rtapi_snprintf(name, HAL_NAME_LEN, "%s.read-all", LCEC_MODULE_NAME);
if (hal_export_funct(name, lcec_read_all, NULL, 0, 0, comp_id) != 0) {
rtapi_print_msg (RTAPI_MSG_ERR, LCEC_MSG_PFX "read-all funct export failed\n");
goto fail2;
}
// export write-all function
rtapi_snprintf(name, HAL_NAME_LEN, "%s.write-all", LCEC_MODULE_NAME);
if (hal_export_funct(name, lcec_write_all, NULL, 0, 0, comp_id) != 0) {
rtapi_print_msg (RTAPI_MSG_ERR, LCEC_MSG_PFX "write-all funct export failed\n");
goto fail2;
}
rtapi_print_msg(RTAPI_MSG_INFO, LCEC_MSG_PFX "installed driver for %d slaves\n", slave_count);
hal_ready (comp_id);
return 0;
fail2:
lcec_clear_config();
fail1:
hal_exit(comp_id);
fail0:
return -EINVAL;
}
void rtapi_app_exit(void) {
lcec_master_t *master;
// deactivate all masters
for (master = first_master; master != NULL; master = master->next) {
ecrt_master_deactivate(master->master);
}
lcec_clear_config();
hal_exit(comp_id);
}
int lcec_parse_config(void) {
int shmem_id;
void *shmem_ptr;
LCEC_CONF_HEADER_T *header;
size_t length;
void *conf;
int slave_count;
const lcec_typelist_t *type;
lcec_master_t *master;
lcec_slave_t *slave;
lcec_slave_dc_t *dc;
lcec_slave_watchdog_t *wd;
ec_pdo_entry_reg_t *pdo_entry_regs;
LCEC_CONF_TYPE_T conf_type;
LCEC_CONF_MASTER_T *master_conf;
LCEC_CONF_SLAVE_T *slave_conf;
LCEC_CONF_DC_T *dc_conf;
LCEC_CONF_WATCHDOG_T *wd_conf;
LCEC_CONF_SYNCMANAGER_T *sm_conf;
LCEC_CONF_PDO_T *pdo_conf;
LCEC_CONF_PDOENTRY_T *pe_conf;
LCEC_CONF_COMPLEXENTRY_T *ce_conf;
LCEC_CONF_SDOCONF_T *sdo_conf;
ec_pdo_entry_info_t *generic_pdo_entries;
ec_pdo_info_t *generic_pdos;
ec_sync_info_t *generic_sync_managers;
lcec_generic_pin_t *generic_hal_data;
hal_pin_dir_t generic_hal_dir;
lcec_slave_sdoconf_t *sdo_config;
// initialize list
first_master = NULL;
last_master = NULL;
// try to get config header
shmem_id = rtapi_shmem_new(LCEC_CONF_SHMEM_KEY, comp_id, sizeof(LCEC_CONF_HEADER_T));
if (shmem_id < 0) {
rtapi_print_msg (RTAPI_MSG_ERR, LCEC_MSG_PFX "couldn't allocate user/RT shared memory\n");
goto fail0;
}
if (lcec_rtapi_shmem_getptr(shmem_id, &shmem_ptr) < 0 ) {
rtapi_print_msg (RTAPI_MSG_ERR, LCEC_MSG_PFX "couldn't map user/RT shared memory\n");
goto fail1;
}
// check magic, get length and close shmem
header = shmem_ptr;
if (header->magic != LCEC_CONF_SHMEM_MAGIC) {
rtapi_print_msg (RTAPI_MSG_ERR, LCEC_MSG_PFX "lcec_conf is not loaded\n");
goto fail1;
}
length = header->length;
rtapi_shmem_delete(shmem_id, comp_id);
// reopen shmem with proper size
shmem_id = rtapi_shmem_new(LCEC_CONF_SHMEM_KEY, comp_id, sizeof(LCEC_CONF_HEADER_T) + length);
if (shmem_id < 0) {
rtapi_print_msg (RTAPI_MSG_ERR, LCEC_MSG_PFX "couldn't allocate user/RT shared memory\n");
goto fail0;
}
if (lcec_rtapi_shmem_getptr(shmem_id, &shmem_ptr) < 0 ) {
rtapi_print_msg (RTAPI_MSG_ERR, LCEC_MSG_PFX "couldn't map user/RT shared memory\n");
goto fail1;
}
// get pointer to config
conf = shmem_ptr + sizeof(LCEC_CONF_HEADER_T);
// process config items
slave_count = 0;
master = NULL;
slave = NULL;
generic_pdo_entries = NULL;
generic_pdos = NULL;
generic_sync_managers = NULL;
generic_hal_data = NULL;
generic_hal_dir = 0;
sdo_config = NULL;
pe_conf = NULL;
while((conf_type = ((LCEC_CONF_NULL_T *)conf)->confType) != lcecConfTypeNone) {
// get type
switch (conf_type) {
case lcecConfTypeMaster:
// get config token
master_conf = (LCEC_CONF_MASTER_T *)conf;
conf += sizeof(LCEC_CONF_MASTER_T);
// alloc master memory
master = lcec_zalloc(sizeof(lcec_master_t));
if (master == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "Unable to allocate master %d structure memory\n", master_conf->index);
goto fail2;
}
// initialize master
master->index = master_conf->index;
strncpy(master->name, master_conf->name, LCEC_CONF_STR_MAXLEN);
master->name[LCEC_CONF_STR_MAXLEN - 1] = 0;
master->mutex = 0;
master->app_time = 0;
master->reference_time = 0;
master->app_time_period = master_conf->appTimePeriod;
master->sync_ref_cnt = 0;
master->sync_ref_cycles = master_conf->refClockSyncCycles;
// add master to list
LCEC_LIST_APPEND(first_master, last_master, master);
break;
case lcecConfTypeSlave:
// get config token
slave_conf = (LCEC_CONF_SLAVE_T *)conf;
conf += sizeof(LCEC_CONF_SLAVE_T);
// check for master
if (master == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "Master node for slave missing\n");
goto fail2;
}
// check for valid slave type
if (slave_conf->type == lcecSlaveTypeGeneric) {
type = NULL;
} else {
for (type = types; type->type != slave_conf->type && type->type != lcecSlaveTypeInvalid; type++);
if (type->type == lcecSlaveTypeInvalid) {
rtapi_print_msg(RTAPI_MSG_WARN, LCEC_MSG_PFX "Invalid slave type %d\n", slave_conf->type);
continue;
}
}
// create new slave
slave = lcec_zalloc(sizeof(lcec_slave_t));
if (slave == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "Unable to allocate slave %s.%s structure memory\n", master->name, slave_conf->name);
goto fail2;
}
// initialize slave
generic_pdo_entries = NULL;
generic_pdos = NULL;
generic_sync_managers = NULL;
generic_hal_data = NULL;
generic_hal_dir = 0;
sdo_config = NULL;
slave->index = slave_conf->index;
strncpy(slave->name, slave_conf->name, LCEC_CONF_STR_MAXLEN);
slave->name[LCEC_CONF_STR_MAXLEN - 1] = 0;
slave->master = master;
// add slave to list
LCEC_LIST_APPEND(master->first_slave, master->last_slave, slave);
if (type != NULL) {
// normal slave
slave->vid = type->vid;
slave->pid = type->pid;
slave->pdo_entry_count = type->pdo_entry_count;
slave->proc_init = type->proc_init;
} else {
// generic slave
slave->vid = slave_conf->vid;
slave->pid = slave_conf->pid;
slave->pdo_entry_count = slave_conf->pdoMappingCount;
slave->proc_init = lcec_generic_init;
// alloc hal memory
if ((generic_hal_data = hal_malloc(sizeof(lcec_generic_pin_t) * slave_conf->pdoMappingCount)) == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "hal_malloc() for slave %s.%s failed\n", master->name, slave_conf->name);
goto fail2;
}
memset(generic_hal_data, 0, sizeof(lcec_generic_pin_t) * slave_conf->pdoMappingCount);
// alloc pdo entry memory
generic_pdo_entries = lcec_zalloc(sizeof(ec_pdo_entry_info_t) * slave_conf->pdoEntryCount);
if (generic_pdo_entries == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "Unable to allocate slave %s.%s generic pdo entry memory\n", master->name, slave_conf->name);
goto fail2;
}
// alloc pdo memory
generic_pdos = lcec_zalloc(sizeof(ec_pdo_info_t) * slave_conf->pdoCount);
if (generic_pdos == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "Unable to allocate slave %s.%s generic pdo memory\n", master->name, slave_conf->name);
goto fail2;
}
// alloc sync manager memory
generic_sync_managers = lcec_zalloc(sizeof(ec_sync_info_t) * (slave_conf->syncManagerCount + 1));
if (generic_sync_managers == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "Unable to allocate slave %s.%s generic sync manager memory\n", master->name, slave_conf->name);
goto fail2;
}
generic_sync_managers->index = 0xff;
}
// alloc sdo config memory
if (slave_conf->sdoConfigLength > 0) {
sdo_config = lcec_zalloc(slave_conf->sdoConfigLength + sizeof(lcec_slave_sdoconf_t));
if (sdo_config == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "Unable to allocate slave %s.%s generic pdo entry memory\n", master->name, slave_conf->name);
goto fail2;
}
}
slave->hal_data = generic_hal_data;
slave->generic_pdo_entries = generic_pdo_entries;
slave->generic_pdos = generic_pdos;
slave->generic_sync_managers = generic_sync_managers;
if (slave_conf->configPdos) {
slave->sync_info = generic_sync_managers;
}
slave->sdo_config = sdo_config;
slave->dc_conf = NULL;
slave->wd_conf = NULL;
// update master's POD entry count
master->pdo_entry_count += slave->pdo_entry_count;
// update slave count
slave_count++;
break;
case lcecConfTypeDcConf:
// get config token
dc_conf = (LCEC_CONF_DC_T *)conf;
conf += sizeof(LCEC_CONF_DC_T);
// check for slave
if (slave == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "Slave node for dc config missing\n");
goto fail2;
}
// check for double dc config
if (slave->dc_conf != NULL) {
rtapi_print_msg(RTAPI_MSG_WARN, LCEC_MSG_PFX "Double dc config for slave %s.%s\n", master->name, slave->name);
continue;
}
// create new dc config
dc = lcec_zalloc(sizeof(lcec_slave_dc_t));
if (dc == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "Unable to allocate slave %s.%s dc config memory\n", master->name, slave->name);
goto fail2;
}
// initialize dc conf
dc->assignActivate = dc_conf->assignActivate;
dc->sync0Cycle = dc_conf->sync0Cycle;
dc->sync0Shift = dc_conf->sync0Shift;
dc->sync1Cycle = dc_conf->sync1Cycle;
dc->sync1Shift = dc_conf->sync1Shift;
// add to slave
slave->dc_conf = dc;
break;
case lcecConfTypeWatchdog:
// get config token
wd_conf = (LCEC_CONF_WATCHDOG_T *)conf;
conf += sizeof(LCEC_CONF_WATCHDOG_T);
// check for slave
if (slave == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "Slave node for watchdog config missing\n");
goto fail2;
}
// check for double wd config
if (slave->wd_conf != NULL) {
rtapi_print_msg(RTAPI_MSG_WARN, LCEC_MSG_PFX "Double watchdog config for slave %s.%s\n", master->name, slave->name);
continue;
}
// create new wd config
wd = lcec_zalloc(sizeof(lcec_slave_watchdog_t));
if (wd == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "Unable to allocate slave %s.%s watchdog config memory\n", master->name, slave->name);
goto fail2;
}
// initialize wd conf
wd->divider = wd_conf->divider;
wd->intervals = wd_conf->intervals;
// add to slave
slave->wd_conf = wd;
break;
case lcecConfTypeSyncManager:
// get config token
sm_conf = (LCEC_CONF_SYNCMANAGER_T *)conf;
conf += sizeof(LCEC_CONF_SYNCMANAGER_T);
// check for syncmanager
if (generic_sync_managers == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "Sync manager for generic device missing\n");
goto fail2;
}
// check for pdos
if (generic_pdos == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "PDOs for generic device missing\n");
goto fail2;
}
// initialize sync manager
generic_sync_managers->index = sm_conf->index;
generic_sync_managers->dir = sm_conf->dir;
generic_sync_managers->n_pdos = sm_conf->pdoCount;
generic_sync_managers->pdos = sm_conf->pdoCount == 0 ? NULL : generic_pdos;
// get hal direction
switch (sm_conf->dir) {
case EC_DIR_INPUT:
generic_hal_dir = HAL_OUT;
break;
case EC_DIR_OUTPUT:
generic_hal_dir = HAL_IN;
break;
default:
generic_hal_dir = 0;
}
// next syncmanager
generic_sync_managers++;
generic_sync_managers->index = 0xff;
break;
case lcecConfTypePdo:
// get config token
pdo_conf = (LCEC_CONF_PDO_T *)conf;
conf += sizeof(LCEC_CONF_PDO_T);
// check for pdos
if (generic_pdos == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "PDOs for generic device missing\n");
goto fail2;
}
// check for pdos entries
if (generic_pdo_entries == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "PDO entries for generic device missing\n");
goto fail2;
}
// initialize pdo
generic_pdos->index = pdo_conf->index;
generic_pdos->n_entries = pdo_conf->pdoEntryCount;
generic_pdos->entries = pdo_conf->pdoEntryCount == 0 ? NULL : generic_pdo_entries;
// next pdo
generic_pdos++;
break;
case lcecConfTypePdoEntry:
// get config token
pe_conf = (LCEC_CONF_PDOENTRY_T *)conf;
conf += sizeof(LCEC_CONF_PDOENTRY_T);
// check for pdos entries
if (generic_pdo_entries == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "PDO entries for generic device missing\n");
goto fail2;
}
// check for hal data
if (generic_hal_data == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "HAL data for generic device missing\n");
goto fail2;
}
// check for hal dir
if (generic_hal_dir == 0) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "HAL direction for generic device missing\n");
goto fail2;
}
// initialize pdo entry
generic_pdo_entries->index = pe_conf->index;
generic_pdo_entries->subindex = pe_conf->subindex;
generic_pdo_entries->bit_length = pe_conf->bitLength;
// initialize hal data
if (pe_conf->halPin[0] != 0) {
strncpy(generic_hal_data->name, pe_conf->halPin, LCEC_CONF_STR_MAXLEN);
generic_hal_data->name[LCEC_CONF_STR_MAXLEN - 1] = 0;
generic_hal_data->type = pe_conf->halType;
generic_hal_data->subType = pe_conf->subType;
generic_hal_data->floatScale = pe_conf->floatScale;
generic_hal_data->floatOffset = pe_conf->floatOffset;
generic_hal_data->bitOffset = 0;
generic_hal_data->bitLength = pe_conf->bitLength;
generic_hal_data->dir = generic_hal_dir;
generic_hal_data->pdo_idx = pe_conf->index;
generic_hal_data->pdo_sidx = pe_conf->subindex;
generic_hal_data++;
}
// next pdo entry
generic_pdo_entries++;
break;
case lcecConfTypeComplexEntry:
// get config token
ce_conf = (LCEC_CONF_COMPLEXENTRY_T *)conf;
conf += sizeof(LCEC_CONF_COMPLEXENTRY_T);
// check for pdoEntry
if (pe_conf == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "pdoEntry for generic device missing\n");
goto fail2;
}
// check for hal data
if (generic_hal_data == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "HAL data for generic device missing\n");
goto fail2;
}
// initialize hal data
if (ce_conf->halPin[0] != 0) {
strncpy(generic_hal_data->name, ce_conf->halPin, LCEC_CONF_STR_MAXLEN);
generic_hal_data->name[LCEC_CONF_STR_MAXLEN - 1] = 0;
generic_hal_data->type = ce_conf->halType;
generic_hal_data->subType = ce_conf->subType;
generic_hal_data->floatScale = ce_conf->floatScale;
generic_hal_data->floatOffset = ce_conf->floatOffset;
generic_hal_data->bitOffset = ce_conf->bitOffset;
generic_hal_data->bitLength = ce_conf->bitLength;
generic_hal_data->dir = generic_hal_dir;
generic_hal_data->pdo_idx = pe_conf->index;
generic_hal_data->pdo_sidx = pe_conf->subindex;
generic_hal_data++;
}
break;
case lcecConfTypeSdoConfig:
// get config token
sdo_conf = (LCEC_CONF_SDOCONF_T *)conf;
conf += sizeof(LCEC_CONF_SDOCONF_T) + sdo_conf->length;
// copy attributes
sdo_config->index = sdo_conf->index;
sdo_config->subindex = sdo_conf->subindex;
sdo_config->length = sdo_conf->length;
// copy data
memcpy(sdo_config->data, sdo_conf->data, sdo_config->length);
sdo_config = (lcec_slave_sdoconf_t *) &sdo_config->data[sdo_config->length];
sdo_config->index = 0xffff;
break;
default:
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "Unknown config item type\n");
goto fail2;
}
}
// close shmem
rtapi_shmem_delete(shmem_id, comp_id);
// allocate PDO entity memory
for (master = first_master; master != NULL; master = master->next) {
pdo_entry_regs = lcec_zalloc(sizeof(ec_pdo_entry_reg_t) * (master->pdo_entry_count + 1));
if (pdo_entry_regs == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "Unable to allocate master %s PDO entry memory\n", master->name);
goto fail2;
}
master->pdo_entry_regs = pdo_entry_regs;
}
return slave_count;
fail2:
lcec_clear_config();
fail1:
rtapi_shmem_delete(shmem_id, comp_id);
fail0:
return -1;
}
void lcec_clear_config(void) {
lcec_master_t *master, *prev_master;
lcec_slave_t *slave, *prev_slave;
// iterate all masters
master = last_master;
while (master != NULL) {
prev_master = master->prev;
// iterate all masters
slave = master->last_slave;
while (slave != NULL) {
prev_slave = slave->prev;
// cleanup slave
if (slave->proc_cleanup != NULL) {
slave->proc_cleanup(slave);
}
// free slave
if (slave->sdo_config != NULL) {
lcec_free(slave->sdo_config);
}
if (slave->generic_pdo_entries != NULL) {
lcec_free(slave->generic_pdo_entries);
}
if (slave->generic_pdos != NULL) {
lcec_free(slave->generic_pdos);
}
if (slave->generic_sync_managers != NULL) {
lcec_free(slave->generic_sync_managers);
}
if (slave->dc_conf != NULL) {
lcec_free(slave->dc_conf);
}
if (slave->wd_conf != NULL) {
lcec_free(slave->wd_conf);
}
lcec_free(slave);
slave = prev_slave;
}
// release master
if (master->master) {
ecrt_release_master(master->master);
}
// free PDO entry memory
if (master->pdo_entry_regs != NULL) {
lcec_free(master->pdo_entry_regs);
}
// free master
lcec_free(master);
master = prev_master;
}
}
void lcec_request_lock(void *data) {
lcec_master_t *master = (lcec_master_t *) data;
rtapi_mutex_get(&master->mutex);
}
void lcec_release_lock(void *data) {
lcec_master_t *master = (lcec_master_t *) data;
rtapi_mutex_give(&master->mutex);
}
lcec_master_data_t *lcec_init_master_hal(const char *pfx) {
lcec_master_data_t *hal_data;
// alloc hal data
if ((hal_data = hal_malloc(sizeof(lcec_master_data_t))) == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "hal_malloc() for %s failed\n", pfx);
return NULL;
}
memset(hal_data, 0, sizeof(lcec_master_data_t));
// export pins
if (hal_pin_u32_newf(HAL_OUT, &(hal_data->slaves_responding), comp_id, "%s.slaves-responding", pfx) != 0) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "exporting pin %s.slaves-responding failed\n", pfx);
return NULL;
}
if (hal_pin_bit_newf(HAL_OUT, &(hal_data->state_init), comp_id, "%s.state-init", pfx) != 0) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "exporting pin %s.state-init failed\n", pfx);
return NULL;
}
if (hal_pin_bit_newf(HAL_OUT, &(hal_data->state_preop), comp_id, "%s.state-preop", pfx) != 0) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "exporting pin %s.state-preop failed\n", pfx);
return NULL;
}
if (hal_pin_bit_newf(HAL_OUT, &(hal_data->state_safeop), comp_id, "%s.state-safeop", pfx) != 0) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "exporting pin %s.state-safeop failed\n", pfx);
return NULL;
}
if (hal_pin_bit_newf(HAL_OUT, &(hal_data->state_op), comp_id, "%s.state-op", pfx) != 0) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "exporting pin %s.state-op failed\n", pfx);
return NULL;
}
if (hal_pin_bit_newf(HAL_OUT, &(hal_data->link_up), comp_id, "%s.link-up", pfx) != 0) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "exporting pin %s.link-up failed\n", pfx);
return NULL;
}
if (hal_pin_bit_newf(HAL_OUT, &(hal_data->all_op), comp_id, "%s.all-op", pfx) != 0) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "exporting pin %s.all-op failed\n", pfx);
return NULL;
}
// initialize pins
*(hal_data->slaves_responding) = 0;
*(hal_data->state_init) = 0;
*(hal_data->state_preop) = 0;
*(hal_data->state_safeop) = 0;
*(hal_data->state_op) = 0;
*(hal_data->link_up) = 0;
*(hal_data->all_op) = 0;
return hal_data;
}
lcec_slave_state_t *lcec_init_slave_state_hal(char *master_name, char *slave_name) {
lcec_slave_state_t *hal_data;
// alloc hal data
if ((hal_data = hal_malloc(sizeof(lcec_slave_state_t))) == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "hal_malloc() for %s.%s.%s failed\n", LCEC_MODULE_NAME, master_name, slave_name);
return NULL;
}
memset(hal_data, 0, sizeof(lcec_master_data_t));
// export pins
if (hal_pin_bit_newf(HAL_OUT, &(hal_data->online), comp_id, "%s.%s.%s.slave-online", LCEC_MODULE_NAME, master_name, slave_name) != 0) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "exporting pin %s.%s.%s.slaves-online failed\n", LCEC_MODULE_NAME, master_name, slave_name);
return NULL;
}
if (hal_pin_bit_newf(HAL_OUT, &(hal_data->operational), comp_id, "%s.%s.%s.slave-oper", LCEC_MODULE_NAME, master_name, slave_name) != 0) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "exporting pin %s.%s.%s.slaves-oper failed\n", LCEC_MODULE_NAME, master_name, slave_name);
return NULL;
}
if (hal_pin_bit_newf(HAL_OUT, &(hal_data->state_init), comp_id, "%s.%s.%s.slave-state-init", LCEC_MODULE_NAME, master_name, slave_name) != 0) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "exporting pin %s.%s.%s.state-state-init failed\n", LCEC_MODULE_NAME, master_name, slave_name);
return NULL;
}
if (hal_pin_bit_newf(HAL_OUT, &(hal_data->state_preop), comp_id, "%s.%s.%s.slave-state-preop", LCEC_MODULE_NAME, master_name, slave_name) != 0) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "exporting pin %s.%s.%s.state-state-preop failed\n", LCEC_MODULE_NAME, master_name, slave_name);
return NULL;
}
if (hal_pin_bit_newf(HAL_OUT, &(hal_data->state_safeop), comp_id, "%s.%s.%s.slave-state-safeop", LCEC_MODULE_NAME, master_name, slave_name) != 0) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "exporting pin %s.%s.%s.state-state-safeop failed\n", LCEC_MODULE_NAME, master_name, slave_name);
return NULL;
}
if (hal_pin_bit_newf(HAL_OUT, &(hal_data->state_op), comp_id, "%s.%s.%s.slave-state-op", LCEC_MODULE_NAME, master_name, slave_name) != 0) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "exporting pin %s.%s.%s.state-state-op failed\n", LCEC_MODULE_NAME, master_name, slave_name);
return NULL;
}
// initialize pins
*(hal_data->online) = 0;
*(hal_data->operational) = 0;
*(hal_data->state_init) = 0;
*(hal_data->state_preop) = 0;
*(hal_data->state_safeop) = 0;
*(hal_data->state_op) = 0;
return hal_data;
}
void lcec_update_master_hal(lcec_master_data_t *hal_data, ec_master_state_t *ms) {
*(hal_data->slaves_responding) = ms->slaves_responding;
*(hal_data->state_init) = (ms->al_states & 0x01) != 0;
*(hal_data->state_preop) = (ms->al_states & 0x02) != 0;
*(hal_data->state_safeop) = (ms->al_states & 0x04) != 0;
*(hal_data->state_op) = (ms->al_states & 0x08) != 0;
*(hal_data->link_up) = ms->link_up;
*(hal_data->all_op) = (ms->al_states == 0x08);
}
void lcec_update_slave_state_hal(lcec_slave_state_t *hal_data, ec_slave_config_state_t *ss) {
*(hal_data->online) = ss->online;
*(hal_data->operational) = ss->operational;
*(hal_data->state_init) = (ss->al_state & 0x01) != 0;
*(hal_data->state_preop) = (ss->al_state & 0x02) != 0;
*(hal_data->state_safeop) = (ss->al_state & 0x04) != 0;
*(hal_data->state_op) = (ss->al_state & 0x08) != 0;
}
void lcec_read_all(void *arg, long period) {
lcec_master_t *master;
// initialize global state
global_ms.slaves_responding = 0;
global_ms.al_states = 0;
global_ms.link_up = (first_master != NULL);
// process slaves
for (master = first_master; master != NULL; master = master->next) {
lcec_read_master(master, period);
}
// update global state pins
lcec_update_master_hal(global_hal_data, &global_ms);
}
void lcec_write_all(void *arg, long period) {
lcec_master_t *master;
// process slaves
for (master = first_master; master != NULL; master = master->next) {
lcec_write_master(master, period);
}
}
void lcec_read_master(void *arg, long period) {
lcec_master_t *master = (lcec_master_t *) arg;
lcec_slave_t *slave;
ec_master_state_t ms;
// receive process data & master state
rtapi_mutex_get(&master->mutex);
ecrt_master_receive(master->master);
ecrt_domain_process(master->domain);
ecrt_master_state(master->master, &ms);
rtapi_mutex_give(&master->mutex);
// update state pins
lcec_update_master_hal(master->hal_data, &ms);
// update global state
global_ms.slaves_responding += ms.slaves_responding;
global_ms.al_states |= ms.al_states;
global_ms.link_up = global_ms.link_up && ms.link_up;
// process slaves
for (slave = master->first_slave; slave != NULL; slave = slave->next) {
// get slaves state
rtapi_mutex_get(&master->mutex);
ecrt_slave_config_state(slave->config, &slave->state);
rtapi_mutex_give(&master->mutex);
lcec_update_slave_state_hal(slave->hal_state_data, &slave->state);
// process read function
if (slave->proc_read != NULL) {
slave->proc_read(slave, period);
}
}
}
void lcec_write_master(void *arg, long period) {
lcec_master_t *master = (lcec_master_t *) arg;
lcec_slave_t *slave;
// process slaves
for (slave = master->first_slave; slave != NULL; slave = slave->next) {
if (slave->proc_write != NULL) {
slave->proc_write(slave, period);
}
}
// send process data
rtapi_mutex_get(&master->mutex);
// update application time
master->app_time += master->app_time_period;
/*
// update application time
master->app_time += master->app_time_period;
ecrt_master_application_time(master->master, master->app_time);
// sync ref clock to master
if (master->sync_ref_cycles > 0) {
if (master->sync_ref_cnt == 0) {
master->sync_ref_cnt = master->sync_ref_cycles;
ecrt_master_sync_reference_clock(master->master);
}
master->sync_ref_cnt--;
}
// sync slaves to ref clock
ecrt_master_sync_slave_clocks(master->master);
*/if (master->reference_time == 0)
{
ecrt_master_reference_clock_time(master->master, &master->reference_time); //consigo el tiempo del reference clock y lo guardo en master->reference_time
master->app_time = master->reference_time;
}
else
{
ecrt_master_reference_clock_time(master->master, &master->reference_time); //consigo el tiempo del reference clock y lo guardo en master->reference_time
}
ecrt_master_sync_slave_clocks(master->master); // sync slaves to ref clock
ecrt_master_application_time(master->master, master->reference_time+master->app_time_period);
// send domain data
ecrt_domain_queue(master->domain);
ecrt_master_send(master->master);
rtapi_mutex_give(&master->mutex);
}
ec_sdo_request_t *lcec_read_sdo(struct lcec_slave *slave, uint16_t index, uint8_t subindex, size_t size) {
lcec_master_t *master = slave->master;
ec_sdo_request_t *sdo;
ec_request_state_t sdo_state;
long ticks_start;
// create request
if (!(sdo = ecrt_slave_config_create_sdo_request(slave->config, index, subindex, size))) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "slave %s.%s: Failed to create SDO request (0x%04x:0x%02x)\n", master->name, slave->name, index, subindex);
return NULL;
}
// set timeout
ecrt_sdo_request_timeout(sdo, LCEC_SDO_REQ_TIMEOUT);
// send request
ecrt_sdo_request_read(sdo);
// wait for completition (master's time out does not work here. why???)
ticks_start = lcec_get_ticks();
while ((sdo_state = ecrt_sdo_request_state(sdo)) == EC_REQUEST_BUSY && (lcec_get_ticks() - ticks_start) < LCEC_SDO_REQ_TIMEOUT) {
lcec_schedule();
}
// check state
if (sdo_state != EC_REQUEST_SUCCESS) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "slave %s.%s: Failed to execute SDO request (0x%04x:0x%02x)\n", master->name, slave->name, index, subindex);
return NULL;
}
return sdo;
}
_______________________________________________
etherlab-users mailing list
[email protected]
http://lists.etherlab.org/mailman/listinfo/etherlab-users