开源EtherCAT主站SOEM入门使用

开源EtherCAT主站SOEM入门使用

文章目录

目的

去年工作上用到了EtherCAT,EtherCAT同学是分主从的,作为学习测试来说主站最常用的是TwinCAT。不过TwinCAT支持的网卡信号有限,又没法应用到各种平台上。所以实际使用时又找了一些主站方案,适应性最广的就是SOEM库(Simple Open EtherCAT Master Library)。

项目地址:https://github.com/OpenEtherCATsociety/SOEM
文档地址:https://docs.rt-labs.com/soem/

SOEM库目前主要分为1.4.0版本和2.0.0版本,前者仓库中自带文档,后者文档需要去RT-Labs官网看(需要注册账号),本文将使用2.0.0版本:

在这里插入图片描述

本文测试时EtherCAT从站使用了LAN9252和AX58100,找那种支持DIO独立工作的板子:

在这里插入图片描述


这两个从站芯片都支持不依赖外部的控制器独立工作在DIO模式下。LAN9252支持总共16通道DIO,AX58100支持总共32路DIO。

上手体验

Releases中包含了已经编译好的示例和静态库,示例可以直接拿来体验:

在这里插入图片描述


需要注意的是Windows上需要安装Npcap才可以(如果有问题可以尝试安装WinPcap):
https://npcap.com/
https://www.winpcap.org/

对于EtherCAT主站来说其实就是收发特定格式的以太网报文而已,但是Windows上默认是不支持收发以太网原始帧的,需要借助诸如Npcap等库。

slaveinfo 示例可以读取EEPROM的数据。直接运行会提示用法:

在这里插入图片描述


指定网卡运行:

在这里插入图片描述

simple_ng 示例是个最简单的读写信号示例:

在这里插入图片描述

基础使用

SOEM库提供的是基础的EtherCAT数据帧的收发,应用层的功能和网络管理都是需要用户写代码手动处理的:

SOEM is a library that lets your application send and receive EtherCAT frames. It does not contain any logic for maintaining the EtherCAT network in an operational state; however, it has all the building blocks for doing so. In SOEM, it is the user application’s responsibility to handle these tasks:Reading and writing process data.Keeping local I/O data synchronized with the global I/O map.Detecting errors reported by SOEM.Managing errors reported by SOEM.

SOEM库使用有多种方式,这里在项目中直接使用SOEM源码。项目结构图下:

在这里插入图片描述

CMakeLists.txt 文件内容如下:

cmake_minimum_required(VERSION 3.28) project(soem_demo VERSION 1.0.1) add_subdirectory(SOEM-2.0.0) add_executable(soem_demo main.cpp) target_link_libraries(soem_demo soem)

main.cpp 文件内容如下:

