周立功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("數據:\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"));

            }
        }
    }
    
    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能接收是因為,我在操作的時候犯懶,同時打開了兩個通道,也就是另一個通道有人接收,所以才能持續發數。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM