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

文章目录

      • 简介
      • 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://xiahunao.cn/news/251687.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Feign的另一种玩法-动态Feign

1.Feign传统方式的不足 ①.在微服务架构中,当我们使用Feign传统方式进行服务调用的时候,需要在每个服务消费者中添加FeignClient接口,编写对应的方法,而且当服务生产者Handler新增方法之后,服务消费者也要在FeignClient接口中添加方法,这样的话,会有些累赘. 那么能不能在调用服…

Feign总结

目录 Feign简介 Feign能干什么 Feign与Ribbon区别 Feign和Open Feign的区别 Feign和Dubbo的区别 Feign的使用 Feign的调用方式 Feign中使用熔断器 Feign的核心原理 Feign远程调用的基本流程 Feign 远程调用的重要组件 远程接口的本地JDK Proxy代理实例 调用处理器…

feign漫谈

feign的简单使用。 文章目录 一. 什么是feign二. 准备工作三. 如何使用3.1 定义pom文件3.2 定义配置文件及启动类注解3.3 定义feign接口 四. 部署 一. 什么是feign 远程调用框架 二. 准备工作 需要nacos环境&#xff1a; 涉及到feign调用&#xff0c;就没法抛开注册中心&am…

Feign简介

Feign feign是声明式的web service客户端&#xff0c;它让微服务之间的调用变得更简单了&#xff0c;类似controller调用service。Spring Cloud集成了Ribbon和Eureka&#xff0c;可在使用Feign时提供负载均衡的http客户端。 Feign是什么? Feign是一个声明式WebService客户端…

feign的使用入门篇

Feign简介 Feign是一个声明式的Web服务客户端&#xff0c;使用Feign可使得Web服务客户端的写入更加方便。 它具有可插拔注释支持&#xff0c;包括Feign注解和JAX-RS注解、Feign还支持可插拔编码器和解码器、Spring Cloud增加了对Spring MVC注释的支持&#xff0c;并HttpMessag…

Feign的简介及使用

一、Feign简介 Feign是一个声明式的http客户端&#xff0c;官方地址:https://github.com/OpenFeign/feign 其作用就是帮助我们优雅的实现http请求的发送&#xff0c;解决代码可读性差&#xff0c;编程体验不统一、参数复杂URL难以维护的问题。 二、使用Feign的步骤 1.引入依赖…

架构愿景: 构建良好软件的关键

在产品开发生命周期的各个阶段&#xff0c;牢记架构愿景&#xff0c;始终坚持每个决策都符合愿景原则&#xff0c;是避免架构腐化的唯一方式。原文: Architecture Vision — A critical ingredient in building well-maintained software 上一篇文章《软件架构: 一切皆有代价》…

Feign原理

是一个HTTP请求调用轻量级框架&#xff0c;可以以Java接口注解的方式调用HTTP请求&#xff0c;而不用像Java中通过封装HTTP请求报文的方式直接调用。 Feign解决了什么问题 在服务调用的场景中&#xff0c;我们经常调用基于HTTP协议的服务&#xff0c;而我们经常使用到的框架可…

Feign是什么?

Feign能干什么&#xff1f; Feign旨在是编写Java Http客户端变得更加容易。 之前使用RibbonRestTemplate时&#xff0c;利用RESTTemplate请求的封装处理&#xff0c;形成了一套模板化的调用方法。但是在实际开发中&#xff0c;有偶遇对于服务依赖的调用可能不止一处&#xff…

Feign 与 OpenFeign

1. 什么是Feign Netflix Feign 是 Netflix 公司发布的一种实现负载均衡和服务调用的开源组件。Spring Cloud 将其与 Netflix 中的其他开源服务组件&#xff08;例如 Eureka、Ribbon 以及 Hystrix 等&#xff09;一起整合进 Spring Cloud Netflix 模块中&#xff0c;整合后全称为…

Feign的工作原理

文章目录 Feign的简单介绍Feign的工作原理1.创建远程接口的本地代理实例2.封装Request对象并进行编码3.feign.Client发送请求并对获取结果进行解码 总结 Feign的简单介绍 Feign组件主要用于微服务项目中&#xff0c;用来简化服务之间的远程调用&#xff0c;相信大家对他的使用…

OpenFeign和feign使用简介

1.OpenFeign简介 Feign是一个声明式的Web Service客户端。它的出现使开发Web Service客户端变得很简单。使用Feign只需要创建一个接口加上对应的注解&#xff0c;比如&#xff1a;FeignClient注解。Feign有可插拔的注解&#xff0c;包括Feign注解和JAX-RS注解。 Feign也支持编码…

Feign详解与实例

基本介绍 Feign是一种负载均衡的HTTP客户端, 使用Feign调用API就像调用本地方法一样&#xff0c;从避免了调用目标微服务时&#xff0c;需要不断的解析/封装json 数据的繁琐。Feign集成了Ribbon。Ribboneureka是面向微服务编程&#xff0c;而Feign是面向接口编程。 Fegin是一个…

FeignClient

FeignClient的调用 注意&#xff1a; 依赖可能会出现冲突&#xff0c;请根据spring-cloud版本选择并修改 1. 引入依赖&#xff1a; <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artif…

Feign的介绍

Feign是springcloud里面的一个功能组件&#xff0c;那么它是实现一个什么功能呢&#xff1f; 首先我们可以先从字面意思上去理解一下它&#xff0c;Feign&#xff0c;英文翻译过来就是伪装的意思&#xff0c;实际上它的功能也是和伪装相关的&#xff0c;在我们之前在客户端配置…

@FeignClient使用详解

FeignClient标签的常用属性如下&#xff1a; name&#xff1a;指定FeignClient的名称&#xff0c;如果项目使用了Ribbon&#xff0c;name属性会作为微服务的名称&#xff0c;用于服务发现url: url一般用于调试&#xff0c;可以手动指定FeignClient调用的地址decode404:当发生h…

Feign详解

一. Feign概述 Feign是Spring Cloud提供的声明式、模板化的HTTP客户端&#xff0c; 它使得调用远程服务就像调用本地服务一样简单&#xff0c;只需要创建一个接口并添加一个注解即可。 Spring Cloud集成Feign并对其进行了增强&#xff0c;使Feign支持了Spring MVC注解&#x…

Feign(简介和使用)

1. Feign介绍 通过RestTemplate调用其它服务的API时&#xff0c;所需要的参数须在请求的URL中进行拼接&#xff0c;如果参数少的话或许我们还可以忍受&#xff0c;一旦有多个参数的话&#xff0c;这时拼接请求字符串就会效率低下 Feign是一个声明式的Web Service客户端&#…

什么是Feign?

服务间调用介绍 现有的服务调用方式 利用拼接的方式。 虽然上面有的用不错就很好了 Feign解决了什么问题 Feign的调用方式 Feign体系架构解析-武装到牙齿 上一节我们了解了feign的主要功能&#xff0c;它就像一个自拍杆一样&#xff0c;方便了Eureka的远程调用。可是怎么看…

简单理解Feign的原理与使用

文章目录 SpringCloud 总架构图一、简介1.1、负载均衡的概念2.2、Feign概念 二、入门案例2.1、导入依赖2.2、Feign的客户端2.3、调用Feign2.4、开启Feign功能2.5、启动测试2.6、Feign实现原理简单分析 三、负载均衡(Ribbon)四、熔断器支持五、请求压缩和响应压缩六、配置日志级…