日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Win32 串口编程(一)

發布時間:2025/3/12 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Win32 串口编程(一) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

翻譯自:ms-help://MS.MSDNQTR.v80.chs/MS.MSDN.v80/MS.WIN32COM.v10.en/dnfiles/html/msdn_serial.htm

老外寫的文章,雖比較全面,但很啰嗦,不如看各個函數的文檔來得快。為方便以后查閱,列出本文涉及的主要函數如下:

CreateFile、ReadFile、WriteFile、GetOverlappedResult、WaitForSingleObject

SetCommMask、WaitCommEvent

ClearCommError、GetCommModemStatus、EscapeCommFunction

GetCommState、BuildCommDCB、SetCommState、SetCommTimeouts

此外,新浪的博客系統限制文章最大長度為40000字節,只好把這篇文章分成幾部分了。

0 簡介

本文僅關注在Windows NT和95間兼容的API。Windows 95支持Telephony API(TAPI),但Windows NT 3.x不支持TAPI,所以本文不討論它。

本文的示例程序MTTTY(Multithreaded TTY)使用了三個線程:一個進行內存管理的界面線程;控制所有寫入操作的寫入者線程;讀取數據和處理端口狀態改變的讀取/狀態線程。示例采用了一些不同的堆來進行內存管理;還大量使用了同步方法來進行線程間通信。

1 打開端口

使用CreateFile函數打開端口。打開端口時有兩種方法:重疊的和非重疊的。下列代碼片段以重疊方式打開端口:

?

HANDLE?hComm;
hComm?=?CreateFile(?gszPort,??
????????????????????GENERIC_READ?|?GENERIC_WRITE,?
????????????????????0,?
????????????????????0,?
????????????????????OPEN_EXISTING,
????????????????????FILE_FLAG_OVERLAPPED,
????????????????????0);
if?(hComm?==?INVALID_HANDLE_VALUE)
???//?error?opening?port;?abort

?

?

?

?

?

?







去掉參數中的FILE_FLAG_OVERLAPPED就是非重疊操作方式了。
用CreateFile打開通信端口時,有下列限制:

  • fdwShareMode 必須是0。通信端口不能像文件那樣被共享。要共享通信端口,需要使用句柄繼承或者復制操作。
  • fdwCreate 必須指定 OPEN_EXISTING 標志。
  • hTemplateFile 參數必須是 NULL

端口名通常是COM1、COM2、COM3和COM4。Win32 API不提供確定系統中有哪些端口可用的機制。Windows NT和Windows 95跟蹤系統已安裝端口的方法是不同的,所以不太可能提供兼容的確定可用端口的方法。某些系統可能有多于4個端口,而傳統的通信端口最大個數是4。硬件廠商和串口驅動編寫者可以自由地為端口命名。所以,程序最好可以讓用戶指定要使用的端口名字。如果端口不存在,則試圖打開端口時會返回ERROR_FILE_NOT_FOUND錯誤,這時應該提示用戶端口不可用。

2 讀寫操作

通信端口的讀寫操作與文件I/O操作非常相似,它們使用同樣的函數。Win32的I/O操作可分為兩種:重疊(overlapped)的和非重疊的(nonoverlapped)。平臺SDK文檔分別使用異步(asynchronous)和同步(synchronous)來表示這兩種I/O方式。

很多開發者都熟悉非重疊I/O,因為它就是傳統的I/O方式:函數返回時,所請求的操作已經完成。然而在重疊I/O的情況下,系統則可能在操作還沒有完成的情形下立即返回,隨后才通知調用者操作完成。程序可以在發起I/O請求和請求被完成之間進行一些后臺工作。

2.1 非重疊I/O

非重疊I/O的工作方式很簡單:I/O操作進行時,調用線程被阻塞;操作完成后,函數返回,調用線程可以繼續執行。在多線程應用中,這種I/O方式很有用:一個線程阻塞在某I/O操作上時,其他線程可以繼續工作。應用程序應該保證對端口的串行訪問。某個線程阻塞在等待某I/O操作上時,其他線程后續的通信API調用也都將阻塞。比如說,一個線程在等待ReadFile調用返回時,另一個線程的WriteFile函數調用將阻塞。

在選擇使用非重疊還是重疊方式時,可移植性是要考慮的因素之一。有時候重疊操作并不是好的選擇,因為很多操作系統不支持它;然而很多操作系統都支持某種形式的多線程。所以從兼容性方面考慮,多線程非重疊I/O可能是最好的選擇。

2.2 重疊I/O

重疊I/O不像非重疊I/O那樣簡單易懂,但卻靈活高效。使用重疊方式打開的端口允許多個線程同時進行I/O操作,并且在操作進行期間可以進行其他的工作。此外,重疊操作的行為方式還允許單個線程提交多個不同的請求,然后在操作進行期間進行其他后臺工作。

在單線程和多線程應用中,都必須在提交I/O請求和處理操作結果間進行一些同步操作。線程可能需要在操作結果可用前阻塞;當然也可以進行其他工作。如果沒有其他需要進行的工作,則重疊I/O的優點是更好的用戶響應性能。

