EtherCAT FoE简介

FoE(File Access over EtherCAT)可实现EtherCAT节点之间的文件传输,本文介绍FoE的基本原理,以及FoE在开源EtherCAT主站Etherlab中的实现过程。

一、软件更新方式

在嵌入式产品开发调试过程中,我们一般使用仿真器更新程序。当产品发布后,我们通常使用串口、CAN或者蓝牙等端口更新程序。如果是EtherCAT从站设备,使用FoE在bootstrap模式也可以实现更新程序的功能。
这里写图片描述
如上图所示,应用程序通常为.bin格式的文件,也有的使用.hex或者.s19格式的。将应用程序bin文件更新到从站的Flash,涉及到3部分:
(1) EtherCAT主站,负责读取bin文件的内容,并按FoE的格式发送到从站,如Twincat在从站的online界面使用download和upload按钮操作即可,本文使用的主站为Etherlab。
(2) 从站协议栈,包含FoE功能,负责接收和解析主站发送的FoE帧。
(3) 烧录功能,将接收到的bin文件内容烧录到Flash,这部分功能与芯片类型密切相关,不同的芯片烧录Flash的步骤,提供的API都不尽相同,通常还需要将Flash分片,分别存储bootloader代码和应用层代码。

二、FoE帧格式

FoE帧格式如下图所示:
这里写图片描述
其中OpCode的取值范围为1-6,不同的值代表不同的功能,data中数据的含义也不同。

当OpCode =1 时,表示读文件请求:
这里写图片描述
其中Password为0时表示不需要密码。

当OpCode=2时,表示写文件请求:
这里写图片描述

当OpCode=3时,表示写入数据:
这里写图片描述
其中Packet Number表示数据包计数,每发送一次数据包加1,通信双方使用该值保证数据包按顺序收发。

当OpCode=4时,表示对接收数据的应答:
这里写图片描述

当OpCode=5时,表示发生错误,包含错误代码和描述:
这里写图片描述

当OpCode=6时,表示传输百分比等内容:
这里写图片描述

三、FoE实例

作为示例,使用Ubuntu中安装的Etherlab主站,将bin文件TestFoE.bin下载到从站。
TestFoE.bin文件的部分内容如下:
这里写图片描述
将TestFoE.bin拷贝到/opt/FoE目录下,并在终端输入命令:

 #ethercat foe_write -p 0 TestFoE.bin

等待文件传输完成:
这里写图片描述
在总线上可监测到FoE相关的数据。
这里写图片描述

四、Etherlab FoE源码简析

执行FoE命令时,Etherlab的执行流程大致如下图所示:
这里写图片描述
1、读取文件内容
在终端输入foe_write命令后,首先执行的是CommandFoeWrite::execute(),主要完成:
(1)打开文件,并拷贝文件内容。
(2)调用对应从站的foe写功能。

void CommandFoeWrite::execute(const StringVector &args)
{...file.open(args[0].c_str(), ifstream::in | ifstream::binary);...loadFoeData(&data, file); //将文件内容拷贝到data...try {m.writeFoe(&data);} catch (MasterDeviceException &e) {...
}

其中,m.writeFoe(&data)将调用ec_ioctl_slave_foe_write(),将foe写请求挂载到从站的foe请求队列中:

static ATTRIBUTES int ec_ioctl_slave_foe_write(ec_master_t *master, /**< EtherCAT master. */void *arg /**< ioctl() argument. */)
{...// schedule FoE write request.list_add_tail(&request.list, &slave->foe_requests);...
}

在ec_fsm_slave_action_process_foe()中检测到foe队列中有写请求,则开始foe写状态机:

int ec_fsm_slave_action_process_foe(ec_fsm_slave_t *fsm, /**< Slave state machine. */ec_datagram_t *datagram /**< Datagram to use. */)
{...// take the first request to be processedrequest = list_entry(slave->foe_requests.next, ec_foe_request_t, list); ...fsm->state = ec_fsm_slave_state_foe_request;ec_fsm_foe_transfer(&fsm->fsm_foe, slave, request);//调用ec_fsm_foe_write_startec_fsm_foe_exec(&fsm->fsm_foe, datagram);return 1;      
}

