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