[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, &param) == -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