Hi all,

I'm working on a kernel module which should control Beckhoff IN/OUT modules. 
The network is formed by a coupler (EK1100), digital inputs (EL1252) and 
digital outputs (EL2252).

For the EL2252, I have to make it work with DC in ModeB. I read most of the 
documentation/info available on this forum, but I cannot get a working 
configuration: or it does not work at all or there is a period glitch some time 
(the output switch on period to late).


Here is the my cyclic task only ethercat related function (full code attached):


while (!kthread_should_stop()) {
    usleep_range(period_time, period_time);

    // receive process data
    ecrt_master_receive(master);
    ecrt_domain_process(domain1);

    /* cyclic operations (read/write regs) */

    ecrt_domain_queue(domain1);
#if 1
    now = get_boot_time_ns();
    app_time += app_period + (now - sleep_time);
    ecrt_master_application_time(master, app_time);

    /* Get the reference clock from the DC master (first slave with DC) */
    ecrt_master_reference_clock_time(master, &ref_time);

    /* Sync other slaves with this reference time */
    ecrt_master_sync_slave_clocks(master);
#else
    /* Time method seem great but don't work... */

    /* Get the reference clock from the DC master (first slave with DC) */
    ecrt_master_reference_clock_time(master, &ref_time);

    /* Sync other slaves with this reference time */
    app_time += app_period + ref_time;
    ecrt_master_application_time(master, app_time);

    ecrt_master_sync_slave_clocks(master);
#endif

    ecrt_master_send(master);

    /* Update app_period... */


Any suggestion on what is wrong with my code/understanding ?


The way to toggle an output on the EL2252 is:

  1.  Set output state and Time the output should change
  2.  Activation

In my code, the output changes state each 10ms (just for the experiment)



Linux: RT-Preempt on Debian Wheezy (kernel 3.2)

Driver: e1000e


Thanks for your help

J-P
/******************************************************************************
 *
 *  Distributed clocks sample for the IgH EtherCAT master.
 *
 *  $Id: dc_rtai_sample.c,v bc2d4bf9cbe5 2012/09/06 18:22:24 fp $
 *
 *  Copyright (C) 2006-2008  Florian Pose, Ingenieurgemeinschaft IgH
 *
 *  This file is part of the IgH EtherCAT Master.
 *
 *  The IgH EtherCAT Master is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License version 2, as
 *  published by the Free Software Foundation.
 *
 *  The IgH EtherCAT Master is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
 *  Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with the IgH EtherCAT Master; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *  ---
 *
 *  The license mentioned above concerns the source code only. Using the
 *  EtherCAT technology and brand is only permitted in compliance with the
 *  industrial property and similar rights of Beckhoff Automation GmbH.
 *
 *****************************************************************************/

/* Linux */
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/time.h>
#include <linux/err.h>

/* EtherCAT */
#include "ecrt.h"

#define PFX "BECKHOFF: "

#define BusCouplerPos   0, 0
#define El1252Pos       0, 1
#define El2252Pos       0, 2

#define Beckhoff_EK1100 0x00000002, 0x044c2c52
#define Beckhoff_EL1252 0x00000002, 0x04e43052
#define Beckhoff_EL2252 0x00000002, 0x08cc3052

#define PERIOD_1Khz_MS  1
#define PERIOD_US (PERIOD_1Khz_MS*1000)

/*****************************************************************************/

#define DIFF_NS(A, B) (((B).tv_sec - (A).tv_sec) * NSEC_PER_SEC + \
    (B).tv_nsec - (A).tv_nsec)

/*****************************************************************************/


enum TRIGGER_STATE {
	IDLE,
	SET_TRIGGER,
	ACTIVATE_TRIGGER
};


/*****************************************************************************/

ec_pdo_entry_info_t el1252_entries[] = {
//   Index,  SIndx,Len
    {0x6000, 0x01, 1},  /* Input */
    {0x6000, 0x02, 1},  /* Input */
    {0x0000, 0x00, 6},  /* gap */
    {0x1d09, 0xae, 8},  /* Status1 */
    {0x1d09, 0xaf, 8},  /* Status2 */
    {0x1d09, 0xb0, 64}, /* LatchPos1 */
    {0x1d09, 0xb8, 64}, /* LatchNeg1 */
    {0x1d09, 0xc0, 64}, /* LatchPos2 */
    {0x1d09, 0xc8, 64}, /* LatchNeg2 */
};

