c++串口上位機調試助手的總結和部分代碼

          我記得是大一下學期學習的C++,是選修課。老師也是隨便點幾章講講,當時什麼都不懂,就記得一些類的概念。畢業了,工作了。公司給了個小考覈,其中有一項串口上位機軟件的,於是花了幾天時間,站在前人的肩膀上,做了一個還看得過去的串口上位機軟件。今天就總結一下學習的感受和貼出部分重要的代碼,以防以後需要,找不到。

       我個人覺得學習力,就是學習某種知識的能力,我們學習的時間越短,效率就越高,能力就越強。不過學習力也是需要培養的,方法和經驗也需要積累的。高手也是從菜鳥開始的,以下是我做上位機的一些感受。

       一、我覺得學會搜索很重要。很多東西前人都做過,我們只需站在前人的肩膀上,再加工或修改就會變成自己的,這樣可以縮短時間,提高效率。所以能有效的搜索到最靠近的資料,就越快達到目標。個人覺得www.baigoogledu.com這個集成谷歌、百度的搜索很好用。

       二、在什麼都不懂的情況下,我們要先拿來,借鑑別人所作的東西,去完成自己要做的東西。

       三、當我們已經會做某一東西時,我們要回過頭來看看。想想爲什麼要這樣做?這樣做的目的是什麼?有沒有其他更好的方法?這種方法有沒有缺點等等。這樣既能鞏固我們學習的知識,又能引發我們的思考,讓我們達到更高的層次。

       四、高手們常教導我們,筆記很重要。及時記錄一些內容,並整理有利於以後翻閱,查找,我也是剛剛起步。

以下是我寫上位機的一些重要代碼。一些變量代碼命名有些不規範,希望自己在以後學習和運用中不斷改正。我轉了一篇C++串口調試助手的文章,步驟可以模仿,在這裏就不寫步驟了,就直接貼部分代碼了。

在初始化函數中添加:

 // TODO: Add extra initialization here

 m_bAutoSend=FALSE;  //m_bAutoSend爲全局變量
 m_nCycleTime=1000;

 m_GPSCOM.SetCommPort(1);   //選擇COM1
 m_GPSCOM.SetInputMode(1); //輸入方式爲二進制方式
 m_GPSCOM.SetInBufferSize(1024); //設置輸入緩衝區大小
 m_GPSCOM.SetOutBufferSize(512); //設置輸出緩衝區大小
// m_GPSCOM.SetSettings("9600,n,8,1"); //波特率115200,無校驗,8個數據位,1個停止位
 if(m_GPSCOM.GetPortOpen())   //如果串口是打開的
   m_GPSCOM.SetPortOpen(FALSE);//則關閉串口

 //參數1表示每當串口接收緩衝區中有多於
 //或等於1個字符時將引發一個接收數據的OnComm事件
 m_GPSCOM.SetRThreshold(1);
 m_GPSCOM.SetInputLen(0);  //設置當前接收區數據長度爲0
 //m_GPSCOM.GetInput();    //先預讀緩衝區以清除殘留數據 

 

顯示接收到的內容

m_RXDATA建立的變量是CEidtl類型,不是CString

 

static long  RXcount=0;
void CGPS_COM1Dlg::OnGPSCOM()           //編輯框接收函數——用於顯示串口接收到的數據
{
 // TODO: Add your control notification handler code here
 VARIANT variant_inp;
 COleSafeArray safearray_inp;
 LONG len,k;
 BYTE rxdata[2048]; //設置BYTE數組 An 8-bit integerthat is not signed.
 CString strtemp;
 CString strRXData;
  
 strRXData.Empty();
    if(m_GPSCOM.GetCommEvent()==2)     //事件值爲2表示接收緩衝區內有字符
 {
  variant_inp=m_GPSCOM.GetInput();   //讀緩衝區
  safearray_inp=variant_inp;           //VARIANT型變量轉換爲ColeSafeArray型變量
  len=safearray_inp.GetOneDimSize(); //得到有效數據長度
  for(k=0;k<len;k++)
   safearray_inp.GetElement(&k,rxdata+k);//轉換爲BYTE型數組

  for(k=0;k<len;k++)             //將數組轉換爲Cstring型變量
  {
   BYTE bt=*(char*)(rxdata+k);      //字符型
   if(m_hexdisplay.GetCheck())
   {
    strtemp.Format("%02x ",bt);         //將十六進制送入臨時變量strtemp存放
   }
   else
   {
    strtemp.Format("%c",bt);         //將字符送入臨時變量strtemp存放
   }
   strRXData=strRXData+strtemp;    //加入接收編輯框對應字符串
   
  }

  m_RXDATA.ReplaceSel(strRXData);
  
  RXcount=RXcount+len;
  CString rxdatacount;
  rxdatacount.Format("%ld",RXcount);
//  rxdatacount="接收:"+rxdatacount;

  m_RXdatacount.SetWindowText(rxdatacount); //顯示接收計數
  
 }
}

