OpenEtherCATsociety / SOES

Simple Open Source EtherCAT Slave
Other
608 stars 259 forks source link

The slave device always sends back a data packet 1ms after the master device sends a data packet #185

Open XBigRiceH opened 3 months ago

XBigRiceH commented 3 months ago

Hi there,

I'm currently using SOEM and SOES.

SOEM is running on my Windows laptop and SOES is running on stm32f407. When I test the round trip latency, I always need around 3ms to receive an updated value.

So I used Wireshark to find out what the problem was, and then I found that each packet from the slave had a 1ms delay between the last packet from the master.

I have no idea about what should I do next........What could be the reason for this?

The screenshot of Wireshark and my esi files are as follows:

image

image

#ifndef __UTYPES_H__
#define __UTYPES_H__

#include "cc.h"

/* Object dictionary storage */

typedef struct
{
   /* Identity */

   uint32_t serial;

   /* Inputs */

   uint64_t New_Array6000[1];

   /* Outputs */

   uint64_t New_Array7000[1];

} _Objects;

extern _Objects Obj;

#endif /* __UTYPES_H__ */
#ifndef __ECAT_OPTIONS_H__
#define __ECAT_OPTIONS_H__

#define USE_FOE          0
#define USE_EOE          0

#define MBXSIZE          512
#define MBXSIZEBOOT      512
#define MBXBUFFERS       3

#define MBX0_sma         0x1000
#define MBX0_sml         MBXSIZE
#define MBX0_sme         MBX0_sma+MBX0_sml-1
#define MBX0_smc         0x26
#define MBX1_sma         0x1200
#define MBX1_sml         MBXSIZE
#define MBX1_sme         MBX1_sma+MBX1_sml-1
#define MBX1_smc         0x22

#define MBX0_sma_b       0x1000
#define MBX0_sml_b       MBXSIZEBOOT
#define MBX0_sme_b       MBX0_sma_b+MBX0_sml_b-1
#define MBX0_smc_b       0x26
#define MBX1_sma_b       0x1200
#define MBX1_sml_b       MBXSIZEBOOT
#define MBX1_sme_b       MBX1_sma_b+MBX1_sml_b-1
#define MBX1_smc_b       0x22

#define SM2_sma          0x1400
#define SM2_smc          0x24
#define SM2_act          1
#define SM3_sma          0x1A00
#define SM3_smc          0x20
#define SM3_act          1

#define MAX_MAPPINGS_SM2 1
#define MAX_MAPPINGS_SM3 1

#define MAX_RXPDO_SIZE   512
#define MAX_TXPDO_SIZE   1877

#endif /* __ECAT_OPTIONS_H__ */
#include "esc_coe.h"
#include "utypes.h"
#include <stddef.h>

#ifndef HW_REV
#define HW_REV "1.0"
#endif

#ifndef SW_REV
#define SW_REV "1.0"
#endif

static const char acName1000[] = "Device Type";
static const char acName1008[] = "Manufacturer Device Name";
static const char acName1009[] = "Manufacturer Hardware Version";
static const char acName100A[] = "Manufacturer Software Version";
static const char acName1018[] = "Identity Object";
static const char acName1018_00[] = "Max SubIndex";
static const char acName1018_01[] = "Vendor ID";
static const char acName1018_02[] = "Product Code";
static const char acName1018_03[] = "Revision Number";
static const char acName1018_04[] = "Serial Number";
static const char acName1600[] = "New Array";
static const char acName1600_00[] = "Max SubIndex";
static const char acName1600_01[] = "New Array Element 1";
static const char acName1A00[] = "New Array";
static const char acName1A00_00[] = "Max SubIndex";
static const char acName1A00_01[] = "New Array Element 1";
static const char acName1C00[] = "Sync Manager Communication Type";
static const char acName1C00_00[] = "Max SubIndex";
static const char acName1C00_01[] = "Communications Type SM0";
static const char acName1C00_02[] = "Communications Type SM1";
static const char acName1C00_03[] = "Communications Type SM2";
static const char acName1C00_04[] = "Communications Type SM3";
static const char acName1C12[] = "Sync Manager 2 PDO Assignment";
static const char acName1C12_00[] = "Max SubIndex";
static const char acName1C12_01[] = "PDO Mapping";
static const char acName1C13[] = "Sync Manager 3 PDO Assignment";
static const char acName1C13_00[] = "Max SubIndex";
static const char acName1C13_01[] = "PDO Mapping";
static const char acName6000[] = "New Array";
static const char acName6000_00[] = "Max SubIndex";
static const char acName6000_01[] = "New Array Element 1";
static const char acName7000[] = "New Array";
static const char acName7000_00[] = "Max SubIndex";
static const char acName7000_01[] = "New Array Element 1";

