当前位置: 首页 > news >正文

EtherCAT主站SOEM -- 2 -- SOEM之ethercatbase.h/c文件解析

EtherCAT主站SOEM -- 2 -- SOEM之ethercatbase.h/c文件解析

  • 一 ethercatbase.h/c文件功能预览:
  • 二 ethercatbase.h/c 文件的主要函数的作用:
    • 2.1 ecx_writedatagramdata:
    • 2.2 ecx_setupdatagram:
    • 2.3 ecx_adddatagram:
    • 2.4 分布式时钟同步 ecx_LRWDC 函数:
    • 2.5 函数(例如 ecx_BWR、ecx_BRD、ecx_APRD、ecx_ARMW 等):
  • 三 ethercatbase.c 原文件的中文注释
  • 该文档修改记录:
  • 总结

一 ethercatbase.h/c文件功能预览:

函数名称功能和用途使用场景
ecx_setupdatagram设置EtherCAT数据报文的头部信息,包括通信类型、索引、ADP、ADO、长度和数据。用于配置EtherCAT数据报文的头部信息,以便进行数据通信。
ecx_adddatagram向EtherCAT数据报文中添加数据段,包括通信类型、索引、更多标志、ADP、ADO、长度和数据。适用于逐步构建EtherCAT数据报文,可以添加多个数据段。
ecx_BWR向EtherCAT从站写入数据,指定ADP、ADO、长度和数据。用于将数据写入EtherCAT从站的寄存器或数据区域。
ecx_BRD从EtherCAT从站读取数据,指定ADP、ADO、长度和数据。用于从EtherCAT从站的寄存器或数据区域读取数据。
ecx_APRD从EtherCAT从站异步读取数据,指定ADP、ADO、长度和数据。用于异步读取EtherCAT从站的寄存器或数据区域数据。
ecx_ARMW从EtherCAT从站异步写入数据,指定ADP、ADO、长度和数据。用于异步写入数据到EtherCAT从站的寄存器或数据区域。
ecx_FRMW向EtherCAT从站发送帧数据,指定ADP、ADO、长度和数据。用于发送帧数据到EtherCAT从站。
ecx_APRDw从EtherCAT从站异步读取字数据,指定ADP和ADO。用于异步读取EtherCAT从站的字数据。
ecx_FPRD从EtherCAT从站快速读取数据,指定ADP、ADO、长度和数据。用于快速读取EtherCAT从站的数据。
ecx_FPRDw从EtherCAT从站快速读取字数据,指定ADP和ADO。用于快速读取EtherCAT从站的字数据。
ecx_APWRw向EtherCAT从站异步写入字数据,指定ADP、ADO和数据。用于异步写入字数据到EtherCAT从站。
ecx_APWR向EtherCAT从站写入数据,指定ADP、ADO、长度和数据。用于向EtherCAT从站的寄存器或数据区域写入数据。
ecx_FPWRw向EtherCAT从站快速写入字数据,指定ADP、ADO和数据。用于快速写入字数据到EtherCAT从站。
ecx_FPWR向EtherCAT从站快速写入数据,指定ADP、ADO、长度和数据。用于快速写入数据到EtherCAT从站。
ecx_LRW从EtherCAT从站的逻辑地址中读取数据,指定逻辑地址、长度和数据。用于从EtherCAT从站的逻辑地址读取数据。
ecx_LRD从EtherCAT从站的逻辑地址中读取字数据,指定逻辑地址和数据。用于从EtherCAT从站的逻辑地址读取字数据。
ecx_LWR向EtherCAT从站的逻辑地址中写入数据,指定逻辑地址、长度和数据。用于向EtherCAT从站的逻辑地址写入数据。
ecx_LRWDC从EtherCAT从站的逻辑地址中读取数据,包括数据校验和时序信息,指定逻辑地址、长度、数据、数据校验标志和时间信息。用于从EtherCAT从站的逻辑地址读取数据,同时获取数据的校验和时序信息。
ec_setupdatagram(适用于EC_VER1) 设置EtherCAT数据报文的头部信息,包括通信类型、索引、ADP、ADO、长度和数据。(适用于EC_VER1) 用于配置EtherCAT数据报文的头部信息,以便进行数据通信。
ec_adddatagram(适用于EC_VER1) 向EtherCAT数据报文中添加数据段,包括通信类型、索引、更多标志、ADP、ADO、长度和数据。(适用于EC_VER1) 适用于逐步构建EtherCAT数据报文,可以添加多个数据段。
ec_BWR(适用于EC_VER1) 向EtherCAT从站写入数据,指定ADP、ADO、长度和数据。(适用于EC_VER1) 用于将数据写入EtherCAT从站的寄存器或数据区域。
ec_BRD(适用于EC_VER1) 从EtherCAT从站读取数据,指定ADP、ADO、长度和数据。(适用于EC_VER1) 用于从EtherCAT从站的寄存器或数据区域读取数据。
                                                                                                                                                                 | ...                                                                                                          

二 ethercatbase.h/c 文件的主要函数的作用:

