Added --enable-loop-control to make use of the loop control registers.
authorFlorian Pose <fp@igh-essen.com>
Fri, 20 Feb 2015 16:06:23 +0100
changeset 2600 1a969896d52e
parent 2598 19ff84bbbcb3
child 2601 9b36a16271e6
child 2633 3b61ec73acd2
Added --enable-loop-control to make use of the loop control registers.
configure.ac
master/fsm_master.c
master/fsm_slave_scan.c
master/slave.c
master/slave.h
--- a/configure.ac	Thu Feb 19 15:19:29 2015 +0100
+++ b/configure.ac	Fri Feb 20 16:06:23 2015 +0100
@@ -1031,6 +1031,35 @@
 fi
 
 #------------------------------------------------------------------------------
+# use loop control registers to open slave ports
+#------------------------------------------------------------------------------
+
+AC_MSG_CHECKING([whether to use loop control registers])
+
+AC_ARG_ENABLE([loop-control],
+    AS_HELP_STRING([--enable-loop-control],
+                   [Use loop control registers (default: no)]),
+    [
+        case "${enableval}" in
+            yes) loopctl=1
+                ;;
+            no) loopctl=0
+                ;;
+            *) AC_MSG_ERROR([Invalid value for --enable-loop-control])
+                ;;
+        esac
+    ],
+    [loopctl=0]
+)
+
+if test "x${loopctl}" = "x1"; then
+    AC_DEFINE([EC_LOOP_CONTROL], [1], [Use loop control registers])
+    AC_MSG_RESULT([yes])
+else
+    AC_MSG_RESULT([no])
+fi
+
+#------------------------------------------------------------------------------
 
 AC_CONFIG_FILES([
         Doxyfile
--- a/master/fsm_master.c	Thu Feb 19 15:19:29 2015 +0100
+++ b/master/fsm_master.c	Fri Feb 20 16:06:23 2015 +0100
@@ -54,10 +54,17 @@
 
 void ec_fsm_master_state_start(ec_fsm_master_t *);
 void ec_fsm_master_state_broadcast(ec_fsm_master_t *);
-void ec_fsm_master_state_read_state(ec_fsm_master_t *);
+void ec_fsm_master_state_read_al_status(ec_fsm_master_t *);
+#ifdef EC_LOOP_CONTROL
+void ec_fsm_master_state_read_dl_status(ec_fsm_master_t *);
+void ec_fsm_master_state_open_port(ec_fsm_master_t *);
+#endif
 void ec_fsm_master_state_acknowledge(ec_fsm_master_t *);
 void ec_fsm_master_state_configure_slave(ec_fsm_master_t *);
 void ec_fsm_master_state_clear_addresses(ec_fsm_master_t *);
+#ifdef EC_LOOP_CONTROL
+void ec_fsm_master_state_loop_control(ec_fsm_master_t *);
+#endif
 void ec_fsm_master_state_dc_measure_delays(ec_fsm_master_t *);
 void ec_fsm_master_state_scan_slave(ec_fsm_master_t *);
 void ec_fsm_master_state_dc_read_offset(ec_fsm_master_t *);
@@ -415,7 +422,7 @@
             ec_datagram_zero(datagram);
             fsm->datagram->device_index = fsm->slave->device_index;
             fsm->retries = EC_FSM_RETRIES;
-            fsm->state = ec_fsm_master_state_read_state;
+            fsm->state = ec_fsm_master_state_read_al_status;
         }
     } else {
         ec_fsm_master_restart(fsm);
@@ -594,7 +601,7 @@
         ec_datagram_zero(fsm->datagram);
         fsm->datagram->device_index = fsm->slave->device_index;
         fsm->retries = EC_FSM_RETRIES;
-        fsm->state = ec_fsm_master_state_read_state;
+        fsm->state = ec_fsm_master_state_read_al_status;
         return;
     }
 
@@ -604,6 +611,151 @@
 
 /*****************************************************************************/
 