const _objd SDO1000[] =
{
  {0x0, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1000, 0x00001389, NULL},
};
const _objd SDO1008[] =
{
  {0x0, DTYPE_VISIBLE_STRING, 16, ATYPE_RO, acName1008, 0, "rm"},
};
const _objd SDO1009[] =
{
  {0x0, DTYPE_VISIBLE_STRING, 24, ATYPE_RO, acName1009, 0, HW_REV},
};
const _objd SDO100A[] =
{
  {0x0, DTYPE_VISIBLE_STRING, 24, ATYPE_RO, acName100A, 0, SW_REV},
};
const _objd SDO1018[] =
{
  {0x00, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName1018_00, 4, NULL},
  {0x01, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1018_01, 0, NULL},
  {0x02, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1018_02, 0x0001, NULL},
  {0x03, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1018_03, 0, NULL},
  {0x04, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1018_04, 0x00000000, &Obj.serial},
};
const _objd SDO1600[] =
{
  {0x00, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName1600_00, 1, NULL},
  {0x01, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1600_01, 0x70000140, NULL},
};
const _objd SDO1A00[] =
{
  {0x00, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName1A00_00, 1, NULL},
  {0x01, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1A00_01, 0x60000140, NULL},
};
const _objd SDO1C00[] =
{
  {0x00, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName1C00_00, 4, NULL},
  {0x01, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName1C00_01, 1, NULL},
  {0x02, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName1C00_02, 2, NULL},
  {0x03, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName1C00_03, 3, NULL},
  {0x04, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName1C00_04, 4, NULL},
};
const _objd SDO1C12[] =
{
  {0x00, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName1C12_00, 1, NULL},
  {0x01, DTYPE_UNSIGNED16, 16, ATYPE_RO, acName1C12_01, 0x1600, NULL},
};
const _objd SDO1C13[] =
{
  {0x00, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName1C13_00, 1, NULL},
  {0x01, DTYPE_UNSIGNED16, 16, ATYPE_RO, acName1C13_01, 0x1A00, NULL},
};
const _objd SDO6000[] =
{
  {0x00, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName6000_00, 1, NULL},
  {0x01, DTYPE_UNSIGNED64, 64, ATYPE_RO | ATYPE_TXPDO, acName6000_01, 0, &Obj.New_Array6000[0]},
};
const _objd SDO7000[] =
{
  {0x00, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName7000_00, 1, NULL},
  {0x01, DTYPE_UNSIGNED64, 64, ATYPE_RO | ATYPE_RXPDO, acName7000_01, 0, &Obj.New_Array7000[0]},
};

const _objectlist SDOobjects[] =
{
  {0x1000, OTYPE_VAR, 0, 0, acName1000, SDO1000},
  {0x1008, OTYPE_VAR, 0, 0, acName1008, SDO1008},
  {0x1009, OTYPE_VAR, 0, 0, acName1009, SDO1009},
  {0x100A, OTYPE_VAR, 0, 0, acName100A, SDO100A},
  {0x1018, OTYPE_RECORD, 4, 0, acName1018, SDO1018},
  {0x1600, OTYPE_RECORD, 1, 0, acName1600, SDO1600},
  {0x1A00, OTYPE_RECORD, 1, 0, acName1A00, SDO1A00},
  {0x1C00, OTYPE_ARRAY, 4, 0, acName1C00, SDO1C00},
  {0x1C12, OTYPE_ARRAY, 1, 0, acName1C12, SDO1C12},
  {0x1C13, OTYPE_ARRAY, 1, 0, acName1C13, SDO1C13},
  {0x6000, OTYPE_ARRAY, 1, 0, acName6000, SDO6000},
  {0x7000, OTYPE_ARRAY, 1, 0, acName7000, SDO7000},
  {0xffff, 0xff, 0xff, 0xff, NULL, NULL}
};
XBigRiceH commented 3 months ago

Note: currently using the free-run mode, lan9252 chip, normal spi@21Mbits/s

I then tried the same thing in the Ubuntu system, the result is much better but still not good. The delay between packets is reduced to ~0.04ms but I still need ~0.7ms to get an updated value in round-trip testing. After some measurement, I got each loop of ecat_slv() used ~0.4ms

nakarlsson commented 3 months ago

When running in free-run mode the SOEM & SOES update loops are decoupled.

The picture below highlight that you would need to send frames at a faster rate than required. Also, the local application would need to run faster to be able to read data, run application, write data,

image

XBigRiceH commented 3 months ago

When running in free-run mode the SOEM & SOES update loops are decoupled.

The picture below highlight that you would need to send frames at a faster rate than required. Also, the local application would need to run faster to be able to read data, run application, write data,

image

Running decoupled is OK for me because I don't need any sync feature I just want to transport data as fast as they can. Does anyone ever measure the communication time of one read&write cycle using SPI? Because I think the slave will always read the latest data, so maybe the delay I find is totally caused by the slow communication speed?

nakarlsson commented 3 months ago

The Microchip reference driver for lan9252 is quit slow, several places where you wait for completion.

Since you have a system up and running you can try to profile it? Either measure the time using a timer clock or use a physical GPIO and oscilloscope?

XBigRiceH commented 3 months ago

Today I tried to use ax58100, because its HW interface seems much faster than LAN9252, and the actual test results support this. Even if my PDO length increases to 96 bytes, the roundtrip time is still reduced to ~0.3ms. However, I then used GPIO and logic signal analyzer to measure the time cost of SOES. The result shows the ecat_slv() loop only costs ~71us, of which TXPDO write takes ~23us and RXPDO read takes ~21us, which is much less than the roundtrip time(~0.3ms = ~300us)...?

Note: Now using AX58100 by SPI@50M, SOEM running on Ubuntu22 with realtime-kernel. Wireshark shows the ECAT packet is sent every ~50us Note 2: Since I'm new to EtherCAT and spi protocols, I wanted to ask if someone may have measured the latency before for my reference because I'm unsure if I've messed something up.

My new PDO info:

#ifndef __UTYPES_H__
#define __UTYPES_H__

#include "cc.h"

/* Object dictionary storage */

typedef struct
{
   /* Identity */

   uint32_t serial;

   /* Inputs */

   uint64_t New_Array6000[8];

   /* Outputs */

   uint64_t New_Array7000[4];

   /* Parameters */

   uint8_t New_Array2000[128];

} _Objects;

extern _Objects Obj;

#endif /* __UTYPES_H__ */

My latency test result: Green: one ecat_slv() loop Blue: one RXPDO_update() loop Purple: one TXPDO_update() loop image

Test method: Write one GPIO to High at the start and write it to LOW at the end image image image