參考資源:VC++編寫簡單串口上位機程序
串口通信,MCU跟PC通信經常用到的一種通信方式,做界面、寫上位機程序的編程語言、編譯環境等不少,VB、C#、LABVIEW等等,我會的語言很少,C語言用得比較多,但是還沒有找到如何用C語言來寫串口通信上位機程序的資料,在圖書管理找到了用VC++編寫串口上位機的資料,參考書籍,用自己相當蹩腳的C++寫出了一個簡單的串口上位機程序,分享一下,體驗一下單片機和PC通信的樂趣。 編譯環境:VC++6.0
操作系統:Windows XP
程序實現功能: 1、 PC初始化COM1口,使用n81方式,波特率9600與單片機通信。PC的COM口編號可以通過如下方式修改:
當然也可以通過上位機軟件編寫,通過按鈕來選擇COM端口號,但是此次僅僅是簡單的例程,就沒有弄那么復雜了。COM1口可用的話,會提示串口初始化完畢。否則會提示串口已經打開Port already open,表示串口已經打開,被占用了。
2、 點擊開始轉換,串口會向單片機發送0xaa,單片機串口中斷接收到0xaa后啟動ADC轉換一次,並把轉換結果ADCL、ADCH共兩個字節的結果發送至PC,PC進行數值轉換后在窗口里顯示。(見文章末尾圖)
3、 為防止串口被一只占用,點擊關閉串口可以關閉COM1,供其它程序使用,點擊后按鈕變為打開串口,點擊可重新打開COM1。
程序的編寫:
1、 打開VC++6.0建立基於對話框的MFC應用程序Test,
2、 在項目中插入MSComm控件:工程->增加到工程->Components and Controls->雙擊Registered ActiveX Controls->選擇Microsoft Communications Control, version 6.0->Insert,按默認值添加,你會發現多了個電話圖標,這是增加后串口通信控件。
3、 刪除確認、取消和提示框,添加“電話”、進程、靜態文本、按鈕、編輯框,拖動添加的控件,根據喜好布局。
4、 右擊編輯框Edit選擇屬性,在樣式里設置,勾選多行、垂直滾動,其它可按默認值。
右擊靜態文本Text選擇屬性,在常規設置里,修改標題。
右擊按鈕PushButton選擇屬性,在在常規設置里,修改標題。
修改后界面如下,程序寫出來運行時“電話”標志會自動消失。
5、 查看->建立類向導MFC ClassWizard->Member Viariable,選擇ClassName為CTestDlg的類,Control ID為MSCOMM1,雙擊它,為它添加控制變量m_comm1。
類似的,選擇IDC_BUTTON2添加控制變量m_serial。
(建立類向導也可以右擊然后在彈出的快捷菜單里選擇建立類向導)
至此,基本框架已經出來了,編譯后運行可以看到如下所示的界面。(組建->全部組件,然后 組建->執行)
6、 點擊左側的視圖窗口,可以在三種模式下切換,第三個是打開我們的源代碼窗口,第一個是類,第二個是窗體的資源視圖。
選擇File View,展開test files->Header Files,打開testDlg.h,在全局變量下添加如下代碼,然后保存:
int gllen;//定義整型標量gllen,用於記錄接收數據的個數
CProgressCtrl * pbar; //指向進度條的指針,用於操作進度條
CString strRXDdata; //編輯框顯示的文本,記錄歷次轉換值
7、 點擊Recourse View,展開test recourses->Dialog,雙擊IDD_TEST_DIALOG,編輯我們的主界面對話框。
雙擊“電話”,彈出如下對話框,按確認鍵:
VC會進入源碼編輯窗口,這個函數是用來處理串口事件的,當PC串口接收到數據時,會產生一個數據緩沖區有數據的消息事件,然后調用執行這個函數。添加如下代碼,進行數據處理,窗口更新等操作:

