使用PCIE主站卡控制IO从站step by step(二)

描述

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数据,最终将产生控制抖动等问题。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分