本文導讀
今天,正運動小助手為大家分享一下運動控製卡周期上報,通過提前設置經常讀取的參數主動周期上報,可以減少PC主動輪詢的時間。此次介紹將以ECI2A18B為例,主要講解如何使用C++編程語言來進行周期上報函數的編寫和功能的開發。
01 ECI2A18B控製卡硬件介紹
ECI2A18B經濟型多軸運動控製卡是一款脈衝型、模塊化的網絡型運動控製卡。控製卡本身最多支持10軸,用以實現直線插補、任意圓弧插補、空間圓弧、螺旋插補、電子凸輪、電子齒輪、同步跟隨、虛擬軸、機械手指令等簡單的軌跡控製需求;采用優化的網絡通訊協議可以實現實時的運動控製。

ECI2A18B控製卡功能特點:
(1)本身支持6差分脈衝軸+4單端脈衝軸運動控製,最多可擴展至12軸運動控製。
(2)脈衝輸出模式:脈衝/方向或雙脈衝。
(3)AXIS接口支持編碼器位置測量,可以配置為手輪輸入模式。
(4)專用的手輪輸入接口。
(5)每軸最大輸出脈衝頻率10MHz。
(6)通過CAN總線,最多可擴展到256個隔離輸入口和256個隔離輸出口。
(7)軸正負限位信號口/原點信號口可以隨意配置到任何輸入口。
(8)通用數字輸出口最大輸出電流可達500mA,可直接驅動部分電磁閥。
(9)RS232接口、以太網接口、CAN接口。
(10)支持最多達12軸直線插補、任意圓弧插補、螺旋插補。
(11)支持點位運動、電子凸輪、直線插補、圓弧插補、連續插補運動、機械手指令。
(12)支持Basic多文件多任務編程。
(13)多種程序加密手段,保護客戶的知識產權。

接口定義:

ECI2000係列經濟型多軸運動控製卡可用於電子半導體設備(檢測類設備、組裝類設備、鎖附類設備、焊錫機)、點膠設備和流水線等12軸以內脈衝應用場合。
控製器支持windows、linux、Mac、Android、wince各種操作係統下的開發,提供vc、c#、vb.net、labview等各種環境的dll庫,如下圖。上位機軟件編程參考《ZMotion PC函數庫編程手冊》。

02 為什麼要進行周期上報,作用是什麼?
1、當PC主動輪詢的次數過多時,可能會導致以下問題:
(1)消耗係統資源
輪lun詢xun會hui使shi係xi統tong資zi源yuan消xiao耗hao增zeng加jia,無wu論lun是shi任ren務wu輪lun詢xun或huo定ding時shi器qi輪lun詢xun,都dou會hui消xiao耗hao係xi統tong的de部bu分fen資zi源yuan。在zai多duo用yong戶hu或huo者zhe資zi源yuan受shou限xian的de環huan境jing裏li,這zhe極ji有you可ke能neng致zhi使shi係xi統tong性xing能neng下xia滑hua。
(2)浪費CPU資源
隻要是輪詢都會造成CPUziyuandelangfei。zheshiyinweilunxunhuihuizaixitongneibujianduandeyunxing,bulundangqianshebeidezhuangtaishifouyougaibian。shijishang,shebeidezhuduozhuangtaibingbujingchanggaibian,lunxunkongzhuanzhihuiwuduanxiaohaoCPU的時間。
(3)影響電源管理
向PC報告外圍設備次數增多會使功耗提高,這可能縮短電池壽命或增加能源消耗,從而影響電源管理。
(4)降低響應速度
如果輪詢頻率過高,係統響應其他任務的速度或許會變慢。造成原因是由於CPU會不間斷的輪詢當前狀態,從而響應處理其他計算或與用戶交互任務會減慢。
(5)網絡負載增加
若輪詢涉及網絡通信,輪詢請求過多就可能加大網絡負載,造成網絡擁堵或者延遲加劇。
(6)服務器壓力增大
zaikehuduanfuwuqijiagouli,yaoshipinfanjinxinglunxunqingqiu,jiukenenggeifuwuqidailaiyali。zhuyaojizhongzaizaifuwuqiziyuanbuzushi,yexuhuishifuwuzhiliangjiangdihuozhechuxianqingqiuchaoshideqingkuang。
2、多種獲取方式對於程序運行占比的區別:
在探討單條獲取、多條獲取以及周期性獲取對程序運行產生的影響時,我們需要考量這些操作的特性以及它們對程序整體性能可能存在的潛在影響。
(1)單條獲取
單(dan)條(tiao)獲(huo)取(qu)即(ji)程(cheng)序(xu)每(mei)次(ci)僅(jin)處(chu)理(li)一(yi)個(ge)單(dan)獨(du)的(de)數(shu)據(ju)項(xiang)。此(ci)方(fang)式(shi)簡(jian)單(dan)直(zhi)接(jie),然(ran)而(er)處(chu)理(li)大(da)量(liang)數(shu)據(ju)時(shi)效(xiao)率(lv)不(bu)高(gao),因(yin)為(wei)每(mei)次(ci)操(cao)作(zuo)都(dou)會(hui)有(you)上(shang)下(xia)文(wen)切(qie)換(huan)與(yu)資(zi)源(yuan)管(guan)理(li)方(fang)麵(mian)的(de)開(kai)銷(xiao)。此(ci)時(shi),程(cheng)序(xu)運(yun)行(xing)時(shi)間(jian)主(zhu)要(yao)耗(hao)費(fei)在(zai)數(shu)據(ju)處(chu)理(li)上(shang)。
(2)多條獲取
多(duo)條(tiao)獲(huo)取(qu)意(yi)味(wei)著(zhe)同(tong)時(shi)處(chu)理(li)多(duo)個(ge)數(shu)據(ju)項(xiang)。在(zai)現(xian)代(dai)計(ji)算(suan)機(ji)係(xi)統(tong)裏(li),常(chang)借(jie)助(zhu)多(duo)線(xian)程(cheng)或(huo)者(zhe)並(bing)發(fa)技(ji)術(shu)達(da)成(cheng)這(zhe)一(yi)操(cao)作(zuo),這(zhe)樣(yang)做(zuo)能(neng)大(da)幅(fu)提(ti)升(sheng)數(shu)據(ju)處(chu)理(li)的(de)吞(tun)吐(tu)量(liang)。不(bu)過(guo),多(duo)線(xian)程(cheng)雖(sui)有(you)好(hao)處(chu),卻(que)可(ke)能(neng)被(bei)鎖(suo)爭(zheng)、內nei存cun競jing爭zheng和he上shang下xia文wen切qie換huan等deng問wen題ti所suo抵di消xiao。所suo以yi,多duo條tiao獲huo取qu也ye許xu會hui縮suo短duan單dan個ge數shu據ju項xiang處chu理li的de相xiang對dui運yun行xing時shi間jian,但dan總zong體ti運yun行xing時shi間jian能neng否fou減jian少shao取qu決jue於yu多duo線xian程cheng優you化hua的de成cheng效xiao。
(3)周期性獲取
周期獲取即按照固定的時間間隔重複開展數據獲取操作。在諸如實時監控係統、定ding時shi任ren務wu這zhe類lei需xu要yao定ding期qi更geng新xin數shu據ju狀zhuang態tai的de應ying用yong場chang景jing中zhong較jiao為wei常chang見jian。其qi運yun行xing時shi間jian占zhan比bi由you任ren務wu的de周zhou期qi性xing和he每mei個ge周zhou期qi內nei實shi際ji工gong作zuo量liang決jue定ding。若ruo周zhou期qi性xing任ren務wu負fu載zai較jiao輕qing,則ze對dui程cheng序xu整zheng體ti運yun行xing時shi間jian影ying響xiang不bu大da。
應用場合:
在實際應用裏,具體的應用場景、數據特性以及性能要求決定了選擇何種數據獲取策略。比如,若程序要對單個事件快速響應,單條獲取或許更合適;要是旨在使數據處理速度最大化,多條獲取可能更有利;而對於那些需要定期保持數據新鮮度的應用而言,周期性獲取則不可或缺。
03 新建MFC項目並添加函數庫
1、首先打開Visual Studio 2022,點擊創建新項目。

2、選擇開發語言為“Visual C++”和程序類型“MFC應用程序”。

3、點擊下一步即可。

4、選擇類型為“基於對話框”,下一步或者完成。

5、前往正運動官網下載PC函數庫,路徑如下(本文采用64位函數庫為例)。
(1)進入官網,選擇支持與服務,打開下載中心選擇庫文件,就能找到所有的PC函數庫。

(2)點擊下載Windows C++(64位),可按需求另存為想要保存的路徑下。