+#ifdef EC_LOOP_CONTROL
+
+/** Master action: Read DL status of current slave.
+ */
+void ec_fsm_master_action_read_dl_status(
+        ec_fsm_master_t *fsm /**< Master state machine. */
+        )
+{
+    ec_datagram_fprd(fsm->datagram, fsm->slave->station_address, 0x0110, 2);
+    ec_datagram_zero(fsm->datagram);
+    fsm->datagram->device_index = fsm->slave->device_index;
+    fsm->retries = EC_FSM_RETRIES;
+    fsm->state = ec_fsm_master_state_read_dl_status;
+}
+
+/*****************************************************************************/
+
+/** Master action: Open slave port.
+ */
+void ec_fsm_master_action_open_port(
+        ec_fsm_master_t *fsm /**< Master state machine. */
+        )
+{
+    EC_SLAVE_INFO(fsm->slave, "Opening ports.\n");
+
+    ec_datagram_fpwr(fsm->datagram, fsm->slave->station_address, 0x0101, 1);
+    EC_WRITE_U8(fsm->datagram->data, 0x54); // port 0 auto, 1-3 auto-close
+    fsm->datagram->device_index = fsm->slave->device_index;
+    fsm->retries = EC_FSM_RETRIES;
+    fsm->state = ec_fsm_master_state_open_port;
+}
+
+/*****************************************************************************/
+
+/** Master state: READ DL STATUS.
+ *
+ * Fetches the DL state of a slave.
+ */
+void ec_fsm_master_state_read_dl_status(
+        ec_fsm_master_t *fsm /**< Master state machine. */
+        )
+{
+    ec_slave_t *slave = fsm->slave;
+    ec_datagram_t *datagram = fsm->datagram;
+    unsigned int i;
+
+    if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) {
+        return;
+    }
+
+    if (datagram->state != EC_DATAGRAM_RECEIVED) {
+        EC_SLAVE_ERR(slave, "Failed to receive AL state datagram: ");
+        ec_datagram_print_state(datagram);
+        ec_fsm_master_restart(fsm);
+        return;
+    }
+
+    // did the slave not respond to its station address?
+    if (datagram->working_counter != 1) {
+        // try again next time
+        ec_fsm_master_action_next_slave_state(fsm);
+        return;
+    }
+
+    ec_slave_set_dl_status(slave, EC_READ_U16(datagram->data));
+
+    // process port state machines
+    for (i = 0; i < EC_MAX_PORTS; i++) {
+        ec_slave_port_t *port = &slave->ports[i];
+
+        switch (port->state) {
+            case EC_SLAVE_PORT_DOWN:
+                if (port->link.loop_closed) {
+                    if (port->link.link_up) {
+                        port->link_detection_jiffies = jiffies;
+                        port->state = EC_SLAVE_PORT_WAIT;
+                    }
+                }
+                else { // loop open
+                    port->state = EC_SLAVE_PORT_UP;
+                }
+                break;
+            case EC_SLAVE_PORT_WAIT:
+                if (port->link.link_up) {
+                    if (jiffies - port->link_detection_jiffies >
+                            HZ * EC_PORT_WAIT_MS / 1000) {
+                        port->state = EC_SLAVE_PORT_UP;
+                        ec_fsm_master_action_open_port(fsm);
+                        return;
+                    }
+                }
+                else { // link down
+                    port->state = EC_SLAVE_PORT_DOWN;
+                }
+                break;
+            default: // EC_SLAVE_PORT_UP
+                if (!port->link.link_up) {
+                    port->state = EC_SLAVE_PORT_DOWN;
+                }
+                break;
+        }
+    }
+
+    // process next slave
+    ec_fsm_master_action_next_slave_state(fsm);
+}
+
+/*****************************************************************************/
+
+/** Master state: OPEN_PORT.
+ *
+ * Opens slave ports.
+ */
+void ec_fsm_master_state_open_port(
+        ec_fsm_master_t *fsm /**< Master state machine. */
+        )
+{
+    ec_slave_t *slave = fsm->slave;
+    ec_datagram_t *datagram = fsm->datagram;
+
+    if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) {
+        return;
+    }
+
+    if (datagram->state != EC_DATAGRAM_RECEIVED) {
+        EC_SLAVE_ERR(slave, "Failed to receive port open datagram: ");
+        ec_datagram_print_state(datagram);
+        ec_fsm_master_restart(fsm);
+        return;
+    }
+
+    // did the slave not respond to its station address?
+    if (datagram->working_counter != 1) {
+        EC_SLAVE_ERR(slave, "Did not respond to port open command!\n");
+        return;
+    }
+
+    // process next slave
+    ec_fsm_master_action_next_slave_state(fsm);
+}
+
+#endif
+
+/*****************************************************************************/
+
 /** Master action: Configure.
  */
 void ec_fsm_master_action_configure(
@@ -654,17 +806,22 @@
         return;
     }
 
