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

news/2024/11/30 10:40:38/

文章目录

      • 简介
      • 1、源码简介
        • 1、ec_FOEread
        • 2、ec_FOEwrite
        • 3、ec_FOEdefinehook
      • 2、程序思路
      • 3、修改实现
        • 1、ecx_FOEwrite_gxf
        • 2、ecx_FOEread_gxf
      • 4、其他
      • 5、结果
      • 6、源码连接

简介

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


http://www.ppmy.cn/news/109086.html

相关文章

Python 中的循环向后迭代

文章目录 在 Python 中向后循环使用 reserved() 函数向后循环使用 range() 函数向后循环 总结 在 Python 中&#xff0c;数字迭代是通过使用循环技术来完成的。 在本文中&#xff0c;我们使用 Python 中的 reserved() 函数和 range() 函数执行循环。 在 Python 中向后循环 有许…

ChatGPT与网络安全

文章目录 一、“AI用于攻击”二、“AI用于安全&#xff08;防御&#xff09;”三、“AI的防御”四、“AI被攻击” ChatGPT作为基于生成式预训练模型&#xff08;GPT&#xff09;的聊天机器人&#xff0c;其核心技术是自然语言处理&#xff08;NLP&#xff09;。随着NLP技术的不…

剑指 Offer 14- I. 剪绳子解题思路

文章目录 题目解题思路优化 题目 给你一根长度为 n 的绳子&#xff0c;请把绳子剪成整数长度的 m 段&#xff08;m、n都是整数&#xff0c;n>1并且m>1&#xff09;&#xff0c;每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1]…*k[m-1] 可能的最大乘积是多少&…

消息队列内容

问题有哪些&#xff1f; &#xff08;1&#xff09;消息队列为什么会出现&#xff1f; &#xff08;2&#xff09;消息队列能用来干什么&#xff1f; &#xff08;3&#xff09;使用消息队列存在的问题&#xff1f; &#xff08;4&#xff09;如何解决重复消费的问题&#…

Happy Equation(数论+讨论)

Happy Equation 题意 ​ 在 1 ≤ x ≤ 2 p 1\leq x\leq2^{p} 1≤x≤2p内&#xff0c;问有多少个x满足 a x ≡ x a ( m o d m ) a^{x}\equiv x^{a}\ (mod\ \ m) ax≡xa (mod m) 分析 ​ 通过打表发现&#xff0c;当a为奇数的时候&#xff0c;结果全为1&#xff0c;那么只需…

设计模式之~原型模式

定义&#xff1a;用原型实例指导创建对象的种类&#xff0c;并且通过拷贝这些原型创建新的对象。原型模式其实就是从一个对象再创建另外一个可定制的对象&#xff0c;而且不需知道任何创建的细节。 优点&#xff1a; 一般在初始化的信息不发生变化的情况下&#xff0c;克隆是最…

管理系统总结(前端:Vue-cli, 后端Jdbc连接mysql数据库,项目部署tomcat里)

根据所学的知识, 写一个管理系统, 顺便总结一些知识点 准备: 前端用vue-cli的框架, 后端用jdbc连接数据库, 项目部署tomcat服务器来完成交互 ●前端的vue-cli框架搭建可以看 点击跳转 的第二小结 ●后端的tomcat在idea里的相关的配置与集成,可以看 点击跳跃 文章目录 一、 前段…

NLP:文本预处理总览

1 用n-gram语言模型过滤低质量内容 使用n-gram语言模型对文本进行评估&#xff0c;从而过滤掉低质量的内容。具体来说&#xff0c;可以通过以下步骤进行&#xff1a; 1 将文本分成n-gram序列&#xff0c;其中n是一个整数。 2 使用已经训练好的n-gram语言模型对每个n-gram序列…