#include<iostream>#include"SOEM-2.0.0/include/soem/soem.h"usingnamespace std;#defineMAX_ADAPTERS_QTY(32)#defineMAX_ADAPTER_NAME(128)intbasic_examples(char*ifname){printf("SOEM baisc examples start: \n"); ecx_contextt ctx; uint8 IOmap[4096]={0};int expectedWKC;/************************* Init BUS *************************/if(!ecx_init(&ctx, ifname)){printf("Init adapter failed.\n");return-1;}if(ecx_config_init(&ctx)<=0){printf("Enum and init slaves failed.\n");return-1;}else{for(size_t i =0; i < ctx.slavecount; i++){printf("Slave %03d: ", i);printf("Name %s, ", ctx.slavelist[i].name);printf("Output %d bytes %d bits", ctx.slavelist[i].Obytes, ctx.slavelist[i].Obits);printf("Input %d bytes %d bits", ctx.slavelist[i].Ibytes, ctx.slavelist[i].Ibits);printf("Delay %d ns, Has DC %d\n", ctx.slavelist[i].pdelay, ctx.slavelist[i].hasdc);}}ecx_config_map_group(&ctx, IOmap,0); expectedWKC = ctx.grouplist[0].outputsWKC *2+ ctx.grouplist[0].inputsWKC;ecx_configdc(&ctx);ecx_statecheck(&ctx,0, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE *4);ecx_send_processdata(&ctx);ecx_receive_processdata(&ctx, EC_TIMEOUTRET);// using slave 0, which will broadcast the request to all slaves on the network ctx.slavelist[0].state = EC_STATE_OPERATIONAL;ecx_writestate(&ctx,0);for(size_t i =0; i <10; i++){ecx_send_processdata(&ctx);ecx_receive_processdata(&ctx, EC_TIMEOUTRET);ecx_statecheck(&ctx,0, EC_STATE_OPERATIONAL, EC_TIMEOUTSTATE /10);if(ctx.slavelist[0].state == EC_STATE_OPERATIONAL)break;}if(ctx.slavelist[0].state != EC_STATE_OPERATIONAL){printf("Set operational state failed.\n");return-1;}/************************* Main loop *************************/for(size_t turn =0; turn <10000; turn++){ ec_timet start, end, diff;osal_usleep(5000);/********* Change output data if need *********/// change by slavefor(size_t i =0; i < ctx.slavecount; i++){for(size_t j =0; j < ctx.slavelist[i].Obytes; j++){ ctx.slavelist[i].outputs[j]= turn %256;}}// change by group// for (size_t i = 0; i < ctx.grouplist[0].Obytes; i++)// {// ctx.grouplist[0].outputs[i] = turn % 256;// } start =osal_current_time();ecx_send_processdata(&ctx);int wkc =ecx_receive_processdata(&ctx, EC_TIMEOUTRET); end =osal_current_time();osal_time_diff(&start,&end,&diff);printf("Iteration %05d: ", turn +1);printf("%08d usec, WKC %d",(int)(diff.tv_sec *1000000+ diff.tv_nsec /1000), wkc);printf(", O:");for(size_t n =0; n < ctx.grouplist[0].Obytes;++n){printf(" %02X", ctx.grouplist[0].outputs[n]);}printf(", I:");for(size_t n =0; n < ctx.grouplist[0].Ibytes;++n){printf(" %02X", ctx.grouplist[0].inputs[n]);}printf(", T: %lld",(longlong)ctx.DCtime);if(((turn +1)%1000))printf("\r");elseprintf("\n");if(wkc != expectedWKC)break;// error: stop or reinit slave}/************************* Stop BUS *************************/ ctx.slavelist[0].state = EC_STATE_INIT;ecx_writestate(&ctx,0);ecx_close(&ctx);printf("SOEM baisc examples end.\n");return0;}char*find_and_select_adapters(void){char adapter_name[MAX_ADAPTERS_QTY][MAX_ADAPTER_NAME]={0};int index =0; ec_adaptert *adapter =NULL; ec_adaptert *head =NULL;printf("\nAvailable adapters:\n"); head = adapter =ec_find_adapters();while(adapter !=NULL){printf("%02d - %s (%s)\n", index, adapter->name, adapter->desc);strncpy(adapter_name[index], adapter->name, MAX_ADAPTER_NAME); index++; adapter = adapter->next;}ec_free_adapters(head);printf("Please input the network adapter number: ");scanf("%d",&index);staticchar selected_name[MAX_ADAPTER_NAME];strncpy(selected_name, adapter_name[index], MAX_ADAPTER_NAME);return selected_name;}intmain(){char*ifname =find_and_select_adapters();basic_examples(ifname);return0;}

使用情况如下:

在这里插入图片描述


测试过程中output数据是在代码中不断改变的,input数据是板子上接了开关的IO口,拨动开关时数据会显示变化。

资料和例程

本文所设计的一些EtherCAT_SOEM相关资料可以从下面下载:
https://pan.baidu.com/s/1lDRk10TK2AKJm_mdseNO9Q?pwd=39qh
https://download.ZEEKLOG.net/download/Naisu_kun/92573410

其中LAN9252和AX58100烧录的ESI(EtherCAT SubDevice Information)文件分别如下:

  • AX58100_EVB_DIO_ESI_00010200_00000001_20220323.xml
  • Microchip EVB-LAN9252-DIGIO-8IN-8OUT.xml