ethercatbase.h 文件包含了一系列数据结构、常量和函数声明,用于实现 EtherCAT 主站的核心功能。以下是一些可能包含在 ethercatbase.h 文件中的内容:

  • 数据结构:定义了用于管理 EtherCAT 主站的数据结构,包括主站状态、从站配置、数据对象映射等。

  • 常量:包含了一些与 EtherCAT 相关的常量,如 EtherCAT 数据帧的大小、标志位等。

  • 函数声明:定义了用于初始化、配置和管理 EtherCAT 主站的函数接口。

ethercatbase.c 文件包含了 SOEM 库中实际实现 EtherCAT 主站功能的代码。它包括了与 EtherCAT 通信相关的底层功能,以及用于初始化和配置 EtherCAT 主站的代码。以下是一些 ethercatbase.c 文件中可能包含的功能:

  • 初始化 EtherCAT 主站:包括初始化数据结构、配置网络接口等。

  • 从站配置:配置网络中的 EtherCAT 从站,包括分配地址、数据对象映射等。

  • 数据帧处理:处理来自 EtherCAT 从站的数据帧,以实现数据交换。

  • 周期性通信:根据配置的周期时间,发送和接收 EtherCAT 数据帧。

  • 状态管理:管理主站的状态,以确保网络正常运行。

总之,ethercatbase.h 和 ethercatbase.c 文件是 SOEM 库的核心部分,用于实现 EtherCAT 主站的基本功能,包括初始化、配置、数据交换和状态管理。

2.1 ecx_writedatagramdata:

这个函数用于向 EtherCAT 数据报中写入数据。它会根据提供的命令(Command)、长度(Length)和数据(Data)来填充数据报的数据部分。这是在 EtherCAT 主站发送数据给从站的过程中使用的函数。

2.2 ecx_setupdatagram:

该函数用于生成并设置 EtherCAT 数据报,这些数据报将包含在标准以太网帧中。您可以通过此函数指定命令、索引、地址位置、地址偏移、长度和数据等信息,以准备 EtherCAT 数据报。这是为了创建要发送到 EtherCAT 从站的命令和数据。

2.3 ecx_adddatagram:

这个函数用于将 EtherCAT 数据报添加到现有的以太网帧中,使得多个数据报可以连续发送到不同的 EtherCAT 从站。它有助于组装一个完整的以太网帧,包括多个 EtherCAT 数据报。

2.4 分布式时钟同步 ecx_LRWDC 函数:

类似于 ecx_LRW,但它还包括分布式时钟同步的处理。它在同一帧中发送两个数据报,一个用于 LRW 操作,另一个用于时钟分布,从而实现与参考从站的同步和获取分布式时钟时间。

2.5 函数(例如 ecx_BWR、ecx_BRD、ecx_APRD、ecx_ARMW 等):

这些函数实现了不同的 EtherCAT 数据传输原语,允许主站与从站进行数据读取和写入。它们涉及广播命令和指定地址位置和偏移来进行数据交互。

总的来说,ethercatbase.c 中的函数提供了与 EtherCAT 从站进行通信所需的基本功能。这些函数允许 EtherCAT 主站配置数据报、发送命令和数据,以及处理从站的响应。SOEM 库的 ethercatbase.c 文件是构建自定义 EtherCAT 主站应用程序的重要组成部分。

三 ethercatbase.c 原文件的中文注释

