• 周立功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://blog.csdn.net/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("数据:
    ");
                        if(frameInfo[i].DataLen > 8)
                            frameInfo[i].DataLen = 8;
                        //原始数据----但是这里没有保存
                        for(int j = 0; j < frameInfo[i].DataLen;j++)
                        {
                            tmpstr.Format(_T("%04x 
    "),frameInfo[i].Data[j]);
                            str += tmpstr;                
                        }
    
                        ::SendMessage(dlg->GetSafeHwnd(),WM_WAVEFORM,WPARAM(&frameInfo[i]),NULL);
            
                        //TRACE(_T("receive
    "));
    
                }
            }
        }
        
        return 0;
    }

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

    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能接收是因为,我在操作的时候犯懒,同时打开了两个通道,也就是另一个通道有人接收,所以才能持续发数。

  • 相关阅读:
    Oracle的导入导出
    Android利用数据库传送数据
    Android相对布局实例
    git拉取github项目
    kafka支持认证SASL_PLAINTEXT
    fluentd插件开发
    解决go依赖包安装问题
    ES/Kibana支持search-guard认证
    logkit docker运行和代码下载
    CentOS安装指定git版本
  • 原文地址:https://www.cnblogs.com/ling123/p/8626093.html
Copyright © 2020-2023  润新知