+#ifdef EC_LOOP_CONTROL
+    // read DL status
+    ec_fsm_master_action_read_dl_status(fsm);
+#else
     // process next slave
     ec_fsm_master_action_next_slave_state(fsm);
-}
-
-/*****************************************************************************/
-
-/** Master state: READ STATE.
+#endif
+}
+
+/*****************************************************************************/
+
+/** Master state: READ AL STATUS.
  *
  * Fetches the AL state of a slave.
  */
-void ec_fsm_master_state_read_state(
+void ec_fsm_master_state_read_al_status(
         ec_fsm_master_t *fsm /**< Master state machine. */
         )
 {
@@ -694,7 +851,7 @@
     }
 
     // A single slave responded
-    ec_slave_set_state(slave, EC_READ_U8(datagram->data));
+    ec_slave_set_al_status(slave, EC_READ_U8(datagram->data));
 
     if (!slave->error_flag) {
         // Check, if new slave state has to be acknowledged
@@ -711,8 +868,13 @@
         return;
     }
 
-    // slave has error flag set; process next one
+#ifdef EC_LOOP_CONTROL
+    // read DL status
+    ec_fsm_master_action_read_dl_status(fsm);
+#else
+    // process next slave
     ec_fsm_master_action_next_slave_state(fsm);
+#endif
 }
 
 /*****************************************************************************/
@@ -755,6 +917,73 @@
 
 /*****************************************************************************/
 
+/** Start measuring DC delays.
+ */
+void ec_fsm_master_enter_dc_measure_delays(
+        ec_fsm_master_t *fsm /**< Master state machine. */
+        )
+{
+    EC_MASTER_DBG(fsm->master, 1, "Sending broadcast-write"
+            " to measure transmission delays on %s link.\n",
+            ec_device_names[fsm->dev_idx != 0]);
+
+    ec_datagram_bwr(fsm->datagram, 0x0900, 1);
+    ec_datagram_zero(fsm->datagram);
+    fsm->datagram->device_index = fsm->dev_idx;
+    fsm->retries = EC_FSM_RETRIES;
+    fsm->state = ec_fsm_master_state_dc_measure_delays;
+}
+
+/*****************************************************************************/
+
+#ifdef EC_LOOP_CONTROL
+
+/** Start writing loop control registers.
+ */
+void ec_fsm_master_enter_loop_control(
+        ec_fsm_master_t *fsm /**< Master state machine. */
+        )
+{
+    EC_MASTER_DBG(fsm->master, 1, "Broadcast-writing"
+            " loop control registers on %s link.\n",
+            ec_device_names[fsm->dev_idx != 0]);
+
+    ec_datagram_bwr(fsm->datagram, 0x0101, 1);
+    EC_WRITE_U8(fsm->datagram->data, 0x54); // port 0 auto, 1-3 auto-close
+    fsm->datagram->device_index = fsm->dev_idx;
+    fsm->retries = EC_FSM_RETRIES;
+    fsm->state = ec_fsm_master_state_loop_control;
+}
+
+/*****************************************************************************/
+
+/** Master state: LOOP CONTROL.
+ */
+void ec_fsm_master_state_loop_control(
+        ec_fsm_master_t *fsm /**< Master state machine. */
+        )
+{
+    ec_master_t *master = fsm->master;
+    ec_datagram_t *datagram = fsm->datagram;
+
+    if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) {
+        return;
+    }
+
+    if (datagram->state != EC_DATAGRAM_RECEIVED) {
+        EC_MASTER_ERR(master, "Failed to receive loop control"
+                " datagram on %s link: ",
+                ec_device_names[fsm->dev_idx != 0]);
+        ec_datagram_print_state(datagram);
+    }
+
+    ec_fsm_master_enter_dc_measure_delays(fsm);
+}
+
+#endif
+
+/*****************************************************************************/
+
 /** Master state: CLEAR ADDRESSES.
  */
 void ec_fsm_master_state_clear_addresses(
@@ -786,15 +1015,11 @@
                 fsm->slaves_responding[fsm->dev_idx]);
     }
 
