Hello Thomas,
we follow a rather pragmatic way to bring those slaves to operational:
Do the configuration with TwinCat and make sure the Profibus
Slave/Master goes to work properly.
Export from TwinCat the configuration of the EtherCat slave (6731-xxxx)
to an xml-file.
Make the xml-file more readable with "xmllint --format".
Now how have to search manually (with an editor) for the configuration
SDO's of your slave.
You are looking for the objects:
0xf800: Master configuration
0x8000: Slave 1 config
0x8010: Slave 2 config
....
cut these out and put them into your C program, find below an example
for a program with a Profibus Master and a Profibus Slave talking to
each other.
Regards Wilhelm.
/*****************************************************************************
*
* $Id: main.cpp,v 55c679db2375 2010/11/10 14:05:17 fp $
*
* Copyright (C) 2010 Florian Pose, Ingenieurgemeinschaft IgH
* Modified by Wilhelm Hagemeister, Ingenieurgemeinschaft IgH
* Demo shows how to configure a Beckhoff profibus slave/master EL6731
(0010)
*
****************************************************************************/
#include<errno.h>
#include<signal.h>
#include<stdio.h>
#include<string.h>
#include<time.h>
#include<sched.h>
#include<unistd.h>
#include<sys/mman.h>
#include<pthread.h>
/****************************************************************************/
#include<ecrt.h>
/****************************************************************************/
/** Task period in ns. */
#define PERIOD_NS (1000000)
#define CLOCK_TO_USE CLOCK_REALTIME
#define MEASURE_TIMING
/****************************************************************************/
#define NSEC_PER_SEC (1000000000)
#define FREQUENCY (NSEC_PER_SEC / PERIOD_NS)
#define DIFF_NS(A, B) (((B).tv_sec - (A).tv_sec) * NSEC_PER_SEC + \
(B).tv_nsec - (A).tv_nsec)
/****************************************************************************/
// EtherCAT
#define CouplerPos 0, 0
#define ProfiPosSlave 0, 1
#define ProfiPosMaster 0, 2
#define Beckhoff_EK1100 0x00000002, 0x044c2c52
#define Beckhoff_EL6731 0x00000002, 0x1a4b3052
static ec_master_t *master = NULL;
static ec_master_state_t master_state = {};
static ec_domain_t *domain1 = NULL;
static ec_domain_state_t domain1_state = {};
static ec_slave_config_t *sc = NULL;
static ec_slave_config_state_t sc_state = {};
/****************************************************************************/
/** Profibus configuration.
* Stealed from TwinCAT configuration export.
*/
const char *pb_config_slave_0x8000 =
"2d00020000000000000000000000a8010000f4"
"f0000000000a02000000008801140b095f00800008d9e9";
/*----------------------*/
const char *pb_config_master_0xf800 =
"11000109e8030b0020030910647e0401280004000000000000000000";
const char *pb_config_master_0x8000 =
"2d00020001100000c0a8045a0202a8010000f4f00"
"00000001602000000008801140b095f00c000080c"
"8100000000c0a8045a0202d9e9";
// process data
static uint8_t *domain1_pd = NULL;
// offsets for PDO entries
static unsigned int off_out_slave;
static unsigned int off_in_slave;
static unsigned int off_out_master;
static unsigned int off_in_master;
const static ec_pdo_entry_reg_t domain1_regs[] = {
{ProfiPosSlave, Beckhoff_EL6731, 0x7000, 1,&off_out_slave},
{ProfiPosSlave, Beckhoff_EL6731, 0x6000, 1,&off_in_slave},
{ProfiPosMaster, Beckhoff_EL6731, 0x7000, 1,&off_out_master},
{ProfiPosMaster, Beckhoff_EL6731, 0x6000, 1,&off_in_master},
{}
};
static unsigned int counter = 0;
unsigned int run = 1;
/*****************************************************************************/
/* Master 0, Slave 1, "EL6731-0010"
* Vendor ID: 0x00000002
* Product code: 0x1a4b3052
* Revision number: 0x0011000a
*/
ec_pdo_entry_info_t slave_1_pdo_entries[] = {
{0x7000, 0x01, 160},
{0x6000, 0x01, 160},
{0xa000, 0x01, 8},
{0xf100, 0x02, 1},
{0xa000, 0x02, 1},
{0x0000, 0x00, 6},
};
ec_pdo_info_t slave_1_pdos[] = {
{0x1600, 1, slave_1_pdo_entries + 0}, /* DPS RxPDO-Map Slave */
{0x1a00, 1, slave_1_pdo_entries + 1}, /* DPS TxPDO-Map Slave */
{0x1a80, 4, slave_1_pdo_entries + 2},
};
ec_sync_info_t slave_1_syncs[] = {
{0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
{1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
{2, EC_DIR_OUTPUT, 1, slave_1_pdos + 0, EC_WD_DISABLE},
{3, EC_DIR_INPUT, 2, slave_1_pdos + 1, EC_WD_DISABLE},
{0xff}
};
/* Master 0, Slave 2, "EL6731"
* Vendor ID: 0x00000002
* Product code: 0x1a4b3052
* Revision number: 0x0011000a
*/
ec_pdo_entry_info_t slave_2_pdo_entries[] = {
{ 0x7000, 1, 160 }, /* 0 */
{ 0x6000, 1, 160 }, /* 1 */
{ 0xA000, 1, 8 }, /* 2 */
{ 0xF100, 1, 8 }, /* 3 */
{ 0xF100, 3, 8 }, /* 4 */
{ 0xF100, 2, 1 }, /* 5 */
{ 0xA000, 2, 1 }, /* 6 */
{ 0x0000, 0, 6 },
};
ec_pdo_info_t slave_2_pdos[] = {
{0x1600, 1, slave_2_pdo_entries + 0}, /* DPS RxPDO-Map Slave */
{0x1a00, 1, slave_2_pdo_entries + 1}, /* DPS TxPDO-Map Slave */
{0x1a80, 6, slave_2_pdo_entries + 2},
};
ec_sync_info_t slave_2_syncs[] = {
{0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
{1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
{2, EC_DIR_OUTPUT, 1, slave_2_pdos + 0, EC_WD_DISABLE},
{3, EC_DIR_INPUT, 2, slave_2_pdos + 1, EC_WD_DISABLE},
{0xff}
};
/*****************************************************************************/
/* Helper funktion, buffer has be allocated and big enough */
unsigned int strToCharArray(const char *str,unsigned char *buffer)
{
unsigned int i;
unsigned char byteVal;
// const char *str = pb_config;
// unsigned char cf_data[256];
size_t size = strlen(str);
for (i = 0; i< size; i++) {
sscanf(str, "%2X",&byteVal);
str += 2;
buffer[i] = (uint8_t) byteVal;
}
return (size / 2);
}
/*****************************************************************************/
void check_domain1_state(void)
{
ec_domain_state_t ds;
ecrt_domain_state(domain1,&ds);
if (ds.working_counter != domain1_state.working_counter)
printf("Domain1: WC %u.\n", ds.working_counter);
if (ds.wc_state != domain1_state.wc_state)
printf("Domain1: State %u.\n", ds.wc_state);
domain1_state = ds;
}
/*****************************************************************************/
void check_master_state(void)
{
ec_master_state_t ms;
ecrt_master_state(master,&ms);
if (ms.slaves_responding != master_state.slaves_responding)
printf("%u slave(s).\n", ms.slaves_responding);
if (ms.al_states != master_state.al_states)
printf("AL states: 0x%02X.\n", ms.al_states);
if (ms.link_up != master_state.link_up)
printf("Link is %s.\n", ms.link_up ? "up" : "down");
master_state = ms;
}
/*****************************************************************************/
void check_slave_config_states(void)
{
ec_slave_config_state_t s;
ecrt_slave_config_state(sc,&s);
if (s.al_state != sc_state.al_state)
printf("AnaIn: State 0x%02X.\n", s.al_state);
if (s.online != sc_state.online)
printf("AnaIn: %s.\n", s.online ? "online" : "offline");
if (s.operational != sc_state.operational)
printf("AnaIn: %soperational.\n",
s.operational ? "" : "Not ");
sc_state = s;
}
/*****************************************************************************/
void cyclic_task()
{
int i;
uint8_t x, reset = 0;
static uint8_t pcounter = 0;
// receive process data
ecrt_master_receive(master);
ecrt_domain_process(domain1);
// check process data state (optional)
check_domain1_state();
if (counter) {
counter--;
} else { // do this at 1 Hz
counter = FREQUENCY;
// check for master state (optional)
check_master_state();
// check for islave configuration state(s) (optional)
check_slave_config_states();
//write a byte to the master
pcounter++;
EC_WRITE_U8(domain1_pd + off_out_master + 1,pcounter);
// read some bytes from the slave
printf("\nSlave input data: ");
for (i=0;i<16;i++)
printf(" %d",EC_READ_U8(domain1_pd + off_in_slave +i));
printf("\n");
}
// send process data
ecrt_domain_queue(domain1);
ecrt_master_send(master);
}
/****************************************************************************/
void signal_handler(int signum)
{
switch (signum) {
case SIGINT:
case SIGTERM:
run = 0;
break;
}
}
/****************************************************************************/
#define MAX_SAFE_STACK (8*1024)
void stack_prefault(void)
{
unsigned char dummy[MAX_SAFE_STACK];
memset(&dummy, 0, MAX_SAFE_STACK);
}
/****************************************************************************/
void *rt_thread_func(void *data)
{
struct timespec wakeupTime;
int ret;
#ifdef MEASURE_TIMING
struct timespec startTime, endTime, lastStartTime = {}, lastOutputTime;
uint32_t period_ns = 0, exec_ns = 0, latency_ns = 0,
latency_min_ns = 0, latency_max_ns = 0,
period_min_ns = 0, period_max_ns = 0,
exec_min_ns = 0, exec_max_ns = 0;
#endif
printf("rt_thread running.\n");
// get current time
clock_gettime(CLOCK_TO_USE,&wakeupTime);
lastStartTime = wakeupTime;
while (run) {
wakeupTime.tv_nsec += PERIOD_NS;
while (wakeupTime.tv_nsec>= NSEC_PER_SEC) {
wakeupTime.tv_nsec -= NSEC_PER_SEC;
wakeupTime.tv_sec++;
}
ret = clock_nanosleep(CLOCK_TO_USE, TIMER_ABSTIME,&wakeupTime,
NULL);
if (ret) {
printf("clock_nanosleep(): %s\n", strerror(ret));
}
#ifdef MEASURE_TIMING
clock_gettime(CLOCK_TO_USE,&startTime);
latency_ns = DIFF_NS(wakeupTime, startTime);
period_ns = DIFF_NS(lastStartTime, startTime);
exec_ns = DIFF_NS(lastStartTime, endTime);
lastStartTime = startTime;
//printf("period_ns=%10u exec_ns=%5u\n", period_ns, exec_ns);
if (latency_ns> latency_max_ns) {
latency_max_ns = latency_ns;
}
if (latency_ns< latency_min_ns) {
latency_min_ns = latency_ns;
}
if (period_ns> period_max_ns) {
period_max_ns = period_ns;
}
if (period_ns< period_min_ns) {
period_min_ns = period_ns;
}
if (exec_ns> exec_max_ns) {
exec_max_ns = exec_ns;
}
if (exec_ns< exec_min_ns) {
exec_min_ns = exec_ns;
}
#endif
cyclic_task();
#ifdef MEASURE_TIMING
if (DIFF_NS(lastOutputTime, startTime)>= NSEC_PER_SEC) {
// output timing stats
printf("period %10u ... %10u\n",
period_min_ns, period_max_ns);
printf("exec %10u ... %10u\n",
exec_min_ns, exec_max_ns);
printf("latency %10u ... %10u\n\n",
latency_min_ns, latency_max_ns);
period_max_ns = 0;
period_min_ns = 0xffffffff;
exec_max_ns = 0;
exec_min_ns = 0xffffffff;
latency_max_ns = 0;
latency_min_ns = 0xffffffff;
lastOutputTime = startTime;
}
clock_gettime(CLOCK_TO_USE,&endTime);
#endif
}
pthread_exit(NULL);
}
/****************************************************************************/
int main(int argc, char **argv)
{
struct sigaction sa;
struct sched_param param;
pthread_t rt_thread;
int ret;
int cnt;
unsigned char pb_config_buf[256];
sa.sa_handler = signal_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGTERM,&sa, 0)) {
fprintf(stderr, "Failed to install signal handler!\n");
return -1;
}
if (sigaction(SIGINT,&sa, 0)) {
fprintf(stderr, "Failed to install signal handler!\n");
return -1;
}
master = ecrt_request_master(0);
if (!master)
return -1;
domain1 = ecrt_master_create_domain(master);
if (!domain1)
return -1;
printf("Configuring PDOs...\n");
/* EK1100 */
if (!(sc = ecrt_master_slave_config(master, CouplerPos,
Beckhoff_EK1100))) {
fprintf(stderr, "Failed to get slave configuration.\n");
return -1;
}
/* EL6731 Slave*/
if (!(sc = ecrt_master_slave_config(master, ProfiPosSlave,
Beckhoff_EL6731))) {
fprintf(stderr, "Failed to get slave configuration.\n");
return -1;
}
cnt = strToCharArray(pb_config_slave_0x8000,pb_config_buf);
ecrt_slave_config_complete_sdo(sc, 0x8000, pb_config_buf, cnt);
if (ecrt_slave_config_pdos(sc, EC_END, slave_1_syncs)) {
fprintf(stderr, "Failed to configure PDOs.\n");
return -1;
}
/* EL6731 Master*/
if (!(sc = ecrt_master_slave_config(master, ProfiPosMaster,
Beckhoff_EL6731))) {
fprintf(stderr, "Failed to get slave configuration.\n");
return -1;
}
cnt = strToCharArray(pb_config_master_0xf800,pb_config_buf);
ecrt_slave_config_complete_sdo(sc, 0xf800, pb_config_buf, cnt);
cnt = strToCharArray(pb_config_master_0x8000,pb_config_buf);
ecrt_slave_config_complete_sdo(sc, 0x8000, pb_config_buf, cnt);
if (ecrt_slave_config_pdos(sc, EC_END, slave_2_syncs)) {
fprintf(stderr, "Failed to configure PDOs (PB-Master).\n");
return -1;
}
if (ecrt_domain_reg_pdo_entry_list(domain1, domain1_regs)) {
fprintf(stderr, "PDO entry registration failed!\n");
return -1;
}
printf("Activating master...\n");
if (ecrt_master_activate(master))
return -1;
if (!(domain1_pd = ecrt_domain_data(domain1))) {
return -1;
}
param.sched_priority = 80;
if (sched_setscheduler(0, SCHED_FIFO,¶m) == -1) {
perror("sched_setscheduler failed");
exit(-1);
}
if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
perror("mlockall failed");
exit(-2);
}
stack_prefault();
printf("Starting timer...\n");
ret = pthread_create(&rt_thread, NULL, rt_thread_func, NULL);
if (ret< 0) {
fprintf(stderr, "Failed to create realtime thread: %i.\n", ret);
return ret;
}
while (run) {
sleep(1);
}
return ret;
}
/****************************************************************************/
Am 15.09.2012 08:46, schrieb Thomas Paoloni:
Hi all,
I'm fighting with a Beckhoff 6731-0010 (Profibus slave to ethercat
bridge) which I'm not able to bring in OP state.
Nobody from Beckhoff is able to help me, they only seems to know their
twincat software, and nobody can explain how the same operations made by
twincat can be made trought SDO and C programming.
Even sniffing traffic with wireshark and trying to reproduce the same
with my code works because I can see some ADS packets which I can't
reproduce with Linux master.
Does anybody know more than me about this node, or maybe some other
network masters/slaves like CanOpen, Profibus, etc. that I think should
basically work in the same way ?
Thanks in advance,
Thomas.
_______________________________________________
etherlab-users mailing list
etherlab-users@etherlab.org
http://lists.etherlab.org/mailman/listinfo/etherlab-users