MTTTY使用了重疊I/O。它創建用于讀取數據和監測端口狀態的線程,并且還定時進行一些后臺工作;此外它還另外創建一個線程用于寫入數據。

重疊I/O操作分為兩個部分:創建I/O操作和檢測操作完成。創建I/O操作涉及到建立OVERLAPPED結構體、創建用于同步的手動復位事件、調用恰當的函數(ReadFile或者WriteFile)。I/O操作可能立即完成,也可能不能立即完成,不能認為一個重疊I/O操作請求總是生成一個重疊操作。如果操作立即完成,程序應該可以繼續進行通常的處理。檢測操作完成涉及到等待事件句柄、檢查操作完成結果、處理數據。與重疊I/O相關的工作更多的原因是有更多的失敗點。非重疊操作中,簡單地通過函數返回值判斷操作是否失敗;而重疊操作中,則可能在創建操作請求時失敗,或者操作阻塞期間失敗,也可能是操作超時,或者是等待操作完成信號超時。

2.2.1 讀操作

下面的代碼片段展示了提交重疊的讀操作請求的方法。注意,如果ReadFile返回TRUE,調用了一個函數處理數據。代碼定義了fWaitingOnRead標志,它表示是否有重疊的讀取操作存在,用于阻止在一個操作進行中時提交另一個讀取操作請求。

?

DWORD?dwRead;
BOOL?fWaitingOnRead?=?FALSE;
OVERLAPPED?osReader?=?{0};
//?Create?the?overlapped?event.?Must?be?closed?before?exiting
//?to?avoid?a?handle?leak.
osReader.hEvent?=?CreateEvent(NULL,?TRUE,?FALSE,?NULL);
if?(osReader.hEvent?==?NULL)
???//?Error?creating?overlapped?event;?abort.
if?(!fWaitingOnRead)?{
???//?Issue?read?operation.
???if?(!ReadFile(hComm,?lpBuf,?READ_BUF_SIZE,?&dwRead,?&osReader))?{
??????if?(GetLastError()?!=?ERROR_IO_PENDING)?????//?read?not?delayed?
?????????//?Error?in?communications;?report?it.
??????else
?????????fWaitingOnRead?=?TRUE;
???}
???else?{????
??????//?read?completed?immediately
??????HandleASuccessfulRead(lpBuf,?dwRead);
????}
}

?

OVERLAPPED結構體的事件句柄被傳遞給WaitForSingleObject以等待事件授信,操作完成。注意,事件受信表示操作完成,而不是操作成功完成。應該用GetOverlappedResult來取得操作結果,它返回TRUE表示操作成功完成;FALSE表示有錯誤發生,用GetLastError可以取得具體的錯誤碼。也可以用GetOverlappedResult來檢測操作完成:GetOverlappedResult返回FALSE,GetLastError返回ERROR_IO_INCOMPLETE表示操作進行中。如果對bWait參數傳入TRUE,則效果就是重疊操作變成了非重疊的,直到操作完成,函數才返回。下面的代碼片段展示了一種檢測重疊讀取操作完成的方法。注意fWaitingOnRead標志的使用,它是檢測代碼的控制入口,只有在某操作進行中時,才應該調用檢測代碼。

#define?READ_TIMEOUT??????500??????//?milliseconds
DWORD?dwRes;
if?(fWaitingOnRead)?{
???dwRes?=?WaitForSingleObject(osReader.hEvent,?READ_TIMEOUT);
???switch(dwRes)
???{
??????//?Read?completed.
??????case?WAIT_OBJECT_0:
??????????if?(!GetOverlappedResult(hComm,?&osReader,?&dwRead,?FALSE))
?????????????//?Error?in?communications;?report?it.
??????????else
?????????????//?Read?completed?successfully.
?????????????HandleASuccessfulRead(lpBuf,?dwRead);
??????????//??Reset?flag?so?that?another?opertion?can?be?issued.
??????????fWaitingOnRead?=?FALSE;
??????????break;
??????case?WAIT_TIMEOUT:
??????????//?Operation?isn't?complete?yet.?fWaitingOnRead?flag?isn't
??????????//?changed?since?I'll?loop?back?around,?and?I?don't?want
??????????//?to?issue?another?read?until?the?first?one?finishes.
??????????//
??????????//?This?is?a?good?time?to?do?some?background?work.
??????????break;???????????????????????
??????default:
??????????//?Error?in?the?WaitForSingleObject;?abort.
??????????//?This?indicates?a?problem?with?the?OVERLAPPED?structure's
??????????//?event?handle.
??????????break;
???}
}

?

?

2.2.2 寫入操作

寫入操作跟讀取操作非常相似。下面的代碼片段展示了如何提交寫入操作,并等待操作完成。

?