-    EC_MASTER_DBG(master, 1, "Sending broadcast-write"
-            " to measure transmission delays on %s link.\n",
-            ec_device_names[fsm->dev_idx != 0]);
-
-    ec_datagram_bwr(datagram, 0x0900, 1);
-    ec_datagram_zero(datagram);
-    fsm->datagram->device_index = fsm->dev_idx;
-    fsm->retries = EC_FSM_RETRIES;
-    fsm->state = ec_fsm_master_state_dc_measure_delays;
+#ifdef EC_LOOP_CONTROL
+    ec_fsm_master_enter_loop_control(fsm);
+#else
+    ec_fsm_master_enter_dc_measure_delays(fsm);
+#endif
 }
 
 /*****************************************************************************/
@@ -947,7 +1172,14 @@
     }
 
     fsm->idle = 1;
+
+#ifdef EC_LOOP_CONTROL
+    // read DL status
+    ec_fsm_master_action_read_dl_status(fsm);
+#else
+    // process next slave
     ec_fsm_master_action_next_slave_state(fsm);
+#endif
 }
 
 /*****************************************************************************/
--- a/master/fsm_slave_scan.c	Thu Feb 19 15:19:29 2015 +0100
+++ b/master/fsm_slave_scan.c	Fri Feb 20 16:06:23 2015 +0100
@@ -493,8 +493,6 @@
 {
     ec_datagram_t *datagram = fsm->datagram;
     ec_slave_t *slave = fsm->slave;
-    uint16_t dl_status;
-    unsigned int i;
 
     if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
         return;
@@ -514,15 +512,7 @@
         return;
     }
 
-    dl_status = EC_READ_U16(datagram->data);
-    for (i = 0; i < EC_MAX_PORTS; i++) {
-        slave->ports[i].link.link_up =
-            dl_status & (1 << (4 + i)) ? 1 : 0;
-        slave->ports[i].link.loop_closed =
-            dl_status & (1 << (8 + i * 2)) ? 1 : 0;
-        slave->ports[i].link.signal_detected =
-            dl_status & (1 << (9 + i * 2)) ? 1 : 0;
-    }
+    ec_slave_set_dl_status(slave, EC_READ_U16(datagram->data));
 
 #ifdef EC_SII_ASSIGN
     ec_fsm_slave_scan_enter_assign_sii(fsm);
--- a/master/slave.c	Thu Feb 19 15:19:29 2015 +0100
+++ b/master/slave.c	Fri Feb 20 16:06:23 2015 +0100
@@ -95,7 +95,7 @@
         slave->ports[i].desc = EC_PORT_NOT_IMPLEMENTED;
 
         slave->ports[i].link.link_up = 0;
-        slave->ports[i].link.loop_closed = 0;
+        slave->ports[i].link.loop_closed = 1;
         slave->ports[i].link.signal_detected = 0;
         slave->sii.physical_layer[i] = 0xFF;
 
@@ -103,6 +103,11 @@
 
         slave->ports[i].next_slave = NULL;
         slave->ports[i].delay_to_next_dc = 0U;
