diff --git a/NEWS b/NEWS index f43546f7..fa19ec84 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,7 @@ Changes since 1.4.0: * Added a userspace library for accessing the application interface. This library is licensed under LGPLv2. +* Added distributed clocks support. * Added VoE mailbox protocol support. * Separated datagram initialization from filling the payload with zeros. Introduced new method ec_datagram_zero() for that. diff --git a/TODO b/TODO index 8081072d..50129619 100644 --- a/TODO +++ b/TODO @@ -10,6 +10,10 @@ vim: spelllang=en spell Version 1.5.0: +* Distributed clocks: + - Delay calculation. + - Synchronize the reference clock to the master clock. +* Read alias from register 0x0012 instead of SII. * Output link state in 'ethercat master'. * Finish library implementation. * Re-work EoE code. @@ -22,12 +26,10 @@ Version 1.5.0: * Check force_config flag before error. * Remove allow_scanning flag. * Test File access over EtherCAT (FoE) reading. -* Distributed clocks. * Implement ecrt_master_slave() in kernel space. * Check for ioctl() interface version. * Improve application-triggered SDO transfers by moving the statemachine into the SDO handlers. -* FoE in application interface? * Apply watchdog patches from J. Mohre. Future issues: diff --git a/include/ecrt.h b/include/ecrt.h index 9adc9809..7b7ce719 100644 --- a/include/ecrt.h +++ b/include/ecrt.h @@ -41,6 +41,10 @@ * * Changes in version 1.5: * + * - Added the distributed clocks feature and the respective methods + * ecrt_slave_config_dc_assign_activate() and + * ecrt_slave_config_dc_sync_cycle_times() to configure a slave for cyclic + * operation. * - Changed the meaning of the negative return values of * ecrt_slave_config_reg_pdo_entry() and ecrt_slave_config_sdo*(). * - Imlemented the Vendor-specific over EtherCAT mailbox protocol. See @@ -671,6 +675,25 @@ int ecrt_slave_config_reg_pdo_entry( is desired */ ); +/** Sets the AssignActivate word necessary for DC operation. + * + * The AssignActivate word is vendor-specific and can be taken from the XML + * device description file (Device -> Dc -> AssignActivate). Set this to zero, + * if the slave shall be not operated without distributed clocks (default). + */ +void ecrt_slave_config_dc_assign_activate( + ec_slave_config_t *sc, /**< Slave configuration. */ + uint16_t assign_activate /**< AssignActivate word. */ + ); + +/** Sets the cylce times for the SYNC0 and SYNC1 signals. + */ +void ecrt_slave_config_dc_sync_cycle_times( + ec_slave_config_t *sc, /**< Slave configuration. */ + uint32_t sync0_cycle_time, /**< SYNC0 cycle time [ns]. */ + uint32_t sync1_cycle_time /**< SYNC1 cycle time [ns]. */ + ); + /** Add an SDO configuration. * * An SDO configuration is stored in the slave configuration object and is diff --git a/master/fsm_slave_config.c b/master/fsm_slave_config.c index d200426a..7a8c6ec4 100644 --- a/master/fsm_slave_config.c +++ b/master/fsm_slave_config.c @@ -52,6 +52,9 @@ void ec_fsm_slave_config_state_sdo_conf(ec_fsm_slave_config_t *); void ec_fsm_slave_config_state_pdo_sync(ec_fsm_slave_config_t *); void ec_fsm_slave_config_state_pdo_conf(ec_fsm_slave_config_t *); void ec_fsm_slave_config_state_fmmu(ec_fsm_slave_config_t *); +void ec_fsm_slave_config_state_dc_cycle(ec_fsm_slave_config_t *); +void ec_fsm_slave_config_state_dc_start(ec_fsm_slave_config_t *); +void ec_fsm_slave_config_state_dc_assign(ec_fsm_slave_config_t *); void ec_fsm_slave_config_state_safeop(ec_fsm_slave_config_t *); void ec_fsm_slave_config_state_op(ec_fsm_slave_config_t *); @@ -63,6 +66,8 @@ void ec_fsm_slave_config_enter_sdo_conf(ec_fsm_slave_config_t *); void ec_fsm_slave_config_enter_pdo_conf(ec_fsm_slave_config_t *); void ec_fsm_slave_config_enter_pdo_sync(ec_fsm_slave_config_t *); void ec_fsm_slave_config_enter_fmmu(ec_fsm_slave_config_t *); +void ec_fsm_slave_config_enter_dc_cycle(ec_fsm_slave_config_t *); +void ec_fsm_slave_config_enter_dc_assign(ec_fsm_slave_config_t *); void ec_fsm_slave_config_enter_safeop(ec_fsm_slave_config_t *); void ec_fsm_slave_config_state_end(ec_fsm_slave_config_t *); @@ -602,6 +607,7 @@ void ec_fsm_slave_config_enter_sdo_conf( ec_slave_t *slave = fsm->slave; // No CoE configuration to be applied? + // FIXME check for config if (list_empty(&slave->config->sdo_configs)) { // skip SDO configuration ec_fsm_slave_config_enter_pdo_conf(fsm); return; @@ -805,7 +811,7 @@ void ec_fsm_slave_config_enter_fmmu( } if (!slave->base_fmmu_count) { // skip FMMU configuration - ec_fsm_slave_config_enter_safeop(fsm); + ec_fsm_slave_config_enter_dc_cycle(fsm); return; } @@ -861,6 +867,161 @@ void ec_fsm_slave_config_state_fmmu( return; } + ec_fsm_slave_config_enter_dc_cycle(fsm); +} + +/*****************************************************************************/ + +/** Check for DCs to be configured. + */ +void ec_fsm_slave_config_enter_dc_cycle( + ec_fsm_slave_config_t *fsm /**< slave state machine */ + ) +{ + ec_datagram_t *datagram = fsm->datagram; + ec_slave_t *slave = fsm->slave; + ec_slave_config_t *config = slave->config; + + if (config->dc_assign_activate) { + if (!slave->base_dc_supported) { + EC_WARN("Attempt to enable synchronized mode for slave %u," + " that seems not to support distributed clocks!\n", + slave->ring_position); + } + + // set DC cycle times + ec_datagram_fpwr(datagram, slave->station_address, 0x09A0, 8); + EC_WRITE_U32(datagram->data, config->dc_sync_cycle_times[0]); + EC_WRITE_U32(datagram->data + 4, config->dc_sync_cycle_times[1]); + fsm->retries = EC_FSM_RETRIES; + fsm->state = ec_fsm_slave_config_state_dc_cycle; + } else { + ec_fsm_slave_config_enter_dc_assign(fsm); + } +} + +/*****************************************************************************/ + +/** Slave configuration state: DC CYCLE. + */ +void ec_fsm_slave_config_state_dc_cycle( + ec_fsm_slave_config_t *fsm /**< slave state machine */ + ) +{ + ec_datagram_t *datagram = fsm->datagram; + ec_slave_t *slave = fsm->slave; + + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) + return; + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_slave_config_state_error; + EC_ERR("Failed to receive DC cycle times datagram for slave %u" + " (datagram state %u).\n", + slave->ring_position, datagram->state); + return; + } + + if (datagram->working_counter != 1) { + slave->error_flag = 1; + fsm->state = ec_fsm_slave_config_state_error; + EC_ERR("Failed to set DC cycle times of slave %u: ", + slave->ring_position); + ec_datagram_print_wc_error(datagram); + return; + } + + // set DC start time + ec_datagram_fpwr(datagram, slave->station_address, 0x0990, 8); + EC_WRITE_U64(datagram->data, 0x37E11D600ULL); // 15 s, FIXME + fsm->retries = EC_FSM_RETRIES; + fsm->state = ec_fsm_slave_config_state_dc_start; +} + +/*****************************************************************************/ + +/** Slave configuration state: DC START. + */ +void ec_fsm_slave_config_state_dc_start( + ec_fsm_slave_config_t *fsm /**< slave state machine */ + ) +{ + ec_datagram_t *datagram = fsm->datagram; + ec_slave_t *slave = fsm->slave; + + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) + return; + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_slave_config_state_error; + EC_ERR("Failed to receive DC start time datagram for slave %u" + " (datagram state %u).\n", + slave->ring_position, datagram->state); + return; + } + + if (datagram->working_counter != 1) { + slave->error_flag = 1; + fsm->state = ec_fsm_slave_config_state_error; + EC_ERR("Failed to set DC start time of slave %u: ", + slave->ring_position); + ec_datagram_print_wc_error(datagram); + return; + } + + ec_fsm_slave_config_enter_dc_assign(fsm); +} + +/*****************************************************************************/ + +/** Set the DC AssignActivate word. + */ +void ec_fsm_slave_config_enter_dc_assign( + ec_fsm_slave_config_t *fsm /**< slave state machine */ + ) +{ + ec_datagram_t *datagram = fsm->datagram; + ec_slave_t *slave = fsm->slave; + ec_slave_config_t *config = slave->config; + + // assign sync unit to EtherCAT or PDI + ec_datagram_fpwr(datagram, slave->station_address, 0x0980, 2); + EC_WRITE_U16(datagram->data, config->dc_assign_activate); + fsm->retries = EC_FSM_RETRIES; + fsm->state = ec_fsm_slave_config_state_dc_assign; +} + +/*****************************************************************************/ + +/** Slave configuration state: DC ASSIGN. + */ +void ec_fsm_slave_config_state_dc_assign( + ec_fsm_slave_config_t *fsm /**< slave state machine */ + ) +{ + ec_datagram_t *datagram = fsm->datagram; + ec_slave_t *slave = fsm->slave; + + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) + return; + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_slave_config_state_error; + EC_ERR("Failed to receive DC activation datagram for slave %u" + " (datagram state %u).\n", + slave->ring_position, datagram->state); + return; + } + + if (datagram->working_counter != 1) { + slave->error_flag = 1; + fsm->state = ec_fsm_slave_config_state_error; + EC_ERR("Failed to set DC cyclia operation state of slave %u: ", + slave->ring_position); + ec_datagram_print_wc_error(datagram); + return; + } + ec_fsm_slave_config_enter_safeop(fsm); } diff --git a/master/slave_config.c b/master/slave_config.c index 6fe04ac5..b2959f8d 100644 --- a/master/slave_config.c +++ b/master/slave_config.c @@ -61,20 +61,26 @@ void ec_slave_config_init( unsigned int i; sc->master = master; + sc->alias = alias; sc->position = position; sc->vendor_id = vendor_id; sc->product_code = product_code; + sc->slave = NULL; for (i = 0; i < EC_MAX_SYNC_MANAGERS; i++) ec_sync_config_init(&sc->sync_configs[i]); + sc->used_fmmus = 0; + + sc->dc_assign_activate = 0x0000; + sc->dc_sync_cycle_times[0] = 0x00000000; + sc->dc_sync_cycle_times[1] = 0x00000000; + INIT_LIST_HEAD(&sc->sdo_configs); INIT_LIST_HEAD(&sc->sdo_requests); INIT_LIST_HEAD(&sc->voe_handlers); - - sc->used_fmmus = 0; } /*****************************************************************************/ @@ -399,7 +405,7 @@ ec_voe_handler_t *ec_slave_config_find_voe_handler( } /****************************************************************************** - * Realtime interface + * Application interface *****************************************************************************/ int ecrt_slave_config_sync_manager(ec_slave_config_t *sc, uint8_t sync_index, @@ -665,6 +671,22 @@ int ecrt_slave_config_reg_pdo_entry( return -ENOENT; } +/*****************************************************************************/ + +void ecrt_slave_config_dc_assign_activate(ec_slave_config_t *sc, + uint16_t assign_activate) +{ + sc->dc_assign_activate = assign_activate; +} + +/*****************************************************************************/ + +void ecrt_slave_config_dc_sync_cycle_times(ec_slave_config_t *sc, + uint32_t sync0_cycle_time, uint32_t sync1_cycle_time) +{ + sc->dc_sync_cycle_times[0] = sync0_cycle_time; + sc->dc_sync_cycle_times[1] = sync1_cycle_time; +} /*****************************************************************************/ @@ -880,6 +902,8 @@ EXPORT_SYMBOL(ecrt_slave_config_pdo_mapping_add); EXPORT_SYMBOL(ecrt_slave_config_pdo_mapping_clear); EXPORT_SYMBOL(ecrt_slave_config_pdos); EXPORT_SYMBOL(ecrt_slave_config_reg_pdo_entry); +EXPORT_SYMBOL(ecrt_slave_config_dc_assign_activate); +EXPORT_SYMBOL(ecrt_slave_config_dc_sync_cycle_times); EXPORT_SYMBOL(ecrt_slave_config_sdo); EXPORT_SYMBOL(ecrt_slave_config_sdo8); EXPORT_SYMBOL(ecrt_slave_config_sdo16); diff --git a/master/slave_config.h b/master/slave_config.h index eb622950..a7109a6f 100644 --- a/master/slave_config.h +++ b/master/slave_config.h @@ -66,6 +66,9 @@ struct ec_slave_config { ec_fmmu_config_t fmmu_configs[EC_MAX_FMMUS]; /**< FMMU configurations. */ uint8_t used_fmmus; /**< Number of FMMUs used. */ + uint16_t dc_assign_activate; /**< Vendor-specific AssignActivate word. */ + uint32_t dc_sync_cycle_times[2]; /**< SYNC[0,1] cycle times. */ + struct list_head sdo_configs; /**< List of SDO configurations. */ struct list_head sdo_requests; /**< List of SDO requests. */ struct list_head voe_handlers; /**< List of VoE handlers. */