VC串口通信實驗


實驗一:VC串口通信實驗

一、實驗目的

通過實驗讓學生了解串口通信的工作原理。

 

二、實驗器材

計算機兩台

串口連接線一根
GND(pin5) 

GND(pin5)

TXD(pin3)  RXD(pin2)

RXD(pin2)  TXD(pin3)

RTS(pin7)  CTS(pin8)

CTS(pin8)  RTS(pin7)

DSR(pin6)  DTR(pin4)

DTR(pin4)  DSR(pin6)

 

三、實驗要求

用VC++對計算機的串口進行編程,並作一個簡單的串口查詢通訊程序。用一條九針的DB-9串口通訊線將計算機的com1口和com2口相連,com1作為接受口,com2作為發送口。

四、實驗步驟

1.建立項目

打開VC++6.0,建立一個基於對話框的MFC應用程序SCommTest(與我源代碼一致,等會你會方便一點);

2.在項目中插入MSComm控件 

  選擇Project菜單下Add To Project子菜單中的 Components and Controls…選項,在彈出的對話框中雙擊Registered ActiveX Controls項(稍等一會,這個過程較慢),則所有注冊過的ActiveX控件出現在列表框中。 選擇Microsoft Communications Control, version 6.0,單擊Insert按鈕將它插入到我們的Project中來,接受缺省的選項。(如果你在控件列表中看不到Microsoft Communications Control, version 6.0,那可能是你在安裝VC6時沒有把ActiveX一項選上,重新安裝VC6,選上ActiveX就可以了),這時在ClassView視窗中就可以看到CMSComm類了,(注意:此類在ClassWizard中看不到,重構clw文件也一樣),並且在控件工具欄Controls中出現了電話圖標(如圖1所示),現在要做的是用鼠標將此圖標拖到對話框中,程序運行后,這個圖標是看不到的。

 

3.利用ClassWizard定義CMSComm類控制對象 

打開ClassWizard->Member Viariables選項卡,選擇CSCommTestDlg類,為IDC_MSCOMM1添加控制變量:m_ctrlComm,這時你可以看一看,在對話框頭文件中自動加入了//{{AFX_INCLUDES()  #include "mscomm.h"  //}}AFX_INCLUDES (這時運行程序,如果有錯,那就再從頭開始)。

4.在對話框中添加控件 

向主對話框中添加兩個編輯框,一個用於接收顯示數據ID為IDC_EDIT_RXDATA,另一個用於輸入發送數據,ID為IDC_EDIT_TXDATA,再添加一個按鈕,功能是按一次就把發送編輯框中的內容發送一次,將其ID設為IDC_BUTTON_MANUALSEND。別忘記了將接收編輯框的Properties->Styles中把Miltiline和Vertical Scroll屬性選上,發送編輯框若你想輸入多行文字,也可選上Miltiline。再打開ClassWizard->Member Viariables選項卡,選擇CSCommTestDlg類,為IDC_EDIT_RXDATA添加CString變量m_strRXData, 為IDC_EDIT_TXDATA添加CString變量m_strTXData。說明: m_strRXData和m_strTXData分別用來放入接收和發送的字符數據。

5.添加串口事件消息處理函數OnComm()

打開ClassWizard->Message Maps,選擇類CSCommTestDlg,選擇IDC_MSCOMM1,雙擊消息OnComm,將彈出的對話框中將函數名改為OnComm 。 這個函數是用來處理串口消息事件的,如每當串口接收到數據,就會產生一個串口接收數據緩沖區中有字符的消息事件,我們剛才添加的函數就會執行,我們在OnComm()函數加入相應的處理代碼就能實現自已想要的功能了。請你在函數中加入如下代碼:

void CSCommTestDlg::OnComm()

{

// 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;
    if(m_ctrlComm.GetCommEvent()==2) //事件值為2表示接收緩沖區內有字符
    {             ////////以下你可以根據自己的通信協議加入處理代碼
        variant_inp=m_ctrlComm.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); //字符型
            strtemp.Format("%c",bt); //將字符送入臨時變量strtemp存放
            m_strRXData+=strtemp; //加入接收編輯框對應字符串 
        }
    }
    UpdateData(FALSE); //更新編輯框內容
}

6.打開串口和設置串口參數 

你可以在你需要的時候打開串口,例如在程序中做一個開始按鈕,在該按鈕的處理函數中打開串口。現在我們在主對話框的CSCommTestDlg::OnInitDialog()打開串口,加入如下代碼:

// TODO: Add extra initialization here

if(m_ctrlComm.GetPortOpen())

m_ctrlComm.SetPortOpen(FALSE);

m_ctrlComm.SetCommPort(1); //選擇com1

if( !m_ctrlComm.GetPortOpen())

