/*------+------+------+------+------+------+------+------+------+------+------+------
串口編程的一個實例
為了讓您更好地理解串口編程,下面我們分別編寫兩個例程(見附帶的源碼部分),
這兩個例程都實現了工控機與百特顯示儀表通過RS485接口進行的串口通信。其中
第一個例程采用同步串口操作,第二個例程采用異步串口操作。
我們只介紹軟件部分,RS485接口接線方法不作介紹,感興趣的讀者可以查閱相關資料。
/*------+------+------+------+------+------+------+------+------+------+------+----*/
/*------+------+------+------+------+------+------+------+------+------+------+------+
打開VC++6.0,新建基於對話框的工程RS485Comm,在主對話框窗口IDD_RS485COMM_DIALOG
上添加兩個按鈕,ID分別為IDC_SEND和IDC_RECEIVE,標題分別為“發送”和“接收”;添加一
個靜態文本框IDC_DISP,用於顯示串口接收到的內容。
------+------+------+------+------+------+------+------+------+------+------+-------*/
//在RS485CommDlg.cpp文件中添加全局變量:
HANDLE hCom; //全局變量,串口句柄
//在RS485CommDlg.cpp文件中的OnInitDialog()函數添加如下代碼:
// TODO: Add extra initialization here
hCom=CreateFile( "COM1", //COM1口
GENERIC_READ|GENERIC_WRITE, //允許讀和寫
0, //獨占方式
NULL,
OPEN_EXISTING, //打開而不是創建
0, //同步方式
NULL );
if(hCom==(HANDLE)-1)
{
AfxMessageBox("打開COM失敗!");
return FALSE;
}
SetupComm(hCom,100,100); //輸入緩沖區和輸出緩沖區的大小都是1024
COMMTIMEOUTS TimeOuts;
//設定讀超時
TimeOuts.ReadIntervalTimeout=MAXDWORD;
TimeOuts.ReadTotalTimeoutMultiplier=0;
TimeOuts.ReadTotalTimeoutConstant=0;
//在讀一次輸入緩沖區的內容後讀操作就立即返回,
//而不管是否讀入了要求的字符。
//設定寫超時
TimeOuts.WriteTotalTimeoutMultiplier=100;
TimeOuts.WriteTotalTimeoutConstant=500;
SetCommTimeouts(hCom,&TimeOuts); //設置超時
DCB dcb;
GetCommState(hCom,&dcb);
dcb.BaudRate=9600; //波特率為9600
dcb.ByteSize=8; //每個字節有8位
dcb.Parity=NOPARITY; //無奇偶校驗位
dcb.StopBits=TWOSTOPBITS; //兩個停止位
SetCommState(hCom,&dcb);
PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);
//分別雙擊IDC_SEND按鈕和IDC_RECEIVE按鈕,添加兩個按鈕的響應函數:
void CRS485CommDlg::OnSend()
{
/*------+------+------+------+------+------+------+------+-------
| TODO: Add your control notification handler code here |
| 在此需要簡單介紹百特公司XMA5000的通訊協議: |
| 該儀表RS485通訊采用主機廣播方式通訊。 |
| 串行半雙工,幀11位,1個起始位(0),8個數據位,2個停止位(1) |
| 如:讀儀表顯示的瞬時值,主機發送:DC1 AAA BB ETX |
| 其中:DC1是標准ASCII碼的一個控制符號,碼值為11H(十進制的17) |
| 在XMA5000的通訊協議中,DC1表示讀瞬時值 |
| AAA是從機地址碼,也就是XMA5000顯示儀表的通訊地址 |
| BB為通道號,讀瞬時值時該值為01 |
| ETX也是標准ASCII碼的一個控制符號,碼值為03H |
| 在XMA5000的通訊協議中,ETX表示主機結束符 |
------+------+------+------+------+------+------+------+------+*/
char lpOutBuffer[7];
memset(lpOutBuffer,''\0'',7); //前7個字節先清零
lpOutBuffer[0]=''\x11''; //發送緩沖區的第1個字節為DC1
lpOutBuffer[1]=''0''; //第2個字節為字符0(30H)
lpOutBuffer[2]=''0''; //第3個字節為字符0(30H)
lpOutBuffer[3]=''1''; // 第4個字節為字符1(31H)
lpOutBuffer[4]=''0''; //第5個字節為字符0(30H)
lpOutBuffer[5]=''1''; //第6個字節為字符1(31H)
lpOutBuffer[6]=''\x03''; //第7個字節為字符ETX
//從該段代碼可以看出,儀表的通訊地址為001
DWORD dwBytesWrite=7;
COMSTAT ComStat;
DWORD dwErrorFlags;
BOOL bWriteStat;
ClearCommError(hCom,&dwErrorFlags,&ComStat);
bWriteStat=WriteFile(hCom,lpOutBuffer,dwBytesWrite,& dwBytesWrite,NULL);
if(!bWriteStat)
{
AfxMessageBox("寫串口失敗!");
}
}
void CRS485CommDlg::OnReceive()
{
// TODO: Add your control notification handler code here
char str[100];
memset(str,''\0'',100);
DWORD wCount=100; //讀取的字節數
BOOL bReadStat;
bReadStat=ReadFile(hCom,str,wCount,&wCount,NULL);
if(!bReadStat)
AfxMessageBox("讀串口失敗!");
PurgeComm(hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
m_disp=str;
UpdateData(FALSE);
}
//您可以觀察返回的字符串,其中有和儀表顯示值相同的部分,您可以進行相應的字符串操作取出儀表的顯示值。
//打開ClassWizard,為靜態文本框IDC_DISP添加CString類型變量m_disp,同時添加WM_CLOSE的相應函數:
void CRS485CommDlg::OnClose()
{
// TODO: Add your message handler code here and/or call default
CloseHandle(hCom); //程序退出時關閉串口
CDialog::OnClose();
}
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
//程序的相應部分已經在代碼內部作了詳細介紹。連接好硬件部分,編譯運行程序,細心體會串口同步操作部分。
//例程2
/*------+------+------+------+------+------+------+------+------+------+------+------+------+-------
打開VC++6.0,新建基於對話框的工程RS485Comm,在主對話框窗口IDD_RS485COMM_DIALOG上添加兩個按鈕,
ID分別為IDC_SEND和IDC_RECEIVE,標題分別為“發送”和“接收”;添加一個靜態文本框IDC_DISP,用於顯示
串口接收到的內容。在RS485CommDlg.cpp文件中添加全局變量:
/*------+------+------+------+------+------+------+------+------+------+------+------+------+------*/
HANDLE hCom; //全局變量,
//串口句柄在RS485CommDlg.cpp文件中的OnInitDialog()函數添加如下代碼:
hCom=CreateFile( "COM1", //COM1口
GENERIC_READ|GENERIC_WRITE, //允許讀和寫
0, //獨占方式
NULL,
OPEN_EXISTING, //打開而不是創建
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, //重疊方式
NULL );
if(hCom==(HANDLE)-1)
{
AfxMessageBox("打開COM失敗!");
return FALSE;
}
SetupComm(hCom,100,100); //輸入緩沖區和輸出緩沖區的大小都是100
COMMTIMEOUTS TimeOuts;
//設定讀超時
TimeOuts.ReadIntervalTimeout=MAXDWORD;
TimeOuts.ReadTotalTimeoutMultiplier=0;
TimeOuts.ReadTotalTimeoutConstant=0;
//在讀一次輸入緩沖區的內容後讀操作就立即返回,
//而不管是否讀入了要求的字符。
//設定寫超時
TimeOuts.WriteTotalTimeoutMultiplier=100;
TimeOuts.WriteTotalTimeoutConstant=500;
SetCommTimeouts(hCom,&TimeOuts); //設置超時
DCB dcb;
GetCommState(hCom,&dcb);
dcb.BaudRate=9600; //波特率為9600
dcb.ByteSize=8; //每個字節有8位
dcb.Parity=NOPARITY; //無奇偶校驗位
dcb.StopBits=TWOSTOPBITS; //兩個停止位
SetCommState(hCom,&dcb);
PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);
//分別雙擊IDC_SEND按鈕和IDC_RECEIVE按鈕,添加兩個按鈕的響應函數:
void CRS485CommDlg::OnSend()
{
// TODO: Add your control notification handler code here
OVERLAPPED m_osWrite;
memset(&m_osWrite,0,sizeof(OVERLAPPED));
m_osWrite.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
char lpOutBuffer[7];
memset(lpOutBuffer,''\0'',7);
lpOutBuffer[0]=''\x11'';
lpOutBuffer[1]=''0'';
lpOutBuffer[2]=''0'';
lpOutBuffer[3]=''1'';
lpOutBuffer[4]=''0'';
lpOutBuffer[5]=''1'';
lpOutBuffer[6]=''\x03'';
DWORD dwBytesWrite=7;
COMSTAT ComStat;
DWORD dwErrorFlags;
BOOL bWriteStat;
ClearCommError(hCom,&dwErrorFlags,&ComStat);
bWriteStat=WriteFile(hCom,lpOutBuffer,
dwBytesWrite,& dwBytesWrite,&m_osWrite);
if(!bWriteStat)
{
if(GetLastError()==ERROR_IO_PENDING)
{
WaitForSingleObject(m_osWrite.hEvent,1000);
}
}
}
void CRS485CommDlg::OnReceive()
{
// TODO: Add your control notification handler code here
OVERLAPPED m_osRead;
memset(&m_osRead,0,sizeof(OVERLAPPED));
m_osRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
COMSTAT ComStat;
DWORD dwErrorFlags;
char str[100];
memset(str,''\0'',100);
DWORD dwBytesRead=100;//讀取的字節數
BOOL bReadStat;
ClearCommError(hCom,&dwErrorFlags,&ComStat);
dwBytesRead=min(dwBytesRead, (DWORD)ComStat.cbInQue);
bReadStat=ReadFile(hCom,str,
dwBytesRead,&dwBytesRead,&m_osRead);
if(!bReadStat)
{
if(GetLastError()==ERROR_IO_PENDING) //GetLastError()函數返回ERROR_IO_PENDING,表明串口正在進行讀操作
{
WaitForSingleObject(m_osRead.hEvent,2000);
//使用WaitForSingleObject函數等待,直到讀操作完成或延時已達到2秒鐘
//當串口讀操作進行完畢後,m_osRead的hEvent事件會變為有信號
}
}
PurgeComm(hCom, PURGE_TXABORT|
PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
m_disp=str;
UpdateData(FALSE);
}
//打開ClassWizard,為靜態文本框IDC_DISP添加CString類型變量m_disp,同時添加WM_CLOSE的相應函數:
void CRS485CommDlg::OnClose()
{
// TODO: Add your message handler code here and/or call default
CloseHandle(hCom); //程序退出時關閉串口
CDialog::OnClose();
}
//您可以仔細對照這兩個例程,細心體會串口同步操作和異步操作的區別。
作者“情っ㈠顆欲枯旳草丶ゞ”