+
+#ifdef EC_LOOP_CONTROL
+        slave->ports[i].state = EC_SLAVE_PORT_DOWN;
+        slave->ports[i].link_detection_jiffies = 0;
+#endif
     }
 
     slave->base_fmmu_bit_operation = 0;
@@ -278,10 +283,50 @@
 /*****************************************************************************/
 
 /**
+ * Sets the data-link state of a slave.
+ */
+
+void ec_slave_set_dl_status(ec_slave_t *slave, /**< EtherCAT slave */
+        uint16_t new_state /**< content of registers 0x0110-0x0111. */
+        )
+{
+    unsigned int i;
+    uint8_t state;
+
+    for (i = 0; i < EC_MAX_PORTS; i++) {
+        // link status
+        state = new_state & (1 << (4 + i)) ? 1 : 0;
+        if (slave->ports[i].link.link_up != state) {
+            EC_SLAVE_DBG(slave, 1, "Port %u link status changed to %s.\n",
+                    i, state ? "up" : "down");
+            slave->ports[i].link.link_up = state;
+        }
+
+        // loop status
+        state = new_state & (1 << (8 + i * 2)) ? 1 : 0;
+        if (slave->ports[i].link.loop_closed != state) {
+            EC_SLAVE_DBG(slave, 1, "Port %u loop status changed to %s.\n",
+                    i, state ? "closed" : "open");
+            slave->ports[i].link.loop_closed = state;
+        }
+
+        // signal detection
+        state = new_state & (1 << (9 + i * 2)) ? 1 : 0;
+        if (slave->ports[i].link.signal_detected != state) {
+            EC_SLAVE_DBG(slave, 1, "Port %u signal status changed to %s.\n",
+                    i, state ? "yes" : "no");
+            slave->ports[i].link.signal_detected = state;
+        }
+    }
+}
+
+/*****************************************************************************/
+
+/**
  * Sets the application state of a slave.
  */
 
-void ec_slave_set_state(ec_slave_t *slave, /**< EtherCAT slave */
+void ec_slave_set_al_status(ec_slave_t *slave, /**< EtherCAT slave */
         ec_slave_state_t new_state /**< new application state */
         )
 {
--- a/master/slave.h	Thu Feb 19 15:19:29 2015 +0100
+++ b/master/slave.h	Fri Feb 20 16:06:23 2015 +0100
@@ -113,6 +113,24 @@
 
 /*****************************************************************************/
 
+#ifdef EC_LOOP_CONTROL
+
+/** Slave port state.
+ */
+typedef enum {
+    EC_SLAVE_PORT_DOWN,
+    EC_SLAVE_PORT_WAIT,
+    EC_SLAVE_PORT_UP
+} ec_slave_port_state_t;
+
+/** Wait time in [ms] from detecting a link to opening a port.
+ */
+#define EC_PORT_WAIT_MS 2000
+
+#endif
+
+/*****************************************************************************/
+
 /** Slave port.
  */
 typedef struct {
@@ -123,6 +141,10 @@
                                             measurement. */
     uint32_t delay_to_next_dc; /**< Delay to next slave with DC support behind
                                  this port [ns]. */
+#ifdef EC_LOOP_CONTROL
+    ec_slave_port_state_t state; /**< Port state for loop control. */
+    unsigned long link_detection_jiffies; /**< Time of link detection. */
+#endif
 } ec_slave_port_t;
 
 /*****************************************************************************/
@@ -245,7 +267,8 @@
 void ec_slave_clear_sync_managers(ec_slave_t *);
 
 void ec_slave_request_state(ec_slave_t *, ec_slave_state_t);
-void ec_slave_set_state(ec_slave_t *, ec_slave_state_t);
+void ec_slave_set_dl_status(ec_slave_t *, uint16_t);
+void ec_slave_set_al_status(ec_slave_t *, ec_slave_state_t);
 
 // SII categories
 int ec_slave_fetch_sii_strings(ec_slave_t *, const uint8_t *, size_t);