Added interface to select the reference clock and to sync to it.
Added rtai_rtdm_dc example, thanks to Graeme Foot.
This commit is contained in:
parent
c720cf8e10
commit
becf05dbd6
|
|
@ -102,6 +102,8 @@ examples/rtai/Makefile
|
|||
examples/rtai/Makefile.in
|
||||
examples/rtai_rtdm/.libs
|
||||
examples/rtai_rtdm/ec_rtai_rtdm_example
|
||||
examples/rtai_rtdm_dc/.libs
|
||||
examples/rtai_rtdm_dc/ec_rtai_rtdm_dc_example
|
||||
examples/user/.deps
|
||||
examples/user/.libs
|
||||
examples/user/Makefile
|
||||
|
|
|
|||
|
|
@ -804,6 +804,7 @@ AC_CONFIG_FILES([
|
|||
examples/rtai/Kbuild
|
||||
examples/rtai/Makefile
|
||||
examples/rtai_rtdm/Makefile
|
||||
examples/rtai_rtdm_dc/Makefile
|
||||
examples/tty/Kbuild
|
||||
examples/tty/Makefile
|
||||
examples/user/Makefile
|
||||
|
|
|
|||
|
|
@ -49,7 +49,8 @@ endif
|
|||
|
||||
if ENABLE_RTAI
|
||||
SUBDIRS += \
|
||||
rtai_rtdm
|
||||
rtai_rtdm \
|
||||
rtai_rtdm_dc
|
||||
endif
|
||||
endif
|
||||
|
||||
|
|
@ -59,6 +60,7 @@ DIST_SUBDIRS = \
|
|||
mini \
|
||||
rtai \
|
||||
rtai_rtdm \
|
||||
rtai_rtdm_dc \
|
||||
tty \
|
||||
user \
|
||||
xenomai \
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
# Copyright (C) 2006-2012 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.
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
noinst_PROGRAMS = ec_rtai_rtdm_dc_example
|
||||
|
||||
ec_rtai_rtdm_dc_example_SOURCES = main.c
|
||||
|
||||
ec_rtai_rtdm_dc_example_CFLAGS = \
|
||||
-Wall \
|
||||
-I$(top_srcdir)/include \
|
||||
$(RTAI_LXRT_CFLAGS)
|
||||
|
||||
ec_rtai_rtdm_dc_example_LDFLAGS = \
|
||||
$(RTAI_LXRT_LDFLAGS) -llxrt -lrtdm \
|
||||
-L$(top_builddir)/lib/.libs -lethercat_rtdm
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
|
@ -0,0 +1,547 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2011 IgH Andreas Stewering-Bone
|
||||
* 2012 Florian Pose <fp@igh-essen.com>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <rtai_lxrt.h>
|
||||
#include <rtdm/rtdm.h>
|
||||
|
||||
#include "ecrt.h"
|
||||
|
||||
#define rt_printf(X, Y)
|
||||
|
||||
#define NSEC_PER_SEC 1000000000
|
||||
|
||||
RT_TASK *task;
|
||||
|
||||
static unsigned int cycle_ns = 1000000; /* 1 ms */
|
||||
|
||||
static int run = 1;
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
// EtherCAT
|
||||
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 uint8_t *domain1_pd = NULL;
|
||||
|
||||
static ec_slave_config_t *sc_dig_out_01 = NULL;
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
// EtherCAT distributed clock variables
|
||||
|
||||
#define DC_FILTER_CNT 1024
|
||||
#define SYNC_MASTER_TO_REF 1
|
||||
|
||||
static uint64_t dc_start_time_ns = 0LL;
|
||||
static uint64_t dc_time_ns = 0;
|
||||
#if SYNC_MASTER_TO_REF
|
||||
static uint8_t dc_started = 0;
|
||||
static int32_t dc_diff_ns = 0;
|
||||
static int32_t prev_dc_diff_ns = 0;
|
||||
static int64_t dc_diff_total_ns = 0LL;
|
||||
static int64_t dc_delta_total_ns = 0LL;
|
||||
static int dc_filter_idx = 0;
|
||||
static int64_t dc_adjust_ns;
|
||||
#endif
|
||||
static int64_t system_time_base = 0LL;
|
||||
static uint64_t wakeup_time = 0LL;
|
||||
static uint64_t overruns = 0LL;
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
// process data
|
||||
|
||||
#define BusCoupler01_Pos 0, 0
|
||||
#define DigOutSlave01_Pos 0, 1
|
||||
|
||||
#define Beckhoff_EK1100 0x00000002, 0x044c2c52
|
||||
#define Beckhoff_EL2004 0x00000002, 0x07d43052
|
||||
|
||||
// offsets for PDO entries
|
||||
static unsigned int off_dig_out0 = 0;
|
||||
|
||||
// process data
|
||||
|
||||
const static ec_pdo_entry_reg_t domain1_regs[] = {
|
||||
{DigOutSlave01_Pos, Beckhoff_EL2004, 0x7000, 0x01, &off_dig_out0, NULL},
|
||||
{}
|
||||
};
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/* Slave 1, "EL2004"
|
||||
* Vendor ID: 0x00000002
|
||||
* Product code: 0x07d43052
|
||||
* Revision number: 0x00100000
|
||||
*/
|
||||
|
||||
ec_pdo_entry_info_t slave_1_pdo_entries[] = {
|
||||
{0x7000, 0x01, 1}, /* Output */
|
||||
{0x7010, 0x01, 1}, /* Output */
|
||||
{0x7020, 0x01, 1}, /* Output */
|
||||
{0x7030, 0x01, 1}, /* Output */
|
||||
};
|
||||
|
||||
ec_pdo_info_t slave_1_pdos[] = {
|
||||
{0x1600, 1, slave_1_pdo_entries + 0}, /* Channel 1 */
|
||||
{0x1601, 1, slave_1_pdo_entries + 1}, /* Channel 2 */
|
||||
{0x1602, 1, slave_1_pdo_entries + 2}, /* Channel 3 */
|
||||
{0x1603, 1, slave_1_pdo_entries + 3}, /* Channel 4 */
|
||||
};
|
||||
|
||||
ec_sync_info_t slave_1_syncs[] = {
|
||||
{0, EC_DIR_OUTPUT, 4, slave_1_pdos + 0, EC_WD_ENABLE},
|
||||
{0xff}
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
* Realtime task
|
||||
****************************************************************************/
|
||||
|
||||
/** Get the time in ns for the current cpu, adjusted by system_time_base.
|
||||
*
|
||||
* \attention Rather than calling rt_get_time_ns() directly, all application
|
||||
* time calls should use this method instead.
|
||||
*
|
||||
* \ret The time in ns.
|
||||
*/
|
||||
uint64_t system_time_ns(void)
|
||||
{
|
||||
RTIME time = rt_get_time_ns();
|
||||
|
||||
if (system_time_base > time) {
|
||||
rt_printk("%s() error: system_time_base greater than"
|
||||
" system time (system_time_base: %lld, time: %llu\n",
|
||||
__func__, system_time_base, time);
|
||||
return time;
|
||||
}
|
||||
else {
|
||||
return time - system_time_base;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/** Convert system time to RTAI time in counts (via the system_time_base).
|
||||
*/
|
||||
RTIME system2count(
|
||||
uint64_t time
|
||||
)
|
||||
{
|
||||
RTIME ret;
|
||||
|
||||
if ((system_time_base < 0) &&
|
||||
((uint64_t) (-system_time_base) > time)) {
|
||||
rt_printk("%s() error: system_time_base less than"
|
||||
" system time (system_time_base: %lld, time: %llu\n",
|
||||
__func__, system_time_base, time);
|
||||
ret = time;
|
||||
}
|
||||
else {
|
||||
ret = time + system_time_base;
|
||||
}
|
||||
|
||||
return nano2count(ret);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Synchronise the distributed clocks
|
||||
*/
|
||||
void sync_distributed_clocks(void)
|
||||
{
|
||||
#if SYNC_MASTER_TO_REF
|
||||
uint32_t ref_time = 0;
|
||||
uint64_t prev_app_time = dc_time_ns;
|
||||
#endif
|
||||
|
||||
dc_time_ns = system_time_ns();
|
||||
|
||||
// set master time in nano-seconds
|
||||
ecrt_master_application_time(master, dc_time_ns);
|
||||
|
||||
#if SYNC_MASTER_TO_REF
|
||||
// get reference clock time to synchronize master cycle
|
||||
ecrt_master_reference_clock_time(master, &ref_time);
|
||||
dc_diff_ns = (uint32_t) prev_app_time - ref_time;
|
||||
#else
|
||||
// sync reference clock to master
|
||||
ecrt_master_sync_reference_clock(master);
|
||||
#endif
|
||||
|
||||
// call to sync slaves to ref slave
|
||||
ecrt_master_sync_slave_clocks(master);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Return the sign of a number
|
||||
*
|
||||
* ie -1 for -ve value, 0 for 0, +1 for +ve value
|
||||
*
|
||||
* \retval the sign of the value
|
||||
*/
|
||||
#define sign(val) \
|
||||
({ typeof (val) _val = (val); \
|
||||
((_val > 0) - (_val < 0)); })
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Update the master time based on ref slaves time diff
|
||||
*
|
||||
* called after the ethercat frame is sent to avoid time jitter in
|
||||
* sync_distributed_clocks()
|
||||
*/
|
||||
void update_master_clock(void)
|
||||
{
|
||||
#if SYNC_MASTER_TO_REF
|
||||
// calc drift (via un-normalised time diff)
|
||||
int32_t delta = dc_diff_ns - prev_dc_diff_ns;
|
||||
prev_dc_diff_ns = dc_diff_ns;
|
||||
|
||||
// normalise the time diff
|
||||
dc_diff_ns =
|
||||
((dc_diff_ns + (cycle_ns / 2)) % cycle_ns) - (cycle_ns / 2);
|
||||
|
||||
// only update if primary master
|
||||
if (dc_started) {
|
||||
|
||||
// add to totals
|
||||
dc_diff_total_ns += dc_diff_ns;
|
||||
dc_delta_total_ns += delta;
|
||||
dc_filter_idx++;
|
||||
|
||||
if (dc_filter_idx >= DC_FILTER_CNT) {
|
||||
// add rounded delta average
|
||||
dc_adjust_ns +=
|
||||
((dc_delta_total_ns + (DC_FILTER_CNT / 2)) / DC_FILTER_CNT);
|
||||
|
||||
// and add adjustment for general diff (to pull in drift)
|
||||
dc_adjust_ns += sign(dc_diff_total_ns / DC_FILTER_CNT);
|
||||
|
||||
// limit crazy numbers (0.1% of std cycle time)
|
||||
if (dc_adjust_ns < -1000) {
|
||||
dc_adjust_ns = -1000;
|
||||
}
|
||||
if (dc_adjust_ns > 1000) {
|
||||
dc_adjust_ns = 1000;
|
||||
}
|
||||
|
||||
// reset
|
||||
dc_diff_total_ns = 0LL;
|
||||
dc_delta_total_ns = 0LL;
|
||||
dc_filter_idx = 0;
|
||||
}
|
||||
|
||||
// add cycles adjustment to time base (including a spot adjustment)
|
||||
system_time_base += dc_adjust_ns + sign(dc_diff_ns);
|
||||
}
|
||||
else {
|
||||
dc_started = (dc_diff_ns != 0);
|
||||
|
||||
if (dc_started) {
|
||||
// output first diff
|
||||
rt_printk("First master diff: %d.\n", dc_diff_ns);
|
||||
|
||||
// record the time of this initial cycle
|
||||
dc_start_time_ns = dc_time_ns;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
void rt_check_domain_state(void)
|
||||
{
|
||||
ec_domain_state_t ds = {};
|
||||
|
||||
ecrt_domain_state(domain1, &ds);
|
||||
|
||||
if (ds.working_counter != domain1_state.working_counter) {
|
||||
rt_printf("Domain1: WC %u.\n", ds.working_counter);
|
||||
}
|
||||
|
||||
if (ds.wc_state != domain1_state.wc_state) {
|
||||
rt_printf("Domain1: State %u.\n", ds.wc_state);
|
||||
}
|
||||
|
||||
domain1_state = ds;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
void rt_check_master_state(void)
|
||||
{
|
||||
ec_master_state_t ms;
|
||||
|
||||
ecrt_master_state(master, &ms);
|
||||
|
||||
if (ms.slaves_responding != master_state.slaves_responding) {
|
||||
rt_printf("%u slave(s).\n", ms.slaves_responding);
|
||||
}
|
||||
|
||||
if (ms.al_states != master_state.al_states) {
|
||||
rt_printf("AL states: 0x%02X.\n", ms.al_states);
|
||||
}
|
||||
|
||||
if (ms.link_up != master_state.link_up) {
|
||||
rt_printf("Link is %s.\n", ms.link_up ? "up" : "down");
|
||||
}
|
||||
|
||||
master_state = ms;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/** Wait for the next period
|
||||
*/
|
||||
void wait_period(void)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
RTIME wakeup_count = system2count(wakeup_time);
|
||||
RTIME current_count = rt_get_time();
|
||||
|
||||
if ((wakeup_count < current_count)
|
||||
|| (wakeup_count > current_count + (50 * cycle_ns))) {
|
||||
rt_printk("%s(): unexpected wake time!\n", __func__);
|
||||
}
|
||||
|
||||
switch (rt_sleep_until(wakeup_count)) {
|
||||
case RTE_UNBLKD:
|
||||
rt_printk("rt_sleep_until(): RTE_UNBLKD\n");
|
||||
continue;
|
||||
|
||||
case RTE_TMROVRN:
|
||||
rt_printk("rt_sleep_until(): RTE_TMROVRN\n");
|
||||
overruns++;
|
||||
|
||||
if (overruns % 100 == 0) {
|
||||
// in case wake time is broken ensure other processes get
|
||||
// some time slice (and error messages can get displayed)
|
||||
rt_sleep(cycle_ns / 100);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// done if we got to here
|
||||
break;
|
||||
}
|
||||
|
||||
// calc next wake time (in sys time)
|
||||
wakeup_time += cycle_ns;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
void my_cyclic(void)
|
||||
{
|
||||
int cycle_counter = 0;
|
||||
unsigned int blink = 0;
|
||||
|
||||
// oneshot mode to allow adjustable wake time
|
||||
rt_set_oneshot_mode();
|
||||
|
||||
// set first wake time in a few cycles
|
||||
wakeup_time = system_time_ns() + 10 * cycle_ns;
|
||||
|
||||
// start the timer
|
||||
start_rt_timer(nano2count(cycle_ns));
|
||||
|
||||
rt_make_hard_real_time();
|
||||
|
||||
while (run) {
|
||||
// wait for next period (using adjustable system time)
|
||||
wait_period();
|
||||
|
||||
cycle_counter++;
|
||||
|
||||
if (!run) {
|
||||
break;
|
||||
}
|
||||
|
||||
// receive EtherCAT
|
||||
ecrt_master_receive(master);
|
||||
ecrt_domain_process(domain1);
|
||||
|
||||
rt_check_domain_state();
|
||||
|
||||
if (!(cycle_counter % 1000)) {
|
||||
rt_check_master_state();
|
||||
}
|
||||
|
||||
if (!(cycle_counter % 200)) {
|
||||
blink = !blink;
|
||||
}
|
||||
|
||||
EC_WRITE_U8(domain1_pd + off_dig_out0, blink ? 0x00 : 0x0F);
|
||||
|
||||
// queue process data
|
||||
ecrt_domain_queue(domain1);
|
||||
|
||||
// sync distributed clock just before master_send to set
|
||||
// most accurate master clock time
|
||||
sync_distributed_clocks();
|
||||
|
||||
// send EtherCAT data
|
||||
ecrt_master_send(master);
|
||||
|
||||
// update the master clock
|
||||
// Note: called after ecrt_master_send() to reduce time
|
||||
// jitter in the sync_distributed_clocks() call
|
||||
update_master_clock();
|
||||
}
|
||||
|
||||
rt_make_soft_real_time();
|
||||
stop_rt_timer();
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Signal handler
|
||||
***************************************************************************/
|
||||
|
||||
void signal_handler(int sig)
|
||||
{
|
||||
run = 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Main function
|
||||
***************************************************************************/
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
ec_slave_config_t *sc_ek1100;
|
||||
int ret;
|
||||
|
||||
signal(SIGTERM, signal_handler);
|
||||
signal(SIGINT, signal_handler);
|
||||
|
||||
mlockall(MCL_CURRENT | MCL_FUTURE);
|
||||
|
||||
printf("Requesting master...\n");
|
||||
master = ecrt_request_master(0);
|
||||
if (!master) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
domain1 = ecrt_master_create_domain(master);
|
||||
if (!domain1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("Creating slave configurations...\n");
|
||||
|
||||
// Create configuration for bus coupler
|
||||
sc_ek1100 =
|
||||
ecrt_master_slave_config(master, BusCoupler01_Pos, Beckhoff_EK1100);
|
||||
if (!sc_ek1100) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
sc_dig_out_01 =
|
||||
ecrt_master_slave_config(master, DigOutSlave01_Pos, Beckhoff_EL2004);
|
||||
if (!sc_dig_out_01) {
|
||||
fprintf(stderr, "Failed to get slave configuration.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ecrt_slave_config_pdos(sc_dig_out_01, EC_END, slave_1_syncs)) {
|
||||
fprintf(stderr, "Failed to configure PDOs.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ecrt_domain_reg_pdo_entry_list(domain1, domain1_regs)) {
|
||||
fprintf(stderr, "PDO entry registration failed!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set the initial master time and select a slave to use as the DC
|
||||
* reference clock, otherwise pass NULL to auto select the first capable
|
||||
* slave. Note: This can be used whether the master or the ref slave will
|
||||
* be used as the systems master DC clock.
|
||||
*/
|
||||
dc_start_time_ns = system_time_ns();
|
||||
dc_time_ns = dc_start_time_ns;
|
||||
ecrt_master_application_time(master, dc_start_time_ns);
|
||||
|
||||
ret = ecrt_master_select_reference_clock(master, sc_ek1100);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Failed to select reference clock: %s\n",
|
||||
strerror(-ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
printf("Activating master...\n");
|
||||
if (ecrt_master_activate(master)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(domain1_pd = ecrt_domain_data(domain1))) {
|
||||
fprintf(stderr, "Failed to get domain data pointer.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Create cyclic RT-thread */
|
||||
struct sched_param param;
|
||||
param.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1;
|
||||
if (sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {
|
||||
puts("ERROR IN SETTING THE SCHEDULER");
|
||||
perror("errno");
|
||||
return -1;
|
||||
}
|
||||
|
||||
task = rt_task_init(nam2num("ec_rtai_rtdm_example"),
|
||||
0 /* priority */, 0 /* stack size */, 0 /* msg size */);
|
||||
|
||||
my_cyclic();
|
||||
|
||||
rt_task_delete(task);
|
||||
|
||||
printf("End of Program\n");
|
||||
ecrt_release_master(master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
|
@ -57,6 +57,13 @@
|
|||
* ecrt_reg_request_data(), ecrt_reg_request_state(),
|
||||
* ecrt_reg_request_write(), ecrt_reg_request_read() and the feature flag
|
||||
* EC_HAVE_REG_ACCESS.
|
||||
* - Added method to select the reference clock,
|
||||
* ecrt_master_select_reference_clock() and the feature flag
|
||||
* EC_HAVE_SELECT_REF_CLOCK to check, if the method is available.
|
||||
* - Added method to get the reference clock time,
|
||||
* ecrt_master_reference_clock_time() and the feature flag
|
||||
* EC_HAVE_REF_CLOCK_TIME to have the possibility to synchronize the master
|
||||
* clock to the reference clock.
|
||||
*
|
||||
* Changes in version 1.5:
|
||||
*
|
||||
|
|
@ -163,6 +170,14 @@
|
|||
*/
|
||||
#define EC_HAVE_REG_ACCESS
|
||||
|
||||
/* Defined if the method ecrt_master_select_reference_clock() is available.
|
||||
*/
|
||||
#define EC_HAVE_SELECT_REF_CLOCK
|
||||
|
||||
/* Defined if the method ecrt_master_reference_clock_time() is available.
|
||||
*/
|
||||
#define EC_HAVE_REF_CLOCK_TIME
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** End of list marker.
|
||||
|
|
@ -664,6 +679,20 @@ ec_slave_config_t *ecrt_master_slave_config(
|
|||
uint32_t product_code /**< Expected product code. */
|
||||
);
|
||||
|
||||
/** Selects the reference clock for distributed clocks.
|
||||
*
|
||||
* If this method is not called for a certain master, or if the slave
|
||||
* configuration pointer is NULL, then the first slave with DC functionality
|
||||
* will provide the reference clock.
|
||||
*
|
||||
* \return 0 on success, otherwise negative error code.
|
||||
*/
|
||||
int ecrt_master_select_reference_clock(
|
||||
ec_master_t *master, /**< EtherCAT master. */
|
||||
ec_slave_config_t *sc /**< Slave config of the slave to use as the
|
||||
* reference slave (or NULL). */
|
||||
);
|
||||
|
||||
/** Obtains master information.
|
||||
*
|
||||
* No memory is allocated on the heap in
|
||||
|
|
@ -989,6 +1018,27 @@ void ecrt_master_sync_slave_clocks(
|
|||
ec_master_t *master /**< EtherCAT master. */
|
||||
);
|
||||
|
||||
/** Get the lower 32 bit of the reference clock system time.
|
||||
*
|
||||
* This method can be used to synchronize the master to the reference clock.
|
||||
*
|
||||
* The reference clock system time is queried via the
|
||||
* ecrt_master_sync_slave_clocks() method, that reads the system time of the
|
||||
* reference clock and writes it to the slave clocks (so be sure to call it
|
||||
* cyclically to get valid data).
|
||||
*
|
||||
* \attention The returned time is the system time of the reference clock
|
||||
* minus the transmission delay of the reference clock.
|
||||
*
|
||||
* \retval 0 success, system time was written into \a time.
|
||||
* \retval -ENXIO No reference clock found.
|
||||
* \retval -EIO Slave synchronization datagram was not received.
|
||||
*/
|
||||
int ecrt_master_reference_clock_time(
|
||||
ec_master_t *master, /**< EtherCAT master. */
|
||||
uint32_t *time /**< Pointer to store the queried system time. */
|
||||
);
|
||||
|
||||
/** Queues the DC synchrony monitoring datagram for sending.
|
||||
*
|
||||
* The datagram broadcast-reads all "System time difference" registers (\a
|
||||
|
|
|
|||
40
lib/master.c
40
lib/master.c
|
|
@ -200,6 +200,31 @@ ec_slave_config_t *ecrt_master_slave_config(ec_master_t *master,
|
|||
return sc;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
int ecrt_master_select_reference_clock(ec_master_t *master,
|
||||
ec_slave_config_t *sc)
|
||||
{
|
||||
uint32_t config_index;
|
||||
int ret;
|
||||
|
||||
if (sc) {
|
||||
config_index = sc->index;
|
||||
}
|
||||
else {
|
||||
config_index = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
ret = ioctl(master->fd, EC_IOCTL_SELECT_REF_CLOCK, config_index);
|
||||
if (EC_IOCTL_IS_ERROR(ret)) {
|
||||
fprintf(stderr, "Failed to select reference clock: %s\n",
|
||||
strerror(EC_IOCTL_ERRNO(ret)));
|
||||
return -EC_IOCTL_ERRNO(ret);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
int ecrt_master(ec_master_t *master, ec_master_info_t *master_info)
|
||||
|
|
@ -681,6 +706,21 @@ void ecrt_master_sync_slave_clocks(ec_master_t *master)
|
|||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
int ecrt_master_reference_clock_time(ec_master_t *master, uint32_t *time)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ioctl(master->fd, EC_IOCTL_REF_CLOCK_TIME, time);
|
||||
if (EC_IOCTL_IS_ERROR(ret)) {
|
||||
fprintf(stderr, "Failed to get reference clock time: %s\n",
|
||||
strerror(EC_IOCTL_ERRNO(ret)));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
void ecrt_master_sync_monitor_queue(ec_master_t *master)
|
||||
|
|
|
|||
|
|
@ -1562,6 +1562,45 @@ static int ec_ioctl_create_slave_config(
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Select the DC reference clock.
|
||||
*/
|
||||
static int ec_ioctl_select_ref_clock(
|
||||
ec_master_t *master, /**< EtherCAT master. */
|
||||
void *arg, /**< ioctl() argument. */
|
||||
ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
|
||||
)
|
||||
{
|
||||
uint32_t config_index = (uint32_t) arg;
|
||||
ec_slave_config_t *sc = NULL;
|
||||
int ret = 0;
|
||||
|
||||
if (unlikely(!ctx->requested)) {
|
||||
ret = -EPERM;
|
||||
goto out_return;
|
||||
}
|
||||
|
||||
if (down_interruptible(&master->master_sem)) {
|
||||
ret = -EINTR;
|
||||
goto out_return;
|
||||
}
|
||||
|
||||
if (config_index != 0xFFFFFFFF) {
|
||||
if (!(sc = ec_master_get_config(master, config_index))) {
|
||||
ret = -ENOENT;
|
||||
goto out_up;
|
||||
}
|
||||
}
|
||||
|
||||
ecrt_master_select_reference_clock(master, sc);
|
||||
|
||||
out_up:
|
||||
up(&master->master_sem);
|
||||
out_return:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Activates the master.
|
||||
*/
|
||||
static int ec_ioctl_activate(
|
||||
|
|
@ -1842,6 +1881,35 @@ static int ec_ioctl_sync_slaves(
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Get the system time of the reference clock.
|
||||
*/
|
||||
static int ec_ioctl_ref_clock_time(
|
||||
ec_master_t *master, /**< EtherCAT master. */
|
||||
void *arg, /**< ioctl() argument. */
|
||||
ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
|
||||
)
|
||||
{
|
||||
uint32_t time;
|
||||
int ret;
|
||||
|
||||
if (unlikely(!ctx->requested)) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
ret = ecrt_master_reference_clock_time(master, &time);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (copy_to_user((void __user *) arg, &time, sizeof(time))) {
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/** Queue the sync monitoring datagram.
|
||||
*/
|
||||
static int ec_ioctl_sync_mon_queue(
|
||||
|
|
@ -3923,6 +3991,13 @@ long EC_IOCTL(ec_master_t *master, ec_ioctl_context_t *ctx,
|
|||
}
|
||||
ret = ec_ioctl_create_slave_config(master, arg, ctx);
|
||||
break;
|
||||
case EC_IOCTL_SELECT_REF_CLOCK:
|
||||
if (!ctx->writable) {
|
||||
ret = -EPERM;
|
||||
break;
|
||||
}
|
||||
ret = ec_ioctl_select_ref_clock(master, arg, ctx);
|
||||
break;
|
||||
case EC_IOCTL_ACTIVATE:
|
||||
if (!ctx->writable) {
|
||||
ret = -EPERM;
|
||||
|
|
@ -3978,6 +4053,13 @@ long EC_IOCTL(ec_master_t *master, ec_ioctl_context_t *ctx,
|
|||
}
|
||||
ret = ec_ioctl_sync_slaves(master, arg, ctx);
|
||||
break;
|
||||
case EC_IOCTL_REF_CLOCK_TIME:
|
||||
if (!ctx->writable) {
|
||||
ret = -EPERM;
|
||||
break;
|
||||
}
|
||||
ret = ec_ioctl_ref_clock_time(master, arg, ctx);
|
||||
break;
|
||||
case EC_IOCTL_SYNC_MON_QUEUE:
|
||||
if (!ctx->writable) {
|
||||
ret = -EPERM;
|
||||
|
|
|
|||
108
master/ioctl.h
108
master/ioctl.h
|
|
@ -56,7 +56,7 @@
|
|||
*
|
||||
* Increment this when changing the ioctl interface!
|
||||
*/
|
||||
#define EC_IOCTL_VERSION_MAGIC 22
|
||||
#define EC_IOCTL_VERSION_MAGIC 23
|
||||
|
||||
// Command-line tool
|
||||
#define EC_IOCTL_MODULE EC_IOR(0x00, ec_ioctl_module_t)
|
||||
|
|
@ -96,58 +96,60 @@
|
|||
#define EC_IOCTL_REQUEST EC_IO(0x1e)
|
||||
#define EC_IOCTL_CREATE_DOMAIN EC_IO(0x1f)
|
||||
#define EC_IOCTL_CREATE_SLAVE_CONFIG EC_IOWR(0x20, ec_ioctl_config_t)
|
||||
#define EC_IOCTL_ACTIVATE EC_IOR(0x21, ec_ioctl_master_activate_t)
|
||||
#define EC_IOCTL_DEACTIVATE EC_IO(0x22)
|
||||
#define EC_IOCTL_SEND EC_IO(0x23)
|
||||
#define EC_IOCTL_RECEIVE EC_IO(0x24)
|
||||
#define EC_IOCTL_MASTER_STATE EC_IOR(0x25, ec_master_state_t)
|
||||
#define EC_IOCTL_MASTER_LINK_STATE EC_IOWR(0x26, ec_ioctl_link_state_t)
|
||||
#define EC_IOCTL_APP_TIME EC_IOW(0x27, ec_ioctl_app_time_t)
|
||||
#define EC_IOCTL_SYNC_REF EC_IO(0x28)
|
||||
#define EC_IOCTL_SYNC_SLAVES EC_IO(0x29)
|
||||
#define EC_IOCTL_SYNC_MON_QUEUE EC_IO(0x2a)
|
||||
#define EC_IOCTL_SYNC_MON_PROCESS EC_IOR(0x2b, uint32_t)
|
||||
#define EC_IOCTL_RESET EC_IO(0x2c)
|
||||
#define EC_IOCTL_SC_SYNC EC_IOW(0x2d, ec_ioctl_config_t)
|
||||
#define EC_IOCTL_SC_WATCHDOG EC_IOW(0x2e, ec_ioctl_config_t)
|
||||
#define EC_IOCTL_SC_ADD_PDO EC_IOW(0x2f, ec_ioctl_config_pdo_t)
|
||||
#define EC_IOCTL_SC_CLEAR_PDOS EC_IOW(0x30, ec_ioctl_config_pdo_t)
|
||||
#define EC_IOCTL_SC_ADD_ENTRY EC_IOW(0x31, ec_ioctl_add_pdo_entry_t)
|
||||
#define EC_IOCTL_SC_CLEAR_ENTRIES EC_IOW(0x32, ec_ioctl_config_pdo_t)
|
||||
#define EC_IOCTL_SC_REG_PDO_ENTRY EC_IOWR(0x33, ec_ioctl_reg_pdo_entry_t)
|
||||
#define EC_IOCTL_SC_DC EC_IOW(0x34, ec_ioctl_config_t)
|
||||
#define EC_IOCTL_SC_SDO EC_IOW(0x35, ec_ioctl_sc_sdo_t)
|
||||
#define EC_IOCTL_SC_EMERG_SIZE EC_IOW(0x36, ec_ioctl_sc_emerg_t)
|
||||
#define EC_IOCTL_SC_EMERG_POP EC_IOWR(0x37, ec_ioctl_sc_emerg_t)
|
||||
#define EC_IOCTL_SC_EMERG_CLEAR EC_IOW(0x38, ec_ioctl_sc_emerg_t)
|
||||
#define EC_IOCTL_SC_EMERG_OVERRUNS EC_IOWR(0x39, ec_ioctl_sc_emerg_t)
|
||||
#define EC_IOCTL_SC_SDO_REQUEST EC_IOWR(0x3a, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_SC_REG_REQUEST EC_IOWR(0x3b, ec_ioctl_reg_request_t)
|
||||
#define EC_IOCTL_SC_VOE EC_IOWR(0x3c, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_SC_STATE EC_IOWR(0x3d, ec_ioctl_sc_state_t)
|
||||
#define EC_IOCTL_SC_IDN EC_IOW(0x3e, ec_ioctl_sc_idn_t)
|
||||
#define EC_IOCTL_DOMAIN_OFFSET EC_IO(0x3f)
|
||||
#define EC_IOCTL_DOMAIN_PROCESS EC_IO(0x40)
|
||||
#define EC_IOCTL_DOMAIN_QUEUE EC_IO(0x41)
|
||||
#define EC_IOCTL_DOMAIN_STATE EC_IOWR(0x42, ec_ioctl_domain_state_t)
|
||||
#define EC_IOCTL_SDO_REQUEST_INDEX EC_IOWR(0x43, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_SDO_REQUEST_TIMEOUT EC_IOWR(0x44, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_SDO_REQUEST_STATE EC_IOWR(0x45, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_SDO_REQUEST_READ EC_IOWR(0x46, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_SDO_REQUEST_WRITE EC_IOWR(0x47, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_SDO_REQUEST_DATA EC_IOWR(0x48, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_REG_REQUEST_DATA EC_IOWR(0x49, ec_ioctl_reg_request_t)
|
||||
#define EC_IOCTL_REG_REQUEST_STATE EC_IOWR(0x4a, ec_ioctl_reg_request_t)
|
||||
#define EC_IOCTL_REG_REQUEST_WRITE EC_IOWR(0x4b, ec_ioctl_reg_request_t)
|
||||
#define EC_IOCTL_REG_REQUEST_READ EC_IOWR(0x4c, ec_ioctl_reg_request_t)
|
||||
#define EC_IOCTL_VOE_SEND_HEADER EC_IOW(0x4d, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_REC_HEADER EC_IOWR(0x4e, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_READ EC_IOW(0x4f, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_READ_NOSYNC EC_IOW(0x50, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_WRITE EC_IOWR(0x51, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_EXEC EC_IOWR(0x52, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_DATA EC_IOWR(0x53, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_SET_SEND_INTERVAL EC_IOW(0x54, size_t)
|
||||
#define EC_IOCTL_SELECT_REF_CLOCK EC_IOW(0x21, uint32_t)
|
||||
#define EC_IOCTL_ACTIVATE EC_IOR(0x22, ec_ioctl_master_activate_t)
|
||||
#define EC_IOCTL_DEACTIVATE EC_IO(0x23)
|
||||
#define EC_IOCTL_SEND EC_IO(0x24)
|
||||
#define EC_IOCTL_RECEIVE EC_IO(0x25)
|
||||
#define EC_IOCTL_MASTER_STATE EC_IOR(0x26, ec_master_state_t)
|
||||
#define EC_IOCTL_MASTER_LINK_STATE EC_IOWR(0x27, ec_ioctl_link_state_t)
|
||||
#define EC_IOCTL_APP_TIME EC_IOW(0x28, ec_ioctl_app_time_t)
|
||||
#define EC_IOCTL_SYNC_REF EC_IO(0x29)
|
||||
#define EC_IOCTL_SYNC_SLAVES EC_IO(0x2a)
|
||||
#define EC_IOCTL_REF_CLOCK_TIME EC_IOR(0x2b, uint32_t)
|
||||
#define EC_IOCTL_SYNC_MON_QUEUE EC_IO(0x2c)
|
||||
#define EC_IOCTL_SYNC_MON_PROCESS EC_IOR(0x2d, uint32_t)
|
||||
#define EC_IOCTL_RESET EC_IO(0x2e)
|
||||
#define EC_IOCTL_SC_SYNC EC_IOW(0x2f, ec_ioctl_config_t)
|
||||
#define EC_IOCTL_SC_WATCHDOG EC_IOW(0x30, ec_ioctl_config_t)
|
||||
#define EC_IOCTL_SC_ADD_PDO EC_IOW(0x31, ec_ioctl_config_pdo_t)
|
||||
#define EC_IOCTL_SC_CLEAR_PDOS EC_IOW(0x32, ec_ioctl_config_pdo_t)
|
||||
#define EC_IOCTL_SC_ADD_ENTRY EC_IOW(0x33, ec_ioctl_add_pdo_entry_t)
|
||||
#define EC_IOCTL_SC_CLEAR_ENTRIES EC_IOW(0x34, ec_ioctl_config_pdo_t)
|
||||
#define EC_IOCTL_SC_REG_PDO_ENTRY EC_IOWR(0x35, ec_ioctl_reg_pdo_entry_t)
|
||||
#define EC_IOCTL_SC_DC EC_IOW(0x36, ec_ioctl_config_t)
|
||||
#define EC_IOCTL_SC_SDO EC_IOW(0x37, ec_ioctl_sc_sdo_t)
|
||||
#define EC_IOCTL_SC_EMERG_SIZE EC_IOW(0x38, ec_ioctl_sc_emerg_t)
|
||||
#define EC_IOCTL_SC_EMERG_POP EC_IOWR(0x39, ec_ioctl_sc_emerg_t)
|
||||
#define EC_IOCTL_SC_EMERG_CLEAR EC_IOW(0x3a, ec_ioctl_sc_emerg_t)
|
||||
#define EC_IOCTL_SC_EMERG_OVERRUNS EC_IOWR(0x3b, ec_ioctl_sc_emerg_t)
|
||||
#define EC_IOCTL_SC_SDO_REQUEST EC_IOWR(0x3c, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_SC_REG_REQUEST EC_IOWR(0x3d, ec_ioctl_reg_request_t)
|
||||
#define EC_IOCTL_SC_VOE EC_IOWR(0x3e, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_SC_STATE EC_IOWR(0x3f, ec_ioctl_sc_state_t)
|
||||
#define EC_IOCTL_SC_IDN EC_IOW(0x40, ec_ioctl_sc_idn_t)
|
||||
#define EC_IOCTL_DOMAIN_OFFSET EC_IO(0x41)
|
||||
#define EC_IOCTL_DOMAIN_PROCESS EC_IO(0x42)
|
||||
#define EC_IOCTL_DOMAIN_QUEUE EC_IO(0x43)
|
||||
#define EC_IOCTL_DOMAIN_STATE EC_IOWR(0x44, ec_ioctl_domain_state_t)
|
||||
#define EC_IOCTL_SDO_REQUEST_INDEX EC_IOWR(0x45, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_SDO_REQUEST_TIMEOUT EC_IOWR(0x46, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_SDO_REQUEST_STATE EC_IOWR(0x47, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_SDO_REQUEST_READ EC_IOWR(0x48, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_SDO_REQUEST_WRITE EC_IOWR(0x49, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_SDO_REQUEST_DATA EC_IOWR(0x4a, ec_ioctl_sdo_request_t)
|
||||
#define EC_IOCTL_REG_REQUEST_DATA EC_IOWR(0x4b, ec_ioctl_reg_request_t)
|
||||
#define EC_IOCTL_REG_REQUEST_STATE EC_IOWR(0x4c, ec_ioctl_reg_request_t)
|
||||
#define EC_IOCTL_REG_REQUEST_WRITE EC_IOWR(0x4d, ec_ioctl_reg_request_t)
|
||||
#define EC_IOCTL_REG_REQUEST_READ EC_IOWR(0x4e, ec_ioctl_reg_request_t)
|
||||
#define EC_IOCTL_VOE_SEND_HEADER EC_IOW(0x4f, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_REC_HEADER EC_IOWR(0x50, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_READ EC_IOW(0x51, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_READ_NOSYNC EC_IOW(0x52, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_WRITE EC_IOWR(0x53, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_EXEC EC_IOWR(0x54, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_VOE_DATA EC_IOWR(0x55, ec_ioctl_voe_t)
|
||||
#define EC_IOCTL_SET_SEND_INTERVAL EC_IOW(0x56, size_t)
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
|
|||
ec_datagram_init(&master->ref_sync_datagram);
|
||||
snprintf(master->ref_sync_datagram.name, EC_DATAGRAM_NAME_SIZE,
|
||||
"refsync");
|
||||
ret = ec_datagram_apwr(&master->ref_sync_datagram, 0, 0x0910, 8);
|
||||
ret = ec_datagram_prealloc(&master->ref_sync_datagram, 4);
|
||||
if (ret < 0) {
|
||||
ec_datagram_clear(&master->ref_sync_datagram);
|
||||
EC_MASTER_ERR(master, "Failed to allocate reference"
|
||||
|
|
@ -265,7 +265,8 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
|
|||
goto out_clear_sync;
|
||||
}
|
||||
|
||||
ec_master_find_dc_ref_clock(master);
|
||||
master->dc_ref_config = NULL;
|
||||
master->dc_ref_clock = NULL;
|
||||
|
||||
// init character device
|
||||
ret = ec_cdev_init(&master->cdev, master, device_number);
|
||||
|
|
@ -395,6 +396,8 @@ void ec_master_clear_slave_configs(ec_master_t *master)
|
|||
{
|
||||
ec_slave_config_t *sc, *next;
|
||||
|
||||
master->dc_ref_config = NULL;
|
||||
|
||||
list_for_each_entry_safe(sc, next, &master->configs, list) {
|
||||
list_del(&sc->list);
|
||||
ec_slave_config_clear(sc);
|
||||
|
|
@ -1872,6 +1875,27 @@ void ec_master_find_dc_ref_clock(
|
|||
{
|
||||
ec_slave_t *slave, *ref = NULL;
|
||||
|
||||
if (master->dc_ref_config) {
|
||||
// Use application-selected reference clock
|
||||
slave = master->dc_ref_config->slave;
|
||||
|
||||
if (slave) {
|
||||
if (slave->base_dc_supported && slave->has_dc_system_time) {
|
||||
ref = slave;
|
||||
}
|
||||
else {
|
||||
EC_MASTER_WARN(master, "Slave %u can not act as a"
|
||||
" DC reference clock!", slave->ring_position);
|
||||
}
|
||||
}
|
||||
else {
|
||||
EC_MASTER_WARN(master, "DC reference clock config (%u-%u)"
|
||||
" has no slave attached!\n", master->dc_ref_config->alias,
|
||||
master->dc_ref_config->position);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Use first slave with DC support as reference clock
|
||||
for (slave = master->slaves;
|
||||
slave < master->slaves + master->slave_count;
|
||||
slave++) {
|
||||
|
|
@ -1881,9 +1905,22 @@ void ec_master_find_dc_ref_clock(
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
master->dc_ref_clock = ref;
|
||||
|
||||
// This call always succeeds, because the datagram has been pre-allocated.
|
||||
if (ref) {
|
||||
EC_MASTER_INFO(master, "Using slave %u as DC reference clock.\n",
|
||||
ref->ring_position);
|
||||
}
|
||||
else {
|
||||
EC_MASTER_INFO(master, "No DC reference clock found.\n");
|
||||
}
|
||||
|
||||
// These calls always succeed, because the
|
||||
// datagrams have been pre-allocated.
|
||||
ec_datagram_fpwr(&master->ref_sync_datagram,
|
||||
ref ? ref->station_address : 0xffff, 0x0910, 4);
|
||||
ec_datagram_frmw(&master->sync_datagram,
|
||||
ref ? ref->station_address : 0xffff, 0x0910, 4);
|
||||
}
|
||||
|
|
@ -2392,6 +2429,26 @@ ec_slave_config_t *ecrt_master_slave_config(ec_master_t *master,
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
int ecrt_master_select_reference_clock(ec_master_t *master,
|
||||
ec_slave_config_t *sc)
|
||||
{
|
||||
if (sc) {
|
||||
ec_slave_t *slave = sc->slave;
|
||||
|
||||
// output an early warning
|
||||
if (slave &&
|
||||
(!slave->base_dc_supported || !slave->has_dc_system_time)) {
|
||||
EC_MASTER_WARN(master, "Slave %u can not act as"
|
||||
" a reference clock!", slave->ring_position);
|
||||
}
|
||||
}
|
||||
|
||||
master->dc_ref_config = sc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
int ecrt_master(ec_master_t *master, ec_master_info_t *master_info)
|
||||
{
|
||||
EC_MASTER_DBG(master, 1, "ecrt_master(master = 0x%p,"
|
||||
|
|
@ -2525,6 +2582,25 @@ void ecrt_master_application_time(ec_master_t *master, uint64_t app_time)
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
int ecrt_master_reference_clock_time(ec_master_t *master, uint32_t *time)
|
||||
{
|
||||
if (!master->dc_ref_clock) {
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if (master->sync_datagram.state != EC_DATAGRAM_RECEIVED) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
// Get returned datagram time, transmission delay removed.
|
||||
*time = EC_READ_U32(master->sync_datagram.data) -
|
||||
master->dc_ref_clock->transmission_delay;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void ecrt_master_sync_reference_clock(ec_master_t *master)
|
||||
{
|
||||
EC_WRITE_U32(master->ref_sync_datagram.data, master->app_time);
|
||||
|
|
@ -2985,11 +3061,13 @@ EXPORT_SYMBOL(ecrt_master_callbacks);
|
|||
EXPORT_SYMBOL(ecrt_master);
|
||||
EXPORT_SYMBOL(ecrt_master_get_slave);
|
||||
EXPORT_SYMBOL(ecrt_master_slave_config);
|
||||
EXPORT_SYMBOL(ecrt_master_select_reference_clock);
|
||||
EXPORT_SYMBOL(ecrt_master_state);
|
||||
EXPORT_SYMBOL(ecrt_master_link_state);
|
||||
EXPORT_SYMBOL(ecrt_master_application_time);
|
||||
EXPORT_SYMBOL(ecrt_master_sync_reference_clock);
|
||||
EXPORT_SYMBOL(ecrt_master_sync_slave_clocks);
|
||||
EXPORT_SYMBOL(ecrt_master_reference_clock_time);
|
||||
EXPORT_SYMBOL(ecrt_master_sync_monitor_queue);
|
||||
EXPORT_SYMBOL(ecrt_master_sync_monitor_process);
|
||||
EXPORT_SYMBOL(ecrt_master_sdo_download);
|
||||
|
|
|
|||
|
|
@ -224,6 +224,8 @@ struct ec_master {
|
|||
compensation. */
|
||||
ec_datagram_t sync_mon_datagram; /**< Datagram used for DC synchronisation
|
||||
monitoring. */
|
||||
ec_slave_config_t *dc_ref_config; /**< Application-selected DC reference
|
||||
clock slave config. */
|
||||
ec_slave_t *dc_ref_clock; /**< DC reference clock slave. */
|
||||
|
||||
unsigned int scan_busy; /**< Current scan state. */
|
||||
|
|
|
|||
Loading…
Reference in New Issue