ZLG致远电子的PCIe EtherCAT通讯卡该如何使用?PDO过程数据该如何操作?具体编程又该如何实现?续接上一章节,本文将为您详细讲解。
EtherCAT工业总线技术在工业自动化领域展现出了广泛的应用价值,特别是在运动控制、机器人技术和测量技术等方面。ZLG致远电子 PCIe EtherCAT通讯卡 基于自主知识产权的系统之上开发,实现了软硬件间的无缝连接,极大地提升了系统的稳定性、可靠性以及安全性。同时,该通讯卡还支持线路冗余以及热插拔功能,可轻松实现多轴同步控制和数据的高速传输。此外,ZLG致远电子PCIe EtherCAT通讯卡还为用户提供了便捷的二次开发库,支持VC、C#、Linux、Python等各类主流开发环境,满足客户不同层次的开发需求。值得一提的是,ZLG致远电子PCIe EtherCAT通讯卡通过将商业级EtherCAT主站协议和实时内核相结合的方式,有效释放主机资源,完美解决传统EtherCAT主站在非实时操作系统下运行所带来的各类问题,为用户带来了更加高效、稳定的解决方案。图1 PCIe EtherCAT通讯卡表1 PCIe EtherCAT通讯卡型号
基于上一章《使用PCIe EtherCAT通讯卡控制IO从站step by step(一)》中所讲诉的内容,我们已经完成了开发环境的搭建,以及主函数的建立,接下来,我们将会进一步完善主函数中的代码,实现对从站的PDO数据读写。
1. 代码编写
打开ecat_api_io_test.cpp文件。根据AWStudio软件导出的eni文件定义过程数据的结构体,打开eni文件,移动光标到文件尾部,找到注释的节点ENI_PDO_LIST。图2 ENI文件(1)图3 ENI文件(2)节点中的inputs为从站返回的PDO过程数据,outputs为主站发送到从站的PDO过程数据,根据每个变量的位宽bit_size,我们可以定义对应的类型。
根据图2的信息,我们可以看出输出有三个变量,DI_1,AI_1,AI_2,长度分别为8位,8位,16位,输入有三个变量DO_1,AO_1,AO_2,长度分别位8位,8位,16位,定义结构体:
//过程数据,写入从站的数据格式typedef struct{ uint8_t DI_1; uint8_t AI_1; uint16_t AI_2;}PDO_OUTPUTS_T;
//过程数据,从站返回的数据格式typedef struct{ uint8_t DO_1; uint8_t AO_1; uint16_t AO_2;}PDO_INPUTS_T;
定义完过程数据的结构体后。
第一步,输入需要控制的pcie卡别名以及通道号,获取Ecat控制句柄。
EXIT_IF_FAIL(EcatOpen(&hHandle, BOARD_ALIAS(buff, alias), channel));
第二步,输入上位机程序导出的eni文件,启动Ecat主站。
EXIT_IF_FAIL(EcatBusRun(hHandle, fileName));
第三步,将Ecat主站状态切换为8(Operational)。
EXIT_IF_FAIL(EcatRequestMasterState(hHandle, EcatStateO));
等待主站切换状态。
uint8_t query = EcatStateNotSet;do{ EXIT_IF_FAIL(EcatGetMasterState(hHandle, &query)); //输出当前状态 _DBG_("request_state=%d, query_state=%d", EcatStateO, query); if (query == EcatStateO){ break;; } std::sleep_for(std::seconds(1));}while(1);
第四步,定义PDO过程数据的指针并指向本地缓存空间,这一步将会让我们更加方便快捷地读写PDO数据。
图4 PDO数据的收发原理执行EcatPINMap函数,将会修改第3个参数inputBuff,outputBuff的指向,让其直接指向本地输入输出缓存区,固不需要再额外申请空间。
PDO_OUTPUTS_T *outputBuff;PDO_INPUTS_T *inputBuff;//将指针inputBuff,outputBuff分别指向本地缓存的空间EXIT_IF_FAIL(EcatPINMap(hHandle, PI_AREA_LOCAL_INPUT, (void**)&inputBuff));EXIT_IF_FAIL(EcatPINMap(hHandle, PI_AREA_LOCAL_OUTPUT, (void**)&outputBuff));
第五步,向发送队列中添加空数据,添加空数据的数量取决于PC系统抖动的程度,抖动越小,添加的空数据越少,控制指令的滞后性越小。
for(auto i = 0; i < 2; i++){ EcatPIOutputQueuePush(hHandle, false, 100);}
第六步,使能过程数据PDO通信。
EXIT_IF_FAIL(EcatPIEnable(hHandle));
第七步,通过EcatPIInputQueuePop接口,等待从站数据返回,然后读写PDO过程数据,最后调用EcatPIOutputQueuePush接口将数据添加到发送队列。当前例子中,在收到从站返回的数据后,主站会将收到的输入数据写到输出的变量。
bool loopFlag = true;while(loopFlag){ //阻塞式等待PDO数据 if (!EcatPIInputQueuePop(hHandle, false, 100)){ /*********************************************************/ //修改过程数据 printf("0x%02x, 0x%02x, 0x%04x\r\n", inputBuff->DO_1, inputBuff->AO_1, inputBuff->AO_2); outputBuff->DI_1 = inputBuff->DO_1; outputBuff->AI_1 = inputBuff->AO_1; outputBuff->AI_2 = inputBuff->AO_2; /*********************************************************/ //将数据添加到PDO的发送队列中 if (EcatPIOutputQueuePush(hHandle, false, 100)){ _ERR_("PI push error."); break; } }}
第八步,释放句柄。
EXIT_IF_FAIL(EcatClose(hHandle));
完整的ecat_api_io_test.cpp文件。
#include #include //用于sleep#include //用于sleep#include "pci_errno.h"#include "pci_zecm.h"#include "pci_dbg.h"
//过程数据,写入从站的数据格式typedef struct{ uint8_t DI_1; uint8_t AI_1; uint16_t AI_2;}PDO_OUTPUTS_T;
//过程数据,从站返回的数据格式typedef struct{ uint8_t DO_1; uint8_t AO_1; uint16_t AO_2}PDO_INPUTS_T;
int32_t testDemo(int alias, int channel, const char* fileName){ int32_t result = 0; char buff[256]; ECAT_HANDLE hHandle; //初始化hHandle句柄 EXIT_IF_FAIL(EcatOpen(&hHandle, BOARD_ALIAS(buff, alias), channel)); //启动主站 EXIT_IF_FAIL(EcatBusRun(hHandle, fileName)); //将状态切换为8(Operational) EXIT_IF_FAIL(EcatRequestMasterState(hHandle, EcatStateO));
//等待主站切换状态 uint8_t query = EcatStateNotSet; do{ EXIT_IF_FAIL(EcatGetMasterState(hHandle, &query)); //输出当前状态 _DBG_("request_state=%d, query_state=%d", EcatStateO, query); if (query == EcatStateO){ break;; } std::sleep_for(std::seconds(1)); }while(1);
PDO_OUTPUTS_T *outputBuff; PDO_INPUTS_T *inputBuff; //将指针inputBuff,outputBuff分别指向本地缓存的空间 EXIT_IF_FAIL(EcatPINMap(hHandle, PI_AREA_LOCAL_INPUT, (void**)&inputBuff)); EXIT_IF_FAIL(EcatPINMap(hHandle, PI_AREA_LOCAL_OUTPUT, (void**)&outputBuff));
//向发送队列中添加空数据,添加空数据的数量取决于PC系统抖动的程度,抖动越小,添加的空数据越少,控制指令的滞后性越小 for(auto i = 0; i < 2; i++){ EcatPIOutputQueuePush(hHandle, false, 100); } //使能过程数据PDO通信 EXIT_IF_FAIL(EcatPIEnable(hHandle));
bool loopFlag = true; while(loopFlag){ //阻塞式等待PDO数据 if (!EcatPIInputQueuePop(hHandle, false, 100)){ /*********************************************************/ //修改过程数据 printf("0x%02x, 0x%02x, 0x%04x\r\n", inputBuff->DO_1,inputBuff->AO_1, inputBuff->AO_2); outputBuff->DI_1 = inputBuff->DO_1; outputBuff->AI_1 = inputBuff->AO_1; outputBuff->AI_2 = inputBuff->AO_2; /*********************************************************/ //将数据添加到PDO的发送队列中 if (EcatPIOutputQueuePush(hHandle, false, 100)){ _ERR_("PI push error."); break; } } }
//释放句柄 EXIT_IF_FAIL(EcatClose(hHandle)); return result;}
int main(int argc, char* argv[]){ ECAT_HANDLE hHandle; char buff[256]; uint32_t channel = 0, alias = 0; std::string eniFile; if (argc != 4){ std::cout << "usage: " << argv[0] << " encoder_id channel eni.xml" << std::endl; return 1; } alias = atoi(argv[1]); channel = atoi(argv[2]); eniFile = argv[3]; if (channel > 1){ channel = 1; } testDemo(alias, channel, eniFile.c_str()); return 0;}
编译
运行测试
输出,程序将持续打印从站的输入状态
但真正开发的时候,建议将打印信息等耗时的操作注释后再编译,否则,程序将可能会因为打印动作耗时过长而导致主机无法快速填充pdo数据,最终将产生控制抖动等问题。
全部0条评论
快来发表一下你的评论吧 !