?? 用mfc實(shí)現(xiàn)串口編程.txt
字號(hào):
。。。。。 // 處理其他MSComm控件
}
void CMainFrame::OnCommSend() {
。。。。。。。。 // 準(zhǔn)備需要發(fā)送的命令,放在TxData[]中
CByteArray array;
array.RemoveAll();
array.SetSize(Count);
for(i=0;i<Count;i++)
array.SetAt(i, TxData);
m_ComPort.SetOutput(COleVariant(array)); // 發(fā)送數(shù)據(jù)
}
請(qǐng)大家認(rèn)真關(guān)注第⑷、⑸中內(nèi)容,在實(shí)際工作中是重點(diǎn)、難點(diǎn)所在。
㈡ 使用32位的API 通信函數(shù):
可能很多朋友會(huì)覺(jué)得奇怪:用32位API函數(shù)編寫串口通信程序,不就是把16位的API
換成32位嗎?16位的串口通信程序可是多年之前就有很多人研討過(guò)了……
此文主要想介紹一下在API串口通信中如何結(jié)合非阻塞通信、多線程等手段,編寫出
高質(zhì)量的通信程序。特別是在CPU處理任務(wù)比較繁重、與外圍設(shè)備中有大量的通信數(shù)
據(jù)時(shí),更有實(shí)際意義。
⑴.在中MainFrm.cpp定義全局變量
HANDLE hCom; // 準(zhǔn)備打開(kāi)的串口的句柄
HANDLE hCommWatchThread ;//輔助線程的全局函數(shù)
⑵.打開(kāi)串口,設(shè)置串口
hCom =CreateFile( "COM2", GENERIC_READ | GENERIC_WRITE, // 允許讀寫
0, // 此項(xiàng)必須為0
NULL, // no security attrs
OPEN_EXISTING, //設(shè)置產(chǎn)生方式
FILE_FLAG_OVERLAPPED, // 我們準(zhǔn)備使用異步通信
NULL );
請(qǐng)大家注意,我們使用了FILE_FLAG_OVERLAPPED結(jié)構(gòu)。這正是使用API實(shí)現(xiàn)非阻塞通信的關(guān)鍵所在。
ASSERT(hCom!=INVALID_HANDLE_VALUE); //檢測(cè)打開(kāi)串口操作是否成功
SetCommMask(hCom, EV_RXCHAR|EV_TXEMPTY );//設(shè)置事件驅(qū)動(dòng)的類型
SetupComm( hCom, 1024,512) ; //設(shè)置輸入、輸出緩沖區(qū)的大小
PurgeComm( hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR
| PURGE_RXCLEAR ); //清干凈輸入、輸出緩沖區(qū)
COMMTIMEOUTS CommTimeOuts ; //定義超時(shí)結(jié)構(gòu),并填寫該結(jié)構(gòu)
…………
SetCommTimeouts( hCom, &CommTimeOuts ) ;//設(shè)置讀寫操作所允許的超時(shí)
DCB dcb ; // 定義數(shù)據(jù)控制塊結(jié)構(gòu)
GetCommState(hCom, &dcb ) ; //讀串口原來(lái)的參數(shù)設(shè)置
dcb.BaudRate =9600; dcb.ByteSize =8; dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT ;dcb.fBinary = TRUE ;dcb.fParity = FALSE;
SetCommState(hCom, &dcb ) ; //串口參數(shù)配置
上述的COMMTIMEOUTS結(jié)構(gòu)和DCB都很重要,實(shí)際工作中需要仔細(xì)選擇參數(shù)。
⑶啟動(dòng)一個(gè)輔助線程,用于串口事件的處理。
Windows提供了兩種線程,輔助線程和用戶界面線程。區(qū)別在于:輔助線程沒(méi)有窗口
,所以它沒(méi)有自己的消息循環(huán)。但是輔助線程很容易編程,通常也很有用。
在次,我們使用輔助線程。主要用它來(lái)監(jiān)視串口狀態(tài),看有無(wú)數(shù)據(jù)到達(dá)、通信有無(wú)
錯(cuò)誤;而主線程則可專心進(jìn)行數(shù)據(jù)處理、提供友好的用戶界面等重要的工作。
hCommWatchThread=
CreateThread( (LPSECURITY_ATTRIBUTES) NULL, //安全屬性
0,//初始化線程棧的大小,缺省為與主線程大小相同
(LPTHREAD_START_ROUTINE)CommWatchProc, //線程的全局函數(shù)
GetSafeHwnd(), //此處傳入了主框架的句柄
0, &dwThreadID );
ASSERT(hCommWatchThread!=NULL);
⑷為輔助線程寫一個(gè)全局函數(shù),主要完成數(shù)據(jù)接收的工作。請(qǐng)注意OVERLAPPED結(jié)構(gòu)
的使用,以及怎樣實(shí)現(xiàn)了非阻塞通信。
UINT CommWatchProc(HWND hSendWnd){
DWORD dwEvtMask=0 ;
SetCommMask( hCom, EV_RXCHAR|EV_TXEMPTY );//有哪些串口事件需要監(jiān)視?
WaitCommEvent( hCom, &dwEvtMask, os );// 等待串口通信事件的發(fā)生
檢測(cè)返回的dwEvtMask,知道發(fā)生了什么串口事件:
if ((dwEvtMask & EV_RXCHAR) == EV_RXCHAR){ // 緩沖區(qū)中有數(shù)據(jù)到達(dá)
COMSTAT ComStat ; DWORD dwLength;
ClearCommError(hCom, &dwErrorFlags, &ComStat ) ;
dwLength = ComStat.cbInQue ; //輸入緩沖區(qū)有多少數(shù)據(jù)?
if (dwLength > 0) {
BOOL fReadStat ;
fReadStat = ReadFile( hCom, lpBuffer,dwLength, &dwBytesRead,
&READ_OS( npTTYInfo ) ); //讀數(shù)據(jù)
注:我們?cè)贑reareFile()時(shí)使用了FILE_FLAG_OVERLAPPED,現(xiàn)在ReadFile()也必須使用
LPOVERLAPPED結(jié)構(gòu).否則,函數(shù)會(huì)不正確地報(bào)告讀操作已完成了.
使用LPOVERLAPPED結(jié)構(gòu), ReadFile()立即返回,不必等待讀操作完成,實(shí)現(xiàn)非阻塞
通信.此時(shí), ReadFile()返回FALSE, GetLastError()返回ERROR_IO_PENDING.
if (!fReadStat){
if (GetLastError() == ERROR_IO_PENDING){
while(!GetOverlappedResult(hCom,
&READ_OS( npTTYInfo ), & dwBytesRead, TRUE )){
dwError = GetLastError();
if(dwError == ERROR_IO_INCOMPLETE) continue;
//緩沖區(qū)數(shù)據(jù)沒(méi)有讀完,繼續(xù)
…… ……
::PostMessage((HWND)hSendWnd,WM_NOTIFYPROCESS,0,0);//通知主線程,串口收到數(shù)據(jù) }
所謂的非阻塞通信,也即異步通信。是指在進(jìn)行需要花費(fèi)大量時(shí)間的數(shù)據(jù)讀寫
操作(不僅僅是指串行通信操作)時(shí),一旦調(diào)用ReadFile()、WriteFile(), 就能立
即返回,而讓實(shí)際的讀寫操作在后臺(tái)運(yùn)行;相反,如使用阻塞通信,則必須在讀或
寫操作全部完成后才能返回。由于操作可能需要任意長(zhǎng)的時(shí)間才能完成,于是問(wèn)題
就出現(xiàn)了。
非常阻塞操作還允許讀、寫操作能同時(shí)進(jìn)行(即重疊操作?),在實(shí)際工作中非常有用。
要使用非阻塞通信,首先在CreateFile()時(shí)必須使用FILE_FLAG_OVERLAPPED;然后
在 ReadFile()時(shí)lpOverlapped參數(shù)一定不能為NULL,接著檢查函數(shù)調(diào)用的返回值,
調(diào)用GetLastError(),看是否返回ERROR_IO_PENDING。如是,最后調(diào)用
GetOverlappedResult()返回重疊操作(overlapped operation)的結(jié)果;WriteFile()
的使用類似。
⑸.在主線程中發(fā)送下行命令。
BOOL fWriteStat ; char szBuffer[count];
…………//準(zhǔn)備好發(fā)送的數(shù)據(jù),放在szBuffer[]中
fWriteStat = WriteFile(hCom, szBuffer, dwBytesToWrite,
&dwBytesWritten, &WRITE_OS( npTTYInfo ) ); //寫數(shù)據(jù)
注:我們?cè)贑reareFile()時(shí)使用了FILE_FLAG_OVERLAPPED,現(xiàn)在WriteFile()也必須使
用 LPOVERLAPPED結(jié)構(gòu).否則,函數(shù)會(huì)不正確地報(bào)告寫操作已完成了.
使用LPOVERLAPPED結(jié)構(gòu),WriteFile()立即返回,不必等待寫操作完成,實(shí)現(xiàn)非阻
塞 通信.此時(shí), WriteFile()返回FALSE, GetLastError()返回ERROR_IO_PENDING.
int err=GetLastError();
if (!fWriteStat) {
if(GetLastError() == ERROR_IO_PENDING){
while(!GetOverlappedResult(hCom, &WRITE_OS( npTTYInfo ),
&dwBytesWritten, TRUE )) {
dwError = GetLastError();
if(dwError == ERROR_IO_INCOMPLETE){
// normal result if not finished
dwBytesSent += dwBytesWritten; continue; }
......................
綜上,我們使用了多線程技術(shù),在輔助線程中監(jiān)視串口,有數(shù)據(jù)到達(dá)時(shí)依靠事
件驅(qū)動(dòng),讀入數(shù)據(jù)并向主線程報(bào)告(發(fā)送數(shù)據(jù)在主線程中,相對(duì)說(shuō)來(lái),下行命令的
數(shù)據(jù)總是少得多);并且,WaitCommEvent()、ReadFile()、WriteFile()都使用了
非阻塞通信技術(shù),依靠重疊(overlapped)讀寫操作,讓串口讀寫操作在后臺(tái)運(yùn)行。
依托vc6.0豐富的功能,結(jié)合我們提及的技術(shù),寫出有強(qiáng)大控制能力的串口通信
應(yīng)用程序。就個(gè)人而言,我更偏愛(ài)API技術(shù),因?yàn)榭刂剖侄我`活的多,功能也要強(qiáng)
大得多。
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -