周立功為CAN通信提供了動態庫:官方提供了很多相關動態庫和lib等,如圖
,其中kerneldlls里還有很多動態庫,還有一個配置文件。其實這么多的文件,如果我們只用到USBCAN2通信,只需要kerneldlls里面的usbcan.dll這個動態庫,里面真正封裝了用了windows和can模塊通過usb通信的函數。然后還需要ControlCAN.h這個頭文件,里面有對使用dll函數時一些結構體的聲明和定義。對於ControlCAN.dll和ControlCAN.lib實際上是對使用usbcan.dll動態庫的引導入口。
對於如何使用Qt加載這些動態庫來實現CAN通信,有兩種方式,1是顯式,2是隱式。
顯式:通過代碼加載動態庫我們需要的函數,把這些函數實際放在自己的工程之中;
隱式:通過在工程中加入動態庫入口的路徑,使可執行文件在運行時在相應路徑找動態庫,再使用其函數。
一、顯式
1、新建工程,在工程中(和源文件放在一起)添加ControlCAN.h頭文件、usbcan.dll動態庫。
ControlCAN.h是需要包含在工程中的(右鍵工程-》添加存在路徑-》此時默認勾選好了ControlCAN.h,確認即可);usbcan.dll是需要加載的動態庫
2、使用QLibrary加載動態庫
QLibrary lib("usbcan.dll");
if(true == lib.load())
qDebug()<<"load ok";
3、聲明一個自己的函數,功能與usbcan.dll里的函數的功能想對應
比如,usbcan.dll中有個打開設備的函數,那就自己定義一個打開設備的函數,把usbcan.dll中打開設備函數的功能賦值到自定義函數中--!
頭文件中:
typedef DWORD(__stdcall VCI_OpenDevice)(DWORD,DWORD,DWORD);//不知道為什么,反正沒有就會出錯
typedef DWORD(__stdcall VCI_ResetCAN)(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd);
typedef DWORD(__stdcall VCI_CloseDevice)(DWORD DeviceType,DWORD DeviceInd);
typedef DWORD(__stdcall VCI_InitCAN)(DWORD DeviceType, DWORD DeviceInd, DWORD CANInd, PVCI_INIT_CONFIG pInitConfig);
typedef DWORD(__stdcall VCI_StartCAN)(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd);
typedef ULONG(__stdcall VCI_Transmit)(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,PVCI_CAN_OBJ pSend,ULONG Len);
typedef ULONG(__stdcall VCI_Receive)(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,PVCI_CAN_OBJ pReceive,ULONG Len,INT WaitTime/*=-1*/);
typedef ULONG(__stdcall VCI_GetReceiveNum)(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd);
typedef DWORD(__stdcall VCI_ClearBuffer)(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd);
typedef DWORD(__stdcall VCI_ReadErrInfo)(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,PVCI_ERR_INFO pErrInfo);
typedef DWORD(__stdcall VCI_ReadCANStatus)(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,PVCI_CAN_STATUS pCANStatus);
以上函數全是can通信重要的函數,全部為自己定義的,名字隨便取都可以。
4、使用上面的函數聲明對應的函數對象
頭文件中:
VCI_OpenDevice *pOpenDevice;
VCI_ResetCAN *pResetCAN;
VCI_CloseDevice *pCloseDevice;
VCI_InitCAN *pInitCAN;
VCI_StartCAN *pStartCAN;
VCI_Transmit *pTransmitCAN;
VCI_Receive *pReceive;
VCI_GetReceiveNum *pGetReceiveNum;
VCI_ClearBuffer *pClearBuffer;
VCI_ReadErrInfo *pReadErrInfoCAN;
VCI_ReadCANStatus *pVCI_ReadCANStatus;
5、引入usbcan.dll動態庫
在源文件中:
QLibrary lib("usbcan.dll");
true == lib.load();
6、將usbcan.dll中的函數功能分解到自定義對應函數中
源文件中:
pOpenDevice = (VCI_OpenDevice *)lib.resolve("VCI_OpenDevice");
pCloseDevice = (VCI_CloseDevice *)lib.resolve("VCI_CloseDevice");
pInitCAN = (VCI_InitCAN *)lib.resolve("VCI_InitCAN");
pStartCAN = (VCI_StartCAN *)lib.resolve("VCI_StartCAN");
pTransmitCAN = (VCI_Transmit *)lib.resolve("VCI_Transmit");
pReceive = (VCI_Receive *)lib.resolve("VCI_Receive");
pGetReceiveNum = (VCI_GetReceiveNum *)lib.resolve("VCI_GetReceiveNum");
pClearBuffer = (VCI_ClearBuffer *)lib.resolve("VCI_ClearBuffer");
pReadErrInfoCAN = (VCI_ReadErrInfo*)lib.resolve("VCI_ReadErrInfo");
7、接下來就可以使用自定義的函數來進行CAN總線編程了
二、隱式
1、把ControlCAN.h和ControlCAN.lib放在工程目錄下,和源文件放在一起
2、把kerneldlls文件夾(里面只需要放ini配置文件和usbcan.dll文件即可)和ControlCAN.dll和可執行文件放在一起
3、右鍵Qt工程,添加庫->外部庫,在庫文件中找到ControlCAN.lib文件路徑,不勾選Mac和linux,不勾選為debug版本添加d作為后綴
4、下一步,確定。此時已經把路徑加進去了
5、右鍵工程,添加已經存在文件,此時已經默認勾選了ControlCAN.h,確定即可
PS:
1、周立功提供的動態庫很多變量類型沒有定義的,比如DWORD,可以再ControlCAN.h中
#include "windows.h"//windows
#include "windef.h"//linux
2、打開ControlCAN.h時會提醒編碼不正確,選擇system編碼就行了
PS:
1、時間標識:CAN盒子接收到當前幀的時間,這個時間是CAN盒子啟動的時間,最小單位是0.1ms,比較准
如何將時間標識轉為字符串:
QString MyMethod::getTimeStampStr(UINT TimeStamp)
{
QString resultStr = "";
int hour = TimeStamp/36000000;
int minute = (TimeStamp - hour*36000000)/600000;
int second = (TimeStamp - hour*36000000 - minute*600000)/10000;
int ms = (TimeStamp - hour*36000000 - minute*600000 - second*10000)/10;
int mms = (TimeStamp - hour*36000000 - minute*600000 - second*10000 - ms*10);
resultStr = QString("%1:").arg(hour,2,10,QChar('0'));//時
resultStr += QString("%1:").arg(minute,2,10,QChar('0'));//分
resultStr += QString("%1:").arg(second,2,10,QChar('0'));//秒
resultStr += QString("%1:").arg(ms,3,10,QChar('0'));//毫秒
resultStr += QString::number(mms);//0.1ms
return resultStr;
}
2、如何高速CAN通信
根據我在項目中的實踐,發現利用PC里能達到的最塊CAN通信(使用USBCAN-2)速度是1ms,但是需要很多技巧。
①、使用3個線程類:1個用來接收,1個用來發送,1個用來解析
②、接收線程最好使用最高線程權限:QThread::HighestPriority,其他的用第二高權限(這個根據計算機性能來考慮)
③、如何循環發送報文:在發送線程里再多加一個定時器(這個定時器不是主界面的,而是發送線程的),定時器timeout時間為需要循環發送的時間(可達到1ms);
用戶在主界面設置需要發送的報文為OBJ結構體數組,然后通過狗仔函數的方式傳到發送線程,最后發送就行了。
④、解析過程:接收函數循環接收報文,每接收到n幀就發送到解析線程,然后根據ID解析,將解析數據發送主界面顯示(顯示的時候textedit不要滾動,即不要append,直接替換,否則會卡頓)
PS:
1、打開兩路CAN:VCI_OpenDevice只能調用一次,並且需要在同一個軟件中使用(dll只能調用一次)