周立功USBCAN-II上位机开发(MFC)
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
周⽴功USBCAN-II上位机开发(MFC)
使⽤的USB转CAN的设备是周⽴功的USBCAN-II,在购买的时候,会有上位机⼆次开发的库⽂件、例程和API⽂档等材料,可以参考。
1、库函数的调⽤
⾸先,把库函数⽂件都放在⼯作⽬录下。
库函数⽂件总共有三个⽂件:ControlCAN.h、ControlCAN.lib、ControlCAN.dll和⼀个⽂件夹kerneldlls。
VC调⽤动态库的⽅法
(1) 在扩展名为.CPP的⽂件中包含ControlCAN.h头⽂件。
如:#include “ControlCAN.h”
(2) 在⼯程的连接器设置中连接到ControlCAN.lib⽂件。
如:在VC7环境下,在项⽬属性页⾥的配置属性→连接器→输⼊→附加依赖项中添加ControlCAN.lib
中间换了⼀台电脑,出现电脑丢失ControlCAN.dll的问题,将ControlCAN.dll拷到了可执⾏⽂件的⽂件夹中即可
2、基本操作
2.1 连接设备
我这⾥每次连接都会重新开启接收数据的线程,创建⼀次接收数据的txt⽂档
void CTest_OilDlg::OnBnClickedButtonConnect()
{
//⾸先判断CAN是否打开,,如果已经打开,则先复位及重启CAN--1.8
//关闭程序前必须点击断开连接按钮,否则报错
if(m_connect == 1)
{
m_connect = 0;
//isShow = 0;
Sleep(500);
GetDlgItem(IDC_BUTTON_CONNECT)->SetWindowTextW(_T("连接"));
VCI_CloseDevice(m_deviceType,m_deviceIndex);
showListInfo(_T("断开设备成功"));
//结束⾃发⾃收测试的定时器
KillTimer(0);
//结束当前线程
if(m_pThread != NULL)
{
//::WaitForSingleObject(m_pThread->m_hThread,INFINITE);//该函数会造成死锁
//https:///silvervi/article/details/5874212 将上⾯函数修改成如下,以避免上⾯函数阻塞对话框主线程的消息队列
DWORD dwRet = 0;
MSG msg;
while(true)
{
//等待处理数据线程结束,和等待消息队列中的任何消息
dwRet = MsgWaitForMultipleObjects(1,&m_pThread->m_hThread,false,INFINITE,QS_ALLINPUT);
//dwRet = WaitForSingleObject(m_pThread->m_hThread,50);
switch (dwRet)
{
case WAIT_OBJECT_0:
break;
case WAIT_OBJECT_0 + 1:
//get the message from Queue and dispatch it to specific window
PeekMessage(&msg,NULL,0,0,PM_REMOVE);
DispatchMessage(&msg);
continue;
default:
break;
}
break;
}
//CloseHandle(m_pThread->m_hThread);
delete m_pThread;
m_pThread = NULL;//不太懂
}
//关闭存储数据的⽂件
for(int i = 0;i < 4;i++)
{
//判断⽂件是否打开,若打开了关闭
if(m_waveDataFile[i].m_hFile != CFile::hFileNull)
{
m_waveDataFile[i].Close();
}
}
GetDlgItem(IDC_BUTTON_START)->SetWindowTextW(_T("开始⼯作"));
return;
}
//------------打开设置---------------------//
//设备类型
m_deviceType = VCI_USBCAN2;
//设备索引号,只有⼀个设备,索引号为0
m_deviceIndex = 0;
//第0路CAN--只有⼀路,⽤户选择
CString canNum;
m_selectCANNum.GetWindowTextW(canNum);
m_canNumA = _ttoi(canNum);
if(VCI_OpenDevice(m_deviceType,m_deviceIndex,0) != STATUS_OK)//m_deviceType:设备类型号;m_deviceIndex:设备索引号;最后⼀个是保留参数,⼀般为0 {
MessageBox(_T("打开设备失败!",_T("警告"),MB_OK|MB_ICONQUESTION));
showListInfo(_T("打开设备失败"));
SetHScroll();
return ;
}
else
{
showListInfo(_T("打开设备成功"));
SetHScroll();
}
///-------------对CAN进⾏初始化------------------//
//对CAN进⾏初始化
VCI_INIT_CONFIG init_config;
init_config.AccCode = 0x00000000;
init_config.AccMask = 0xffffffff;//表⽰全部接收,(全部接收,AccMask:0xffffffff;AccCode:0x00000000---这块可以通过测试软件中的滤波设置功能中计算)
init_config.Mode = 0;//正常模式;1:表⽰只听模式(只接收,不影响总线)
init_config.Timing0 = 0x00;
init_config.Timing1 = 0x14;//相当于波特率1000kbps
if(VCI_InitCAN(m_deviceType,m_deviceIndex,m_canNumA,&init_config) != STATUS_OK)
{
MessageBox(_T("初始化CAN失败!"),_T("警告"),MB_OK|MB_ICONQUESTION);
VCI_CloseDevice(m_deviceType,m_deviceIndex);
showListInfo(_T("初始化CAN失败"));
SetHScroll();
return ;
}
else
{
showListInfo(_T("初始化CAN成功"));
SetHScroll();
}
m_connect = 1;
GetDlgItem(IDC_BUTTON_CONNECT)->SetWindowTextW(_T("断开"));
//创建存储数据的⽂件
CTime time0 = CTime::GetCurrentTime();
CString fileName = _T("WaveData");
if(!PathIsDirectory(fileName))
{
::CreateDirectory(fileName,NULL);
}
fileName.Format(_T("WaveData/%d-%d %dh%dm%ds"),time0.GetMonth(),time0.GetDay(),time0.GetHour(),time0.GetMinute(),time0.GetSecond());
if(!PathIsDirectory(fileName))
{
::CreateDirectory(fileName,NULL);
}
CString fileName0 = fileName;
for(int i = 0;i < 4;i++)
{
CString i0;
i0.Format(_T("/%dth"),i+1);
fileName = fileName0 + i0;
fileName += _T(".txt");
m_waveDataFile[i].Open(fileName,CFile::modeWrite|CFile::modeCreate|CFile::modeNoTruncate);//若⽂件存在,则清空
}
//开启接收数据的线程
m_pThread = AfxBeginThread(ReceiveThread,this,0,CREATE_SUSPENDED,NULL);
m_pThread->m_bAutoDelete = false;
}
2.2 接收数据
UINT CTest_OilDlg::ReceiveThread(void *param)
{
CTest_OilDlg *dlg = (CTest_OilDlg*)param;
VCI_CAN_OBJ frameInfo[5000];//⼀次性从缓冲区获取50个帧
VCI_ERR_INFO errInfo;
int len = 1;//获取到的CAN帧的个数
int i = 0;
CString str,tmpstr;
while(1)
{
Sleep(1);
if(dlg->m_connect == 0)
{
break;
}
//获取缓冲区的长度
int lenBuf = VCI_GetReceiveNum(dlg->m_deviceType,dlg->m_deviceIndex,dlg->m_canNumA);
//获取到的数据的个数,如果缓冲区⼤于5000,则取出5000,否则将缓冲区全部取出
len = VCI_Receive(dlg->m_deviceType,dlg->m_deviceIndex,dlg->m_canNumA,frameInfo,5000,400);//每次从缓冲区获取50帧,等待200ms⽆响应后结束if(len <= 0)
{
//注意:如果没有读到数据则必须调⽤此函数来读取出当前的错误码
//千万不能省略这⼀步(即使你可能不想知道错误码是什么)
DWORD error = VCI_ReadErrInfo(dlg->m_deviceType,dlg->m_deviceIndex,dlg->m_canNumA,&errInfo);//返回值为1 表⽰操作成功if((errInfo.ErrCode & 0x0000) == 0x0000)
{
//表⽰错误码是0x0000
}
}
else
{
for(i = 0;i < len;i++)
{
str = _T("数据:\n");
if(frameInfo[i].DataLen > 8)
frameInfo[i].DataLen = 8;
//原始数据----但是这⾥没有保存
for(int j = 0; j < frameInfo[i].DataLen;j++)
{
tmpstr.Format(_T("%04x \n"),frameInfo[i].Data[j]);
str += tmpstr;
}
::SendMessage(dlg->GetSafeHwnd(),WM_WAVEFORM,WPARAM(&frameInfo[i]),NULL);
//TRACE(_T("receive\n"));
}
}
}
return0;
}
这⾥的数据处理是通过发送⾃定义消息的⽅法实现的,因为这些数据同时也要画成曲线显⽰在界⾯上,需要对界⾯进⾏更新操作,这时候需要给界⾯的主线程发消息去实现界⾯更新
2.3 发送数据
void CTest_OilDlg::OnBnClickedButtonSend()
{
//-----------------发送井下仪器⼯作模式命令-------------------//
if(m_connect == 0)
return ;
VCI_CAN_OBJ frameInfo;
//设置发送重发超时时间,建议不⼩于1500ms,默认4000ms
VCI_SetReference(m_deviceType,m_deviceIndex,m_canNumA,4,&m_sendTimeout);
frameInfo.ID = 0x84444444;//需要再确定
frameInfo.SendType = 0;//正常发送
frameInfo.RemoteFlag = 0;//数据帧
frameInfo.ExternFlag = 1;//扩展帧
frameInfo.DataLen = 3;//⼀个字节
frameInfo.Data[0] = 0x04;
frameInfo.Data[1] = 0xff;
//01仪器待机;02:仪器⾃检;03:仪器定时开关机;04:仪器测试;05:仪器连续⼯作
frameInfo.Data[2] = m_selectMode.GetCurSel() + 1;
int ret = VCI_Transmit(m_deviceType,m_deviceIndex,m_canNumA,&frameInfo,1);
if(ret == 1)
{
showListInfo(_T("命令发送成功"));
SetHScroll();
}
else
{
showListInfo(_T("命令发送失败"));
SetHScroll();
}
}
View Code
3、问题
做到现在,程序⾃发⾃收可以,接收下位机数据能接受5个左右的循环就接不到了,后来把数据的操作都屏蔽掉,只接收,发现也接不到,缓冲区内的数据个数为0.这个问题还没解决。
这个问题在使⽤USBCAN给的例程⾥⾯也是存在的,⽬前不清楚什么问题
3.28:这个问题是因为下位机需要跟两个CAN总线交互,⼀个是跟上位机,⼀个是跟其他板⼦,跟其他板⼦交互的CAN必须要有⼈接收,他才会持续的给上位机发数,上位机收不到是因为下位机的另⼀个CAN没有接收,进⼊了中断。
⽽CANtest能接收是因为,我在操作的时候犯懒,同时打开了两个通道,也就是另⼀个通道有⼈接收,所以才能持续发数。