[etherlab-users] Beckhoff 6731-0010 initialization
Dr.-Ing. Wilhelm Hagemeister
hm at igh-essen.com
Sat Sep 15 10:22:53 CEST 2012
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 at etherlab.org
> http://lists.etherlab.org/mailman/listinfo/etherlab-users
More information about the Etherlab-users
mailing list