烧录EEPROM可以使用安装倍福的SSC(EtherCAT Slave Stack Code Tool)工具时自带的 EEPROM Programmer 工具进行烧录:

在这里插入图片描述


上面工具比TwinCAT好的一点是不挑网卡,USB网卡等的都能用。

总结

SOEM总体使用是比较简单的,对于EtherCAT主站来说除了基本的读写,可能更多的挑战会在于对于时间稳定性上的处理。

最近发现类似的CherryECAT库也不错:
https://github.com/cherry-embedded/CherryECAT
https://cherryecat.readthedocs.io/

Read more

python八股文汇总(持续更新版)

python装饰器 一、装饰器是什么? 装饰器是Python中一种"化妆师",它能在不修改原函数代码的前提下,给函数动态添加新功能。 * 本质:一个接收函数作为参数,并返回新函数的工具。 * 作用:像给手机贴膜,既保护屏幕(原函数),又新增防摔功能(装饰逻辑)。 二、核心原理 1. 函数是"对象":Python中函数可以像变量一样传递,这是装饰器的基础。 2. 闭包机制:装饰器通过嵌套函数(闭包)保留原函数,并包裹新功能。 工作流程: 1. 你调用被装饰的函数(如hello())。 2. Python实际执行的是装饰器加工后的新函数。 3. 新函数先执行装饰器添加的逻辑(如权限检查),再执行原函数。 三、常见用途 场景 作用 生活类比 权限验证 检查用户是否登录再执行函数

By Ne0inhk
【免费领源码】96007物流车辆预约平台 计算机毕业设计项目推荐上万套实战教程JAVA,node.js,C++、python、大屏数据可视化

【免费领源码】96007物流车辆预约平台 计算机毕业设计项目推荐上万套实战教程JAVA,node.js,C++、python、大屏数据可视化

目 录 物流车辆预约平台 摘  要 第1章 绪论 1.1背景及意义 1.2 研究现状 1.3  论文组成结构 第2章 相关技术 2.1 B/S体系工作原理 2.2 Django框架介绍 2.3 MySQL数据库 第3章 系统分析 3.1 系统可行性分析 3.1.1技术可行性 3.1.2经济可行性 3.1.3操作可行性 3.2 功能需求分析 3.3 非功能性分析 3.4系统流程分析 3.

By Ne0inhk

ezdxf库终极指南:Python CAD自动化从入门到精通

ezdxf库终极指南:Python CAD自动化从入门到精通 【免费下载链接】ezdxfPython interface to DXF 项目地址: https://gitcode.com/gh_mirrors/ez/ezdxf 想要用Python操控CAD图纸却不知从何入手?ezdxf库为你打开了通往CAD自动化世界的大门。这个纯Python实现的DXF文件处理工具,让你无需安装任何CAD软件就能轻松读写、编辑和生成图纸文件。无论你是机械工程师、建筑设计师,还是数据可视化开发者,掌握ezdxf都将让你的工作效率倍增。 快速入门:5分钟上手ezdxf 安装与环境配置 安装ezdxf库只需一行命令,简单到让人难以置信: pip install ezdxf 验证安装是否成功: import ezdxf print(f"ezdxf版本: {ezdxf.__version__}") 你的第一个DXF文件 让我们从一个简单的例子开始,感受ezdxf的强大之处: import ezdxf # 创建新图纸 -

By Ne0inhk

Python从0到100完整学习指南(必看导航)

Python 从 0 到 100 完整学习路线(2025–2026 实用版) 这是一条目前在中文社区被验证最多次、性价比最高、就业/副业/考研/转行都适用的 Python 学习路径。 分为 8 个大阶段,每个阶段给出: * 核心目标 * 推荐学习时长(每天 2–4 小时估算) * 最值得学的资源(2025–2026 仍活跃且评价最高的) * 必须掌握的技能清单 * 阶段性小目标 / 实战项目建议 阶段划分总览表 阶段名称目标人群建议时长累计总时长核心关键词0准备期完全零基础3–7 天1 周环境、IDE、学习心态1Python 基础语法零基础 → 能写小工具3–6 周1–2 个月变量、循环、函数、类2Pythonic

By Ne0inhk