m_ctrlComm.SetPortOpen(TRUE);//打開串口

else

AfxMessageBox("cannot open serial port");

m_ctrlComm.SetSettings("9600,n,8,1"); //波特率9600,無校驗,8個數據位,1個停止位

m_ctrlComm.SetInputMode(1); //1:表示以二進制方式檢取數據
m_ctrlComm.SetRThreshold(1); 

//參數1表示每當串口接收緩沖區中有多於或等於1個字符時將引發一個接收數據的OnComm事件

m_ctrlComm.SetInputLen(0); //設置當前接收區數據長度為0

m_ctrlComm.GetInput();//先預讀緩沖區以清除殘留數據

現在你可以試試程序了,將串口線接好后,打開串口調試助手,並將串口設在com2,選上自動發送,也可以等會手動發送。再執行你編寫的程序,接收框里應該有數據顯示了。

7.發送數據 

先為發送按鈕添加一個單擊消息即BN_CLICKED處理函數,打開ClassWizard->Message Maps,選擇類CSCommTestDlg,選擇IDC_BUTTON_MANUALSEND,雙擊BN_CLICKED添加OnButtonManualsend()函數,並在函數中添加如下代碼:

void CSCommTestDlg::OnButtonManualsend() 

{

// TODO: Add your control notification handler code here

 UpdateData(TRUE); //讀取編輯框內容

m_ctrlComm.SetOutput(COleVariant(m_strTXData));//發送數據

}

最后說明一下,由於用到VC控件,在沒有安裝VC的計算機上運行時要從VC中把mscomm32.ocx、msvcrt.dll、mfc42.dll拷到Windows目錄下的System子目錄中(win2000為System32)並再進行注冊設置,

8.發送十六進制字符

    在主對話框中加入一個復選接鈕,ID為IDC_CHECK_HEXSEND ,Caption: 十六進制發送,再利用ClassWizard為其添加控制變量:m_ctrlHexSend;

    在ClassView中為SCommTestDlg類添加以下兩個PUBLIC成員函數,並輸入相應代碼;

 

//由於這個轉換函數的格式限制,在發送框中的十六制字符應該每兩個字符之間插入一個空隔
//如:A1 23 45 0B 00 29
//CByteArray是一個動態字節數組,可參看MSDN幫助
int CSCommTestDlg::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 CSCommTestDlg::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);
}

 

  再將CSCommTestDlg::OnButtonManualsend()修改成以下形式:

void CSCommTestDlg::OnButtonManualsend() 
{
// TODO: Add your control notification handler code here
UpdateData(TRUE); //讀取編輯框內容
if(m_ctrlHexSend.GetCheck())
{
CByteArray hexdata;
int len=String2Hex(m_strTXData,hexdata); //此處返回的len可以用於計算發送了多少個十六進制數
m_ctrlComm.SetOutput(COleVariant(hexdata)); //發送十六進制數據
}
else 
m_ctrlComm.SetOutput(COleVariant(m_strTXData));//發送ASCII字符數據

}

現在,你先將串口線接好並打開串口調試助手V2.1,選上以十六制顯示,設置好相應串口,然后運行我們這個程序,在發送框中輸入00 01 02 03 A1 CC等十六進制字符,並選上以十六進制發送,單擊手動發送,在串口調試助手的接收框中應該可以看到00 01 02 03 A1 CC了。

9.在接收框中以十六進制顯示

    在主對話框中加入一個復選接鈕,IDC_CHECK_HEXDISPLAY, Caption: 十六進制顯示,再利用ClassWizard為其添加控制變量:m_ctrlHexDisplay。 然后修改CSCommTestDlg::OnComm()函數:

void CSCommTestDlg::OnComm() 
{
// 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;
if(m_ctrlComm.GetCommEvent()==2) //事件值為2表示接收緩沖區內有字符
{
variant_inp=m_ctrlComm.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_ctrlHexDisplay.GetCheck())
strtemp.Format("%02X ",bt); //將字符以十六進制方式送入臨時變量strtemp存放,注意這里加入一個空隔
else 
strtemp.Format("%c",bt); //將字符送入臨時變量strtemp存放

m_strRXData+=strtemp; //加入接收編輯框對應字符串 
}
}
UpdateData(FALSE); //更新編輯框內容
}

測試:在串口調試助手發送框中輸入00 01 02 03 A1 CC等十六進制字符,並選上以十六進制發送,單擊手動發送,在本程序運行后選上以十六進制顯示,在串口調試助手中單擊手動發送或自動發送,則在本程序的接收框中應該可以看到00 01 02 03 A1 CC了。

10.如何設置自動發送

     最簡單的設定自動發送周期是用SetTimer()函數,這在數據采集中很有用,在控制中指令的傳送也可能用到定時發送。

