Hello,
I'm currently working a small setup which involves a Beckhoff
FB1111-0141 piggyback module (PDI type: SPI). Eventually, I would like
to run a real-time control loop over this setup, by writing a PWM value
to a register, and reading an encoder value from another on. For a first
proof-of-concept, I'm trying to adapt the 'user' example to my likes.
(console output is between +++ and --- lines)
In short, I'm trying to write the blinking value (alternating 0x0606 and
0x0909) to the output register (0x1a00). I've adapted the PDO
declarations etcetera. Observing the output, I guess the master works
correctly:
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Configuring PDOs...
Activating master...
pd: 0
Starting timer...
Started.
Blink is 1
1 slave(s).
AL states: 0x01.
Link is up.
SPI: State 0x01.
SPI: online.
---------------------------------------------------------------
Yet when I manually check (over Ethercat, not via the PDI) what values
are written into the output registers, they do not seem correct (and
even random).
Furthermore, the slave board always reports itself as being in the INIT
state, with error flag set:
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
burgh...@ethercat-master:~$ sudo ethercat slaves -v
=== Master 0, Slave 0 ===
State: INIT
Flag: E
Identity:
Vendor Id: 0x00000002
Product code: 0x04570862
Revision number: 0x0258008d
Serial number: 0x00000000
DL information:
FMMU bit operation: no
Distributed clocks: yes, 64 bit
DC system time transmission delay: 0 ns
Port Type Link Loop Signal NextSlave RxTime [ns] Diff [ns]
NextDc [ns]
0 MII up open yes - 3409590130 0
0
1 MII down closed no - - -
-
2 N/A down closed no - - -
-
3 N/A down closed no - - -
-
Mailboxes:
Bootstrap RX: 0x0000/0, TX: 0x0000/0
Standard RX: 0x1000/128, TX: 0x1080/128
Supported protocols: CoE
General:
Group: FB1XXX
Image name:
Order number: FB1111 SPI-Slave
Device name: FB1111 SPI-Slave 2xMII
CoE details:
Enable SDO: yes
Enable SDO Info: no
Enable PDO Assign: no
Enable PDO Configuration: no
Enable Upload at startup: no
Enable SDO complete access: no
Flags:
Enable SafeOp: no
Enable notLRW: no
Current consumption: 0 mA
---------------------------------------------------------------
AL registers however report no error and also do not mention the error
state being set!
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
burgh...@ethercat-master:~$ sudo ethercat reg_read -p0 -tuint16 0x0120
0x0001 1
burgh...@ethercat-master:~$ sudo ethercat reg_read -p0 -tuint16 0x0130
0x0001 1
burgh...@ethercat-master:~$ sudo ethercat reg_read -p0 -tuint16 0x0134
0x0000 0
---------------------------------------------------------------
Worse, the system log also gets it's share of error messages:
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
[ 2797.475308] EtherCAT: Requesting master 0...
[ 2797.475313] EtherCAT: Successfully requested master 0.
[ 2797.475385] EtherCAT 0: Domain0: Logical address 0x00000000, 4 byte,
expected working counter 3.
[ 2797.475389] EtherCAT 0: Datagram domain0-0: Logical offset
0x00000000, 4 byte, type LRW.
[ 2797.475410] EtherCAT 0: Master thread exited.
[ 2797.475413] EtherCAT 0: Starting EtherCAT-OP thread.
[ 2797.475465] EtherCAT WARNING 0: 1 datagram UNMATCHED!
[ 2797.776049] EtherCAT 0: Releasing master...
[ 2797.776057] EtherCAT 0: Master thread exited.
[ 2797.776064] EtherCAT 0: Starting EtherCAT-IDLE thread.
[ 2797.776090] EtherCAT 0: Released.
[ 2800.524012] EtherCAT ERROR 0-0: Failed to set state INIT: No response.
---------------------------------------------------------------
I've attached the adapted main.c code; most of the original example code
is still in there, usually commented out.
Could anyone point me to a solution for the following questions:
- why does the slave report to be in error state?
- why doesn't the blink-data get written to the correct registers?
- why does the slave fail to go into PREOP/OP state?
Any help is greatly appreciated!
Kind regards,
Maarten Burghout
University of Twente
Department of Control Engineering
/*****************************************************************************
*
* $Id$
*
* Copyright (C) 2007-2009 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.
*
****************************************************************************/
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
/****************************************************************************/
#include "ecrt.h"
/****************************************************************************/
// Application parameters
#define FREQUENCY 100
#define PRIORITY 1
// Optional features
#define CONFIGURE_PDOS 1
#define SDO_ACCESS 0
/****************************************************************************/
// 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 ec_slave_config_t *sc_spi = NULL;
static ec_slave_config_state_t sc_spi_state = {};
// Timer
static unsigned int sig_alarms = 0;
static unsigned int user_alarms = 0;
/****************************************************************************/
// process data
static uint8_t *domain1_pd = NULL;
/*
#define BusCouplerPos 0, 0
#define DigOutSlavePos 0, 2
#define AnaInSlavePos 0, 3
#define AnaOutSlavePos 0, 4*/
#define SPISlavePos 0, 0
/*
#define Beckhoff_EK1100 0x00000002, 0x044c2c52
#define Beckhoff_EL2004 0x00000002, 0x07d43052
#define Beckhoff_EL2032 0x00000002, 0x07f03052
#define Beckhoff_EL3152 0x00000002, 0x0c503052
#define Beckhoff_EL3102 0x00000002, 0x0c1e3052
#define Beckhoff_EL4102 0x00000002, 0x10063052*/
#define Beckhoff_FB1111 0x00000002, 0x04570862
// offsets for PDO entries
/*
static unsigned int off_ana_in_status;
static unsigned int off_ana_in_value;
static unsigned int off_ana_out;
static unsigned int off_dig_out;*/
static unsigned int off_pwm_value;
static unsigned int off_enc_value;
const static ec_pdo_entry_reg_t domain1_regs[] = {
{SPISlavePos, Beckhoff_FB1111, 0x3101, 1, &off_pwm_value},
{SPISlavePos, Beckhoff_FB1111, 0x3101, 2, &off_enc_value},
{}
};
static unsigned int counter = 0;
static unsigned int blink = 0;
/*****************************************************************************/
#if CONFIGURE_PDOS
//PDO config for PWM and ENC
static ec_pdo_entry_info_t slave_0_pdo_entries[] = {
{0x3101, 1, 16}, // pwm value
{0x3101, 2, 16} // enc value
};
static ec_pdo_info_t slave_0_pdos[] = {
{0x1a00, 1, slave_0_pdo_entries},
{0x1600, 1, slave_0_pdo_entries + 1}
};
static ec_sync_info_t slave_0_syncs[] = {
{0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
{1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
{2, EC_DIR_OUTPUT, 1, slave_0_pdos + 0, EC_WD_DISABLE},
{3, EC_DIR_INPUT, 1, slave_0_pdos + 1, EC_WD_DISABLE},
{0xff}
};
/*
// Analog out -------------------------
static ec_pdo_entry_info_t el4102_pdo_entries[] = {
{0x3001, 1, 16}, // channel 1 value
{0x3002, 1, 16}, // channel 2 value
};
static ec_pdo_info_t el4102_pdos[] = {
{0x1600, 1, el4102_pdo_entries},
{0x1601, 1, el4102_pdo_entries + 1}
};
static ec_sync_info_t el4102_syncs[] = {
{2, EC_DIR_OUTPUT, 2, el4102_pdos},
{3, EC_DIR_INPUT},
{0xff}
};
// Digital out ------------------------
static ec_pdo_entry_info_t el2004_channels[] = {
{0x3001, 1, 1}, // Value 1
{0x3001, 2, 1}, // Value 2
{0x3001, 3, 1}, // Value 3
{0x3001, 4, 1} // Value 4
};
static ec_pdo_info_t el2004_pdos[] = {
{0x1600, 1, &el2004_channels[0]},
{0x1601, 1, &el2004_channels[1]},
{0x1602, 1, &el2004_channels[2]},
{0x1603, 1, &el2004_channels[3]}
};
static ec_sync_info_t el2004_syncs[] = {
{0, EC_DIR_OUTPUT, 4, el2004_pdos},
{1, EC_DIR_INPUT},
{0xff}
};
*/
#endif
/*****************************************************************************/
#if SDO_ACCESS
static ec_sdo_request_t *sdo;
#endif
/*****************************************************************************/
void check_domain1_state(void)
{
ec_domain_state_t ds;
ecrt_domain_state(domain1, &ds);
if (ds.working_counter != domain1_state.working_counter)
printf("Domain1: WC %u.\n", ds.working_counter);
if (ds.wc_state != domain1_state.wc_state)
printf("Domain1: State %u.\n", ds.wc_state);
domain1_state = ds;
}
/*****************************************************************************/
void check_master_state(void)
{
ec_master_state_t ms;
ecrt_master_state(master, &ms);
if (ms.slaves_responding != master_state.slaves_responding)
printf("%u slave(s).\n", ms.slaves_responding);
if (ms.al_states != master_state.al_states)
printf("AL states: 0x%02X.\n", ms.al_states);
if (ms.link_up != master_state.link_up)
printf("Link is %s.\n", ms.link_up ? "up" : "down");
master_state = ms;
}
/*****************************************************************************/
void check_slave_config_states(void)
{
ec_slave_config_state_t s;
ecrt_slave_config_state(sc_spi, &s);
if (s.al_state != sc_spi_state.al_state)
printf("SPI: State 0x%02X.\n", s.al_state);
if (s.online != sc_spi_state.online)
printf("SPI: %s.\n", s.online ? "online" : "offline");
if (s.operational != sc_spi_state.operational)
printf("SPI: %soperational.\n",
s.operational ? "" : "Not ");
sc_spi_state = s;
}
/*****************************************************************************/
#if SDO_ACCESS
void read_sdo(void)
{
switch (ecrt_sdo_request_state(sdo)) {
case EC_REQUEST_UNUSED: // request was not used yet
ecrt_sdo_request_read(sdo); // trigger first read
break;
case EC_REQUEST_BUSY:
fprintf(stderr, "Still busy...\n");
break;
case EC_REQUEST_SUCCESS:
fprintf(stderr, "SDO value: 0x%04X\n",
EC_READ_U16(ecrt_sdo_request_data(sdo)));
ecrt_sdo_request_read(sdo); // trigger next read
break;
case EC_REQUEST_ERROR:
fprintf(stderr, "Failed to read SDO!\n");
ecrt_sdo_request_read(sdo); // retry reading
break;
}
}
#endif
/****************************************************************************/
void cyclic_task()
{
//int i;
// receive process data
ecrt_master_receive(master);
ecrt_domain_process(domain1);
// check process data state (optional)
check_domain1_state();
if (counter) {
counter--;
} else { // do this at 1 Hz
counter = FREQUENCY;
// calculate new process data
blink = !blink;
printf("Blink is %u\n",blink);
// check for master state (optional)
check_master_state();
// check for islave configuration state(s) (optional)
check_slave_config_states();
#if SDO_ACCESS
// read process data SDO
read_sdo();
#endif
}
#if 1
// read process data
printf("SPI read: PWM %#x ENC %#x\n",
EC_READ_U16(domain1_pd + off_pwm_value),
EC_READ_U16(domain1_pd + off_enc_value));
#endif
#if 1
// write process data
printf("SPI write: PWM to address %#x\n",*(domain1_pd + off_pwm_value));
EC_WRITE_U16(domain1_pd + off_pwm_value, blink ? 0x0606 : 0x0909);
#endif
// send process data
ecrt_domain_queue(domain1);
ecrt_master_send(master);
}
/****************************************************************************/
void signal_handler(int signum) {
switch (signum) {
case SIGALRM:
sig_alarms++;
break;
}
}
/****************************************************************************/
int main(int argc, char **argv)
{
ec_slave_config_t *sc;
struct sigaction sa;
struct itimerval tv;
master = ecrt_request_master(0);
if (!master)
return -1;
domain1 = ecrt_master_create_domain(master);
if (!domain1)
return -1;
if (!(sc_spi = ecrt_master_slave_config(
master, SPISlavePos, Beckhoff_FB1111))) {
fprintf(stderr, "Failed to get slave configuration.\n");
return -1;
}
#if SDO_ACCESS
fprintf(stderr, "Creating SDO requests...\n");
if (!(sdo = ecrt_slave_config_create_sdo_request(sc_ana_in, 0x3102, 2, 2)))
{
fprintf(stderr, "Failed to create SDO request.\n");
return -1;
}
ecrt_sdo_request_timeout(sdo, 500); // ms
#endif
#if CONFIGURE_PDOS
printf("Configuring PDOs...\n");
if (ecrt_slave_config_pdos(sc_spi, EC_END, slave_0_syncs)) {
fprintf(stderr, "Failed to configure PDOs.\n");
return -1;
}
/*
if (!(sc = ecrt_master_slave_config(
master, AnaOutSlavePos, Beckhoff_EL4102))) {
fprintf(stderr, "Failed to get slave configuration.\n");
return -1;
}
if (ecrt_slave_config_pdos(sc, EC_END, el4102_syncs)) {
fprintf(stderr, "Failed to configure PDOs.\n");
return -1;
}
if (!(sc = ecrt_master_slave_config(
master, DigOutSlavePos, Beckhoff_EL2032))) {
fprintf(stderr, "Failed to get slave configuration.\n");
return -1;
}
if (ecrt_slave_config_pdos(sc, EC_END, el2004_syncs)) {
fprintf(stderr, "Failed to configure PDOs.\n");
return -1;
}
*/
#endif
// Create configuration for bus coupler
/*
sc = ecrt_master_slave_config(master, BusCouplerPos, Beckhoff_EK1100);
if (!sc)
return -1;
*/
if (ecrt_domain_reg_pdo_entry_list(domain1, domain1_regs)) {
fprintf(stderr, "PDO entry registration failed!\n");
return -1;
}
printf("Activating master...\n");
if (ecrt_master_activate(master))
return -1;
if (!(domain1_pd = ecrt_domain_data(domain1))) {
return -1;
}
#if PRIORITY
pid_t pid = getpid();
if (setpriority(PRIO_PROCESS, pid, -19))
fprintf(stderr, "Warning: Failed to set priority: %s\n",
strerror(errno));
#endif
sa.sa_handler = signal_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGALRM, &sa, 0)) {
fprintf(stderr, "Failed to install signal handler!\n");
return -1;
}
printf("Starting timer...\n");
tv.it_interval.tv_sec = 0;
tv.it_interval.tv_usec = 1000000 / FREQUENCY;
tv.it_value.tv_sec = 0;
tv.it_value.tv_usec = 1000;
if (setitimer(ITIMER_REAL, &tv, NULL)) {
fprintf(stderr, "Failed to start timer: %s\n", strerror(errno));
return 1;
}
printf("Started.\n");
while (1) {
pause();
#if 0
struct timeval t;
gettimeofday(&t, NULL);
printf("%u.%06u\n", t.tv_sec, t.tv_usec);
#endif
while (sig_alarms != user_alarms) {
cyclic_task();
printf("Cyclic task invoked\n");
user_alarms++;
}
}
return 0;
}
/****************************************************************************/
_______________________________________________
etherlab-users mailing list
etherlab-users@etherlab.org
http://lists.etherlab.org/mailman/listinfo/etherlab-users