位掩码(bitmask)在windows 串口事件驱动中的应用
最近在學習Windows串口通信,以事件驅動模式開發相關應用時,肯定會用到以下幾個函數:
SetCommMask(HANDLE hComm, DWORD dwEvtMask);
GetCommMask(HANDLE hComm, LPDWORD lpEvtMask);
WaitCommEvent(HANDLE hComm, LPDWORD lpEvtMask, LPOVERLAPPED lpOverlapped);
這幾個函數的使用我就不再廢話了,大家查MSDN就OK了。 我要說的重點是這幾個函數應用了位掩碼的方法來進行條件分支處理,非常自然,高效。無論對人 還是 對于機器來講,都很優美。 非常值得我們在自己的工程實踐中靈活運用。
其中的核心參數--需要捕獲的事件代碼,我們稱其為“事件掩碼”,用變量 dwEvtMask表示, 定義為16位長度,其可選值如下表所列。
英文單詞或縮寫的標識符常量,非常方便程序員理解,而其一一對應的十六進制常量值也早已在系統里預定義好了,各位直接使用意義直觀的字符串常量就OK了。
| EV_BREAK | A break was detected on input. |
| EV_CTS | The CTS (clear-to-send) signal changed state. |
| EV_DSR | The DSR (data-set-ready) signal changed state. |
| EV_ERR | A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY. |
| EV_RING | A ring indicator was detected. |
| EV_RLSD | The RLSD (receive-line-signal-detect) signal changed state. |
| EV_RXCHAR | A character was received and placed in the input buffer. |
| EV_RXFLAG | The event character was received and placed in the input buffer. The event character is specified in the device'sDCB structure, which is applied to a serial port by using theSetCommState function. |
| EV_TXEMPTY | The last character in the output buffer was sent. |
這個被定義為16位長度的事件掩碼變量,是我們這里要討論的主角。 在我理解了Windows 在這里使用位掩碼的機制之后,我建議你可以把它想象成一個由16盞信號燈排成一排所構成的一個信號燈箱,每盞燈只有開和關兩種狀態。
接下來我們再來看事件列表里的事件代碼的Value,就是那個 0x開頭的十六進制數,我們將它以二進制完全展開,如下所示:
| 事件代碼 | 二進制表示 |
| EV_BREAK | 0000 0000 0100 0000 |
| EV_CTS | 0000 0000 0000 1000 |
| EV_DSR | 0000 0000 0001 0000 |
| EV_ERR | 0000 0000 1000 0000 |
| EV_RING | 0000 0001 0000 0000 |
| EV_RLSD | 0000 0000 0010 0000 |
| EV_RXCHAR | 0000 0000 0000 0001 |
| EV_RXFLAG | 0000 0000 0000 0010 |
| EV_TXEMPTY | 0000 0000 0000 0100 |
秘密就在這里了。你有沒有發現所有事件掩碼值,要么是“1”,要么是2的正整數次冪,這樣設計的結果就是,任何一個事件的掩碼值的二進制表示里,“1”只出現一次,而所有九個事件的掩碼值的二進制表示,同一位的縱向每一列里,“1”最多只出現一次!
現在我們對全部事件的二進制碼進行位操作求 “|”,這有沒有讓你想到點什么?沒錯,就是當初我們設置要關注事件時,如果有兩個或兩個以上事件需要關注時,事件代碼之間用的就是 "|"?操作。”|“ 操作之后,我們得到 “0000 0001 1111 1111”。這里一共九個”1“,而事件也正好是九個,不同位置上的”1“ 對應著不同的事件,這就是位掩碼的意義所在。
/*小隱喻:剛才我說可以把事件掩碼變量 dwEvtMask 想象成由十六個信號燈排成一排所組成的信號燈箱,我們每關注一個事件,就相當于接通對應信號燈的線路,但僅僅只是接通線路,還沒有拉閘通電,當我們關注的事件真的發生了,那對應信號燈就會拉閘通電了。*/
接下來我們回到代碼層面,在設置事件掩碼時,如果我只關注一個事件,比如我只關注 “輸入緩沖區收到了一個字符” 這么一個事件,那么
?SetCommMask( hCom1, EV_RXCHAR);???? //此時事件掩碼變量 dwEvtMask 的值實際上是”0“ ,但相應位置上的信號燈的線路已經接通,我們就用 ”0000 0000 0000 000?“ 來表示這種狀態
如果我要關注兩個事件,比如:
?SetCommMask( hCom1, EV_RXCHAR | EV_ERR);?? //此時事件掩碼變量 dwEvtMask 的值實際上是”0“,但有兩盞信號燈的線路已接通,我們用 ”0000 0000 ?000 000?“來表示
那么三個,四個....哪怕全部九個事件都關注上,也是如此操作,如下:
SetCommMask( hCom1, EV_BREAK |?EV_CTS | EV_DSR | EV_ERR | EV_RING | EV_RLSD | EV_RXCHAR | EV_RXFLAG | EV_TXEMPTY);
?????????????????????????????????????????????? //此時事件掩碼變量 dwEvtMask 的值仍是”0“,但九盞信號燈的線路都已接通,燈箱的狀態為 ”0000 000? ???? ????“
當然我們也可以設置個燈箱指針,這樣操作起來方便點:
DWORD *lpdwEvtMask = (EV_BREAK |?EV_CTS | EV_DSR | EV_ERR | EV_RING | EV_RLSD | EV_RXCHAR | EV_RXFLAG | EV_TXEMPTY);
然后,我們啟動監視功能:
?WaitCommEvent(hCom1, lpdwEvtMask, NULL);??? //為聚焦今天的主題,這里暫不討論重疊方式
一旦 事件掩碼 變量里所羅列的事件中有任何一個或兩個/三個...九個 事件發生,則其位掩碼值將被填寫進 dwEvtMask,即相應位被置1 (此動作由系統完成),在我的隱喻里,則是對應的信號燈通電點亮了。? 當然,代碼里是沒有什么信號燈和可見光的(但是有智慧之光,呵呵),還是需要靠代碼來說話。
?SetCommMask( hCom1, EV_RXCHAR | EV_ERR);???? //假設我們現在關注兩個事件
? ...
?if ( WaitCommEvent(hCom1, lpdwEvtMask, NULL)?)??? //如果條件為真,則說明有關注的事件發生了,可能是一個,也可能是兩個關注的事件都發生了,所以需要進一步鑒別
?? {
??? if ( dwEvtMask & EV_RXCHAR?)??????? //如果EV_RXCHAR事件發生了,則dwEvtMask相應位被置”1“,條件運算的結果就是EV_RXCHAR事件的掩碼值,既非零,又明確指出了是哪個事件
?????? {
??????? ...?????? //響應 EV_RXCHAR 事件
??????? }
???? if ( dwEvtMask & EV_ERR?)
???????{
???????? ...????? //響應 EV_ERR 事件
??????? }
??? }
else
? ...??? //處理錯誤
這里的條件判斷,體現出了運用位掩碼的優勢,其通過位運算就完成了事件篩選,代碼閱讀起來清晰易懂,而機器執行起來又絕對的高速度和高效率,所以我說這樣的設計非常優美。
實際上,弄懂了這些之后,我們也完全可以在自己的代碼實踐中靈活運用 位掩碼,從而使代碼簡潔,高效,優美。
延伸閱讀:
http://blog.csdn.net/qsir/article/details/72457305
http://www.cnblogs.com/zyl910/archive/2012/03/12/noifopex1.html
總結
以上是生活随笔為你收集整理的位掩码(bitmask)在windows 串口事件驱动中的应用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: arguments 类数组
- 下一篇: 电气工程及其自动化学不学c语言,电气工程