ZLG致遠電子的PCIe EtherCAT通訊卡該如何使用?PDO過程數據該如何操作?具體編程又該如何實現?續接上一章節,本文將為您詳細講解。
EtherCAT工業總線技術在工業自動化領域展現出了廣泛的應用價值,特別是在運動控制、機器人技術和測量技術等方面。ZLG致遠電子 PCIe EtherCAT通訊卡 基于自主知識產權的系統之上開發,實現了軟硬件間的無縫連接,極大地提升了系統的穩定性、可靠性以及安全性。同時,該通訊卡還支持線路冗余以及熱插拔功能,可輕松實現多軸同步控制和數據的高速傳輸。此外,ZLG致遠電子PCIe EtherCAT通訊卡還為用戶提供了便捷的二次開發庫,支持VC、C#、Linux、Python等各類主流開發環境,滿足客戶不同層次的開發需求。值得一提的是,ZLG致遠電子PCIe EtherCAT通訊卡通過將商業級EtherCAT主站協議和實時內核相結合的方式,有效釋放主機資源,完美解決傳統EtherCAT主站在非實時操作系統下運行所帶來的各類問題,為用戶帶來了更加高效、穩定的解決方案。圖1PCIeEtherCAT通訊卡表1PCIeEtherCAT通訊卡型號
基于上一章《使用PCIe EtherCAT通訊卡控制IO從站step by step(一)》中所講訴的內容,我們已經完成了開發環境的搭建,以及主函數的建立,接下來,我們將會進一步完善主函數中的代碼,實現對從站的PDO數據讀寫。
1. 代碼編寫
打開ecat_api_io_test.cpp文件。根據AWStudio軟件導出的eni文件定義過程數據的結構體,打開eni文件,移動光標到文件尾部,找到注釋的節點ENI_PDO_LIST。圖2ENI文件(1)圖3ENI文件(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數據。
圖4PDO數據的收發原理執行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數據,最終將產生控制抖動等問題。
-
總線
+關注
關注
10文章
2869瀏覽量
87995 -
PCIe
+關注
關注
15文章
1227瀏覽量
82452 -
通訊卡
+關注
關注
0文章
6瀏覽量
5845
發布評論請先 登錄
相關推薦
評論