在工業(yè)控制中,工控機(一般都基于Windows平臺)經(jīng)常需要與智能儀表通過(guò)串口進(jìn)行通信。串口通信方便易行,應用廣泛。
一般情況下,工控機和各智能儀表通過(guò)RS485總線(xiàn)進(jìn)行通信。RS485的通信方式是半雙工的,只能由作為主節點(diǎn)的工控PC機依次輪詢(xún)網(wǎng)絡(luò )上的各智能控制單元子節點(diǎn)。每次通信都是由PC機通過(guò)串口向智能控制單元發(fā)布命令,智能控制單元在接收到正確的命令后作出應答。
在Win32下,可以使用兩種編程方式實(shí)現串口通信,其一是使用ActiveX控件,這種方法程序簡(jiǎn)單,但欠靈活。其二是調用Windows的API函數,這種方法可以清楚地掌握串口通信的機制,并且自由靈活。本文我們只介紹API串口通信部分。
串口的操作可以有兩種操作方式:同步操作方式和重疊操作方式(又稱(chēng)為異步操作方式)。同步操作時(shí),API函數會(huì )阻塞直到操作完成以后才能返回(在多線(xiàn)程方式中,雖然不會(huì )阻塞主線(xiàn)程,但是仍然會(huì )阻塞監聽(tīng)線(xiàn)程);而重疊操作方式,API函數會(huì )立即返回,操作在后臺進(jìn)行,避免線(xiàn)程的阻塞。
無(wú)論那種操作方式,一般都通過(guò)四個(gè)步驟來(lái)完成:
(1) 打開(kāi)串口
(2) 配置串口
(3) 讀寫(xiě)串口
(4) 關(guān)閉串口
(1) 打開(kāi)串口
Win32系統把文件的概念進(jìn)行了擴展。無(wú)論是文件、通信設備、命名管道、郵件槽、磁盤(pán)、還是控制臺,都是用API函數CreateFile來(lái)打開(kāi)或創(chuàng )建的。該函數的原型為:
HANDLE CreateFile( LPCTSTR lpFileName,DWORD dwDesiredAccess,DWORD dwShareMode,LPSECURITY_ATTRIBUTES lpSecurityAttributes,DWORD dwCreationDistribution,DWORD dwFlagsAndAttributes,HANDLE hTemplateFile);
同步I/O方式打開(kāi)串口的示例代碼:
HANDLE hCom; //全局變量,串口句柄hCom=CreateFile("COM1",//COM1口GENERIC_READ|GENERIC_WRITE, //允許讀和寫(xiě)0, //獨占方式NULL,OPEN_EXISTING, //打開(kāi)而不是創(chuàng )建0, //同步方式NULL);if(hCom==(HANDLE)-1){AfxMessageBox("打開(kāi)COM失敗!");return FALSE;}return TRUE;重疊I/O打開(kāi)串口的示例代碼: HANDLE hCom; //全局變量,串口句柄hCom =CreateFile("COM1", //COM1口GENERIC_READ|GENERIC_WRITE, //允許讀和寫(xiě)0, //獨占方式NULL,OPEN_EXISTING, //打開(kāi)而不是創(chuàng )建FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, //重疊方式NULL);if(hCom ==INVALID_HANDLE_VALUE){AfxMessageBox("打開(kāi)COM失敗!");return FALSE;}return TRUE;(2)、配置串口 在打開(kāi)通訊設備句柄后,常常需要對串口進(jìn)行一些初始化配置工作。這需要通過(guò)一個(gè)DCB結構來(lái)進(jìn)行。DCB結構包含了諸如波特率、數據位數、奇偶校驗和停止位數等信息。在查詢(xún)或配置串口的屬性時(shí),都要用DCB結構來(lái)作為緩沖區。
一般用CreateFile打開(kāi)串口后,可以調用GetCommState函數來(lái)獲取串口的初始配置。要修改串口的配置,應該先修改DCB結構,然后再調用SetCommState函數設置串口。
DCB結構包含了串口的各項參數設置,下面僅介紹幾個(gè)該結構常用的變量:
typedef struct _DCB{………//波特率,指定通信設備的傳輸速率。這個(gè)成員可以是實(shí)際波特率值或者下面的常量值之一:DWORD BaudRate;CBR_110,CBR_300,CBR_600,CBR_1200,CBR_2400,CBR_4800,CBR_9600,CBR_19200, CBR_38400,CBR_56000, CBR_57600, CBR_115200, CBR_128000, CBR_256000, CBR_14400DWORD fParity; // 指定奇偶校驗使能。若此成員為1,允許奇偶校驗檢查…BYTE ByteSize; // 通信字節位數,4—8BYTE Parity; //指定奇偶校驗方法。此成員可以有下列值:EVENPARITY 偶校驗 NOPARITY 無(wú)校驗MARKPARITY 標記校驗 ODDPARITY 奇校驗BYTE StopBits; //指定停止位的位數。此成員可以有下列值:ONESTOPBIT 1位停止位 TWOSTOPBITS 2位停止位ONE5STOPBITS 1.5位停止位………} DCB;winbase.h文件中定義了以上用到的常量。如下:#define NOPARITY 0#define ODDPARITY 1#define EVENPARITY 2#define ONESTOPBIT 0#define ONE5STOPBITS 1#define TWOSTOPBITS 2#define CBR_110 110#define CBR_300 300#define CBR_600 600#define CBR_1200 1200#define CBR_2400 2400#define CBR_4800 4800#define CBR_9600 9600#define CBR_14400 14400#define CBR_19200 19200#define CBR_38400 38400#define CBR_56000 56000#define CBR_57600 57600#define CBR_115200 115200#define CBR_128000 128000#define CBR_256000 256000GetCommState函數可以獲得COM口的設備控制塊,從而獲得相關(guān)參數:BOOL GetCommState(HANDLE hFile, //標識通訊端口的句柄LPDCB lpDCB //指向一個(gè)設備控制塊(DCB結構)的指針);SetCommState函數設置COM口的設備控制塊:BOOL SetCommState(HANDLE hFile,LPDCB lpDCB);除了在BCD中的設置外,程序一般還需要設置I/O緩沖區的大小和超時(shí)。Windows用I/O緩沖區來(lái)暫存串口輸入和輸出的數據。如果通信的速率較高,則應該設置較大的緩沖區。調用SetupComm函數可以設置串行口的輸入和輸出緩沖區的大小。
BOOL SetupComm(HANDLE hFile, // 通信設備的句柄DWORD dwInQueue, // 輸入緩沖區的大?。ㄗ止潝担〥WORD dwOutQueue // 輸出緩沖區的大?。ㄗ止潝担?;在用ReadFile和WriteFile讀寫(xiě)串行口時(shí),需要考慮超時(shí)問(wèn)題。超時(shí)的作用是在指定的時(shí)間內沒(méi)有讀入或發(fā)送指定數量的字符,ReadFile或WriteFile的操作仍然會(huì )結束。
typedef struct _COMMTIMEOUTS {DWORD ReadIntervalTimeout; //讀間隔超時(shí)DWORD ReadTotalTimeoutMultiplier; //讀時(shí)間系數DWORD ReadTotalTimeoutConstant; //讀時(shí)間常量DWORD WriteTotalTimeoutMultiplier; // 寫(xiě)時(shí)間系數DWORD WriteTotalTimeoutConstant; //寫(xiě)時(shí)間常量} COMMTIMEOUTS,*LPCOMMTIMEOUTS;COMMTIMEOUTS結構的成員都以毫秒為單位??偝瑫r(shí)的計算公式是:SetupComm(hCom,1024,1024); //輸入緩沖區和輸出緩沖區的大小都是1024COMMTIMEOUTS TimeOuts;//設定讀超時(shí)TimeOuts.ReadIntervalTimeout=1000;TimeOuts.ReadTotalTimeoutMultiplier=500;TimeOuts.ReadTotalTimeoutConstant=5000;//設定寫(xiě)超時(shí)TimeOuts.WriteTotalTimeoutMultiplier=500;TimeOuts.WriteTotalTimeoutConstant=2000;SetCommTimeouts(hCom,&TimeOuts); //設置超時(shí)DCB dcb;GetCommState(hCom,&dcb);dcb.BaudRate=9600; //波特率為9600dcb.ByteSize=8; //每個(gè)字節有8位dcb.Parity=NOPARITY; //無(wú)奇偶校驗位dcb.StopBits=TWOSTOPBITS; //兩個(gè)停止位SetCommState(hCom,&dcb);PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);在讀寫(xiě)串口之前,還要用PurgeComm()函數清空緩沖區,該函數原型:
BOOL PurgeComm(HANDLE hFile, //串口句柄DWORD dwFlags // 需要完成的操作);參數dwFlags指定要完成的操作,可以是下列值的組合:
PURGE_TXABORT 中斷所有寫(xiě)操作并立即返回,即使寫(xiě)操作還沒(méi)有完成。PURGE_RXABORT 中斷所有讀操作并立即返回,即使讀操作還沒(méi)有完成。PURGE_TXCLEAR 清除輸出緩沖區PURGE_RXCLEAR 清除輸入緩沖區(3)、讀寫(xiě)串口
我們使用ReadFile和WriteFile讀寫(xiě)串口,下面是兩個(gè)函數的聲明:
BOOL ReadFile(HANDLE hFile, //串口的句柄// 讀入的數據存儲的地址,// 即讀入的數據將存儲在以該指針的值為首地址的一片內存區LPVOID lpBuffer,DWORD nNumberOfBytesToRead, // 要讀入的數據的字節數// 指向一個(gè)DWORD數值,該數值返回讀操作實(shí)際讀入的字節數LPDWORD lpNumberOfBytesRead,// 重疊操作時(shí),該參數指向一個(gè)OVERLAPPED結構,同步操作時(shí),該參數為NULL。LPOVERLAPPED lpOverlapped);BOOL WriteFile(HANDLE hFile, //串口的句柄// 寫(xiě)入的數據存儲的地址,// 即以該指針的值為首地址的nNumberOfBytesToWrite// 個(gè)字節的數據將要寫(xiě)入串口的發(fā)送數據緩沖區。LPCVOID lpBuffer,DWORD nNumberOfBytesToWrite, //要寫(xiě)入的數據的字節數// 指向指向一個(gè)DWORD數值,該數值返回實(shí)際寫(xiě)入的字節數LPDWORD lpNumberOfBytesWritten,// 重疊操作時(shí),該參數指向一個(gè)OVERLAPPED結構,// 同步操作時(shí),該參數為NULL。LPOVERLAPPED lpOverlapped);在用ReadFile和WriteFile讀寫(xiě)串口時(shí),既可以同步執行,也可以重疊執行。在同步執行時(shí),函數直到操作完成后才返回。這意味著(zhù)同步執行時(shí)線(xiàn)程會(huì )被阻塞,從而導致效率下降。在重疊執行時(shí),即使操作還未完成,這兩個(gè)函數也會(huì )立即返回,費時(shí)的I/O操作在后臺進(jìn)行。
//同步讀串口char str[100];DWORD wCount;//讀取的字節數BOOL bReadStat;bReadStat=ReadFile(hCom,str,100,&wCount,NULL);if(!bReadStat){AfxMessageBox("讀串口失敗!");return FALSE;}return TRUE;//同步寫(xiě)串口char lpOutBuffer[100];DWORD dwBytesWrite=100;COMSTAT ComStat;DWORD dwErrorFlags;BOOL bWriteStat;ClearCommError(hCom,&dwErrorFlags,&ComStat);bWriteStat=WriteFile(hCom,lpOutBuffer,dwBytesWrite,& dwBytesWrite,NULL);if(!bWriteStat){AfxMessageBox("寫(xiě)串口失敗!");}PurgeComm(hCom, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);在重疊操作時(shí),操作還未完成函數就返回。 typedef struct _OVERLAPPED { // oDWORD Internal;DWORD InternalHigh;DWORD Offset;DWORD OffsetHigh;HANDLE hEvent;} OVERLAPPED; 在使用ReadFile和WriteFile重疊操作時(shí),線(xiàn)程需要創(chuàng )建OVERLAPPED結構以供這兩個(gè)函數使用。線(xiàn)程通過(guò)OVERLAPPED結構獲得當前的操作狀態(tài),該結構最重要的成員是hEvent。hEvent是讀寫(xiě)事件。當串口使用異步通訊時(shí),函數返回時(shí)操作可能還沒(méi)有完成,程序可以通過(guò)檢查該事件得知是否讀寫(xiě)完畢。GetOverlappedResult函數BOOL GetOverlappedResult(HANDLE hFile, // 串口的句柄// 指向重疊操作開(kāi)始時(shí)指定的OVERLAPPED結構LPOVERLAPPED lpOverlapped,// 指向一個(gè)32位變量,該變量的值返回實(shí)際讀寫(xiě)操作傳輸的字節數。LPDWORD lpNumberOfBytesTransferred,// 該參數用于指定函數是否一直等到重疊操作結束。// 如果該參數為T(mén)RUE,函數直到操作結束才返回。// 如果該參數為FALSE,函數直接返回,這時(shí)如果操作沒(méi)有完成,// 通過(guò)調用GetLastError()函數會(huì )返回ERROR_IO_INCOMPLETE。BOOL bWait);該函數返回重疊操作的結果,用來(lái)判斷異步操作是否完成,它是通過(guò)判斷OVERLAPPED結構中的hEvent是否被置位來(lái)實(shí)現的。
char lpInBuffer[1024];DWORD dwBytesRead=1024;COMSTAT ComStat;DWORD dwErrorFlags;OVERLAPPED m_osRead;memset(&m_osRead,0,sizeof(OVERLAPPED));m_osRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);ClearCommError(hCom,&dwErrorFlags,&ComStat);dwBytesRead=min(dwBytesRead,(DWORD)ComStat.cbInQue);if(!dwBytesRead)return FALSE;BOOL bReadStatus;bReadStatus=ReadFile(hCom,lpInBuffer,dwBytesRead,&dwBytesRead,&m_osRead);if(!bReadStatus) //如果ReadFile函數返回FALSE{if(GetLastError()==ERROR_IO_PENDING)//GetLastError()函數返回ERROR_IO_PENDING,表明串口正在進(jìn)行讀操作{WaitForSingleObject(m_osRead.hEvent,2000);//使用WaitForSingleObject函數等待,直到讀操作完成或延時(shí)已達到2秒鐘//當串口讀操作進(jìn)行完畢后,m_osRead的hEvent事件會(huì )變?yōu)橛行盘朠urgeComm(hCom, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);return dwBytesRead;}return 0;}PurgeComm(hCom, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);return dwBytesRead; 對以上代碼再作簡(jiǎn)要說(shuō)明:在使用ReadFile 函數進(jìn)行讀操作前,應先使用ClearCommError函數清除錯誤。ClearCommError函數的原型如下:BOOL ClearCommError(HANDLE hFile, // 串口句柄LPDWORD lpErrors, // 指向接收錯誤碼的變量LPCOMSTAT lpStat // 指向通訊狀態(tài)緩沖區);該函數獲得通信錯誤并報告串口的當前狀態(tài),同時(shí),該函數清除串口的錯誤標志以便繼續輸入、輸出操作。
typedef struct _COMSTAT { // cstDWORD fCtsHold : 1; // Tx waiting for CTS signalDWORD fDsrHold : 1; // Tx waiting for DSR signalDWORD fRlsdHold : 1; // Tx waiting for RLSD signalDWORD fXoffHold : 1; // Tx waiting, XOFF char rec''dDWORD fXoffSent : 1; // Tx waiting, XOFF char sentDWORD fEof : 1; // EOF character sentDWORD fTxim : 1; // character waiting for TxDWORD fReserved : 25; // reservedDWORD cbInQue; // bytes in input bufferDWORD cbOutQue; // bytes in output buffer} COMSTAT, *LPCOMSTAT;本文只用到了cbInQue成員變量,該成員變量的值代表輸入緩沖區的字節數。這段代碼用WaitForSingleObject函數來(lái)等待OVERLAPPED結構的hEvent成員,下面我們再演示一段調用GetOverlappedResult函數等待的異步讀串口示例代碼:
char lpInBuffer[1024];DWORD dwBytesRead=1024;BOOL bReadStatus;DWORD dwErrorFlags;COMSTAT ComStat;OVERLAPPED m_osRead;ClearCommError(hCom,&dwErrorFlags,&ComStat);if(!ComStat.cbInQue)return 0;dwBytesRead=min(dwBytesRead,(DWORD)ComStat.cbInQue);bReadStatus=ReadFile(hCom, lpInBuffer,dwBytesRead,&dwBytesRead,&m_osRead);if(!bReadStatus) //如果ReadFile函數返回FALSE{if(GetLastError()==ERROR_IO_PENDING){GetOverlappedResult(hCom,&m_osRead,&dwBytesRead,TRUE);// GetOverlappedResult函數的最后一個(gè)參數設為T(mén)RUE,//函數會(huì )一直等待,直到讀操作完成或由于錯誤而返回。return dwBytesRead;}return 0;}return dwBytesRead;異步寫(xiě)串口的示例代碼:char buffer[1024];DWORD dwBytesWritten=1024;DWORD dwErrorFlags;COMSTAT ComStat;OVERLAPPED m_osWrite;BOOL bWriteStat;bWriteStat=WriteFile(hCom,buffer,dwBytesWritten,&dwBytesWritten,&m_OsWrite);if(!bWriteStat){if(GetLastError()==ERROR_IO_PENDING){WaitForSingleObject(m_osWrite.hEvent,1000);return dwBytesWritten;}return 0;}return dwBytesWritten;(4)、關(guān)閉串口利用API函數關(guān)閉串口非常簡(jiǎn)單,只需使用CreateFile函數返回的句柄作為參數調用CloseHandle即可:
BOOL CloseHandle(HANDLE hObject; //handle to object to close);串口編程的一個(gè)實(shí)例
為了讓您更好地理解串口編程,下面我們分別編寫(xiě)兩個(gè)例程(見(jiàn)附帶的源碼部分),這兩個(gè)例程都實(shí)現了工控機與百特顯示儀表通過(guò)RS485接口進(jìn)行的串口通信。其中第一個(gè)例程采用同步串口操作,第二個(gè)例程采用異步串口操作。
我們只介紹軟件部分,RS485接口接線(xiàn)方法不作介紹,感興趣的讀者可以查閱相關(guān)資料。
例程1
打開(kāi)VC++6.0,新建基于對話(huà)框的工程RS485Comm,在主對話(huà)框窗口IDD_RS485COMM_DIALOG上添加兩個(gè)按鈕,ID分別為IDC_SEND和IDC_RECEIVE,標題分別為“發(fā)送”和“接收”;添加一個(gè)靜態(tài)文本框IDC_DISP,用于顯示串口接收到的內容。
在RS485CommDlg.cpp文件中添加全局變量:
HANDLE hCom; //全局變量,串口句柄在RS485CommDlg.cpp文件中的OnInitDialog()函數添加如下代碼:
// TODO: Add extra initialization herehCom=CreateFile("COM1",//COM1口GENERIC_READ|GENERIC_WRITE, //允許讀和寫(xiě)0, //獨占方式NULL,OPEN_EXISTING, //打開(kāi)而不是創(chuàng )建0, //同步方式NULL);if(hCom==(HANDLE)-1){AfxMessageBox("打開(kāi)COM失敗!");return FALSE;}SetupComm(hCom,100,100); //輸入緩沖區和輸出緩沖區的大小都是1024COMMTIMEOUTS TimeOuts;//設定讀超時(shí)TimeOuts.ReadIntervalTimeout=MAXDWORD;TimeOuts.ReadTotalTimeoutMultiplier=0;TimeOuts.ReadTotalTimeoutConstant=0;//在讀一次輸入緩沖區的內容后讀操作就立即返回,//而不管是否讀入了要求的字符。//設定寫(xiě)超時(shí)TimeOuts.WriteTotalTimeoutMultiplier=100;TimeOuts.WriteTotalTimeoutConstant=500;SetCommTimeouts(hCom,&TimeOuts); //設置超時(shí)DCB dcb;GetCommState(hCom,&dcb);dcb.BaudRate=9600; //波特率為9600dcb.ByteSize=8; //每個(gè)字節有8位dcb.Parity=NOPARITY; //無(wú)奇偶校驗位dcb.StopBits=TWOSTOPBITS; //兩個(gè)停止位SetCommState(hCom,&dcb);PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);分別雙擊IDC_SEND按鈕和IDC_RECEIVE按鈕,添加兩個(gè)按鈕的響應函數:void CRS485CommDlg::OnSend(){// TODO: Add your control notification handler code here// 在此需要簡(jiǎn)單介紹百特公司XMA5000的通訊協(xié)議://該儀表RS485通訊采用主機廣播方式通訊。//串行半雙工,幀11位,1個(gè)起始位(0),8個(gè)數據位,2個(gè)停止位(1)//如:讀儀表顯示的瞬時(shí)值,主機發(fā)送:DC1 AAA BB ETX//其中:DC1是標準ASCII碼的一個(gè)控制符號,碼值為11H(十進(jìn)制的17)//在XMA5000的通訊協(xié)議中,DC1表示讀瞬時(shí)值//AAA是從機地址碼,也就是XMA5000顯示儀表的通訊地址//BB為通道號,讀瞬時(shí)值時(shí)該值為01//ETX也是標準ASCII碼的一個(gè)控制符號,碼值為03H//在XMA5000的通訊協(xié)議中,ETX表示主機結束符char lpOutBuffer[7];memset(lpOutBuffer,''\0'',7); //前7個(gè)字節先清零lpOutBuffer[0]=''\x11''; //發(fā)送緩沖區的第1個(gè)字節為DC1lpOutBuffer[1]=''0''; //第2個(gè)字節為字符0(30H)lpOutBuffer[2]=''0''; //第3個(gè)字節為字符0(30H)lpOutBuffer[3]=''1''; // 第4個(gè)字節為字符1(31H)lpOutBuffer[4]=''0''; //第5個(gè)字節為字符0(30H)lpOutBuffer[5]=''1''; //第6個(gè)字節為字符1(31H)lpOutBuffer[6]=''\x03''; //第7個(gè)字節為字符ETX//從該段代碼可以看出,儀表的通訊地址為001DWORD dwBytesWrite=7;COMSTAT ComStat;DWORD dwErrorFlags;BOOL bWriteStat;ClearCommError(hCom,&dwErrorFlags,&ComStat);bWriteStat=WriteFile(hCom,lpOutBuffer,dwBytesWrite,& dwBytesWrite,NULL);if(!bWriteStat){AfxMessageBox("寫(xiě)串口失敗!");}}void CRS485CommDlg::OnReceive(){// TODO: Add your control notification handler code herechar 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);}您可以觀(guān)察返回的字符串,其中有和儀表顯示值相同的部分,您可以進(jìn)行相應的字符串操作取出儀表的顯示值。void CRS485CommDlg::OnClose(){// TODO: Add your message handler code here and/or call defaultCloseHandle(hCom); //程序退出時(shí)關(guān)閉串口CDialog::OnClose();}程序的相應部分已經(jīng)在代碼內部作了詳細介紹。連接好硬件部分,編譯運行程序,細心體會(huì )串口同步操作部分。例程2
打開(kāi)VC++6.0,新建基于對話(huà)框的工程RS485Comm,在主對話(huà)框窗口IDD_RS485COMM_DIALOG上添加兩個(gè)按鈕,ID分別為IDC_SEND和IDC_RECEIVE,標題分別為“發(fā)送”和“接收”;添加一個(gè)靜態(tài)文本框IDC_DISP,用于顯示串口接收到的內容。在RS485CommDlg.cpp文件中添加全局變量:
HANDLE hCom; //全局變量,
串口句柄在RS485CommDlg.cpp文件中的OnInitDialog()函數添加如下代碼:
hCom=CreateFile("COM1",//COM1口GENERIC_READ|GENERIC_WRITE, //允許讀和寫(xiě)0, //獨占方式NULL,OPEN_EXISTING, //打開(kāi)而不是創(chuàng )建FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, //重疊方式NULL);if(hCom==(HANDLE)-1){AfxMessageBox("打開(kāi)COM失敗!");return FALSE;}SetupComm(hCom,100,100); //輸入緩沖區和輸出緩沖區的大小都是100COMMTIMEOUTS TimeOuts;//設定讀超時(shí)TimeOuts.ReadIntervalTimeout=MAXDWORD;TimeOuts.ReadTotalTimeoutMultiplier=0;TimeOuts.ReadTotalTimeoutConstant=0;//在讀一次輸入緩沖區的內容后讀操作就立即返回,//而不管是否讀入了要求的字符。//設定寫(xiě)超時(shí)TimeOuts.WriteTotalTimeoutMultiplier=100;TimeOuts.WriteTotalTimeoutConstant=500;SetCommTimeouts(hCom,&TimeOuts); //設置超時(shí)DCB dcb;GetCommState(hCom,&dcb);dcb.BaudRate=9600; //波特率為9600dcb.ByteSize=8; //每個(gè)字節有8位dcb.Parity=NOPARITY; //無(wú)奇偶校驗位dcb.StopBits=TWOSTOPBITS; //兩個(gè)停止位SetCommState(hCom,&dcb);PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);分別雙擊IDC_SEND按鈕和IDC_RECEIVE按鈕,添加兩個(gè)按鈕的響應函數:void CRS485CommDlg::OnSend(){// TODO: Add your control notification handler code hereOVERLAPPED 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 hereOVERLAPPED 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,表明串口正在進(jìn)行讀操作{WaitForSingleObject(m_osRead.hEvent,2000);//使用WaitForSingleObject函數等待,直到讀操作完成或延時(shí)已達到2秒鐘//當串口讀操作進(jìn)行完畢后,m_osRead的hEvent事件會(huì )變?yōu)橛行盘杴}PurgeComm(hCom, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);m_disp=str;UpdateData(FALSE);}打開(kāi)ClassWizard,為靜態(tài)文本框IDC_DISP添加CString類(lèi)型變量m_disp,同時(shí)添加WM_CLOSE的相應函數:void CRS485CommDlg::OnClose(){// TODO: Add your message handler code here and/or call defaultCloseHandle(hCom); //程序退出時(shí)關(guān)閉串口CDialog::OnClose();}您可以仔細對照這兩個(gè)例程,細心體會(huì )串口同步操作和異步操作的區別。好了,就到這吧,祝您好運。
聯(lián)系客服