eefocus_3896990 发表于 2025-5-30 08:26:55

EtherCAT主站实现:主站基于安路DR190M

EtherCAT主站实现:主站基于安路DR190M

# 1、EtherCAT主站

安路今年推出一款的FPSoC DR190M,组合了硬核处理器系统和FPGA,通过高带宽总线进行二者的互联。多核 ARM/RISC-V 处理器系统与安路FPGA可编程逻辑相结合于一颗芯片中,提供了应用类ARM/RISC-V处理器的性能 与生态系统,并且具备安路FPGA的灵活性、低功耗、可扩展 SoC平台。ARM/RISC-V CPU是处理器系统的核心,同时系统还包含on-chip RAM,内存接口和丰富的外设互联接口,定位复杂嵌入式系统、低功耗和高性能芯片市场。

DR190M芯片的PS端有两个以太网控制器,基于以太网的功能,可以实现EtherCAT主站控制功能。

!(https://www.eefocus.com/forum/data/attachment/forum/202505/30/082506fethq3pq0i8vpo0m.png)

## 1.1 编译SOEM


SOEM(Simple Opensource EtherCAT Master)协议栈是很便于使用的开源EtherCAT Master主站协议栈。下载(https://github.com/OpenEtherCATsociety/SOEM)源码。

!(https://www.eefocus.com/forum/data/attachment/forum/202505/30/082518jdb6vdedmk4a2ddn.png)

在DR190M开发环境中配置交叉编译需要使用的编译器。配置脚本的内容下,路径需要根据具体的开发环境进行修改

```bash
#!/bin/sh
export PATH=$PATH:<具体的工具链安放位置>/MYD-YM90X-Distribution-L6.1.54-V1.0.0//toolchains/aarch64-linux/bin
export CROSS_COMPILE=aarch64-linux-gnu
export ARCH=arm64
export PREFIX=aarch64-linux-gnu
export AS=aarch64-linux-gnu-as
export LD=aarch64-linux-gnu-ld
export CC=aarch64-linux-gnu-gcc
export CXX=aarch64-linux-gnu-g++
export AR=aarch64-linux-gnu-ar
export NM=aarch64-linux-gnu-nm
export STRIP=aarch64-linux-gnu-strip
export OBJCOPY=aarch64-linux-gnu-objcopy
export OBJDUMP=aarch64-linux-gnu-objdump
```

使用source指令导入上述脚本,使开发环境配置生效。

```bash
source xxx.sh
```

解压下载的SOEM源代码。
!(https://www.eefocus.com/forum/data/attachment/forum/202505/30/082538y41k4onuyyunromu.png)

修改其中的示例代码slaveinfo用于**的演示。

```c
#include <stdlib.h>
#include <sched.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include <math.h>

#include "osal.h"
#include "ethercattype.h"
#include "nicdrv.h"
#include "ethercatbase.h"
#include "ethercatmain.h"
#include "ethercatdc.h"
#include "ethercatcoe.h"
#include "ethercatfoe.h"
#include "ethercatconfig.h"
#include "ethercatprint.h"
#include "ethercat.h"


static char IOmap;

volatile uint8_t mode = 0;
static uint8_t slave = 1;

static volatile boolean inOP;
volatile int wkc;

int expectedWKC;
uint8 currentgroup = 0;
uint16 output_fsm=0;


void show_slave_info()
{
    for (int cnt = 1; cnt <= ec_slavecount; cnt++)
    {
      printf("\nSlave:%d\n Name:%s\n Output size: %dbits\n Input size: %dbits\n State: %d\n Delay: %d\n Has DC: %d\n",cnt, ec_slave.name, ec_slave.Obits,ec_slave.Ibits, ec_slave.state,ec_slave.pdelay, ec_slave.hasdc);
      printf(" Activeports:%d.%d.%d.%d\n", (ec_slave.activeports & 0x01) > 0 ,(ec_slave.activeports & 0x02) > 0 ,(ec_slave.activeports & 0x04) > 0 ,(ec_slave.activeports & 0x08)>0 );
      printf(" Configured address: %x\n", ec_slave.configadr);
      printf(" Outputs address: %p\n", ec_slave.outputs);
      printf(" Inputs address: %p\n", ec_slave.inputs);

      for (int j = 0; j < ec_slave.FMMUunused; j++)
      {
            printf(" FMMU%1d Ls:%x Ll:%4d Lsb:%d Leb:%d Ps:%x Psb:%d Ty:%x Act:%x\n", j, \
                  (int) ec_slave.FMMU.LogStart, ec_slave.FMMU.LogLength, \
                  ec_slave.FMMU.LogStartbit, ec_slave.FMMU.LogEndbit, \
                  ec_slave.FMMU.PhysStart, ec_slave.FMMU.PhysStartBit, \
                  ec_slave.FMMU.FMMUtype, ec_slave.FMMU.FMMUactive);
      }
      printf(" FMMUfunc 0:%d 1:%d 2:%d 3:%d\n", ec_slave.FMMU0func, ec_slave.FMMU1func, \
                ec_slave.FMMU2func, ec_slave.FMMU3func);
    }
}


int main(void)
{
    const char *ifname = "eth0";
    ecx_context.manualstatechange = 1;
   
    printf("Test LED \r\n");

    if (ecx_init(&ecx_context, ifname))
    {
            printf("EtherCAT Init NIC\r\n");
      wkc = ecx_config_init(&ecx_context, FALSE);
      
      if (wkc > 0)
      {
            printf("wkc is %d\r\n",wkc);
            ecx_context.slavelist.state = EC_STATE_PRE_OP;
            ecx_writestate(&ecx_context, slave);
            ecx_statecheck(&ecx_context, slave, EC_STATE_PRE_OP, 5000);
            if (ecx_context.slavelist.state != EC_STATE_PRE_OP)
            {
                ecx_readstate(&ecx_context);
                for(int i = 1; i <= *(ecx_context.slavecount) ; i++)
                {
                  if(ecx_context.slavelist.state != EC_STATE_PRE_OP)
                  {
                  }
                }
            }

            ecx_configdc(&ecx_context);
            ecx_config_map_group(&ecx_context, &IOmap, currentgroup);

            expectedWKC = (ec_group.outputsWKC * 2) + ec_group.inputsWKC;

            ecx_context.slavelist.state = EC_STATE_SAFE_OP;
            ecx_writestate(&ecx_context, slave);
            ecx_statecheck(&ecx_context, slave, EC_STATE_SAFE_OP, 5000);
            if (ecx_context.slavelist.state != EC_STATE_SAFE_OP)
            {
                ecx_readstate(&ecx_context);
                for(int i = 1; i <= *(ecx_context.slavecount) ; i++)
                {
                  if(ecx_context.slavelist.state != EC_STATE_SAFE_OP)
                  {

                  }
                }
            }
            ecx_readstate(&ecx_context);
            show_slave_info();

            inOP = FALSE;

            expectedWKC = (ec_group.outputsWKC * 2) + ec_group.inputsWKC;

            ecx_context.slavelist.state = EC_STATE_OPERATIONAL;
            ecx_send_processdata(&ecx_context);
            wkc = ecx_receive_processdata(&ecx_context, EC_TIMEOUTRET);
            ecx_writestate(&ecx_context, slave);

            if (ecx_context.slavelist.state == EC_STATE_OPERATIONAL)
            {
                inOP = TRUE;

                ec_send_processdata();
                wkc = ec_receive_processdata(EC_TIMEOUTRET);

                while (inOP)
                {
                  switch(output_fsm){
                  case 0:
                        output_fsm=1;
                        *(ecx_context.slavelist.outputs)=1;
                        break;
                  case 1:
                        output_fsm=2;
                        *(ecx_context.slavelist.outputs)=2;
                        break;
                  case 2:
                        output_fsm=3;
                        *(ecx_context.slavelist.outputs)=4;
                        break;
                  case 3:
                        output_fsm=4;
                        *(ecx_context.slavelist.outputs)=8;
                        break;
                  case 4:
                        output_fsm=5;
                        *(ecx_context.slavelist.outputs)=16;
                        break;
                  case 5:
                        output_fsm=6;
                        *(ecx_context.slavelist.outputs)=32;
                        break;
                  case 6:
                        output_fsm=7;
                        *(ecx_context.slavelist.outputs)=64;
                        break;
                  case 7:
                        output_fsm=0;
                        *(ecx_context.slavelist.outputs)=128;
                        break;

                  }

                  ec_send_processdata();
                  wkc = ec_receive_processdata(EC_TIMEOUTRET);

                  
                  osal_usleep(1000000);
                }
               
            }
            else
            {
                ecx_close(&ecx_context);
            }
      }
      else
      {
            printf("wkc is %d\r\n",wkc);
      }
    }
    else
    {
            printf("ecx_config_init failed\r\n");
    }
   
    printf("End Program\r\n");
    return 0;
}
```

接下来对SOEM源码进行编译。依次执行下列指令,完成SOEM代码的编译。

```bash
cd SOEM
mkdir -p build
cd build
cmake ..
make
```

!(https://www.eefocus.com/forum/data/attachment/forum/202505/30/082650stqppjtvmvstqsup.png)


复制编译出的`SOEM/build/test/linux/slaveinfo`可执行文件到DR190M开发板。在开发板的eth0口连接一个EtherCAT从站。执行slaveinfo,可以看到EtherCAT从站上的LED循环变化。

!(https://www.eefocus.com/forum/data/attachment/forum/202505/30/082619d4a00mfbbhx0i0qp.gif)


页: [1]
查看完整版本: EtherCAT主站实现:主站基于安路DR190M