ec_pdo_info_t el1252_pdos[] = {
//   Index,  n.PDOs, array of PDO entries
    {0x1a00, 1, el1252_entries + 0}, /* Channel 1 */
    {0x1a01, 2, el1252_entries + 1}, /* Channel 2 */
    {0x1a02, 0, NULL},                    /* Reserved */
    {0x1a13, 6, el1252_entries + 3}, /* Latches */
};

ec_sync_info_t el1252_syncs[] = {
//  Indx, SM direction, n.PDOs, array of PDOs, WD mode
    {0, EC_DIR_INPUT, 3, el1252_pdos + 0, EC_WD_DISABLE},
    {1, EC_DIR_INPUT, 1, el1252_pdos + 3, EC_WD_DISABLE},
    {2, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
    {0xff}
};

/*****************************************************************************/

ec_pdo_entry_info_t el2252_entries[] = {
    {0x1d09, 0x81, 8}, /* Activate */
    {0x1d09, 0x90, 64}, /* StartTime */
    {0x7000, 0x01, 1}, /* Output */
    {0x7000, 0x02, 1}, /* TriState */
    {0x7010, 0x01, 1}, /* Output */
    {0x7010, 0x02, 1}, /* TriState */
    {0x0000, 0x00, 4},
};

ec_pdo_info_t el2252_pdos[] = {
    {0x1602, 1, el2252_entries + 0}, /* DC Sync Activate */
    {0x1603, 1, el2252_entries + 1}, /* DC Sync Start */
    {0x1600, 2, el2252_entries + 2}, /* Channel 1 */
    {0x1601, 3, el2252_entries + 4}, /* Channel 2 */
    {0x1604, 0, NULL}, /* Reserved */
};

ec_sync_info_t el2252_syncs[] = {
    {0, EC_DIR_INPUT, 1, el2252_pdos + 0, EC_WD_DISABLE},
    {1, EC_DIR_INPUT, 1, el2252_pdos + 1, EC_WD_DISABLE},
    {2, EC_DIR_OUTPUT, 3, el2252_pdos + 2, EC_WD_ENABLE},
    {3, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
    {4, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
    {0xff}
};


/*****************************************************************************/

static ec_master_t *master = NULL;

static ec_domain_t *domain1 = NULL;
static uint8_t *domain1_pd; // process data memory

static struct task_struct *cycle_thread;

struct el1252_offsets {
	unsigned int input_1;
	unsigned int input_2;
	unsigned int status_1;
	unsigned int status_2;
	unsigned int latch_Pos1;
	unsigned int latch_neg1;
	unsigned int latch_pos2;
	unsigned int latch_neg2;
	unsigned int input_1_bit;
	unsigned int input_2_bit;
};

struct el2252_offsets {
    unsigned int activate;
    unsigned int start_time;
    unsigned int output_1;
    unsigned int output_1_bits;
    unsigned int tristate_1;
    unsigned int tristate_1_bits;
    unsigned int output_2;
    unsigned int output_2_bits;
    unsigned int tristate_2;
    unsigned int tristate_2_bits;
    unsigned int empty;
    unsigned int empty_bits;
};

static struct el1252_offsets el1252_offsets;
static struct el2252_offsets el2252_offsets;

// PDO entries of domain 1
const static ec_pdo_entry_reg_t domain1_regs[] = {
	{ El1252Pos, Beckhoff_EL1252, 0x6000, 0x01, &el1252_offsets.input_1, &el1252_offsets.input_1_bit},
    { El1252Pos, Beckhoff_EL1252, 0x6000, 0x02, &el1252_offsets.input_2, &el1252_offsets.input_2_bit},
    { El1252Pos, Beckhoff_EL1252, 0x1d09, 0xae, &el1252_offsets.status_1 },
    { El1252Pos, Beckhoff_EL1252, 0x1d09, 0xaf, &el1252_offsets.status_2 },
    { El1252Pos, Beckhoff_EL1252, 0x1d09, 0xb0, &el1252_offsets.latch_Pos1 },
    { El1252Pos, Beckhoff_EL1252, 0x1d09, 0xb8, &el1252_offsets.latch_neg1 },
    { El1252Pos, Beckhoff_EL1252, 0x1d09, 0xc0, &el1252_offsets.latch_pos2 },
    { El1252Pos, Beckhoff_EL1252, 0x1d09, 0xc8, &el1252_offsets.latch_neg2 },
	{ El2252Pos, Beckhoff_EL2252, 0x1d09, 0x81, &el2252_offsets.activate },
	{ El2252Pos, Beckhoff_EL2252, 0x1d09, 0x90, &el2252_offsets.start_time },
	{ El2252Pos, Beckhoff_EL2252, 0x7000, 0x01, &el2252_offsets.output_1,   &el2252_offsets.output_1_bits },
	{ El2252Pos, Beckhoff_EL2252, 0x7000, 0x02, &el2252_offsets.tristate_1, &el2252_offsets.tristate_1_bits },
	{ El2252Pos, Beckhoff_EL2252, 0x7010, 0x01, &el2252_offsets.output_2,   &el2252_offsets.output_2_bits },
	{ El2252Pos, Beckhoff_EL2252, 0x7010, 0x02, &el2252_offsets.tristate_2, &el2252_offsets.tristate_2_bits },
	{}
};





/*****************************************************************************/


static uint64_t get_boot_time_ns(void)
{
	struct timespec ts = current_kernel_time();

	return timespec_to_ns(&ts);
}


/*****************************************************************************/


static void el1252_read_inputs(void)
{
	uint8_t in;
	static uint8_t in1 = 0;
	static uint8_t in2 = 0;

	in = EC_READ_BIT(domain1_pd + el1252_offsets.input_1, el1252_offsets.input_1_bit);
	if (in != in1) {
		printk(KERN_INFO PFX "IN1 state change, current '%d'\n", in);
		in1 = in;
	}

	in = EC_READ_BIT(domain1_pd + el1252_offsets.input_2, el1252_offsets.input_2_bit);
	if (in != in2) {
		printk(KERN_INFO PFX "IN2 state change, current '%d'\n", in);
		in2 = in;
	}
}

static void el2252_set_outputs(void)
{
	static unsigned blink = 0;
	static unsigned counter = 1000;
#if 1 //  delay blinking
	static enum TRIGGER_STATE trigger = IDLE;
#else
	static enum TRIGGER_STATE trigger = SET_TRIGGER;
#endif
	uint64_t trigger_time;

	switch(trigger) {
	case IDLE:
		if (counter) {
			counter--;
		} else {
			trigger = SET_TRIGGER;
			counter = 7;
		}
    	break;
	case ACTIVATE_TRIGGER:
		// Step 2: Activate the trigger
		EC_WRITE_U8(domain1_pd + el2252_offsets.activate, 3);
#if 1
		trigger = IDLE;
#else
		trigger = SET_TRIGGER;
#endif
		break;
	case SET_TRIGGER:
		trigger_time = get_boot_time_ns() + (3000000);

		blink = !blink;

		//printk(KERN_INFO PFX "BLINK (%d)\n", blink);

		EC_WRITE_U8(domain1_pd  + el2252_offsets.activate, 0);
		EC_WRITE_U64(domain1_pd + el2252_offsets.start_time, trigger_time);
		EC_WRITE_BIT(domain1_pd + el2252_offsets.output_1, el2252_offsets.output_1_bits, blink);
		EC_WRITE_BIT(domain1_pd + el2252_offsets.output_2, el2252_offsets.output_2_bits, !blink);

		trigger = ACTIVATE_TRIGGER;
		break;
	}
}


static int cycle_task(void *data)
{
	uint64_t sleep_time;
	uint64_t now;
	uint64_t period_time;
	uint64_t app_time;
	uint64_t app_time_prev;
	uint64_t app_period;
	uint32_t ref_time;
	int32_t  period_error;

	period_time = PERIOD_US;
	app_period  = PERIOD_US*1000;

	app_time = get_boot_time_ns();
	app_time_prev = 0;

	while (!kthread_should_stop()) {

		sleep_time = get_boot_time_ns();

		usleep_range(period_time, period_time);

		// receive process data
		ecrt_master_receive(master);
		ecrt_domain_process(domain1);

		// read inputs
		el1252_read_inputs();

		// Set output
		el2252_set_outputs();

		ecrt_domain_queue(domain1);

		now = get_boot_time_ns();
		app_time += app_period + (now - sleep_time);
		ecrt_master_application_time(master, app_time);

		/* Get the reference clock from the DC master (first slave with DC) */
		ecrt_master_reference_clock_time(master, &ref_time);

		/* Sync other slaves with this reference time */
		ecrt_master_sync_slave_clocks(master);

		ecrt_master_send(master);

		/* Update period time */
//		period_error = app_time_prev - ref_time;
//
//		app_period += period_error;
//		period_time = app_period/1000;
//
//		app_time_prev = app_time;


	}

	return 0;
}


/*****************************************************************************/

int __init init_mod(void)
{
	int ret = -1;
	ec_slave_config_t *sc;

	printk(KERN_INFO PFX "Starting...\n");

	master = ecrt_request_master(0);
	if (!master) {
		ret = -EBUSY;
		printk(KERN_ERR PFX "Requesting master 0 failed!\n");
		goto out_return;
	}

	printk(KERN_INFO PFX "Registering domain...\n");
	if (!(domain1 = ecrt_master_create_domain(master))) {
		printk(KERN_ERR PFX "Domain creation failed!\n");
		goto out_release_master;
	}

	printk(KERN_INFO PFX "Configuring PDOs...\n");

	if (!(sc = ecrt_master_slave_config(master, BusCouplerPos, Beckhoff_EK1100))) {
		printk(KERN_ERR PFX "Failed to get slave configuration.\n");
		goto out_release_master;
	}


	/* == EL1252 PDOs configurations == */

	if (!(sc = ecrt_master_slave_config(master, El1252Pos, Beckhoff_EL1252))) {
		printk(KERN_ERR PFX "Failed to get slave configuration.\n");
		goto out_release_master;
	}

	if (ecrt_slave_config_pdos(sc, EC_END, el1252_syncs)) {
		printk(KERN_ERR PFX "Failed to configure PDOs.\n");
		goto out_release_master;
	}

	ecrt_slave_config_dc(sc, 0x0300, PERIOD_US*1000, 0, 0, 0);


	if (!(sc = ecrt_master_slave_config(master, El2252Pos, Beckhoff_EL2252))) {
		printk(KERN_ERR PFX "Failed to get slave configuration.\n");
		goto out_release_master;
	}

	if (ecrt_slave_config_pdos(sc, EC_END, el2252_syncs)) {
		printk(KERN_ERR PFX "Failed to configure PDOs.\n");
		goto out_release_master;
	}

	ecrt_slave_config_dc(sc, 0x0300, PERIOD_US*1000, (PERIOD_US*1000)/2, 0, 0);

	// Register PDOs
	printk("Configuring domains with registered PDO entries...\n");
	if (ecrt_domain_reg_pdo_entry_list(domain1, domain1_regs)) {
		printk(KERN_ERR PFX "PDO entry registration failed for domain 1!\n");
		goto out_release_master;
    }

	printk(KERN_INFO PFX "Activating master...\n");
	if (ecrt_master_activate(master)) {
		printk(KERN_ERR PFX "Failed to activate master!\n");
		goto out_release_master;
	}

	// Get internal process data for domain
	domain1_pd = ecrt_domain_data(domain1);

	printk(KERN_INFO PFX "Starting cyclic sample thread...\n");
	cycle_thread = kthread_run(cycle_task, NULL, "cycle task");
	if(IS_ERR(cycle_thread)){
		printk(KERN_ERR PFX "Failed to start cycle thread!\n");
		goto out_cycle;
	}

	printk(KERN_INFO PFX "Initialized.\n");
	return 0;

out_cycle:
	ret = PTR_ERR(cycle_thread);
	cycle_thread = NULL;
out_release_master:
	printk(KERN_ERR PFX "Releasing master...\n");
	ecrt_release_master(master);
out_return:
	printk(KERN_ERR PFX "Failed to load. Aborting.\n");
	return ret;
}

/*****************************************************************************/

void __exit cleanup_mod(void)
{
	printk(KERN_INFO PFX "Stopping...\n");
	kthread_stop(cycle_thread);
	ecrt_release_master(master);
}

/*****************************************************************************/

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jean-Pierre Miceli <[email protected]>");
MODULE_DESCRIPTION("Triamec TSD80 drive example");

module_init(init_mod);
module_exit(cleanup_mod);

/*****************************************************************************/
_______________________________________________
etherlab-users mailing list
[email protected]
http://lists.etherlab.org/mailman/listinfo/etherlab-users

Reply via email to