|
我們飛思卡爾智能車的比賽已經接近尾聲了,23號就要去上海決賽了呵。論文基本完成,Duuboo已經排版結束,排出來還真挺長的。 整個過程都挺累的,持續(xù)的時間還這么長,最累的恐怕只有惠哥了,這些比賽完再說吧。這里介紹下在Donald寫的一個串口調試程序FSonPC,主要是方便我們查看運行起來的小車的內部參數(shù)。 程序的作用是接收來自Freescale MC9SDG128單片機發(fā)送來的數(shù)據(jù),并顯示出來。分為兩部分。一部分是普通的數(shù)據(jù),ASCII數(shù)據(jù),另一部分是實時的圖像,也是以ASCII方式傳輸。程序可通過“模式”按鈕設置工作在這兩種模式。當進入CCD模式時,通過“連接”按鈕觸發(fā)DG128單片機發(fā)送圖像信息,這時“連接”按鈕變?yōu)椤皵嚅_”,可通過其觸發(fā)DG128單片機停止發(fā)送,降低單片機內部資源消耗。 程序中串口部分的編程參考的是《Visual C++串口通信技術與工程實踐》,李現(xiàn)勇編著。書中對串口的編程講得很清楚,除了C的,還有VB的。詳細請參考該書。這里介紹本程序的一部分程序代碼。 一、添加串口控件 本程序是在VC6下MFC實現(xiàn),所以添加一個串口控件,方法是選擇Project菜單下Add To Project子菜單中的 Components and Controls選項,在彈出的對話框中進入Registered ActiveX Controls目錄,選擇Microsoft Communications Control, version 6.0,,Insert到Project中就行了。然后可在窗體中添加串口控件,接著Ctrl+W到ClassWizard中為該控件添加一個對應的變量就行。 二、串口工作方式的改變 串口的工作方式有很多種,比如串口的選擇、波特率、校驗位、數(shù)據(jù)位和停止位等。這些的設置可以通過組合框實現(xiàn),實現(xiàn)方法基本一樣,Donald就以波特率說明下。 之前已經為波特率組合框對象IDC_COMBO_BAUD添加對應變量m_cboBaud。在程序主窗體初始化時寫有如下代碼。 // combo Baud init m_cboBaud.AddString(_T("1200")); m_cboBaud.AddString(_T("2400")); m_cboBaud.AddString(_T("4800")); m_cboBaud.AddString(_T("9600")); m_cboBaud.AddString(_T("115200")); m_cboBaud.SetCurSel(3); m_strBaud = _T("9600"); 再在ClassWizard中為IDC_COMBO_BAUD添加CBN_SELCHANGE消息處理函數(shù),再在函數(shù)中添加代碼。 switch (m_cboBaud.GetCurSel()) { case 0: m_strBaud = _T("1200"); break; case 1: m_strBaud = _T("2400"); break; case 2: m_strBaud = _T("4800"); break; case 3: m_strBaud = _T("9600"); break; case 4: m_strBaud = _T("115200"); break; default: m_strBaud = _T("9600"); } InitCommPort(); 通過switch選擇不同的波特率。case的順序按照初始化添加時的順序,開始為0。 三、OnComm()消息處理函數(shù) 在串口控件中,最重要的就是OnComm()函數(shù),這個函數(shù)是用來處理串口消息事件的,每當串口接收到數(shù)據(jù),就會產生一個串口接收數(shù)據(jù)緩沖區(qū)中“有字符”的消息事件,只要在該函數(shù)內寫好處理這些字符的代碼就可以。 在Donald的這個程序里,該程序代碼如下。代碼的結構(switch結構)是參考該書。 VARIANT variant_inp; COleSafeArray safearray_inp; CByteArray arraySend; LONG len,k; BYTE rxdata[1024]; //An 8-bit integer that is not signed for Receive CString strAdd; int i; m_strComRcv.Empty(); switch(m_com.GetCommEvent()) { case 1: // comEvSend arraySend.ReMOVeAll(); arraySend.SetSize(m_strEditTrn.GetLength()); for (i = 0; i < m_strEditTrn.GetLength(); i++) { arraySend.SetAt(i, m_strEditTrn.GetAt(i)); } m_com.SetOutput(COleVariant(arraySend)); break; case 2: // comEvReceive variant_inp = m_com.GetInput(); // Read buffer safearray_inp = variant_inp; // VARIANT to ColeSafeArray len = safearray_inp.GetOneDimSize(); // Get effective length // Store to EDIT_REV for(k = 0; k < len; k++) { safearray_inp.GetElement(&k, rxdata + k); // Change to BYTE array BYTE bt = *(char*)(rxdata+k); // BYTE type strAdd += bt; } if (m_bCcdMode == FALSE){ m_myEdit.AppendText(strAdd); } else{ g_strRcv = strAdd; g_bReceive = TRUE; } break; default: // Error m_com.SetOutBufferCount(0); //AfxMessageBox("Com Err"); break; } 使用控件編程就是相對簡單,系統(tǒng)的串口消息發(fā)生時,程序自動調用該函數(shù)。由switch語句判斷,參數(shù)1為發(fā)送,參數(shù)2為接收。Donald主要介紹下接收的思路,由于接收時有兩種模式,所以通過m_bCcdMode成員變量記錄這兩種模式。當其為FALSE時直接實時顯示在窗口右邊,就像Windows處事的超級終端。如果是TRUE,則不顯示,將信息放到一個全局變量g_strRcv。其實盡量不要用全局變量,但Donald暫時也不懂線程間通信,只能簡單這么做了。并置位全局變量g_bReceive。該變量是在CCD模式下一個線程處理的依據(jù),實時顯示圖像。 四、文本框實時顯示和發(fā)送串口字符 為了能實現(xiàn)Windows超級終端文本框的功能,我們必須手動修改Edit類,然后用CWnd::SubclassDlgItem提供的動態(tài)連接功能,將原來的文本框和修改的類連接起來。 利用ClassWizard新建一個CEdit類自己命名(如CMyEdit),并在這個類中添加WM_CHAR消息處理函數(shù)和。確定后,再在新建類的頭文件中手動添加void AppendText (LPCSTR pText)函數(shù),用于將串口收到的字符輸出在文本框中。以及添加CByteArray arraySend成員變量,用于顯示字符的臨時存儲。 Donald程序中,CMyEdit::AppendText(LPCSTR pText)的內容如下: int nLen = GetWindowTextLength (); CString strTmp = pText; SetFocus (); if(strTmp.GetAt(0) == (char)0X08){ SetSel (nLen - 1, nLen); strTmp.Delete(0); Clear(); } else{ SetSel (nLen, nLen); ReplaceSel (pText); } 修改CMyEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)函數(shù),該函數(shù)實現(xiàn)在文本控件中輸入一字符時自動由串口向外發(fā)送。代碼如下: arraySend.ReMOVeAll(); arraySend.SetSize(1); arraySend.SetAt(0, (char)nChar); ((CFSonPCDlg*)AfxGetApp()->m_pMainWnd)->m_com.SetOutput(COleVariant(arraySend)); //CEdit::OnChar(nChar, nRepCnt, nFlags); 程序的第四行語句必是向Donald的窗體串口控件關聯(lián)變量m_com輸出字符,注意要調用COleVariant()函數(shù)。 五、實時接收圖像線程 眾所周知,如果一個程序用了while(1)語句的話,那么這個程序將是一個死循環(huán),將嚴重占用CPU資源,而這里,我們的程序又需要用while來實時地接收來自單片機的串口信息,于是,最好的解決方法就是用線程。 調用線程的思路是這樣,當程序處于文本框模式時,線程是關閉的。如果進行CCD模式,還沒“連接”,程序狀態(tài)同上,如果進行“連接”狀態(tài),則創(chuàng)建實時接收線程,并設置一定時器以在一定時間未接收到來自串口的握手信號時自動斷開。如果從“連接”狀態(tài)手動或自動“斷開”,則將線程終止,向單片機發(fā)送終止傳送圖像信號并關閉定時器。至此,完成顯示圖像流程。 線程中首先是發(fā)送握手信號FS_CONNECTION。 arraySend.SetSize(1); arraySend.SetAt(0, FS_CONNECTION); g_bOvertime = FALSE; // Send Connection Signal ((CFSonPCDlg*)AfxGetApp()->m_pMainWnd)->m_com.SetOutput(COleVariant(arraySend)); 然后無限循環(huán)等待接收,用了兩層while語句,第一個等待來自單片機的握手信號,第二個處理接收圖像。 while(1) { // Wait for receiving ((CFSonPCDlg*)AfxGetApp()->m_pMainWnd)->GetDlgItem(IDC_STATIC_NOW) ->SetWindowText("等待..."); g_bReceive = FALSE; SetTimer(AfxGetApp()->GetMainWnd()->m_hWnd, 1, 1000, NULL); g_bOvertime = TRUE; while(!g_bReceive); KillTimer(AfxGetApp()->GetMainWnd()->m_hWnd, 1); g_bReceive = FALSE; g_bOvertime = FALSE; if (-1 == g_strRcv.Find(FS_CONNECTION)){ // Not found continue; }else{ ((CFSonPCDlg*)AfxGetApp()->m_pMainWnd)->GetDlgItem(IDC_STATIC_NOW) ->SetWindowText("連接完成, 接收中"); row = 0; col = 0; while(1) { SetTimer(AfxGetApp()->GetMainWnd()->m_hWnd, 1, 1000, NULL); g_bOvertime = TRUE; g_bReceive = FALSE; while(!g_bReceive); KillTimer(AfxGetApp()->GetMainWnd()->m_hWnd, 1); g_bReceive = FALSE; g_bOvertime = FALSE; //AfxMessageBox(g_strRcv); n = g_strRcv.Find(FS_TRANFINISH); if(-1 == n){ // Not finish n = g_strRcv.GetLength(); for(i = 0; i < n; i++){ cRcv[row][col++] = g_strRcv.GetAt(i); if(col == MAX_CCD_Y){ col = 0; if(++row == MAX_CCD_X){ row = 0; } } } }else{ // Finish //n = g_strRcv.Find(FS_TRANFINISH); for(i = 0; i < n; i++){ cRcv[row][col++] = g_strRcv.GetAt(i); if(col == MAX_CCD_Y){ col = 0; if(++row == MAX_CCD_X){ row = 0; } } } ((CFSonPCDlg*)AfxGetApp()->m_pMainWnd)->m_ccdGraph.ChangeBit(cRcv); ((CFSonPCDlg*)AfxGetApp()->m_pMainWnd)->GetDlgItem(IDC_STATIC_NOW) ->SetWindowText("接收完畢"); // Show it ((CFSonPCDlg*)AfxGetApp()->m_pMainWnd)->m_ccdGraph.ShowGraph( ((CFSonPCDlg*)AfxGetApp()->m_pMainWnd)->m_dcViewS); ((CFSonPCDlg*)AfxGetApp()->m_pMainWnd)->m_dcViewB.StretchBlt( 0, 0, MAX_CCD_X * 4, MAX_CCD_Y * 4, ((CFSonPCDlg*)AfxGetApp()->m_pMainWnd) ->m_dcViewS.GetWindow()->GetDC(), 0, 0, MAX_CCD_X, MAX_CCD_Y, SRCCOPY); break; } } } } 結束語 整個程序的總體思路便是如上,并沒有對程序的每一個細節(jié)都作介紹。書上和網上也能查到很多各種串口的程序,寫得相當優(yōu)秀,Donald的程序比較粗糙,但基本能完成我們智能小車的調試功能,目的就達到了。如果感興趣,可向Donald要源碼。 
|