前述工作:
(1)将二次开发的h文件和lib文件拷入pro的文件夹,如图:
(2)将dll文件拷入debug文件夹下,同时需要将kerneldlls文件夹放入debug文件夹下,与exe文件同一个文件夹。如图:
昨天由于kerneldlls文件夹没有放入debug文件夹下,导致程序功能不能正常运行。即:
FLAG = VCI_OpenDevice(nDeviceType,nDeviceInd,nReserved);
调用打开can口命令后,可以编译,可以运行,但是FLAG就是一直是FALSE,说明can口没有打开。找了很多资料后,确定是:不能仅仅将kerneldlls文件夹下的usbcan.dll文件放到debug文件夹下,而是要将kerneldlls文件夹整体放在debug文件夹下,这样程序才能正确调用,并顺利打开can口。
(3)添加h文件,如图:
这个是基本操作,就不再说怎么添加controlcan的h文件的了。
(4)添加库文件。添加后如图所示,win32行。注意必须是x86文件夹下的dll,h和lib。添加x64后会发生错误。
(5)修改controlcan.h文件,由于数据类型不同,需要添加在其头部添加#include "windows.h",并且选择sleect encoding,选择system。这个仅仅是汉字的问题,用系统自带的就好。
这样程序就能正确编译和运行了。
第一部分:启动和运行can口
(1)先搞两个按钮在ui界面,一个打开can,一个运行can。添加槽函数。目的:打开can按钮按下,打开can口和初始化can口;运行can按钮按下,运行can口。
(2)参数初始化:其实不用知道啥意思,知道等于多少就行。我用的usbcan2,第一个参数就等于4。这个查阅下载的pdf就行。
最后一个是结构体,为啥那么下,下载的pdf里面有例程,直接拿过来就好。,然后初始化结构体里面的参数。
里面的具体意义查阅pdf,这个里面介绍的很详细,并且耐着性子看,这个非常重要,好好的看一遍,反正没几页,挺容易就熬过来了。其实当你把h文件还有dll文件加载好之后,后面的参数都会自己给你联想出来,非常的方便。这几个参数是设置波特率,掩码还有模式啥的,和你的can通讯协议一致就好(can发送不能模拟,只能老老实实的通过硬件和can盒连接到电脑上,并且发送固定的数据)。
(3)初始执行代码
void MainWindow::on_OPEN_clicked()
{
FLAG = VCI_OpenDevice(nDeviceType,nDeviceInd,nReserved);
qDebug()<<FLAG;
FLAG = VCI_InitCAN(nDeviceType,nDeviceInd,nCANInd,&InitCan);
qDebug()<<FLAG;
}
这是打开can按钮的槽函数,点击一下就open 和 init can口了。这个函数只能执行一次。同时打印出来的debug都是true。两个true。确定是open成功,并且init成功。
void MainWindow::on_START_clicked()
{
FLAG = VCI_StartCAN(nDeviceType,nDeviceInd,nCANInd);
qDebug()<<FLAG;
}
这是运行can按钮的槽函数,点击一下就运行can口了。执行之前,can盒上的灯是红色的,执行之后就变成了绿色,并且闪烁周期和你的can接收周期一样(取决于你用的哪个can盒,会有不同)。打印出来的debug是true,说明can口启动成功。可以正确的接收和发送数据了。
第二部分:接收数据
添加接收按钮,添加槽函数。界面如下:
测试代码如下:可以打印出接收数据100个。至于receive函数怎么用法,可以好好看看debug。现在我也不是很明白100和400的意义,有待于进一步改写数字加以确定。
ReceiveNum = VCI_Receive(nDeviceType,nDeviceInd,nCANInd,Receive,100,400);
qDebug()<<ReceiveNum;
只要打印出来的数据不是FFFF,就可以保证can帧是可以正产接收的。
然后加入接收代码:测试代码时应该已经添加,这里顺序有点乱了。为了整体介绍,暂且放到后面。
public:
int ReceiveNum;
VCI_CAN_OBJ Receive[100];
其中num是接收个数,下面是个结构体,接收的100个结构体数据,就是说这100个数据中的单单1个数据就包含了很多信息,其中有ID号,帧的格式,还有帧的长度,帧的数据。换句话说receive[0]里面有receive[0].ID,还有receive[0].data[0]到receive[0].data[7],具体看程序:
int strCANID;
int strFormat;
int strType;
int strLen;
int strData[8];
for (int i = 0; i < ReceiveNum; i++)
{
strCANID = Receive[i].ID;
strFormat= Receive[i].RemoteFlag;
strType = Receive[i].ExternFlag;
strLen = Receive[i].DataLen;
strData[0] = Receive[i].Data[0];
strData[1] = Receive[i].Data[1];
strData[2] = Receive[i].Data[2];
strData[3] = Receive[i].Data[3];
strData[4] = Receive[i].Data[4];
strData[5] = Receive[i].Data[5];
strData[6] = Receive[i].Data[6];
strData[7] = Receive[i].Data[7];
}
Receive数组是个结构体的数组,可以将其中的值赋给我们定义好的int值,在传输can帧的时候一般都是固定的,远程帧也是固定的,扩展帧也是固定的,就是除了数据帧之外,其他的帧基本都是固定的。将结构体中的值赋给定义的变量,就可以将变量取出进行自己的设计了。
for (int i = 0; i < ReceiveNum; i++)
{
strCANID = Receive[i].ID;
strFormat= Receive[i].RemoteFlag;
strType = Receive[i].ExternFlag;
strLen = Receive[i].DataLen;
strData[0] = Receive[i].Data[0];
strData[1] = Receive[i].Data[1];
strData[2] = Receive[i].Data[2];
strData[3] = Receive[i].Data[3];
strData[4] = Receive[i].Data[4];
strData[5] = Receive[i].Data[5];
strData[6] = Receive[i].Data[6];
strData[7] = Receive[i].Data[7];
qDebug()<<strData[0]<<strData[1]<<strData[2]<<strData[3]<<strData[4]<<strData[5]<<strData[6]<<strData[7];
}
加入数据段打印行。每个receive打印一次,打印8个8位,然后打印100次。经过测试,输出的值和自定义在can链路上的数据一致。可以基本确定程序执行是正确的。
第三部分:结论
(1)可以保证can口设置,打开,和接收数据的正确性。中间走了不少弯路,一方面百度了很多东西,从一个一个中寻找自己需要的信息,并且加以试验,另一方面给的例程中可以借鉴一部分书写方法,还有一方面就是下载的函数说明pdf。
(2)完全在一个未知的领域搞自己不熟悉的东西,很痛苦,并且搞不出来更痛苦。只能静下心一点一点的按照自己的逻辑一块一块的实现功能,一步一步的去调试,没啥捷径可走。
(3)代码时别人编写的,说明肯定能调试通过,肯定是有方法去弄出来的,不能心急,越急越没有办法弄出来。pdf和参考网页都是攻城狮的心血,需要借鉴过来,都是很宝贵的,能节省你的不少时间。
第四部分:展望
只能利用按钮接收一次数据,估计需要线程,自定义线槽去接收全部数据,又是一个全新的知识。只能硬着头皮继续搞。
注:由于小伙伴需要源代码的时间不同,登录邮箱界面太多麻烦,所以建立了一个订阅号,如果有问题或者需要源码,可添加订阅号,留言后会发送源代码或者有任何问题可留言,将积极解决提出的问题。