Ethercat学习-从站FOE固件更新(QT上位机)

简介

FOE协议与下位机程序实现过程之前文章有提到,这里不做介绍了。这里主要介绍1、QT上位机通过FOE读写下位机的数据;2、QT上位机读写ESC的EEPROM。

1、源码简介

SOEM源码中和foe相关的文件为ethercatfoe.c、ethercatfoe.h。主要包含了下面三个函数。

/* 读请求 参数:请求文件名、密钥、本地用于存储的内存的大小、本地文件的地址、每次通信超时时间 */
int ec_FOEread(uint16 slave, char *filename, uint32 password, int *psize, void *p, int timeout)
/* 写请求 参数:请求文件名、密钥、文件大小、本地文件的地址、每次通信超时时间*/
int ec_FOEwrite(uint16 slave, char *filename, uint32 password, int psize, void *p, int timeout)
/* 定义回调函数 参数:回调函数的地址 */
int ec_FOEdefinehook(void *hook)
1、ec_FOEread

FOE读的整体实现思路如下:

  1. 清空邮箱,防止现有数据影响接下来的判断。

  2. 向下位机发送读请求帧,包含了文件名、密钥等的数据。

  3. 读取下位机的回复的数据帧;

  4. 将数据帧的内容拷贝到本地内存中,判断内存是否越界,计算已接受数据的大小

  5. 如果定义了回调函数,则会调用回调函数。回调函数包含了从机编号、包计数、已接收文件总长度这三个参数

  6. 循环,直到接收完成。

    /** FoE read, 代码片段.
     *
     * @param[in]  context        = context struct
     * @param[in]     slave      = 从机编号.
     * @param[in]     filename   = 请求的文件名.
     * @param[in]     password   = 密钥.
     * @param[in,out] psize      = 本地存储文件的内存的大小.
     * @param[out]    p          = 本地存储文件的内存的地址
     * @param[in]     timeout    = 超时时间us
     * @return Workcounter from last slave response
     */
    int ecx_FOEread(ecx_contextt *context, uint16 slave, char *filename, uint32 password, int *psize, void *p, int timeout)
    {
        .......
       /* 将邮箱清空,为FOE传输做准备 */
       ec_clearmbx(&MbxIn);
       wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
       ec_clearmbx(&MbxOut);
        ......
       /* 封装数据,发送读请求 */
       wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
       if (wkc > 0) 
       {
          do
          {
                   .......
                   /* 一系列判断 */
                   if(aFOEp->OpCode == ECT_FOE_DATA)
                   {
                       /* 获取数据的长度和包编号 */
                      segmentdata = etohs(aFOEp->MbxHeader.length) - 0x0006;
                      packetnumber = etohl(aFOEp->PacketNumber);
                       /* 判断包编号和总计接收的长度是否超过了内存 */
                      if ((packetnumber == ++prevpacket) && (dataread + segmentdata <= buffersize))
                      {
                         /* 数据转存,内存偏移,接收计数累加 */
                         memcpy(p, &aFOEp->Data[0], segmentdata);
                         dataread += segmentdata;
                         p = (uint8 *)p + segmentdata;
                          /* 这个maxdata是邮箱的大小,也是每包数据的最大长度,相等说明不是最后一包数据,继续接收*/
                         if (segmentdata == maxdata)
                         {
                            worktodo = TRUE;
                         }
                         ........
                         /* 回复ack给下位机,如果有回调函数则调用回调 */
                         wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
                         if (wkc <= 0)
                         {
                            worktodo = FALSE;
                         }
                         if (context->FOEhook)
                         {
                            context->FOEhook(slave, packetnumber, dataread);
                         }
                         ..........
    }
    
2、ec_FOEwrite

FOE写的整体实现思路如下:

  1. 清空邮箱,防止现有数据影响接下来的判断。

  2. 向下位机发送写请求帧,包含了文件名、密钥等的数据。

  3. 读取下位机的回复的应答帧;

  4. 判断应答帧的包计数,如果定义了回调函数,则会调用回调函数,回调函数包含了从机编号、包计数、已接收文件总长度这三个参数

  5. 继续发送数据包

  6. 循环,直到发送完成。

    /** FoE write, 代码片段.
     *
     * @param[in]  context        = context struct
     * @param[in]  slave      = 从站编号.
     * @param[in]  filename   = 待发送的文件名.
     * @param[in]  password   = 密钥.
     * @param[in]  psize      = 待发送的文件的大小.
     * @param[out] p          = 待发送文件的地址
     * @param[in]  timeout    = Timeout per mailbox cycle in us, standard is EC_TIMEOUTRXM
     * @return Workcounter from last slave response
     */
    int ecx_FOEwrite(ecx_contextt *context, uint16 slave, char *filename, uint32 password, int psize, void *p, int timeout)
    {
       ........
       /* 清空邮箱为FOE通信做准备 */
       ec_clearmbx(&MbxIn);
       wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0);
       ec_clearmbx(&MbxOut);
       ......
       /* 封装数据发送写请求 */
       wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
       if (wkc > 0) 
       {
          do
          {
             worktodo = FALSE;
             /* clean mailboxbuffer */
             ec_clearmbx(&MbxIn);
             /* 读取从站回复 */
             wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout);
             if (wkc > 0) /* succeeded to read slave response ? */
             {
                
                if ((aFOEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_FOE)
                {
                   switch (aFOEp->OpCode)
                   {
                      case ECT_FOE_ACK:
                      {
                         packetnumber = etohl(aFOEp->PacketNumber);
                         if (packetnumber == sendpacket)
                         {
                             /* 如果定义了回调函数,可以调用回调函数 */
                            if (context->FOEhook)
                            {
                               context->FOEhook(slave, packetnumber, psize);
                            }
                            tsize = psize;
                            if (tsize > maxdata)
                            {
                               tsize = maxdata;
                            }
                            if(tsize || dofinalzero)
                            {
                               worktodo = TRUE;
                               dofinalzero = FALSE;
                               segmentdata = tsize;
                               psize -= segmentdata;
                               /* 判断是否为最后一帧 */
                               if (!psize && (segmentdata == maxdata))
                               {
                                  dofinalzero = TRUE;
                               }
                               ...........
                               /* 封装数据,发送数据帧 */
                               wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM);
                               if (wkc <= 0)
                               {
                                  worktodo = FALSE;
                               }
                            }
                         }
                         else
                         {
                            /* FoE error */
                            wkc = -EC_ERR_TYPE_FOE_PACKETNUMBER;
                         }
                         break;
                      }
    }
    
    
3、ec_FOEdefinehook

用与定义回调函数,就是将我们回调函数入口地址传递给一个变量。我们自己定义的回调函数需要为下面的形式

(*FOEhook)(uint16 slave, int packetnumber, int datasize);

2、程序思路

首先,通过看SOEMd的源码发现,在调用ec_FOEread的时候,我们需要先申请一片内存,将内存地址传递进去,用来存放读取的文件,那么当文件很大的时候,我们申请的内存就需要足够大。同样的调用ec_FOEwrite的时候,我们也需要申请一片内存,将要写的数据全部读取到内存中,然后将内存的地址传递给ec_FOEwrite,当文件很大的时候,我们就需要定义的内存足够大。这种实现方式也算是用空间换时间吧,将文件一次性放到内存中,然后再统一操作数据。还有一点就是ec_FOEread和ec_FOEwrite我们只需要调用一次,将参数传递进去后,就开始进行一个while循环,知道我们所有的操作结束。

本次是用QT来进行FOE数据传输的,在数据传输部分,个人偏向于边读便发送,或者边写便发送的方式。另外在数据收发的过程中,还要保证界面继续刷新,因此需要对ec_FOEread和ec_FOEwrite进行修改。

3、修改实现

1、ecx_FOEwrite_gxf
                   /* 对比源码修改的部分 */                    

                    if (packetnumber == sendpacket)
                     {
                        /* 在收到ack 说明写请求成功,发送自定义信号量,并延时10ms 来更新界面 psize是剩余的                           文件大小 */
                         if(psize != 0)
                             emit foewrite(psize);
                         Sleep(10);
                        /***********************************************************/
                        tsize = psize;
                        if (tsize > maxdata)
                        {
                           tsize = maxdata;
                        }
                        if(tsize || dofinalzero)
                        {
                           worktodo = TRUE;
                           dofinalzero = FALSE;
                           segmentdata = tsize;
                           psize -= segmentdata;
                           /* if last packet was full size, add a zero size packet as final */
                           /* EOF is defined as packetsize < full packetsize */
                           if (!psize && (segmentdata == maxdata))
                           {
                              dofinalzero = TRUE;
                           }
                           FOEp->MbxHeader.length = htoes((uint16)(0x0006 + segmentdata));
                           FOEp->MbxHeader.address = htoes(0x0000);
                           FOEp->MbxHeader.priority = 0x00;
                           /* get new mailbox count value */
                           cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt);
                           context->slavelist[slave].mbx_cnt = cnt;
                           FOEp->MbxHeader.mbxtype = ECT_MBXT_FOE + MBX_HDR_SET_CNT(cnt); /* FoE */
                           FOEp->OpCode = ECT_FOE_DATA;
                           sendpacket++;
                           FOEp->PacketNumber = htoel(sendpacket);
                           /* 屏蔽掉了下面发送地址偏移的代码,因此每次发送的数据地址都是固定的,只需要定义一                               个固定的地址用于存放数据,每次在发送前将数据填充到该地址就可以了,这样我们就不                               需要一个很大的内存了 */
                           memcpy(&FOEp->Data[0], p, segmentdata);
//                           p = (uint8 *)p + segmentdata;
2、ecx_FOEread_gxf
                  /* 对比源码修改部分 */
                 
                 segmentdata = etohs(aFOEp->MbxHeader.length) - 0x0006;
                  packetnumber = etohl(aFOEp->PacketNumber);
                  /* 这里就不需要判断接收数据总大小是否大于申请的内存的大小了,因为 p 的地址不偏移了,每次接收完数据后,根据自定义的信号量去固定的内存地址拷贝已读取到的数据并保存到文件,不需要太大的内存了 */
//                  if ((packetnumber == ++prevpacket) && (dataread + segmentdata <= buffersize))
                  if ((packetnumber == ++prevpacket) && (segmentdata <= maxdata))
                  {
                     memcpy(p, &aFOEp->Data[0], segmentdata);
                     dataread += segmentdata;
                      /* 发送数据的地址不偏移 */
//                     p = (uint8 *)p + segmentdata;
                     if (segmentdata == maxdata)
                     {
                        worktodo = TRUE;
                     }

                    /* 发送自定义信号量,更新界面 dataread已接收的文件大小 */
                     emit foeread(dataread);
                     Sleep(10);
                     FOEp->MbxHeader.length = htoes(0x0006);
                     FOEp->MbxHeader.address = htoes(0x0000);

这里只是简单的介绍了一下程序的实现思路,可能有点绕。

4、其他

测试的例程在连接从站后,直接请求从站的状态从Init跳转到了Bootstrap。在Bootstrap的时候值能通过FOE进行通信。还有一个地方需要注意,在Bootstrap状态的时候,通信的邮箱是boot邮箱,虽然地址、大小一般都和标准邮箱(COE用的邮箱)一样,但是还是需要进行配置的。boot邮箱的配置在EEPROM中,因此在进入Bootstrap的时候会去EEPROM中读取boot邮箱的配置。如果和标准邮箱的配置一样且标准邮箱已经配置过了,就不需要再进行配置了,否则,需要对boot邮箱进行配置。下图就是XML中Boot邮箱的地址、大小的配置信息。需要再SSC-Tool中勾选了BOOTSTRAPMODE_SUPPORTED选项,xml中才会产生。

在这里插入图片描述

5、结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6、源码连接

FOE主站源码:https://github.com/IJustLoveMyself/csdn-example/tree/main/example4

配合测试的STM32F405从站源码:https://github.com/IJustLoveMyself/csdn-example/tree/main/example5

  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: EtherCAT(以太网控制自动化技术)是一种实时以太网网络协议,可以用于高速、高精度的控制系统和数据采集应用中。EtherCAT Foe(文件传输过程)是EtherCAT协议的一部分,它允许使用EtherCAT网络进行文件传输。 在EtherCAT Foe中,传输的文件可以是任何类型的文件,例如控制程序、配置文件、日志文件等。传输文件的命令可以通过EtherCAT网络发送到目标设备上的EtherCAT Master,然后Master将文件传输到目标设备上的EtherCAT Slave。传输文件时,数据包通过EtherCAT网络进行传送和接收,并通过EtherCAT Slave与目标设备的文件系统进行交互。 EtherCAT Foe可用于更新控制系统的软件和配置文件,可以更快地传输文件,并降低了系统的故障率和维护时间。另外,EtherCAT Foe还可以用于远程诊断和维护,这对于分布式控制系统和遥控系统非常重要。 总之,EtherCAT Foe提供了一种快速、高效、可靠的文件传输方式,可以用于各种控制系统和数据采集应用中,有助于提高系统的性能和可靠性。 ### 回答2: Ethernet for Control Automation Technology(EtherCAT)是一种先进的实时以太网技术,它可以在任何标准以太网上让大量的从设备同时通讯,从而实现实时控制和传感应用。EtherCAT的主要特点是高速、实时性强、支持多种拓扑结构、可扩展性强,因此被广泛应用于工业自动化、机器人等领域。 EtherCAT通过在每个节点上实现一个连接单元,将各个从设备连接在一个环形或线性布局的总线上,以实现快速、高效的数据传输和实时控制。它的优势在于它具有比常规方法更快的响应时间和更高的数据传输速度,同时也能够在网络拓扑和设备类型发生变化时保持高度的兼容性。 EtherCAT的应用场景广泛,它可以用于所有需要高效实时通讯和控制的领域,例如制造业、物流、航空航天、自动驾驶和医疗设备等。此外,EtherCAT也能够处理工业自动化和机器人应用中需要处理的更复杂和精细的控制任务,包括运动控制、位置控制和力控制等。 总之,从EtherCAT是一种具有高性能、实时性和可扩展性的实时以太网技术,它被广泛应用于各种需要高效实时通讯和控制的领域,它的发展将推动工业自动化和物联网技术的进一步发展。 ### 回答3: EtherCAT FoeEtherCAT Fieldbus over EtherNet/IP的简称,是一种高性能实时以太网通信协议,它充分利用了以太网的高带宽和广域性,并将其实时能力延伸到了工业自动化领域。 EtherCAT Foe将以太网帧划分为小数据包,使得每个数据包在通过线缆时,可以实现数据的实时传输。这种机制使得控制器能够控制多个从,并能够同时获取多个从的信息。同时,EtherCAT Foe具有实时性高、成本低、容错性强、可扩展性好的特点,因此得到了工业自动化领域的广泛应用。 EtherCAT Foe可以实现模块化、分布式的控制,让通信实时性得到充分保障。同时,它可以在多种操作系统上运行,如Windows、Linux等,广泛应用于各种工业自动化应用场景。 总之,EtherCAT Foe是一种高性能、低成本、实时性好、可扩展性强的工业自动化通信协议,通过它,可以轻松地实现现代化的工业自动化控制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值