(3)函數庫另存為具體路徑如下。

6、將廠商提供的C++庫文件和相關頭文件複製到新建的項目裏。

7、在項目中添加靜態庫和相關頭文件。
(1)先右擊項目文件,接著依次選擇:“添加”→“現有項”。

(2)在彈出的窗口中依次添加靜態庫和相關頭文件。

8、聲明用到的頭文件和定義控製器連接句柄。

至此項目新建完成,可進行MFC項目開發。
04 查看PC函數手冊,熟悉相關函數接口
1、PC函數手冊也可以在正運動官網“支持與服務”→“下載中心”→“編程手冊”中找到。

2、鏈接控製器,獲取鏈接句柄。

3、控製器自動上報相關指令。

4、讀取數字輸入輸出相關指令。

5、讀取Modbus寄存器相關指令。

05 MFC實現軸的周期上報
1、例程界麵如下。

2、通過下拉控件選擇連接控製器/控製卡連接方式。
BOOL CTest_CycleUpDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 將“關於...”菜單項添加到係統菜單中。
// IDM_ABOUTBOX 必須在係統命令範圍內。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if(pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if(!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX,strAboutMenu);
}
}
// 設置此對話框的圖標。當應用程序主窗口不是對話框時,框架將自動
// 執行此操作
SetIcon(m_hIcon, TRUE); // 設置大圖標
SetIcon(m_hIcon, FALSE); // 設置小圖標
// TODO: 在此添加額外的初始化代碼
GetDlgItem(IDC_COMBO2)->SetWindowTextA("網口\n");
CComboBox *connetList;
connetList = (CComboBox *)GetDlgItem(IDC_COMBO2);
connetList->AddString(_T("網口\n"));
connetList->AddString(_T("LOCAL\n"));
connetList->AddString(_T("PCI\n"));
connetList->AddString(_T("串口\n"));
return TRUE; // 除非將焦點設置到控件,否則返回 TRUE
}
3、自動搜索IP。
void CTest_CycleUpDlg::OnCbnDropdownCombo1()
{
char Buffer[256];
CTest_CycleUpDlg* pDlg = (CTest_CycleUpDlg*)AfxGetMainWnd();
GetDlgItemText(IDC_COMBO2,Buffer,256);
Buffer[255] = '\0';
if(0==strcmp(Buffer,"串口\n"))
{
Com_SCAN(pDlg);
}
else if(0==strcmp(Buffer,"網口\n"))
{
IP_SCAN(pDlg);
}
else if(0==strcmp(Buffer,"PCI\n"))
{
PCI_SCAN(pDlg);
}
else if(0==strcmp(Buffer,"LOCAL\n"))
{
CComboBox *m_pEthList;
m_pEthList = (CComboBox *)GetDlgItem(IDC_COMBO1);
m_pEthList->ResetContent();
m_pEthList->AddString(_T("LOCAL1\n"));
}else
{
CString str;
MessageBox("請選擇正確的鏈接類型!");
return;
}
return;
}

4、開啟上報。