方法是:在ClassWizard中選上MessageMap卡,然后在Objects IDs選中CSCommTestDlg類,再在Messages框中選上WM_TIMER消息,單擊ADD_FUNCTION

加入void CSCommTestDlg::OnTimer(UINT nIDEvent) 函數,這個函數是放入“時間到”后要處理的代碼:

void CSCommTestDlg::OnTimer(UINT nIDEvent) 
{
// TODO: Add your message handler code here and/or call default
OnButtonManualsend();
CDialog::OnTimer(nIDEvent);
}

再在在主對話框中加入一個復選接鈕,ID為IDC_CHECK_AUTOSEND Caption: 自動發送(周期1秒),再利用ClassWizard為其添加BN_CLICK消息處理函數void CSCommTestDlg::OnCheckAutosend():

void CSCommTestDlg::OnCheckAutosend() 
{
// TODO: Add your control notification handler code here
m_bAutoSend=!m_bAutoSend;
if(m_bAutoSend)
{
SetTimer(1,1000,NULL);//時間為1000毫秒
}
else
{
KillTimer(1);  //取消定時
}
}

其中:m_bAutoSend為BOOL型變量,在CLASSVIEW中為CSCommTestDlg類加入,並在構造函數中初始化:

      m_bAutoSen=FALSE;
現在可以運行程序測試了。

 

源程序如下:

#include <stdio.h>
#include <dos.h>
#include <conio.h>
#include <io.h>
#include <alloc.h>
#include <ctype.h>
#define Com1Base 0x3f8        //定義串口com1的基地址;
#define Com2Base 0x2f8        //定義串口com2的基地址;
void main()
{
   int st,fp,jp,op;
   int d=0;
   outportb(Com2Base+3,0x80);    //允許訪問com2的波特率因子寄存器,禁止串口中斷,無校驗,無停止位;
   outportb(Com2Base+0,0x0c);    //設置com2的波特率低八位;
   outportb(Com2Base+1,0x00);    //設置com2的波特率高八位;
   outportb(Com2Base+3,0x03);    //禁止訪問com2口的波特率因子寄存器,禁止串口中斷,無校驗,無停止位,八位數據;
   outportb(Com2Base+4,0);      //初始化串口com2的MODEM寄存器;
   outportb(Com2Base+1,0);      //初始化com2的中斷允許寄存器,屏蔽串口com2
                  的中斷;
   outportb(Com1Base+3,0x80);    //允許訪問com1的波特率因子寄存器,禁止串口中斷,無校驗,無停止位;

   outportb(Com1Base+0,0x0c);        //設置com1的波特率低八位;
   outportb(Com1Base+1,0x00);        //設置com2的波特率高八位;
   outportb(Com1Base+3,0x03);        禁止訪問com2口的波特率因子寄存器,禁止串口中斷,無校驗,無停止位,八位數據;

   outportb(Com1Base+4,0);             //初始化串口com2的MODEM寄存器;
   outportb(Com1Base+1,0);             //初始化com2的中斷允許寄存器,屏蔽串口com2
                  的中斷;

   printf("please transfer a character to com2:\n");
   do{
     do{
  st=inportb(Com2Base+5);
  printf("%x\n",st);
       }while(st&0x20!=0x20);          //查詢com2的線路狀態寄存器(LSR)的第五位
                   的狀態是否為1,即發送保持寄存器是否為空?也就是com2口是否准備好發送數據;若否則循環等待;
     while(1)
     {
       printf("%x\n",d+1);
       outportb(Com2Base,++d);    //從com2口發送數據;
       do{
    fp=inportb(Com2Base+5);
}while((fp&0x20)!=0x20);   //查詢等待com2的線路狀態寄存器(LSR)的第五位的狀態是否為1,即發送保持寄存器是否為空?若否則循環等待;
       break;           //com2的線路狀態寄存器(LSR)的第五位的狀態是否為1,即發送保持寄存器為空,跳出數據發送程序。
     }
     printf("display  the character while was just received from com1\n");
     do
      {
    jp=inportb(Com1Base+5);
 }while((jp&0x01)!=1);          //查詢等待com1的線路狀態寄存器(LSR)的第五位的狀態是否為1,即發送保持寄存器是否為空?若否則循環等待;

     printf("%x\n",inportb(Com1Base+0));  //com1口讀入數據,並在屏幕上顯示出來;
    do
     {
  op=inportb(Com1Base+5);
       }while((op&0x01)!=0);    //查詢等待com1的線路狀態寄存器(LSR)的第五位的狀態是否為1,即發送保持寄存器是否為空?若否則循環等待;


      delay(10000);                        //延時;
  }while(!kbhit());             //敲任意鍵退出程序;
}

 


免責聲明!

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



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