2、发送写请求
foe写状态机开始执行的是ec_fsm_foe_write_start(),在其中将调用ec_foe_prepare_wrq_send()发送OpCode=2的写请求:

int ec_foe_prepare_wrq_send(ec_fsm_foe_t *fsm, /**< Finite state machine. */ec_datagram_t *datagram /**< Datagram to use. */)
{...current_size = fsm->tx_filename_len;data = ec_slave_mbox_prepare_send(fsm->slave, datagram,EC_MBOX_TYPE_FILEACCESS, current_size + EC_FOE_HEADER_SIZE);...EC_WRITE_U16( data, EC_FOE_OPCODE_WRQ); // fsm write requestEC_WRITE_U32( data + 2, fsm->tx_packet_no );memcpy(data + EC_FOE_HEADER_SIZE, fsm->tx_filename, current_size);return 0;
}

通过wireshark监控到对应的帧如下:
这里写图片描述

3、等待从站ACK

发送完写请求后,将等待从站返回的ACK,若收到ACK,则开始发送数据:

void ec_fsm_foe_state_ack_read(ec_fsm_foe_t *fsm, /**< FoE statemachine. */ec_datagram_t *datagram /**< Datagram to use. */)
{...if (opCode == EC_FOE_OPCODE_ACK) {fsm->tx_packet_no++;fsm->tx_buffer_offset += fsm->tx_current_size;if (fsm->tx_last_packet) {       fsm->state = ec_fsm_foe_end;//如果最后一帧已经发送,则结束真个FoE状态机return;}if (ec_foe_prepare_data_send(fsm, datagram)) {ec_foe_set_tx_error(fsm, FOE_PROT_ERROR);return;}fsm->state = ec_fsm_foe_state_data_sent;return;}   
}

ACK帧:
这里写图片描述

4、发送数据
在ec_foe_prepare_data_send()发送数据:

int ec_foe_prepare_data_send(ec_fsm_foe_t *fsm, /**< Finite state machine. */ec_datagram_t *datagram /**< Datagram to use. */)
{size_t remaining_size, current_size;uint8_t *data;remaining_size = fsm->tx_buffer_size - fsm->tx_buffer_offset;//如果等待发送的数据长度小于一个邮箱能装载的最大数据,即本次发送能把剩下的数据一次发送完成//本实验从站的邮箱大小设为128字节,减去邮箱头12字节和FoE帧头12字节,即一次最多可传送//116个字节的数据if (remaining_size < fsm->slave->configured_tx_mailbox_size- EC_MBOX_HEADER_SIZE - EC_FOE_HEADER_SIZE) {current_size = remaining_size;fsm->tx_last_packet = 1;} else {current_size = fsm->slave->configured_tx_mailbox_size- EC_MBOX_HEADER_SIZE - EC_FOE_HEADER_SIZE;}data = ec_slave_mbox_prepare_send(fsm->slave,datagram, EC_MBOX_TYPE_FILEACCESS,current_size + EC_FOE_HEADER_SIZE);if (IS_ERR(data)) {return -1;}EC_WRITE_U16(data, EC_FOE_OPCODE_DATA);    // OpCode = DataBlock req.EC_WRITE_U32(data + 2, fsm->tx_packet_no); // PacketNo, Passwordmemcpy(data + EC_FOE_HEADER_SIZE,fsm->tx_buffer + fsm->tx_buffer_offset, current_size);fsm->tx_current_size = current_size;return 0;
}

数据帧:
这里写图片描述
从上图可以看出,由于邮箱大小设为128字节,一次最多传送116字节。
传输的第一帧数据中的内容即是TestFoE.bin文件最开始的内容。

发送完数据后,Etherlab将等待从站返回的ACK。

5、发送完成
当tx_last_packet标志为1时,表示所有的数据发送完成,最后一帧数据:
这里写图片描述
从上图可以看出,最后一帧数据的字节数68,小于一帧数据最大装载量116。
TestFoE.bin文件总共经过了411次写数据/应答的传输过程。

五、参考资料

1.ETG100.6 V1.0.3.2


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部