//不用UpdateData(),這樣寫的好處就是當一直在接收時,也能自由發送而不受UpdateData()函數影響。

 

串口打開或關閉

void CGPS_COM1Dlg::OnButton1()     //串口打開或關閉
{
 // TODO: Add your control notification handler code here
 if(m_ctrlAutoSend.GetCheck())
 {
  AfxMessageBox("請先關閉自動發送,纔可以關閉串口");
 }
 else
 {
  
  if(!m_GPSCOM.GetPortOpen())
  {
   m_GPSCOM.SetPortOpen(TRUE);//打開串口
   SetDlgItemText(IDC_BUTTON1,"關閉串口");

  }
  else
  {
   m_GPSCOM.SetPortOpen(FALSE);//關閉串口
   SetDlgItemText(IDC_BUTTON1,"打開串口");
  }
 }
}

 m_TXDATA建立的變量是CEidtl類型,不是CString

long TX_count=0;
void CGPS_COM1Dlg::OnButton2Tx()   //數據發送函數
{
 // TODO: Add your control notification handler code here
 if(!m_GPSCOM.GetPortOpen())
 {
  AfxMessageBox("請先打開串口");
 }
 else
 {
  UpdateData(TRUE); //讀取編輯框內容
  if(m_strHexSend.GetCheck())
  {
   CByteArray hexdata;
   int len=String2Hex(m_TXDATA,hexdata); //此處返回的len可以用於計算髮送了多少個十六進制數
   m_GPSCOM.SetOutput(COleVariant(hexdata)); //發送十六進制數據
   TX_count+=(long) ((m_TXDATA.GetLength()+1)/3); //十六進制計數要注意算法
  }
  else
  {
   m_GPSCOM.SetOutput(COleVariant(m_TXDATA));//發送ASCII字符數據
   TX_count+=m_TXDATA.GetLength();
  }
  CString strTemp;
  strTemp.Format("%d",TX_count);
  m_TXcount.SetWindowText(strTemp);
 }
}

 

void CGPS_COM1Dlg::OnButton3Clear()  //清除接收編輯框
{
 // TODO: Add your control notification handler code here
 m_RXDATA.SetWindowText("");//清除接收對話框中的數據


}

void CGPS_COM1Dlg::OnCHECK1hexdisplay()
{
 // TODO: Add your control notification handler code here
 
}


void CGPS_COM1Dlg::OnCOMchose()
{
 // TODO: Add your control notification handler code here
 char str[10];
 
 int iPos=m_COMChose.GetCurSel();
 int iNum=m_COMChose.GetLBText( iPos, (LPTSTR) str) ;
 int i=atoi(str+3);
 if(m_GPSCOM.GetPortOpen())
 {
  AfxMessageBox("請先關閉串口,再選擇COM口");
 }
 if(!m_GPSCOM.GetPortOpen())
 {
  m_GPSCOM.SetCommPort(i);   //選擇COM口

 }

}

 

void CGPS_COM1Dlg::OnHexSend()
{
 // TODO: Add your control notification handler code here


}


void CGPS_COM1Dlg::OnTimer(UINT nIDEvent)
{
 // TODO: Add your message handler code here and/or call default
 switch(nIDEvent)
 {
  case 1:  OnButton2Tx();
   break;
/*  case 2:
   m_strCurPath.SetWindowText(m_strCurPath); //重新顯示路徑
   KillTimer(2);//關閉定時器

*/
  default:      ;
   break;
 
 }
 CDialog::OnTimer(nIDEvent);
}

void CGPS_COM1Dlg::OnCheck2_AutoSend()
{
 // TODO: Add your control notification handler code here
 
 m_bAutoSend=!m_bAutoSend;  //標誌是否打開自動發送
 if(m_bAutoSend)
 {
  if(!m_GPSCOM.GetPortOpen())
  {
   m_bAutoSend=!m_bAutoSend;
   m_ctrlAutoSend.SetCheck(0);
   AfxMessageBox("串口沒有打開,請打開串口");
   return;
  }
  else
   SetTimer(1,m_nCycleTime,NULL); //設置定時器1 m_nCycleTime
 }
 else
 {
  KillTimer(1);  //“殺”掉定時器1
 }
}