/** 根据GNU通用公共许可证第2版(附有例外情况)授权。请参阅* 项目根目录中的LICENSE文件以获取完整的许可信息*//** \file* \brief* 基本EtherCAT功能。** 在以太网帧中设置数据报。* EtherCAT数据报原语,广播,自动增量,配置和* 逻辑寻址数据传输。所有基本传输都是阻塞的,因此* 等待帧被返回给主站或超时。如果这不可接受,可以构建您自己的数据报* 并使用nicdrv.c中的函数。*/#include <stdio.h>
#include <string.h>
#include "oshw.h"
#include "osal.h"
#include "ethercattype.h"
#include "ethercatbase.h"/** 将数据写入EtherCAT数据报。** @param[out] datagramdata   = 数据报的数据部分* @param[in]  com            = 命令* @param[in]  length         = 数据缓冲区的长度* @param[in]  data           = 要复制到数据报中的数据缓冲区*/
static void ecx_writedatagramdata(void *datagramdata, ec_cmdtype com, uint16 length, const void * data)
{if (length > 0){switch (com){case EC_CMD_NOP:/* 没有操作。 *//* 没有操作。初始化数据,以使帧处于已知状态。 */memset(datagramdata, 0, length);break;default:memcpy(datagramdata, data, length);break;}}
}/** 生成并设置标准以太网帧中的EtherCAT数据报。** @param[in] port        = 端口上下文结构* @param[out] frame       = 帧缓冲区* @param[in]  com         = 命令* @param[in]  idx         = 用于TX和RX缓冲区的索引* @param[in]  ADP         = 地址位置* @param[in]  ADO         = 地址偏移* @param[in]  length      = 数据报的长度,不包括EtherCAT头部* @param[in]  data        = 要复制到数据报中的数据缓冲区* @return 始终为0*/
int ecx_setupdatagram(ecx_portt *port, void *frame, uint8 com, uint8 idx, uint16 ADP, uint16 ADO, uint16 length, void *data)
{ec_comt *datagramP;uint8 *frameP;frameP = frame;/* 以太网头部已经在帧缓冲区中预设和固定需要在此之后添加EtherCAT头部 */datagramP = (ec_comt*)&frameP[ETH_HEADERSIZE];datagramP->elength = htoes(EC_ECATTYPE + EC_HEADERSIZE + length);datagramP->command = com;datagramP->index = idx;datagramP->ADP = htoes(ADP);datagramP->ADO = htoes(ADO);datagramP->dlength = htoes(length);ecx_writedatagramdata(&frameP[ETH_HEADERSIZE + EC_HEADERSIZE], com, length, data);/* 将WKC设置为零 */frameP[ETH_HEADERSIZE + EC_HEADERSIZE + length] = 0x00;frameP[ETH_HEADERSIZE + EC_HEADERSIZE + length + 1] = 0x00;/* 在缓冲区数组中设置帧的大小 */port->txbuflength[idx] = ETH_HEADERSIZE + EC_HEADERSIZE + EC_WKCSIZE + length;return 0;
}/** 将EtherCAT数据报添加到带有现有数据报的标准以太网帧中。** @param[in] port        = 端口上下文结构* @param[out] frame      = 帧缓冲区* @param[in] com        = 命令* @param[in] idx        = 用于TX和RX缓冲区的索引* @param[in] more       = 如果后续还有更多数据报,则为TRUE* @param[in] ADP        = 地址位置* @param[in] ADO        = 地址偏移* @param[in] length     = 数据报长度,不包括EtherCAT头* @param[in] data       = 要复制到数据报中的数据缓冲区* @return RX帧中数据的偏移,用于在RX后检索数据。*/
uint16 ecx_adddatagram(ecx_portt *port, void *frame, uint8 com, uint8 idx, boolean more, uint16 ADP, uint16 ADO, uint16 length, void *data)
{ec_comt *datagramP;uint8 *frameP;uint16 prevlength;frameP = frame;/* 复制以前的帧大小 */prevlength = (uint16)port->txbuflength[idx];datagramP = (ec_comt*)&frameP[ETH_HEADERSIZE];/* 将新数据报添加到以太网帧大小中 */datagramP->elength = htoes( etohs(datagramP->elength) + EC_HEADERSIZE + length );/* 将“数据报跟随”标志添加到以前子帧dlength */datagramP->dlength = htoes( etohs(datagramP->dlength) | EC_DATAGRAMFOLLOWS );/* 设置新的EtherCAT头位置 */datagramP = (ec_comt*)&frameP[prevlength - EC_ELENGTHSIZE];datagramP->command = com;datagramP->index = idx;datagramP->ADP = htoes(ADP);datagramP->ADO = htoes(ADO);if (more){/* 这不是要添加的最后一个数据报 */datagramP->dlength = htoes(length | EC_DATAGRAMFOLLOWS);}else{/* 这是帧中的最后一个数据报 */datagramP->dlength = htoes(length);}ecx_writedatagramdata(&frameP[prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE], com, length, data);/* 将WKC设置为零 */frameP[prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE + length] = 0x00;frameP[prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE + length + 1] = 0x00;/* 在缓冲区数组中设置帧的大小 */port->txbuflength[idx] = prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE + EC_WKCSIZE + length;/* 返回RX帧中数据的偏移由于以太网头的去除,比tx帧小14字节 */return prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE - ETH_HEADERSIZE;
}/** BRW "广播写入"原语。阻塞。** @param[in] port        = 端口上下文结构* @param[in] ADP         = 地址位置,通常为0* @param[in] ADO         = 地址偏移,从属于内存地址* @param[in] length      = 数据缓冲区的长度* @param[in] data        = 要写入从属的数据缓冲区* @param[in] timeout     = 超时时间(以微秒为单位),标准值为EC_TIMEOUTRET* @return 工作计数器或EC_NOFRAME*/
int ecx_BWR (ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{uint8 idx;int wkc;/* 获取新的索引 */idx = ecx_getindex (port);/* 设置数据报 */ecx_setupdatagram (port, &(port->txbuf[idx]), EC_CMD_BWR, idx, ADP, ADO, length, data);/* 发送数据并等待答复 */wkc = ecx_srconfirm (port, idx, timeout);/* 清除缓冲区状态 */ecx_setbufstat (port, idx, EC_BUF_EMPTY);return wkc;
}/** BRD "广播读取"原语。阻塞。** @param[in] port        = 端口上下文结构* @param[in]  ADP        = 地址位置,通常为0* @param[in]  ADO        = 地址偏移,从属的内存地址* @param[in]  length     = 数据缓冲区的长度* @param[out] data       = 用于存放从属数据的数据缓冲区* @param[in]  timeout    = 超时时间(以微秒为单位),标准值为EC_TIMEOUTRET* @return 工作计数器或EC_NOFRAME*/
int ecx_BRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{uint8 idx;int wkc;/* 获取新的索引 */idx = ecx_getindex(port);/* 设置数据报 */ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_BRD, idx, ADP, ADO, length, data);/* 发送数据并等待答复 */wkc = ecx_srconfirm (port, idx, timeout);if (wkc > 0){/* 复制数据报到数据缓冲区 */memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);}/* 清除缓冲区状态 */ecx_setbufstat(port, idx, EC_BUF_EMPTY);return wkc;
}/** APRD "自动增量地址读取"原语。阻塞。** @param[in] port        = 端口上下文结构* @param[in]  ADP        = 地址位置,每个从属++,具有0的从属执行* @param[in]  ADO        = 地址偏移,从属内存地址* @param[in]  length     = 数据缓冲区的长度* @param[out] data       = 用于存放从属数据的数据缓冲区* @param[in]  timeout    = 超时时间(以微秒为单位),标准值为EC_TIMEOUTRET* @return 工作计数器或EC_NOFRAME*/
int ecx_APRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{int wkc;uint8 idx;idx = ecx_getindex(port);ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_APRD, idx, ADP, ADO, length, data);wkc = ecx_srconfirm(port, idx, timeout);if (wkc > 0){memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);}ecx_setbufstat(port, idx, EC_BUF_EMPTY);return wkc;
}/** APRMW "自动增量地址读取,多次写入"原语。阻塞。** @param[in] port        = 端口上下文结构* @param[in]  ADP        = 地址位置,每个从属++,具有0的从属执行读取,随后的从属执行写入。* @param[in]  ADO        = 地址偏移,从属内存地址* @param[in]  length     = 数据缓冲区的长度* @param[out] data       = 用于存放从属数据的数据缓冲区* @param[in]  timeout    = 超时时间(以微秒为单位),标准值为EC_TIMEOUTRET* @return 工作计数器或EC_NOFRAME*/
int ecx_ARMW(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{int wkc;uint8 idx;idx = ecx_getindex(port);ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_ARMW, idx, ADP, ADO, length, data);wkc = ecx_srconfirm(port, idx, timeout);if (wkc > 0){memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);}ecx_setbufstat(port, idx, EC_BUF_EMPTY);return wkc;
}/** FPRMW "配置地址读取,多次写入"原语。阻塞。** @param[in] port        = 端口上下文结构* @param[in]  ADP        = 地址位置,具有地址的从属执行读取,随后的从属执行写入。* @param[in]  ADO        = 地址偏移,从属内存地址* @param[in]  length     = 数据缓冲区的长度* @param[out] data       = 用于存放从属数据的数据缓冲区* @param[in]  timeout    = 超时时间(以微秒为单位),标准值为EC_TIMEOUTRET* @return 工作计数器或EC_NOFRAME*/
int ecx_FRMW(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{int wkc;uint8 idx;idx = ecx_getindex(port);ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_FRMW, idx, ADP, ADO, length, data);wkc = ecx_srconfirm(port, idx, timeout);if (wkc > 0){memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);}ecx_setbufstat(port, idx, EC_BUF_EMPTY);return wkc;
}/** APRDw "自动增量地址读取" 字返回原语。阻塞。** @param[in] port        = 端口上下文结构* @param[in] ADP         = 地址位置,每个从属++,具有0的从属执行读取。* @param[in] ADO         = 地址偏移,从属内存地址* @param[in] timeout     = 超时时间(以微秒为单位),标准值为EC_TIMEOUTRET* @return 从从属获取的字数据*/
uint16 ecx_APRDw(ecx_portt *port, uint16 ADP, uint16 ADO, int timeout)
{uint16 w;w = 0;ecx_APRD(port, ADP, ADO, sizeof(w), &w, timeout);return w;
}/** FPRD "配置地址读取"原语。阻塞。** @param[in] port        = 端口上下文结构* @param[in]  ADP        = 地址位置,具有地址的从属执行读取。* @param[in]  ADO        = 地址偏移,从属内存地址* @param[in]  length     = 数据缓冲区的长度* @param[out] data       = 用于存放从属数据的数据缓冲区* @param[in]  timeout    = 超时时间(以微秒为单位),标准值为EC_TIMEOUTRET* @return 工作计数器或EC_NOFRAME*/
int ecx_FPRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{int wkc;uint8 idx;idx = ecx_getindex(port);ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_FPRD, idx, ADP, ADO, length, data);wkc = ecx_srconfirm(port, idx, timeout);if (wkc > 0){memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);}ecx_setbufstat(port, idx, EC_BUF_EMPTY);return wkc;
}/** FPRDw "配置地址读取" 字返回原语。阻塞。** @param[in] port        = 端口上下文结构* @param[in] ADP         = 地址位置,具有地址的从属执行读取。* @param[in] ADO         = 地址偏移,从属内存地址* @param[in] timeout     = 超时时间(以微秒为单位),标准值为EC_TIMEOUTRET* @return 从从属获取的字数据*/
uint16 ecx_FPRDw(ecx_portt *port, uint16 ADP, uint16 ADO, int timeout)
{uint16 w;w = 0;ecx_FPRD(port, ADP, ADO, sizeof(w), &w, timeout);return w;
}/** APWR "自动增量地址写入"原语。阻塞。** @param[in] port        = 端口上下文结构* @param[in] ADP         = 地址位置,每个从属++,具有0的从属执行写入。* @param[in] ADO         = 地址偏移,从属内存地址* @param[in] length      = 数据缓冲区的长度* @param[in] data        = 要写入从属的数据缓冲区* @param[in] timeout     = 超时时间(以微秒为单位),标准值为EC_TIMEOUTRET* @return 工作计数器或EC_NOFRAME*/
int ecx_APWR(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{uint8 idx;int wkc;idx = ecx_getindex(port);ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_APWR, idx, ADP, ADO, length, data);wkc = ecx_srconfirm(port, idx, timeout);ecx_setbufstat(port, idx, EC_BUF_EMPTY);return wkc;
}/** APWRw "自动增量地址写入"字原语。阻塞。** @param[in] port        = 端口上下文结构* @param[in] ADP         = 地址位置,每个从属++,具有0的从属执行写入。* @param[in] ADO         = 地址偏移,从属内存地址* @param[in] data        = 要写入从属的字数据* @param[in] timeout     = 超时时间(以微秒为单位),标准值为EC_TIMEOUTRET* @return 工作计数器或EC_NOFRAME*/
int ecx_APWRw(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 data, int timeout)
{return ecx_APWR(port, ADP, ADO, sizeof(data), &data, timeout);
}/** FPWR "配置地址写入"原语。阻塞。** @param[in] port        = 端口上下文结构* @param[in] ADP         = 地址位置,具有地址的从属执行写入。* @param[in] ADO         = 地址偏移,从属内存地址* @param[in] length      = 数据缓冲区的长度* @param[in] data        = 要写入从属的数据缓冲区* @param[in] timeout     = 超时时间(以微秒为单位),标准值为EC_TIMEOUTRET* @return 工作计数器或EC_NOFRAME*/
int ecx_FPWR(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{int wkc;uint8 idx;idx = ecx_getindex(port);ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_FPWR, idx, ADP, ADO, length, data);wkc = ecx_srconfirm(port, idx, timeout);ecx_setbufstat(port, idx, EC_BUF_EMPTY);return wkc;
}/** FPWR "配置地址写入"原语。阻塞。** @param[in] port        = 端口上下文结构* @param[in] ADP         = 地址位置,具有地址的从属执行写入。* @param[in] ADO         = 地址偏移,从属内存地址* @param[in] data        = 要写入从属的字数据* @param[in] timeout     = 超时时间(以微秒为单位),标准值为EC_TIMEOUTRET* @return 工作计数器或EC_NOFRAME*/
int ecx_FPWRw(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 data, int timeout)
{return ecx_FPWR(port, ADP, ADO, sizeof(data), &data, timeout);
}/** LRW "逻辑内存读/写"原语。阻塞。** @param[in] port        = 端口上下文结构* @param[in]     LogAdr  = 逻辑内存地址* @param[in]     length  = 数据缓冲区的长度* @param[in,out] data    = 用于写入和从从属读取数据的数据缓冲区* @param[in]     timeout = 超时时间(以微秒为单位),标准值为EC_TIMEOUTRET* @return 工作计数器或EC_NOFRAME*/
int ecx_LRW(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout)
{uint8 idx;int wkc;idx = ecx_getindex(port);ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_LRW, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);wkc = ecx_srconfirm(port, idx, timeout);if ((wkc > 0) && (port->rxbuf[idx][EC_CMDOFFSET] == EC_CMD_LRW)){memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);}ecx_setbufstat(port, idx, EC_BUF_EMPTY);return wkc;
}/** LRD "逻辑内存读取"原语。阻塞。** @param[in] port        = 端口上下文结构* @param[in]  LogAdr     = 逻辑内存地址* @param[in]  length     = 从从属读取的字节数* @param[out] data       = 从从属读取的数据的数据缓冲区* @param[in]  timeout    = 超时时间(以微秒为单位),标准值为EC_TIMEOUTRET* @return 工作计数器或EC_NOFRAME*/
int ecx_LRD(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout)
{uint8 idx;int wkc;idx = ecx_getindex(port);ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_LRD, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);wkc = ecx_srconfirm(port, idx, timeout);if ((wkc > 0) && (port->rxbuf[idx][EC_CMDOFFSET] == EC_CMD_LRD)){memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);}ecx_setbufstat(port, idx, EC_BUF_EMPTY);return wkc;
}/** LWR "逻辑内存写入"原语。阻塞。** @param[in] port        = 端口上下文结构* @param[in] LogAdr      = 逻辑内存地址* @param[in] length      = 数据缓冲区的长度* @param[in] data        = 要写入从属的数据缓冲区* @param[in] timeout     = 超时时间(以微秒为单位),标准值为EC_TIMEOUTRET* @return 工作计数器或EC_NOFRAME*/
int ecx_LWR(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout)
{uint8 idx;int wkc;idx = ecx_getindex(port);ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_LWR, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);wkc = ecx_srconfirm(port, idx, timeout);ecx_setbufstat(port, idx, EC_BUF_EMPTY);return wkc;
}/** LRW "逻辑内存读/写"原语加时钟分发。阻塞。* 帧由两个数据报组成,一个是LRW,一个是FPRMW。** @param[in] port        = 端口上下文结构* @param[in]     LogAdr  = 逻辑内存地址* @param[in]     length  = 数据缓冲区的长度* @param[in,out] data    = 用于写入和从从属读取数据的数据缓冲区* @param[in]     DCrs    = 分布式时钟参考从属地址* @param[out]    DCtime  = 从参考从属读取的DC时间* @param[in]     timeout = 超时时间(以微秒为单位),标准值为EC_TIMEOUTRET* @return 工作计数器或EC_NOFRAME*/
int ecx_LRWDC(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtime, int timeout)
{uint16 DCtO;uint8 idx;int wkc;uint64 DCtE;idx = ecx_getindex(port);/* 第一个数据报是LRW */ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_LRW, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);/* 第二个数据报是FPRMW */DCtE = htoell(*DCtime);DCtO = ecx_adddatagram(port, &(port->txbuf[idx]), EC_CMD_FRMW, idx, FALSE, DCrs, ECT_REG_DCSYSTIME, sizeof(DCtime), &DCtE);wkc = ecx_srconfirm(port, idx, timeout);if ((wkc > 0) && (port->rxbuf[idx][EC_CMDOFFSET] == EC_CMD_LRW)){memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length);memcpy(&wkc, &(port->rxbuf[idx][EC_HEADERSIZE + length]), EC_WKCSIZE);memcpy(&DCtE, &(port->rxbuf[idx][DCtO]), sizeof(*DCtime));*DCtime = etohll(DCtE);}ecx_setbufstat(port, idx, EC_BUF_EMPTY);return wkc;
}#ifdef EC_VER1
int ec_setupdatagram(void *frame, uint8 com, uint8 idx, uint16 ADP, uint16 ADO, uint16 length, void *data)
{return ecx_setupdatagram (&ecx_port, frame, com, idx, ADP, ADO, length, data);
}uint16 ec_adddatagram (void *frame, uint8 com, uint8 idx, boolean more, uint16 ADP, uint16 ADO, uint16 length, void *data)
{return ecx_adddatagram (&ecx_port, frame, com, idx, more, ADP, ADO, length, data);
}int ec_BWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{return ecx_BWR (&ecx_port, ADP, ADO, length, data, timeout);
}int ec_BRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{return ecx_BRD(&ecx_port, ADP, ADO, length, data, timeout);
}int ec_APRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{return ecx_APRD(&ecx_port, ADP, ADO, length, data, timeout);
}int ec_ARMW(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{return ecx_ARMW(&ecx_port, ADP, ADO, length, data, timeout);
}int ec_FRMW(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{return ecx_FRMW(&ecx_port, ADP, ADO, length, data, timeout);
}uint16 ec_APRDw(uint16 ADP, uint16 ADO, int timeout)
{uint16 w;w = 0;ec_APRD(ADP, ADO, sizeof(w), &w, timeout);return w;
}int ec_FPRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{return ecx_FPRD(&ecx_port, ADP, ADO, length, data, timeout);
}uint16 ec_FPRDw(uint16 ADP, uint16 ADO, int timeout)
{uint16 w;w = 0;ec_FPRD(ADP, ADO, sizeof(w), &w, timeout);return w;
}int ec_APWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{return ecx_APWR(&ecx_port, ADP, ADO, length, data, timeout);
}int ec_APWRw(uint16 ADP, uint16 ADO, uint16 data, int timeout)
{return ec_APWR(ADP, ADO, sizeof(data), &data, timeout);
}int ec_FPWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout)
{return ecx_FPWR(&ecx_port, ADP, ADO, length, data, timeout);
}int ec_FPWRw(uint16 ADP, uint16 ADO, uint16 data, int timeout)
{return ec_FPWR(ADP, ADO, sizeof(data), &data, timeout);
}int ec_LRW(uint32 LogAdr, uint16 length, void *data, int timeout)
{return ecx_LRW(&ecx_port, LogAdr, length, data, timeout);
}int ec_LRD(uint32 LogAdr, uint16 length, void *data, int timeout)
{return ecx_LRD(&ecx_port, LogAdr, length, data, timeout);
}int ec_LWR(uint32 LogAdr, uint16 length, void *data, int timeout)
{return ecx_LWR(&ecx_port, LogAdr, length, data, timeout);
}int ec_LRWDC(uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtime, int timeout)
{return ecx_LRWDC(&ecx_port, LogAdr, length, data, DCrs, DCtime, timeout);
}
#endif

该文档修改记录:

修改时间修改说明
2023年11月3日EtherCAT主站SOEM – 2 – SOEM之ethercatbase.h/c文件解析

总结

以上就是EtherCAT主站SOEM – 2 – SOEM之ethercatbase.h/c文件解析的内容。
有不明白的地方欢迎留言;有建议欢迎留言,我后面编写文档好改进。
创作不容,如果文档对您有帮助,记得给个赞。

相关文章:

EtherCAT主站SOEM -- 2 -- SOEM之ethercatbase.h/c文件解析

EtherCAT主站SOEM -- 2 -- SOEM之ethercatbase.h/c文件解析 一 ethercatbase.h/c文件功能预览&#xff1a;二 ethercatbase.h/c 文件的主要函数的作用&#xff1a;2.1 ecx_writedatagramdata&#xff1a;2.2 ecx_setupdatagram&#xff1a;2.3 ecx_adddatagram&#xff1a;2.4 …...

Spring集成高性能队列Disruptor

Disruptor简介 Disruptor&#xff08;中文翻译为“破坏者”或“颠覆者”&#xff09;是一种高性能、低延迟的并发编程框架&#xff0c;最初由LMAX Exchange开发。它的主要目标是解决在金融交易系统等需要高吞吐量和低延迟的应用中的并发问题。 Disruptor特点 无锁并发&#x…...

C++——类和对象(中)完结

赋值运算符重载 运算符重载 C 为了增强代码的可读性引入了运算符重载 &#xff0c; 运算符重载是具有特殊函数名的函数 &#xff0c;也具有其 返回值类型&#xff0c;函数名字以及参数列表&#xff0c;其返回值类型与参数列表与普通的函数类似。 函数名字为&#xff1a;关键…...

Sqoop的安装和使用

目录 一.安装 二.导入 1.全量导入 一.MySQL导入HDFS 二.MySQL导入Hive 2.增量导入 一.过滤导入hdfs/hive 二.导出 一.安装 1.下载地址&#xff1a;sqoop下载地址 2.解压 tar -zxvf ./sqoop-1.4.7.bin__hadoop-2.6.0.tar.gz -C ../module/ 3.改名和配置归属权限 #改名…...

java毕业设计基于springboot+vue的村委会管理系统

项目介绍 采用JAVA语言&#xff0c;结合SpringBoot框架与Vue框架以及MYSQL数据库设计并实现的。本村委会管理系统主要包括个人中心、村民管理、村委会管理、村民信息管理、土地变更管理、农业补贴管理、党员信息管理等多个模块。它帮助村委会管理实现了信息化、网络化&#xf…...

【C++】多态 ⑪ ( 纯虚函数和抽象类 | 纯虚函数语法 | 抽象类和实现 | 代码示例 )

文章目录 一、纯虚函数和抽象类1、纯虚函数2、纯虚函数语法3、抽象类和实现 二、完整代码示例 一、纯虚函数和抽象类 1、纯虚函数 纯虚函数 : 在 C 语言中 , " 纯虚函数 " 是 特殊类型的 虚函数 , " 纯虚函数 " 在 父类 中 声明 , 但是没有实现 ; 抽象类 …...

node 第十四天 基于express的第三方中间件multer node后端处理用户上传文件

Multer 是一个 node.js 中间件&#xff0c;用于处理multipart/form-data 类型的表单数据&#xff0c;它主要用于上传文件。它是写在 busboy 之上的所以非常高效。前面我们已经知道了怎样利用express提供的静态资源处理中间件express.static()处理用户请求静态资源文件(图片, js…...

KnowledgeGPT:利用检索和存储访问知识库上增强大型语言模型10.30

利用检索和存储访问知识库上增强大型语言模型 摘要引言2 相关研究3方法3.1 任务定义3.2 知识检索3.2.1 代码实现3.2.2 实体链接3.2.3 获取实体信息3.2.4 查找实体或值3.2.5 查找关系 3.3 知识存储 4 实验4.1 实验设置4.2 流行知识库上的查询4.3 基于知识的问题回答 摘要 大型语…...

Angular material Chips Autocomplete

Chips Autocomplete 官网的例子我没法正常使用&#xff0c;无法实现搜索 我的select是个通用组件&#xff0c;现在贴代码&#xff1a; component.ts import {Component,ElementRef,forwardRef,Input,OnChanges,OnDestroy,OnInit,SimpleChanges,ViewChild, } from angular/co…...

『亚马逊云科技产品测评』活动征文|搭建基础运维环境

授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 Developer Centre, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 目录 1、什么是容器化部署 2、连接到控制台 3、安装docker 3.1 更新…...

双指针扫描

import os import sys# 请在此输入您的代码 sinput() Alist(s) nlen(A) t1 for i in range(n//2):if A[i]!A[n-1-i]:t0break if t1:print(Y) else:print(N)n,smap(int,input().split()) alist(map(int,input().split())) #尺取法&#xff0c;变O(n*n)为O(n) #维护一个最短的区间…...

uniapp小程序九宫格抽奖

定义好奖品下标&#xff0c;计时器开始抽奖&#xff0c;请求接口&#xff0c;出现中奖奖品之后&#xff0c;获取中奖商品对应的奖品下标&#xff0c;再次计时器判断当前移动的小标是否为中奖商品的下标&#xff0c;并且是否转到3圈&#xff08;防止转1圈就停止&#xff09;&…...

mysql树状结构查询及注意事项

一、说明 由于Mysql不像oracle一样支持树状查询&#xff0c;需要用户自行处理&#xff0c;本文记录了一种常见的通过自定义函数的方式进行mysql树状查询的方法&#xff0c;以及使用的注意事项。 二、函数 CREATE DEFINERrootlocalhost FUNCTION get_child_menus(in_pid varc…...

TimeGPT-1——第一个时间序列数据领域的大模型他来了

一直有一个问题:时间序列的基础模型能像自然语言处理那样存在吗?一个预先训练了大量时间序列数据的大型模型&#xff0c;是否有可能在未见过的数据上产生准确的预测?最近刚刚发表的一篇论文&#xff0c;Azul Garza和Max Mergenthaler-Canseco提出的TimeGPT-1&#xff0c;将ll…...

通过Google搜索广告传送的携带木马的PyCharm软件版本

导语 最近&#xff0c;一起新的恶意广告活动被发现&#xff0c;利用被入侵的网站通过Google搜索结果推广虚假版本的PyCharm软件。这个活动利用了动态搜索广告&#xff0c;将广告链接指向被黑客篡改的网页&#xff0c;用户点击链接后下载的并不是PyCharm软件&#xff0c;而是多种…...

网站文章收录因素,别人复制文章排名比你原创的好?

我经常看到有站长抱怨“网站不收录”&#xff0c;“排名不好”&#xff0c;“复制的文章为什么秒收”之类的问题。对于SEO从业者来说&#xff0c;这确实是一个打击&#xff0c;认为搜索引擎不公平。凭什么自己原创不收录&#xff0c;别人复制去了&#xff0c;秒收他的&#xff…...

C#开源的一个能利用Windows通知栏背单词的软件 - ToastFish

前言 今天给大家推荐一个C#开源且免费的能利用Windows通知栏背单词的软件&#xff0c;可以让你在上班、上课等恶劣环境下安全隐蔽地背单词&#xff08;利用摸鱼时间背单词的软件&#xff09;&#xff1a;ToastFish。 操作系统要求 目前该软件只支持Windows10及以上系统&…...

速拿offer,超全自动化测试面试题+答案汇总,背完还怕拿不到offer?

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、你会封装自动化…...

LeetCode----1415. 长度为 n 的开心字符串中字典序第 k 小的字符串

题目 一个 「开心字符串」定义为: 仅包含小写字母 [‘a’, ‘b’, ‘c’].对所有在 1 到 s.length - 1 之间的 i ,满足 s[i] != s[i + 1] (字符串的下标从 1 开始)。比方说,字符串 “abc”,“ac”,“b” 和 “abcbabcbcb” 都是开心字符串,但是 “aa”,“baa” 和 “a…...

2310C++协程超传服务器

原文 告别异步回调模型,写代码更简单.同样也是跨平台,仅头文件的,包含头文件即可用,来看看它的用法. 基本用法 提供getpost服务 coro_http_server server(1, 9001);server.set_http_handler<GET, POST>("/", [](coro_http_request &req, coro_http_respo…...

【排序算法】 计数排序(非比较排序)详解!了解哈希思想!

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; 算法—排序篇 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言&#x1f324;️计数排序的概念☁️什么是计数排序&#xff1f;☁️计数排序思想⭐绝对…...

20231103配置cv180zb的编译环境【填坑篇】

20231103配置cv180zb的编译环境【填坑篇】 2023/11/3 11:36 感谢您选择了晶视科技的cv180zb&#xff0c;让我们一起来填坑。 在你根据文档找不到答案的时候&#xff0c;是不是想把他们家那个写文档的家伙打一顿&#xff0c;我顶你。 当你在在网上找一圈&#xff0c;BAIDU/BING/…...

足底筋膜炎如何治疗

足底筋膜炎主要表现为下床站立后或休息后再次走路时&#xff0c;出现足跟部的疼痛与不适症状&#xff0c;活动后可自行缓解&#xff0c;但走路时间长或较剧烈活动后&#xff0c;疼痛会再次加重&#xff0c;甚至有针扎样疼痛感向脚前部发散&#xff0c;影响患者的日常生活。 足…...

rabbitMq路由键介绍

rabbitTemplate.convertAndSend() 是 Spring AMQP 中用于发送消息到 RabbitMQ 的方法。下面是对您提供的代码示例的解释&#xff1a; rabbitTemplate.convertAndSend("ums-platform.ex", "ums.report.routing", param);这行代码主要完成以下几个操作&…...

【python基础】python切片—如何理解[-1:],[:-1],[::-1]的用法

文章目录 前言一、基本语法二、切片1.a[i:j]2.a[i:j:k] 总结&#xff1a;[-1] [:-1] [::-1] [n::-1] 前言 在python中&#xff0c;序列是python最基本的数据结构&#xff0c;包括有string&#xff0c;list&#xff0c;tuple等数据类型&#xff0c;切片对序列型对象的一种索引方…...

剑指JUC原理-9.Java无锁模型

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码&#x1f525;如果感觉博主的文章还不错的话&#xff0c;请&#x1f44d;三连支持&…...

汽车托运使用的场景

在托运车辆时&#xff0c;要仔细的检查车辆的性能&#xff0c;比如电瓶电量是否充足&#xff0c;发动机的性能是否良好&#xff0c;轮胎是否是正常的气压&#xff0c;冬季时需使用防冻液&#xff0c;车内禁止放易燃易爆物品。 托运时还需选择一家好的托运公司&#xff0c;首先要…...

机器学习 - 加油站数据分析

一、实验数据 数据集&#xff1a;“加油站数据.xls” 数据集介绍&#xff1a;该表记录了用户在11月和12月一天24小时内的加油信息&#xff0c;包括&#xff1a;持卡人标识&#xff08;cardholder&#xff09;、卡号&#xff08;cardno&#xff09;、加油站网点号&#xff08;n…...

基于CMFB余弦调制滤波器组的频谱响应matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1、CMFB余弦调制滤波器组原理 4.2、CMFB调制过程 4.3、CMFB特点 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ......................…...

helm一键部署grafana

一键部署命令 helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update helm install prometheus prometheus-community/kube-prometheus-stack暴露服务 kubectl port-forward --address 0.0.0.0 deployment/prometheus-gr…...