BOOL?WriteABuffer(char?*?lpBuf,?DWORD?dwToWrite)
{
???OVERLAPPED?osWrite?=?{0};
???DWORD?dwWritten;
???DWORD?dwRes;
???BOOL?fRes;

???//?Create?this?write?operation's?OVERLAPPED?structure's?hEvent.
???osWrite.hEvent?=?CreateEvent(NULL,?TRUE,?FALSE,?NULL);
???if?(osWrite.hEvent?==?NULL)
??????//?error?creating?overlapped?event?handle
??????return?FALSE;

???//?Issue?write.
???if?(!WriteFile(hComm,?lpBuf,?dwToWrite,?&dwWritten,?&osWrite))?{
??????if?(GetLastError()?!=?ERROR_IO_PENDING)?{?
?????????//?WriteFile?failed,?but?isn't?delayed.?Report?error?and?abort.
?????????fRes?=?FALSE;
??????}
??????else
?????????//?Write?is?pending.
?????????dwRes?=?WaitForSingleObject(osWrite.hEvent,?INFINITE);
?????????switch(dwRes)
?????????{
????????????//?OVERLAPPED?structure's?event?has?been?signaled.?
????????????case?WAIT_OBJECT_0:
?????????????????if?(!GetOverlappedResult(hComm,?&osWrite,?&dwWritten,?FALSE))
???????????????????????fRes?=?FALSE;
?????????????????else
??????????????????//?Write?operation?completed?successfully.
??????????????????fRes?=?TRUE;
?????????????????break;
????????????
????????????default:
?????????????????//?An?error?has?occurred?in?WaitForSingleObject.
?????????????????//?This?usually?indicates?a?problem?with?the
????????????????//?OVERLAPPED?structure's?event?handle.
?????????????????fRes?=?FALSE;
?????????????????break;
?????????}
??????}
???}
???else
??????//?WriteFile?completed?immediately.
??????fRes?=?TRUE;

???CloseHandle(osWrite.hEvent);
???return?fRes;
}

?

注意,上面的代碼使用WaitForSingleObject時,超時值是INFINITE,這使得函數無限等待直到操作完成。這可能讓調用線程似乎是被掛起了;而實際上只是寫入操作需要較長的時間,或者流控制阻塞了傳輸操作。下文將討論的狀態檢查可以檢測到這種情況,但它也不會讓WaitForSingleObject返回。有三種方法可以克服此問題:

  • 把代碼放在單獨的線程中。這樣寫入線程在等待寫操作完成時,其他線程可以進行任何所需的操作。MTTTY就是這么做的。
  • 使用COMMTIMEOUTS使得寫操作在經過一個超時值指定的時間后完成。本文后面的“通信超時”節將詳細討論它。MTTTY也可以使用這種方法。
  • 修改WaitForSingleObject調用,使用超時值。這樣會更麻煩:如果原來的操作仍在進行中,程序提交另一個操作請求,則需要分配新的OVERLAPPED結構和重疊事件。這種記錄跟蹤保持是很困難的,尤其是與“工作隊列”相比較時。MTTTY使用了工作隊列。

上面代碼中的WaitForSingleObject使用了INFINITE作為超時值,其效果等同于使用TRUE作為GetOverlappedResult的fWait參數。下面是等效的更簡潔的代碼:

?

BOOL?WriteABuffer(char?*?lpBuf,?DWORD?dwToWrite)
{
???OVERLAPPED?osWrite?=?{0};
???DWORD?dwWritten;
???BOOL?fRes;

???//?Create?this?writes?OVERLAPPED?structure?hEvent.
???osWrite.hEvent?=?CreateEvent(NULL,?TRUE,?FALSE,?NULL);
???if?(osWrite.hEvent?==?NULL)
??????//?Error?creating?overlapped?event?handle.
??????return?FALSE;

???//?Issue?write.
???if?(!WriteFile(hComm,?lpBuf,?dwToWrite,?&dwWritten,?&osWrite))?{
??????if?(GetLastError()?!=?ERROR_IO_PENDING)?{?
?????????//?WriteFile?failed,?but?it?isn't?delayed.?Report?error?and?abort.
?????????fRes?=?FALSE;
??????}
??????else?{
?????????//?Write?is?pending.
?????????if?(!GetOverlappedResult(hComm,?&osWrite,?&dwWritten,?TRUE))
????????????fRes?=?FALSE;
?????????else
????????????//?Write?operation?completed?successfully.
????????????fRes?=?TRUE;
??????}
???}
???else
??????//?WriteFile?completed?immediately.
??????fRes?=?TRUE;

???CloseHandle(osWrite.hEvent);
???return?fRes;
}

?

GetOverlappedResult并不總是等待重疊操作完成的最好方法。比如說,如果應用需要同時等待另一個事件句柄,則第一個代碼片段模型比第二個更好,因為可以很容易地用WaitForMultipleObjects替換WaitForSingleObject,來等待更多的句柄。MTTTY就是這么做的。

在前一個重疊操作完成前重用OVERLAPPED結構是重疊I/O編程中常現的一個錯誤。如果要在前一個重疊操作完成前提交新的重疊操作請求,則需要分配新的OVERLAPPED結構,其hEvent字段也應該包含新的手動復位事件句柄。只有在重疊操作完成后,OVERLAPPED結構和其事件句柄才可以被重用。

串口通信中使用OVERLAPPED結構時只需要修改hEvent字段,其他字段只需要初始化為零。




總結

以上是生活随笔為你收集整理的Win32 串口编程(一)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。