上期回顾:EtherCAT协议介绍
01
EtherCAT介绍
1
EtherCAT状态机制
ESM (EtherCAT state machine)是用来在启动或者工作时协调主站和从站关系用的,由应用层控制器将从站应用的状态写入AL状态寄存器,主站通过写AL控制寄存器进行状态请求。因此从逻辑上说,ESM位于EtherCAT从站控制器与应用之间。
如下所示,它包含 EtherCAT状态转换的过程。
运行状态 | 描述 |
Initial | 初始化状态 |
Pre-Operational | 预运行状态 |
Safe-Operational | 安全运行状态 |
Operational | 运行状态 |
初始状态(Init)
此时应用层无通信,没有邮箱通信也没有过程数据通信
转换到预工作状态(Init to Pre-Op)
主站配置地址和SM,用于进行邮箱通信;同时主站请求向Pre-Op模式转换;接着从站检查邮箱是否初始化正确
安全工作状态(Safe-Op)
在这一阶段,从站应用程序将传输实际输入数据,此时主站并不对从站输出进行操作,并将输出模式设为安全状态
转换到工作状态(Safe Op to Op)
这一阶段主站开始发送有效的输出数据,同时请求向工作模式转换
工作状态(Op)
此时主站的输入和输出均有效,可以进行过程数据的通信
Bootstrap状态
此外还有个Bootstrap状态,该状态作为可选项,一般是在固件更新时使用,该状态只允许从初始状态切换到该状态,在此状态期间是没有过程数据通信的,一般是用于FOE协议文件传输或固件升级的
下面是具体的一些状态转换,此处不再说明
状态转换 | 描述 |
IP | 启动邮箱通讯 |
PI | 停止邮箱通讯 |
PS | 启动输入更新 |
SP | 停止输入更新 |
SO | 启动输出更新 |
OS | 停止输出更新 |
OP | 停止输入输出更新 |
SI | 停止输入更新,停止邮箱通讯 |
OI | 停止输入输出更新,停止邮箱通讯 |
IB | 启动bootstrap模式 |
BI | 重启节点 |
2
EtherCAT寻址
EtherCAT通信是指主设备从EtherCAT从设备的内部闪存读取和写入数据,通常来说有两种寻址方式来控制内部的ESC寄存器:
1)设备寻址
在设备寻址时,EtherCAT子报文头内的32位地址分为16位从站设备地址和16位从站设备内部物理存储空间地址。设备寻址时,每个报文只寻址唯一的一个从站设备,但对于寻址机制又分为两种:
顺序寻址:使用顺序寻址时,从站的地址由其在网段内的连接位置确定,用一个负数来表示每个从站在网段内由接线顺序决定的位置。顺序寻址子报文在经过每个从站设备时,其顺序地址加1,从站在接收报文时,顺序地址位0的报文就是寻址到自己的报文,因此这种寻址机制也被称为“自动增量寻址”;
例如:主站发起三个子报文分别去寻址三个从站,其中地址分别为0,-1,-2,如上图中的数据帧1,数据帧在到达从站1时,从站1检查到子报文1中的地址为0,从而得知子报文1就是寻址到自己的报文,数据帧经过从站1后,所有的顺序地址都增加1,称为1,0,-1,以此类推...
在设置寻址时,从站的地址与其在网段内的连接顺序无关,地址可以由主站在数据链路层启动阶段配置EtherCAT ID给从站;也可以由从站在上电初始化时从自身的EEPROM内部读取EtherCAT ID进而分配地址。
2)逻辑寻址
前面讲的设备寻址一般来说只针对某一从站进行读写操作,并且是在扫描阶段去使用的,相对与逻辑寻址来说并不常见。逻辑寻址面向过程数据,可以实现多播,同一个子报文可以读取多个从站设备。
在逻辑寻址时,从站地址并不是单独定义的,而是使用了寻址段内4G逻辑地址空间中的一段区域,报文内的32位地址作为整体的数据逻辑地址完成对设备的逻辑寻址。
而谈到逻辑寻址,需要了解FMMU(现场总线内存管理单元),逻辑寻址由FMMU实现,在ESC(从站控制器),主要负责逻辑地址与物理地址的映射,用于将过程数据映射到主站,从而减轻主站的存储器管理负担。
当从站设备收到一个数据逻辑寻址的EtherCAT子报文时,会检查是否由FMMU单元地址匹配,如果有的话,就会将输入类型数据插入至EtherCAT子报文数据区的对应位置。
3
EtherCAT从站同步模式
EtherCAT从站同步模式主要分为以下几种:
(1)自由运行模式(Free Run Mode)
在此模式下,从站设备独立于主站运行,内部没有同步到任何外部信号。设备的周期性操作由内部时钟或定时器控制,通常用于非实时要求的应用场景。
(2)分布式时钟模式(Distributed Clocks,DC)
分布式时钟是 EtherCAT 的关键同步机制之一,广泛用于需要高精度同步的场景。在 DC 模式下,所有 EtherCAT 从站通过一个参考时钟(通常由主站或某个从站提供)实现时间同步。每个从站都会调整其内部时钟,使所有设备在微秒级别内同步。此模式适用于需要精确协调多个设备动作的应用,例如运动控制。
(3)同步管理器事件模式(SyncManager Event Mode)
前面我们说了,FMMU主要用于地址映射,寻找对应的地址(寻址),那SyncManager就是用于交换数据,将数据同步进去。
在此模式下,从站设备通过 SyncManager 同步数据传输。每当主站写入或读取 SyncManager 寄存器时,从站的同步管理器事件会触发中断,从而引发应用程序的执行。这个模式下的同步精度不如 DC 模式高,通常用于不需要严格时间同步的应用场合。
SyncManager主要有两种模式:
1)邮箱(单缓存模式)
使用单个缓存区,通过握手的机制完成数据交换,一般情况下,一端在完成数据的读写后,另一端才能访问该缓存区,这样可以保证数据不被丢失。
一般来说,数据发送方首先将数据写入缓存区,此时缓存区被锁定为只读状态,一直等到数据接收方将数据读走。
该模式通常使用在非周期性的数据交换,分配的缓存区也叫邮箱。
邮箱模式通常使用两个SM通道,一般情况下主站到从站通信使用SM0,从站到主站通信使用SM1。
对于邮箱模式来说,分析其主从站的通信模式:
主站到从站的通信:主站必须检查从站邮箱命令中的应答工作计数器(Working counter),如果工作计数器没有增加(通常是因为从站没有完全读取上一条命令),或者在规定时间期限内没有响应,主站必须重发该邮箱命令。
从站到主站到通信:主站必须确定从站是否用邮箱命令填满了SM,并且尽快地发送适当的读命令
2)缓存
使用三个缓存区,允许EtherCAT主站的控制权和从站控制器在做任何时候都访问数据交换缓存区。
接收数据的那一方随时可以得到最新的数据,数据发送那一方也可以随时更新缓存区里的内容。
假如写缓存区的速度比读缓存区的速度快,则旧数据就会被覆盖。
02
软件开发环境说明
IAR 9.50.2
RZSC v2.0.0
Slave Stack Code (SSC) Tool Version 5.13
Beckhoff Automation TwinCAT3
03
安装Patch软件
下载 mingw-get-setup.exe (https://sourceforge.net/projects/mingw/files/Installer/)并根据提示安装“Mingw-installation-manager”,若安装完成,出现“Mingw-installation-manager”窗口,在左侧窗口中选择“Basic Setup”,在右侧窗口中右键点击“msys-base-bin”,选择“Mark for Installation”。
在左侧窗口中选择“All Packages”,在右侧窗口中右键单击“msys-patch-bin”,并选择“标记为安装”。
在上面的菜单栏中的安装中选择“Apply Changes”
显示“Schedule of Pending Actions”窗口,单击“应用”按钮。
如果显示“All changes were applied successfully; you may now clone this dialogue”,则表示安装 patch.exe 成功。
将安装的 patch.exe 的路径注册到系统环境变量中。注册后,重新启动电脑。
启动命令提示符,输入“where patch”。如果显示了安装的patch.exe的路径,则没有问题。
04
EtherCAT EOE 从站代码生成
从 renesas 官网下载 EtherCAT 示例包(https://www.renesas.cn/cn/zh/document/scd/rzt2m-group-ethercat-sample-program-package?r=1574901),解压后打开 common\ecat_EoE_lwIP\SSCconfig 目录下的 RZT2 EtherCAT EoE.esp 文件,点击上方导航栏的 Project-> Create_new_Slaver_Files,依次配置 Source Folder(生成文件位置需向下延伸一级目录 Src) 和 ESI File。
回到目录 common\ecat_EoE_lwIP\Patch ,执行脚本 apply_patch_ewarm_xSPI0.bat 将生成的从站代码移动到工程目录下。
05
EOE Ethernet配置
首先使用FSP打开工程下的 configuration.xml 文件,添加EtherCAT SSC Port Stack,并进行配置:
使能网卡类型、配置网卡设备参数,这里我们添加两个phy(phy0和phy1):
ETHER_ETH配置
ETHER_ESC配置
ETHER_GMAC配置
为ethercat_ssc_port添加cmt定时器并配置中断优先级
添加Ethernet
ethernet中断触发回调设置为:vEtherISRCallback
到这里,FSP的配置就告一段落了。
06
EOE工程简述
移植后的RT-Thread EtherCAT EOE工程目录层次如下所示:
其中EOE功能的主要部分位于 board/port/ethercat 目录下,对于各个文件夹的解释如下:
application:初始化并配置以太网接口及LWIP TCP/IP协议栈,同时实现一个lwip的TCP服务器;
beckhoff:EtherCAT协议栈从站代码,由SSC生成;
module:设计了一套实例管理器模式,根据ethernet netif及lwip port返回的事件进行阶段性处理。
ether_netif:分别创建三个ethernet线程,负责以太网的读(eth_reader)、写(eth_writer)及以太网事件监测回调处理(eth_monitor);
lwip_port:创建两个lwip port线程,一个负责接收以太网Link回调并将其广播给netif,另外一个负责接收以太网回调并将参数映射到实例管理器管理的所有接口中;为lwip注册网络接口的同时,处理Link事件及回调
renesas:主要包括EtherCAT数据帧的校验和处理,并根据AL状态码处理不同的运行状态,此外还包括对输入输出数据的映射生成和处理等等。
此外在main线程中,会静态初始化两个线程:
main_thread:EtherCAT SSC端口及PHY初始化,EtherCAT从站初始化,运行EtherCAT从站主循环,处理主站的通信请求、更新AL状态机等等。
ecat_thread:初始化并启动LWIP内核
07
以太网PHY初始化配置
这部分的初始化配置在ecat_thread中,进入RM_ETHERCAT_SSC_PORT_Open(), 这个EtherCAT 接口配置函数之后,可以看到EtherCAT Slave Controller 的一些初始化配置,其中就包括了PHY 的初始化:
这里初始化的一个PHY 实例是:p_ether_phy_instance,它是一个 ether_phy_instance_t 类型的变量,这个PHY 的实例是在调用RM_ETHERCAT_SSC_PORT_Open函数的时候形参传递进来的,也就是 gp_ethercat_ssc_port。这种设计方式在下面的ether_netif及lwip port中的定义也有体现。
typedef struct st_ether_phy_instance
{
ether_phy_ctrl_t * p_ctrl; /// 指向PHY实例的控制结构体
ether_phy_cfg_t const * p_cfg; /// 指向实例配置的结构体指针
ether_phy_api_t const * p_api; /// 配置过程中用到的API操作方式所组成的结构体指针
} ether_phy_instance_t;
也就是说其实此处其实打开的是 g_ethercat_ssc_port0:
可以看到 g_ethercat_ssc_port0_ctrl 指向的是 g_ethercat_ssc_port0 的控制结构体指针:
const ethercat_ssc_port_instance_t g_ethercat_ssc_port0 =
{
.p_ctrl = &g_ethercat_ssc_port0_ctrl, // 指向g_ethercat_ssc_port0的控制结构体指针
.p_cfg = &g_ethercat_ssc_port0_cfg, // 指向g_ethercat_ssc_port0的配置结构体指针
.p_api = &g_ethercat_ssc_port_on_ethercat_ssc_port // 指向g_ethercat_ssc_port0配置方法的结构指针
};
PHY 的驱动是ethercat_ssc_port0 外设驱动的子模块,同样我们来看下PHY的初始化:
通过调用 ether_phy_targets_initialize 根据配置的PHY型号执行对应的初始化配置:
08
LWIP协议栈初始化
1
tcpip_init()
lwip协议栈的初始化是通过um_lwip_port_core_open()函数去实现的,在该函数中会调用tcpip_init()函数进行lwip协议栈的初始化,主要会创建一个tcpip_mbox邮箱,用于接收底层或者上层传递过来的消息,此外还会创建一个tcpip_thread线程,所有需要处理的数据都会通过这个函数去处理。
在tcpip_mbox邮箱中会接收来自tcpip_input的数据包,并由该邮箱将其传递给tcpip_thread线程进行处理。
2
netifapi_netif_add()
netifapi_netif_add()由 lwip port open中的um_lwip_port_netif_open()函数调用,该函数主要功能为:将网络接口添加到 lwIP 网络堆栈的调用。
这里我们需要关注两个回调函数:
_netif_add_callback
tcpip_input
tcpip_input内部具体调用的就是tcpip_inpkt(),调用该函数会将ethernet_input()函数作为结构体的一部分传递给内核,当内核接收到数据包时就会调用这个函数。
而在_netif_add_callback()函数中,我们关注三个接口函数:
etharp_output
_link_output
_status_changed
第一个 p_netif->output = etharp_output 是以太网接口的输出回调函数,用于处理IP数据包,主要负责将IP数据包通过以太网发送出去,并处理ARP协议以解析IP到MAC地址的映射关系。
第二个 p_netif->linkoutput 是链路层的输出回调函数,该函数被用来实际发送以太网帧到网络驱动程序,etharp_output 等高层协议函数会处理好数据包内容,最终调用 linkoutput 将数据帧发送到硬件。
第三个是当网络接口状态发生变化时调用的回调函数。比如,网络接口的链路状态(link up/down)发生变化时,lwIP就会调用这个回调函数,如果说netif已经up并且IP地址被设置时,他就会发送一个Event:LWIP_PORT_LAUNCHER_EVENT_IP_UP
09
网卡数据传入LWIP内核流程
当eth接收完数据后会产生一个中断,在中断中会释放一个信号量
此时在ether_reader线程中收到此信号量,会从以太网驱动缓冲区中读取接收到的以太网帧,并通过um_ether_netif_callback_request执行回调p_func
此时event为ETHER_NETIF_CALLBACK_EVENT_RECEIVE_ETHER_FRAME,执行 um_lwip_port_thread_receiver_request();
此时lwip_recv线程收到该Frame,执行_netif_frame_input(p_callback),在此函数中通过um_lwip_port_netif_input()将以太网帧传入到lwip netif中;
紧接着调用tcpip_input回调,将封装的消息传递给tcpip_mbox邮箱中,同时由tcpip_thread对该消息进行解析。
10
EOE从站接收以太网数据帧流程(使用ping指令)
1
初始化前提
首先系统需要以此完成两部分初始化:
ethernet网络接口初始化
ether_monitor Thread: 检查netif link状态,同时将收到的状态发送给回调更新event
ether_reader Thread: 接收来自ethernet的通知并接收数据帧,由ethernet中断触发
ether_writer Thread: 接收来自消息队列的数据帧
tcp/ip网络协议栈初始化
LWIP_PORT_LAUNCHER_EVENT_LINK_UP -> _netif_link_on
LWIP_PORT_LAUNCHER_EVENT_LINK_DOWN -> _netif_link_off
LWIP_PORT_LAUNCHER_EVENT_IP_UP -> um_lwip_port_netif_set_netif_state(LWIP_PORT_NETIF_STATE_UP);
LWIP_PORT_LAUNCHER_EVENT_IP_DOWN -> um_lwip_port_netif_set_netif_state(LWIP_PORT_NETIF_STATE_DOWN);
lwip_launch Thread: 接收来自ethernet link回调的通知,以此判断请求的event
lwip_recv Thread: 接收来自ether link回调的通知,并广播给netif
在此期间以此通过几个Event来完成网络的初始化
ETHER_NETIF_CALLBACK_EVENT_LINK_DOWN:此时的所有网络端口处于初始状态,还未初始化
ETHER_NETIF_CALLBACK_EVENT_LINK_UP:存在端口link up,执行um_lwip_port_task_launcher_request,将event状态设置为LWIP_PORT_LAUNCHER_EVENT_LINK_UP
LWIP_PORT_LAUNCHER_EVENT_LINK_UP:netif链路link up,执行 netif_link_on ,并设置 network interface up(um_lwip_port_netif_set_up),最终执行到status_changed,并执行 um_lwip_port_task_launcher_request 修改 event 为 LWIP_PORT_LAUNCHER_EVENT_IP_UP
LWIP_PORT_LAUNCHER_EVENT_IP_UP:IP is up,执行um_lwip_port_netif_set_netif_state设置netif状态并设置通知回调,将event修改为LWIP_PORT_CALLBACK_EVENT_NETIF_UP
LWIP_PORT_CALLBACK_EVENT_NETIF_UP:network interface link up,检查IP地址是否有效,并创建sock监听
只有当这么几个Event依次触发,才能完成对EtherCAT从站网络的初始化,主从站之间才能正常收发数据:
2
ICMP简要介绍
ICMP一般被认为是IP协议层的一部分,但在结构上位于IP协议层之上。主要用于确认网络状态或者链路不同等场景,当数据报文无法正常到达目标主机时,ICMP就会返回一个差错报文,让源主机知道数据到达情况,常见的ping命令就是属于ICMP 查询报文功能的一部分,当ping成功,也就代表网卡、IP层、ICMP层都能正常通信,也可以证明LWIP移植成功。
下面是使用Wireshark抓包抓取回显应答报文的示例:
3
ARP简要介绍
ARP:地址解析协议,主要是通过解析IP地址得到数据链路层的MAC地址(网卡标识符),在以太网中,一个主机想要与另一个主机直接通信,必须知道目标主机的MAC地址,这时候就需要ARP进行地址解析。
ARP缓存表:在每台主机的内存中都有一个ARP缓存表,记录了IP地址到MAC地址的映射关系,通过arp -a可获取信息
其主要的运作流程是:
1.如果主机A想要发送数据给主机B,那么主机A会先检查自己的ARP缓存中是否有主机B的IP地址及MAC地址的映射,如果有的话才会将主机B的MAC地址作为源地址封装到数据帧中;如果没有,那主机A就会向局域网中广播ARP请求(包括发送方的IP地址、MAC地址,接收方的IP地址),每台其他主机在收到ARP请求后都会检查自己的IP地址是否与ARP请求中接收方的IP地址相同,如不相同,就会丢弃ARP请求包。
2.当交换机接收到此数据帧后,发现此数据帧为广播帧,因此会将此数据帧从非接收的所有接口中发送出去。
3.当主机B接收到此数据帧后,会核对IP地址是否是自己的,并将主机A的IP地址和MAC地址的对应关系记录到自己的ARP缓存表中,同时发送一个ARP响应,其中就包括自己的MAC地址。
4.当主机A接收到这个主机B回应的数据帧后,就会在自己的ARP缓存表中记录主机B的IP地址和MAC映射关系。
具体流程可见下图:
下面这个是EtherCAT EOE工程测试ping命令时ARP缓存表的变化情况
那这里LWIP是怎么处理ARP请求并且获取到从站的IP及MAC地址的呢,我们接着往下看
4
ARP接收数据包流程
先来了解两个API:
LWIP从网卡中接收数据包的函数是:ethernet_input()
ARP数据包的处理函数是:etharp_input(),其主要内容为:
(1)检查ARP报文。
(2)ARP请求报文:如果是请求报文的目标IP和本地的匹配,就组装ARP响应报文并发送出去。
(3)ARP响应报文:
1)更新ARP缓存表。
2)把阻塞在arp entry缓存队列的IP数据报发送出去。
3)释放pbuf。因为ARP报文到此已经处理完毕。
对于ARP处理数据包来说,有这么两种情况:
(1)收到APR应答包:此时说明之前发送的APR请求包得到了回应,那么就根据应答包去更新自身的ARP缓存表;
(2)收到ARP请求包:首先会判断包中的目标是否与主机IP匹配,匹配上的话就记录下源主机的IP及MAC地址,接着在更新自身的ARP表外,还要向源主机发送一个ARP应答包;如果说包中的目标IP与主机IP并不匹配,那么根据ARP表的空余情况选择记录源主机的IP及MAC地址(如果没有空余的表项,则不会记录),并丢弃该请求包。
这里对应我们EtherCAT EOE工程来说,ethernet调用流程为:
ethernet_input(收到ARP数据包) ->etharp_input()-> etharp_raw(发送应答包) ->ethernet_output()-> _link_output -> um_lwip_port_ether_netif_send -> UM_ETHER_NETIF_Send()
5
ARP发送数据包流程
前面我们了解了一个数据包的接收流程,如果收到的是ARP数据包,那么就会交给ARP处理,如果是IP数据包就会调用ip4_input()函数将其传递给上层.
arp通过 etharp_output() 函数接收到IP数据包后,判断该数据包是单播,多播还是广播,对于其处理方式:
对于多播或者广播数据包,直接交给网卡处理(调用ethernet_output)
对于单播,arp需要根据IP找到对应的MAC地址,如果找不到MAC地址,还要延迟发送数据包。ARP首先会创建一个ARP表项,再将数据包挂载到ARP表项上对应的缓存队列上,同时会发出一个ARP请求包,等待目标主机的回应后再发送IP数据包
那么对应netif->linkoutput()其实就是在初始化lwip网卡接口设备时绑定的 _link_output()
11
EtherCAT EOE测试
编译并启动下载调试,终端出现RT-Thread LOGO即为启动成功
将瑞萨EtherCAT EOE例程下的ESI文件(RZT2M_EtherCAT_RSK_rev0200\common\ecat_EoE_lwIP\ESI\Renesas EtherCAT RZT2 EoE.xml)复制到TwinCAT安装目录下(C:\TwinCAT\3.1\Config\Io\EtherCAT)
启动TwinCAT,并烧入EEPROM的EOE固件
使用ping命令测试EOE协议是否运行成功
使用TCPIP测试工具测试TCP通信回显功能
12
其他说明:三网口使用配置说明
打开RZSC,配置ethercat_ssc_port的phy2
设置第三个网口的PHY
设置ETHER_ETH2
设置ETHER_ESC的phy2配置,修改ESC_LINKACT2为P21_0,ESC_PHYLINK2为P00_5
生成RZSC源码,回到IAR工程重新编译下载
EEPROM固件更新
此时T2M的三个网口都可以进行EtherCAT EOE通信,具体测试方法参考如上
——————End——————
想要在RT-Thread平台或社区投放内容?或想参与相关直播活动及赛事?RT-Thread已开放对接窗口,请通过邮件与我们取得联系,期待合作!
合作邮箱: tongfangyi@rt-thread.com