//開啟關閉上報
void CTest_CycleUpDlg::OnBnClickedCheckStart()
{
if(NULL == G_ZmcHandle)
{
MessageBox(_T("控製器未連接"));
return;
}
CString tempstr;
UpdateData(true);
int iret = 0;
if(m_If_StartUp) //開啟上報
{
GetCycleStr();
iret = ZAux_CycleUpEnable(G_ZmcHandle,m_CyclePort,m_CycleTime,Str_CycleCmd);
if(ERR_SUCCESS != iret)
{
tempstr.Format("周期上報打開失敗!錯誤碼:%d 命令:%s\r\n",iret,Str_CycleCmd);
AppendTextOut(tempstr);
return;
}
tempstr.Format("周期上報開始!命令:%s\r\n",Str_CycleCmd);
AppendTextOut(tempstr);
ifirsttimeus = GetTickCount();
SetTimer(1,1,NULL);
}
else
{
m_CycleCount = ZAux_CycleUpGetRecvTimes(G_ZmcHandle,m_CyclePort);
iret = ZAux_CycleUpDisable(G_ZmcHandle,m_CyclePort);
if(ERR_SUCCESS != iret)
{
tempstr.Format("周期上報關閉失敗!錯誤碼:%d \r\n",iret);
AppendTextOut(tempstr);
return;
}
tempstr.Format("周期上報關閉-上報用時:%dms, 上報次數:%d ,平均時間:%.3fms\r\n",(GetTickCount() - ifirsttimeus),m_CycleCount,(float)(GetTickCount() - ifirsttimeus)/m_CycleCount);
AppendTextOut(tempstr);
KillTimer(1);
}
5、選擇獲取參數類型和起始地址及數量。

//獲取上報參數
void CTest_CycleUpDlg::GetCycleStr()
{
memset(Str_CycleCmd,0,sizeof(Str_CycleCmd));
CString TempString = "";
int ilen = 0;
if(m_CycleParaEnAble[0])
{
switch(m_CyclePara[0])
{
case 0: //AXISSTATUS
TempString.Format("AXISSTATUS(%d,%d),",m_CycleParaStart[0],m_CycleParaNum[0]);
break;
case 1: //DPOS
TempString.Format("DPOS(%d,%d),",m_CycleParaStart[0],m_CycleParaNum[0]);
break;
case 2: //IDLE
TempString.Format("IDLE(%d,%d),",m_CycleParaStart[0],m_CycleParaNum[0]);
break;
case 3: //IN
TempString.Format("IN(%d,%d),",m_CycleParaStart[0],m_CycleParaNum[0]);
break;
case 4: //MODBUS_REG
TempString.Format("MODBUS_REG(%d,%d),",m_CycleParaStart[0],m_CycleParaNum[0]);
break;
case 5: //MPOS
TempString.Format("MPOS(%d,%d),",m_CycleParaStart[0],m_CycleParaNum[0]);
break;
case 6: //OP
TempString.Format("OP(%d,%d),",m_CycleParaStart[0],m_CycleParaNum[0]);
break;
case 7: //TABLE
TempString.Format("TABLE(%d,%d),",m_CycleParaStart[0],m_CycleParaNum[0]);
break;
default:
break;
}
}
ilen += TempString.GetLength();
memcpy(Str_CycleCmd,TempString.GetBuffer(0),TempString.GetLength()*sizeof(TCHAR));
if(m_CycleParaEnAble[1])
{
switch(m_CyclePara[1])
{
case 0: //AXISSTATUS
TempString.Format("AXISSTATUS(%d,%d),",m_CycleParaStart[1],m_CycleParaNum[1]);
break;
case 1: //DPOS
TempString.Format("DPOS(%d,%d),",m_CycleParaStart[1],m_CycleParaNum[1]);
break;
case 2: //IDLE
TempString.Format("IDLE(%d,%d),",m_CycleParaStart[1],m_CycleParaNum[1]);
break;
case 3: //IN
TempString.Format("IN(%d,%d),",m_CycleParaStart[1],m_CycleParaNum[1]);
break;
case 4: //MODBUS_REG
TempString.Format("MODBUS_REG(%d,%d),",m_CycleParaStart[1],m_CycleParaNum[1]);
break;
case 5: //MPOS
TempString.Format("MPOS(%d,%d),",m_CycleParaStart[1],m_CycleParaNum[1]);
break;
case 6: //OP
TempString.Format("OP(%d,%d),",m_CycleParaStart[1],m_CycleParaNum[1]);
break;
case 7: //TABLE
TempString.Format("TABLE(%d,%d),",m_CycleParaStart[1],m_CycleParaNum[1]);
break;
default:
break;
}
}
if((ilen + TempString.GetLength()) < 1000)
{
memcpy(&Str_CycleCmd[ilen],TempString.GetBuffer(0),TempString.GetLength()*sizeof(TCHAR));
ilen += TempString.GetLength();
}
if(m_CycleParaEnAble[2])
{
switch(m_CyclePara[2])
{
case 0: //AXISSTATUS
TempString.Format("AXISSTATUS(%d,%d)",m_CycleParaStart[2],m_CycleParaNum[2]);
break;
case 1: //DPOS
TempString.Format("DPOS(%d,%d)",m_CycleParaStart[2],m_CycleParaNum[2]);
break;
case 2: //IDLE
TempString.Format("IDLE(%d,%d)",m_CycleParaStart[2],m_CycleParaNum[2]);
break;
case 3: //IN
TempString.Format("IN(%d,%d)",m_CycleParaStart[2],m_CycleParaNum[2]);
break;
case 4: //MODBUS_REG
TempString.Format("MODBUS_REG(%d,%d)",m_CycleParaStart[2],m_CycleParaNum[2]);
break;
case 5: //MPOS
TempString.Format("MPOS(%d,%d)",m_CycleParaStart[2],m_CycleParaNum[2]);
break;
case 6: //OP
TempString.Format("OP(%d,%d)",m_CycleParaStart[2],m_CycleParaNum[2]);
break;
case 7: //TABLE
TempString.Format("TABLE(%d,%d)",m_CycleParaStart[2],m_CycleParaNum[2]);
break;
default:
break;
}
}
if((ilen + TempString.GetLength()) < 1000)
{
memcpy(&Str_CycleCmd[ilen],TempString.GetBuffer(0),TempString.GetLength()*sizeof(TCHAR));
ilen += TempString.GetLength();
}
}
6、獲取上報結果並輸出。

//獲取上報結果
void CTest_CycleUpDlg::GetCycleInfo()
{
CString ParaString = "";
CString TempString = "";
CString ShowString = "";
int iret = 0;
int ival = 0;
for(int inum=0;inum<3;inum++)
{
ShowString ="";
if(m_CycleParaEnAble[inum])
{
switch(m_CyclePara[inum])
{
case 0: //AXISSTATUS
ParaString = "AXISSTATUS";
break;
case 1: //DPOS
ParaString = "DPOS";
break;
case 2: //IDLE
ParaString = "IDLE";
break;
case 3: //IN
ParaString = "IN";
break;
case 4: //MODBUS_REG
ParaString = "MODBUS_REG";
break;
case 5: //MPOS
ParaString = "MPOS";
break;
case 6: //OP
ParaString = "OP";
break;
case 7: //TABLE
ParaString = "TABLE";
break;
default:
break;
}
ShowString += ParaString;
for(int i =0;i
7、強製上報一次。
//強製上報一次
void CTest_CycleUpDlg::OnBnClickedBtnCycleup()
{
if(NULL == G_ZmcHandle)
{
MessageBox(_T("控製器未連接"));
return;
}
UpdateData(true);
int iret = ZAux_CycleUpForceOnce(G_ZmcHandle, m_CyclePort);
if(ERR_SUCCESS != iret)
{
MessageBox(_T("周期上報刷新失敗!"));
return;
}
}
8、使用正運動RTSys軟件輸出窗口和軸參數窗口方便直接的觀察到我們周期上報的數值。

9、上位機讀取周期上報的值並輸入在文本框。

10、可以點擊下拉框選擇其他參數或更改起始地址及數量讀取不同區域數據。

11、周期上報獲取信息和單次獲取信息的比較。

12、視頻教程。
視頻教程可點擊→“如何通過周期上報實時獲取io狀態等信息之C++篇”查看。
完整代碼獲取地址
▼

本次,正運動運動控製卡周期上報實時數據IO狀態之C++篇就分享到這裏。
更多精彩內容請關注“正運動小助手”公眾號,需要相關開發環境與例程代碼,請谘詢正運動技術銷售工程師:400-089-8936。
本(ben)文(wen)由(you)正(zheng)運(yun)動(dong)技(ji)術(shu)原(yuan)創(chuang),歡(huan)迎(ying)大(da)家(jia)轉(zhuan)載(zai),共(gong)同(tong)學(xue)習(xi),一(yi)起(qi)提(ti)高(gao)中(zhong)國(guo)智(zhi)能(neng)製(zhi)造(zao)水(shui)平(ping)。文(wen)章(zhang)版(ban)權(quan)歸(gui)正(zheng)運(yun)動(dong)技(ji)術(shu)所(suo)有(you),如(ru)有(you)轉(zhuan)載(zai)請(qing)注(zhu)明(ming)文(wen)章(zhang)來(lai)源(yuan)。

正運動技術專注於運動控製技術研究和通用運動控製軟硬件產品的研發,是國家級高新技術企業。正運動技術彙集了來自華為、zhongxingdenggongsideyouxiurencai,zaijianchizizhuchuangxindetongshi,jijilianhegedagaoxiaoxietongyundongkongzhijichujishudeyanjiu,shiguoneigongkonglingyufazhanzuikuaideqiyezhiyi,yeshiguoneishaoyou、完整掌握運動控製核心技術和實時工控軟件平台技術的企業。主要業務有:運動控製卡_運動控製器_EtherCAT運動控製卡_EtherCAT控製器_運動控製係統_視覺控製器__運動控製PLC_運動控製_機器人控製器_視覺定位_XPCIe/XPCI係列運動控製卡等。
|