使用的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 == )
{
m_connect = ;
//isShow = 0;
Sleep(); GetDlgItem(IDC_BUTTON_CONNECT)->SetWindowTextW(_T("连接"));
VCI_CloseDevice(m_deviceType,m_deviceIndex); showListInfo(_T("断开设备成功")); //结束自发自收测试的定时器
KillTimer();
//结束当前线程
if(m_pThread != NULL)
{
//::WaitForSingleObject(m_pThread->m_hThread,INFINITE);//该函数会造成死锁
//https://blog.csdn.net/silvervi/article/details/5874212 将上面函数修改成如下,以避免上面函数阻塞对话框主线程的消息队列
DWORD dwRet = ;
MSG msg;
while(true)
{
//等待处理数据线程结束,和等待消息队列中的任何消息
dwRet = MsgWaitForMultipleObjects(,&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 + :
//get the message from Queue and dispatch it to specific window
PeekMessage(&msg,NULL,,,PM_REMOVE);
DispatchMessage(&msg);
continue;
default:
break;
}
break;
}
//CloseHandle(m_pThread->m_hThread);
delete m_pThread;
m_pThread = NULL;//不太懂 }
//关闭存储数据的文件
for(int i = ;i < ;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路CAN--只有一路,用户选择
CString canNum;
m_selectCANNum.GetWindowTextW(canNum);
m_canNumA = _ttoi(canNum); if(VCI_OpenDevice(m_deviceType,m_deviceIndex,) != 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 = ;//正常模式;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 = ;
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 = ;i < ;i++)
{
CString i0;
i0.Format(_T("/%dth"),i+);
fileName = fileName0 + i0;
fileName += _T(".txt");
m_waveDataFile[i].Open(fileName,CFile::modeWrite|CFile::modeCreate|CFile::modeNoTruncate);//若文件存在,则清空
}
//开启接收数据的线程
m_pThread = AfxBeginThread(ReceiveThread,this,,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[];//一次性从缓冲区获取50个帧
VCI_ERR_INFO errInfo;
int len = ;//获取到的CAN帧的个数
int i = ;
CString str,tmpstr;
while()
{
Sleep();
if(dlg->m_connect == )
{
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,,);//每次从缓冲区获取50帧,等待200ms无响应后结束
if(len <= )
{
//注意:如果没有读到数据则必须调用此函数来读取出当前的错误码
//千万不能省略这一步(即使你可能不想知道错误码是什么)
DWORD error = VCI_ReadErrInfo(dlg->m_deviceType,dlg->m_deviceIndex,dlg->m_canNumA,&errInfo);//返回值为1 表示操作成功
if((errInfo.ErrCode & 0x0000) == 0x0000)
{
//表示错误码是0x0000
} }
else
{
for(i = ;i < len;i++)
{
str = _T("数据:\n");
if(frameInfo[i].DataLen > )
frameInfo[i].DataLen = ;
//原始数据----但是这里没有保存
for(int j = ; 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")); }
}
} return ;
}

这里的数据处理是通过发送自定义消息的方法实现的,因为这些数据同时也要画成曲线显示在界面上,需要对界面进行更新操作,这时候需要给界面的主线程发消息去实现界面更新

2.3 发送数据

void CTest_OilDlg::OnBnClickedButtonSend()
{
//-----------------发送井下仪器工作模式命令-------------------//
if(m_connect == )
return ;
VCI_CAN_OBJ frameInfo;
//设置发送重发超时时间,建议不小于1500ms,默认4000ms
VCI_SetReference(m_deviceType,m_deviceIndex,m_canNumA,,&m_sendTimeout);
frameInfo.ID = 0x84444444;//需要再确定
frameInfo.SendType = ;//正常发送
frameInfo.RemoteFlag = ;//数据帧
frameInfo.ExternFlag = ;//扩展帧
frameInfo.DataLen = ;//一个字节 frameInfo.Data[] = 0x04;
frameInfo.Data[] = 0xff;
//01仪器待机;02:仪器自检;03:仪器定时开关机;04:仪器测试;05:仪器连续工作
frameInfo.Data[] = m_selectMode.GetCurSel() + ; int ret = VCI_Transmit(m_deviceType,m_deviceIndex,m_canNumA,&frameInfo,);
if(ret == )
{
showListInfo(_T("命令发送成功"));
SetHScroll();
}
else
{ showListInfo(_T("命令发送失败"));
SetHScroll();
} }

3、问题

做到现在,程序自发自收可以,接收下位机数据能接受5个左右的循环就接不到了,后来把数据的操作都屏蔽掉,只接收,发现也接不到,缓冲区内的数据个数为0.这个问题还没解决。

这个问题在使用USBCAN给的例程里面也是存在的,目前不清楚什么问题

3.28:这个问题是因为下位机需要跟两个CAN总线交互,一个是跟上位机,一个是跟其他板子,跟其他板子交互的CAN必须要有人接收,他才会持续的给上位机发数,上位机收不到是因为下位机的另一个CAN没有接收,进入了中断。而CANtest能接收是因为,我在操作的时候犯懒,同时打开了两个通道,也就是另一个通道有人接收,所以才能持续发数。

周立功USBCAN-II 上位机开发(MFC)的更多相关文章

  1. USBCAN的使用和上位机开发(MFC)

    USBCAN使用手册 参见:https://blog.51cto.com/12572800/2062839 1. USB CAN软件安装与硬件接线 USB CAN是常用的CAN测试工具.它的软件资料存 ...

  2. 【专题教程第8期】基于emWin模拟器的USB BULK上位机开发,仅需C即可,简单易实现

    说明:1.如果你会emWin话的,就可以轻松制作上位机.做些通信和控制类上位机,比使用C#之类的方便程度一点不差,而且你仅会C语言就可以.2.并且成功将emWin人性化,可以做些Windows系统上的 ...

  3. C#上位机开发(二)—— Hello,World

    上一篇大致了解了一下单片机实际项目开发中上位机开发部分的内容以及VS下载与安装,按照编程惯例,接下来就是“Hello,World!” 1.新建C#项目工程 首先选择新建Windows窗体应用(.NET ...

  4. Winform 快速开发框架,上位机开发,工控机程序开发,CS程序开发

    1.当客户让你做个CS程序时,当你手上一穷二白,所有都要重复造轮,你是不是很烦. 2.但如果有一个通用的,快速开发框架,就可以把你从这些基础的工作解救出来,你专注做业务就好了. 3.本人其中一个项目的 ...

  5. 上位机开发之三菱Q系列PLC通信实践

    经常关注我们公众号或者公开课的学员(如果还没有关注的话,左上角点击一波关注)应该知道,我们会经常使用西门子PLC,其实对于其他品牌的PLC,我们都会讲到,包括三菱.欧姆龙.基恩士.松下及国产台达.信捷 ...

  6. 上位机开发之三菱FX3U以太网通信实践

    上次跟大家介绍了一下上位机与三菱Q系列PLC通信的案例,大家可以通过点击这篇文章:上位机开发之三菱Q系列PLC通信实践(←戳这里) 今天以三菱FX3U PLC为例,跟大家介绍一下,如何实现上位机与其之 ...

  7. 上位机开发之西门子PLC-S7通信实践

    写在前面: 就目前而言,在中国的工控市场上,西门子仍然占了很大的份额,因此对于上位机开发而言,经常会存在需要与西门子PLC进行通信的情况.然后对于西门子PLC来说,通信方式有很多,下面简单列举一下: ...

  8. C#上位机开发(一)—— 了解上位机

    在单片机项目开发中,上位机也是一个很重要的部分,主要用于数据显示(波形.温度等).用户控制(LED,继电器等),下位机(单片机)与 上位机之间要进行数据通信的两种方式都是基于串口的: USB转串口 — ...

  9. 医疗器械c#上位机开发指引教程

    此教程面向的读者:对医疗器械上位机编程有兴趣,或者急需了解医疗器械(尿常规.血液分析.生化.心电.B超等医疗下位仪器)的编程流程.编程细节的程序员. 1.得到仪器协议 当我们需要与医疗器械等下位机数据 ...

随机推荐

  1. DOM EVENT

    属性 此事件发生在何时... onabort 图像的加载被中断. onblur 元素失去焦点. onchange 域的内容被改变. onclick 当用户点击某个对象时调用的事件句柄. ondblcl ...

  2. Node.js最新技术栈之Promise篇

    前言 大家好,我是桑世龙,github和cnodejs上的i5ting,目前在天津创业,公司目前使用技术主要是nodejs,算所谓的MEAN(mongodb + express + angular + ...

  3. PHP empty、isset、isnull的区别

    PHP empty.isset.isnull的区别 empty 如果 变量 是非空或非零的值,则 empty() 返回 FALSE.换句话说,”".0.”0″.NULL.FALSE.arra ...

  4. Python入门之Python引用模块和查找模块路径

    #这篇文章主要介绍了Python引用模块和Python查找模块路径的相关资料,需要的朋友可以参考下 模块间相互独立相互引用是任何一种编程语言的基础能力.对于“模块”这个词在各种编程语言中或许是不同的, ...

  5. P3538 [POI2012]OKR-A Horrible Poem

    P3538 [POI2012]OKR-A Horrible Poem hash+线性筛 题解 <----这篇写的不错(其实是我懒得码字了qwq) UVA10298 Power Strings 的 ...

  6. Java ftp上传文件方法效率对比

    Java ftp上传文件方法效率对比 一.功能简介: txt文件采用ftp方式从windows传输到Linux系统: 二.ftp实现方法 (1)方法一:采用二进制流传输,设置缓冲区,速度快,50M的t ...

  7. c++获取时间戳

    vc获取时间戳的代码如下: SYSTEMTIME st; }; GetLocalTime(&st); // vc专用 _snprintf_s(ts, sizeof(ts), "%4d ...

  8. Python 自学基础(四)——time模块,random模块,sys模块,os模块,loggin模块,json模块,hashlib模块,configparser模块,pickle模块,正则

    时间模块 import time print(time.time()) # 当前时间戳 # time.sleep(1) # 时间延迟1秒 print(time.clock()) # CPU执行时间 p ...

  9. 20145304 Exp8 Web基础

    20145304 Exp8 Web基础 实验后回答问题 (1)什么是表单 表单用于搜集不同类型的用户输入,由三个基本组成部分表单标签.表单域.表单按钮.表单提交有两种方法,分别是get和post,使用 ...

  10. 20145322何志威 Exp8 Web基础

    20145322何志威 Exp8 Web基础 实践过程记录 一.Apache 1 修改/etc/apache2/ports.conf里的端口为5322后重新开启: 2 可以在浏览器中输入localho ...