1 void CDavidDlg::OnOnCommMscomm1() 2 { 3 // TODO: Add your control notification handler code here 4 //**************************************** 5 VARIANT variant1;//定義VARIANT型變量,用於存放接收到的數據 6 COleSafeArray safearray;//定義safearray型變量 7 LONG LEN,k;//定義長整型變量len,k 8 BYTE rxdata[2048];//定義BYTE型數組 9 CString stremp1,stremp2;//定義兩個字符串 10 if(m_comm1.GetCommEvent()==2) //判斷引起OnComm時間的原因 {//如果是接收到特定個字節數,則讀取接收到的數據 11 variant1 = m_comm1.GetInput();//把接收到的數據存放到VARIANT型變量里 12 safearray = variant1;//VARIANT型變量轉換為ColeSafeArray型變量 13 LEN = safearray.GetOneDimSize(); 14 for(k=0;k<LEN;k++) 15 { 16 safearray.GetElement(&k,rxdata+k); //得到接接收到的數據放到BYTE型數組rxdata里 17 } 18 for(k=0;k<LEN;k++) 19 { 20 BYTE bt = (*(unsigned char*)(rxdata+k)); //讀取AD轉換的高字節 21 22 if((k%2)==0) 23 if((k+1)<LEN) 24 { 25 gllen++;//全局的變量,對接收到的轉換結果的個數進行計算 26 stremp2.Format("第%d次轉換結果:",gllen);//顯示第幾次轉換 27 int temp = bt*4+((*(unsigned char *)(rxdata+k+1))>>6); //高低字節合並成實際的轉換結果,注意轉換結果是左 對齊 28 stremp1.Format("%2.2f",(2.56*temp/1024));//計算成實際電壓值 29 SetDlgItemText(IDC_STATIC,("當前電壓值為: "+stremp1+" V")); //更新靜態文本控件 30 pbar -> SetPos(temp);//更新進度條的當前位置 31 strRXDdata += stremp2;//把新的數據放到全局的字符串里 32 strRXDdata += stremp1; 33 strRXDdata += " V\r\n";//字符串加單位V后換行 34 } 35 }
這時重新編譯一下,看會不會有什么錯誤,出現下面提示,可以選擇全部組建來清除。
LINK : LNK4073: cannot create map for .ILK file; linking nonincrementally
出現下面錯誤,請關閉運行的test.exe后重試。
LINK : fatal error LNK1104: cannot open file "Debug/test.exe"
出現下面錯誤兩種錯誤,是由於空間編號問題引起的,當我們添加了編輯框或者“電話”后再添加,其編號自動加一,就會出現控件沒定義。
Z:\vc++串口上位機\test\testDlg.cpp(32) : error C2065: 'IDC_MSCOMM1' : undeclared identifier
Z:\vc++串口上位機\test\testDlg.cpp(139) : error C2065: 'IDC_EDIT1' : undeclared identifier
解決方法是,在RecourseView里,打開窗體IDD_TEST_DIALOG,右擊“電話”或者編輯框等其它出錯的控件,右擊選擇屬性,在常規里修改ID,這里的程序,除BUTTON有1、2兩個之外,其它都是1
全部組建編譯一下,看看有沒有錯誤,沒有錯誤就可以運行一下,可以看到界面更原來是一樣的。有錯誤就修改一下,省得弄多了,錯在哪里都不知道,查起來麻煩。
8、 在源碼編輯里,打開testDlg.cpp文件,進行窗口初始化函數的編寫。
找到BOOL CTestDlg::OnInitDialog()函數,
在SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
后面添加如下初始化代碼:

1 // TODO: Add extra initialization here 2 //**************************************** 3 gllen = 0; //記錄轉換次數全局變量清零 4 if(! m_comm1.GetPortOpen())//判斷串口是否已經打開 5 { 6 m_comm1.SetCommPort(2); //選擇串口號2 7 m_comm1.SetPortOpen(TRUE); //打開串口 8 m_comm1.SetRThreshold(2); //收到兩個字節引發OnComm事件 9 m_comm1.SetInputMode(1);//輸入模式選為二進制 10 m_comm1.SetSettings("57600,n,8,1"); //設置串口參數,波特率57600,無奇偶校驗,1位停止位,8位數據位 11 MessageBox("串口初始化完畢","提示"); //提示串口成功初始化 12 } 13 else MessageBox("串口被占用","提示"); //如果已經打開串口,消息框提醒 14 pbar = (CProgressCtrl*)GetDlgItem(IDC_PROGRESS1);//獲得指向IDC_PROGRESS1的指針 15 pbar -> SetRange(0,1023);//設置進度條的范圍0~1023 16 pbar -> SetPos(0);//當前位置為0 17 m_serial.SetWindowText("關閉串口");//按鈕顯示狀態改變 18 //****************************************
可以看到,串口的參數等等都在在這里初始化的,可以根據自己的需要修改的,具體可以查看VC++里的詳細介紹,看看有哪些參數可以給我們修改來用。
添加后再編譯一下,運行后可以看到多了一個串口初始化的提示信息窗口。
至此,我們已經完成了主要的串口操作及界面,剩下的就是兩個按鈕的操作了。
9、 回到資源視圖的IDD_TEST_DIALOG窗口,雙擊開始轉換按鈕,給它添加事件,點擊后PC通過串口發送0xaa出來,給單片機接收。
添加如下代碼:

1 void CDavidDlg::OnButton1() 2 { 3 // TODO: Add your control notification handler code here 4 //**************************************** 5 CByteArray m_Array; //定義字節數組 6 m_Array.RemoveAll(); //字節數組清空 7 m_Array.SetSize(1); //設定維數為1 8 m_Array.SetAt(0,0xaa); //給m_array[0]賦值0 9 m_comm1.SetOutput(COleVariant(m_Array));//由於SetOutput函數的參數為VARIANT型,必須強制轉換后才能發送 10 //**************************************** 11 }
同樣地,雙擊另外一個按鈕,給串口操作按鈕添加代碼,用於關閉或者打開串口。添加如下代碼:

1 void CDavidDlg::OnButton2() 2 { 3 // TODO: Add your control notification handler code here 4 5 //**************************************** 6 if(! m_comm1.GetPortOpen())//判斷串口是否已經打開 7 { 8 m_comm1.SetPortOpen(TRUE); //如果串口是關閉的,則打開串口 9 m_serial.SetWindowText("關閉串口"); //按鈕顯示狀態改變 10 } 11 else 12 { 13 m_comm1.SetPortOpen(FALSE); //如果已經打開串口,則關閉串口 14 m_serial.SetWindowText("打開串口");//按鈕顯示狀態改變 15 } 16 //**************************************** 17 18 }
至此,一個簡單的串口上位機軟件編寫完成了,可以用來測試下,通過單片機往串口里發送數據,可以看到主窗口的的轉換結果,已經進度條顯示電壓值變化。要把這個程序拿出來用,只需把…\vc++串口上位機\test\Release的test.exe拷出來用就行。Release可以在編譯窗口里選擇win32 release,然后重新編譯一下就出來了。