void CGPS_COM1Dlg::OnChangeEdit_CycleTime()
{
 // TODO: If this is a RICHEDIT control, the control will not
 // send this notification unless you override the CDialog::OnInitDialog()
 // function and call CRichEditCtrl().SetEventMask()
 // with the ENM_CHANGE flag ORed into the mask.
 
 // TODO: Add your control notification handler code here
 
 CEdit* pEdit=(CEdit*)GetDlgItem(IDC_EDIT1);
 CString strText;
 pEdit->GetWindowText(strText);
 m_nCycleTime=atoi(strText);


}

void CGPS_COM1Dlg::OnClearCount()
{
 // TODO: Add your control notification handler code here
 RXcount=0;  
 TX_count=0;
 m_RXdatacount.SetWindowText("");//清除接收對話框中的數據
 m_TXcount.SetWindowText("");//清除接收對話框中的數據
}

void CGPS_COM1Dlg::OnButtonSavedata()
{
 // TODO: Add your control notification handler code here
 UpdateData(TRUE);

 CFile m_rFile;
 LPCSTR lpszPath="c:\\comdata";   //存盤路徑爲c:\\comdata
 SetCurrentDirectory(lpszPath);

 CString  m_Filename,m_name1;
 m_name1=m_strFilename;   //填寫的名字
 m_Filename=m_name1+".txt";  //以TXT格式存儲
 if(0==m_strFilename.GetLength())
 {
  AfxMessageBox("請先輸入保存的文件名");
 }
 else
 {
  if(!m_rFile.Open(m_Filename,CFile::modeCreate | CFile::modeWrite))
  {
   AfxMessageBox("創建記錄文件失敗!");
   return;
  }

  char TxtTemp[60000];   //字符串的長度
  int Maxsize=60000;    //
  int i=m_RXDATA.GetWindowText(TxtTemp,Maxsize);//將接受編輯框的數據取出放到數組TxtTemp中
  
  //在文件開始處寫上保存日期
  CTime t=CTime::GetCurrentTime();
  CString str=t.Format("%Y年%m月%d日%H時%M分%S秒\r\n");
  m_rFile.Write((LPCTSTR)str,str.GetLength());
  //保存數據
  m_rFile.Write((LPCTSTR)TxtTemp,m_RXDATA.GetWindowTextLength());
  m_rFile.Flush(); 
  m_rFile.Close();  //關閉文件
  AfxMessageBox("保存成功");
  UpdateData(FALSE);
 }
}

 

在頭文件中要聲明這兩個函數

  //由於這個轉換函數的格式限制,在發送框中的十六制字符應該每兩個字符之間插入一個空隔
  //如:A1 23 45 0B 00 29
  //CByteArray是一個動態字節數組,可參看MSDN幫助
int CGPS_COM1Dlg::String2Hex(CString str, CByteArray &senddata)
{
  int hexdata,lowhexdata;
  int hexdatalen=0;
  int len=str.GetLength();
  senddata.SetSize(len/2);
  for(int i=0;i<len;)
  {
   char lstr,hstr=str[i];
   if(hstr==' ')
   {
    i++;
    continue;
   }
   i++;
   if(i>=len)
   break;

   lstr=str[i];
   hexdata=ConvertHexChar(hstr);
   lowhexdata=ConvertHexChar(lstr);
   if((hexdata==16)||(lowhexdata==16))
    break;
   else
    hexdata=hexdata*16+lowhexdata;
   i++;
   senddata[hexdatalen]=(char)hexdata;
   hexdatalen++;
  }
  senddata.SetSize(hexdatalen);
  return hexdatalen;
}

  //這是一個將字符轉換爲相應的十六進制值的函數
  //好多C語言書上都可以找到
  //功能:若是在0-F之間的字符,則轉換爲相應的十六進制字符,否則返回-1
char CGPS_COM1Dlg::ConvertHexChar(char ch)
{
  if((ch>='0')&&(ch<='9'))
   return ch-0x30;
  else if((ch>='A')&&(ch<='F'))
    return ch-'A'+10;
  else if((ch>='a')&&(ch<='f'))
    return ch-'a'+10;
  else return (-1);
}

 

我做成時的界面

 

發佈了23 篇原創文章 · 獲贊 12 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章