• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            山寨:不是最好的,是最適合我們的!歡迎體驗山寨 中文版MSDN

            Blog @ Blog

            當華美的葉片落盡,生命的脈絡才歷歷可見。 -- 聶魯達

            常用鏈接

            統計

            積分與排名

            BBS

            Blog

            Web

            最新評論

            Win32串口操作的技巧

            1.開啟一個 Serial Port:

              利用一般開啟檔案的 CreatFile() 即可開啟 serial port device用 CreateFile() API.

            HANDLE CreateFile(
                              LPCTSTR lpFileName, 
            // pointer to name of the file
                              DWORD dwDesiredAccess, // access (read-write) mode
                              DWORD dwShareMode, // share mode
                              LPSECURITY_ATTRIBUTES lpSecurityAttributes, // pointer to security attributes
                              DWORD dwCreationDistribution, // how to create
                              DWORD dwFlagsAndAttributes, // file attributes
                              HANDLE hTemplateFile // handle to file with attributes to copy
                              );
                    lpFileName 為 "COM1" 或是 "COM2"
              dwDersiredAccess 一般為 GENERIC_READ|GENERIC_WRITE
              dwShareMode "必須"為 0, 即不能共享, 但同一個 process 中的不同 thread 在一開啟之后就可以共享.
              lpSecurityAttributes 一般為 NULL
              dwCreateionDistributon 在這里"必須"為 OPEN_EXISTING
              dwFlagsAndAttributes 定義了開啟的屬性, 若是設成 FILE_FLAG_OVERLAPPED 則可使用異步的 I/O.
              hTemplateFile "必須"為 NULL
              傳回檔案 handle
              設定 Serial Port 傳送及接收緩沖區的大小

              在開啟完 serial port 之后, 可以藉由呼叫 SetupComm() 來進行配置傳送時的緩沖區及接收時的緩沖區. 如果沒有呼叫 SetupComm() 的話, Win95 會配置內定的緩沖區

            BOOL SetupComm(
                           HANDLE hFile, 
            // handle of communications device
                           DWORD dwInQueue, // size of input buffer
                           DWORD dwOutQueue // size of output buffer
                           );

              2.關閉 Serial Port file

              利用一般的 CloseHandle() 即可.

            BOOL CloseHandle(
                             HANDLE hObject 
            // handle to object to close
                             )
              3.取得 Seial Port 的信息

              在 Win32 里頭, 將一些通訊時會用到的信息用 COMMPROP 這個結構來表示. (當然不僅僅是 Serial Port) 可以用 GetCommProperties() 來取得:

            BOOL GetCommProperties(
                                   HANDLE hFile, 
            // handle of communications device
                                   LPCOMMPROP lpCommProp // address of communications properties structure
                                   );
                  COMMPROP 長的像這個樣子:
            typedef struct _COMMPROP // cmmp
                WORD wPacketLength; // packet size, in bytes
                WORD wPacketVersion; // packet version
                DWORD dwServiceMask; // services implemented
                DWORD dwReserved1; // reserved
                DWORD dwMaxTxQueue; // max Tx bufsize, in bytes
                DWORD dwMaxRxQueue; // max Rx bufsize, in bytes
                DWORD dwMaxBaud; // max baud rate, in bps
                DWORD dwProvSubType; // specific provider type
                DWORD dwProvCapabilities; // capabilities supported
                DWORD dwSettableParams; // changable parameters
                DWORD dwSettableBaud; // allowable baud rates
                WORD wSettableData; // allowable byte sizes
                WORD wSettableStopParity; // stop bits/parity allowed
                DWORD dwCurrentTxQueue; // Tx buffer size, in bytes
                DWORD dwCurrentRxQueue; // Rx buffer size, in bytes
                DWORD dwProvSpec1; // provider-specific data
                DWORD dwProvSpec2; // provider-specific data
                WCHAR wcProvChar[1]; // provider-specific data
            }
             COMMPROP;
              在這里, lpCommProp 需要 programmer 自行配置空間. 有趣的問題是, 系統在這個結構之后會需要額外的空間. 但是配置者也就是 programmer 卻不知道系統會需要多少. 很簡單的做法是配置一大塊絕對會夠的空間. 另一個聰明的做法是執行兩次 GetCommProperties() , 第一次只配置 sizeof(COMMPROP) 這么大的空間, 因為還沒有開始執行一些動作, 所以系統并不會嘗試著在后面填東西, 所以不會出問題. 接著執行第一次的 GetCommProperties(), 得到結果, 取出結構中的 wPacketLength, 這個 member 代表實際上需要的大小, 然后依據這個大小重新配置一個新的. 這樣的話 , 就不會有浪費任何空間的問題了.

              至于上述 COMMPROP 結構的成員所代表的意思, on-line help 中應該寫的都滿清楚的 .

              4.設定及取得通訊狀態

             

              你可以利用 COMMPROP 來取得一些狀態, 但是當你想改變目前的設定時你需要兩個 API 來完成:

            BOOL GetCommState(
                              HANDLE hFile, 
            // handle of communications device
                              LPDCB lpDCB // address of device-control block structure
                              );

            BOOL SetCommState(
                              HANDLE hFile, 
            // handle of communications device
                              LPDCB lpDCB // address of device-control block structure
                              );

              你可以用 GetCommState() 來取得目前 Serial Port 的狀態, 也可以用 SetCommState() 來設定 Serial Port 的狀態.

              DCB 的結構就請自行翻閱 help 啰.

              另外, programmer 最??刂频膸讉€設定就是 baud rate, parity method, data bits, 還有 stop bit. BuildCommDCB() 提供了對于這幾個常見設定的控制.

            BOOL BuildCommDCB(
                              LPCTSTR lpDef, 
            // pointer to device-control string
                              LPDCB lpDCB // pointer to device-control block
                              );
            lpDef 長的像這樣: "baud=2400 parity=N data=8 stop=1"

              5.通訊設定對話盒

              Win32 API 中提供了一個開啟通訊設定對話盒的 API: CommConfigDialog(), 當呼叫這個 API 時, 會蹦現一個可供設定 Baud Rate, Data Bits, Parity .. 等信息的對話盒, programmer 可以利用它來讓使用者設定一些信息, 并且取得結果.

            BOOL CommConfigDialog(
                                  LPTSTR lpszName, 
            // pointer to device name string
                                  HWND hWnd, // handle to window
                                  LPCOMMCONFIG lpCC // pointer to comm. configuration structure
                                  );
              其中 lpCC 被用來存放設定值的結果.
            typedef struct _COMM_CONFIG {
                DWORD dwSize;
                WORD wVersion;
                WORD wReserved;
                DCB dcb;
                DWORD dwProviderSubType;
                DWORD dwProviderOffset;
                DWORD dwProviderSize;
                WCHAR wcProviderData[
            1];
            }
             COMMCONFIG, *LPCOMMCONFIG;

              在我們呼叫 CommConfigDialog() 之前, dwSize 要設為 sizeof(COMMCONFIG), wVersion 的值在這邊似乎不重要(我不清楚, VC5 的 on-line help 說可以設為 1, 我手中的 book 的范例是設為 0x100), 呼叫完 CommConfigDialog() 之后, 成員 dcb 中的 BaudRate, ByteSize, StopBits, Parity 就是使用者的設定.

              6.Timeout 的機制

              因為傳輸時并不會維持一個絕對穩定的速率. 因為傳輸品質的關系, programer 會需要 timeout 的機制來協助他們做一些控制. 在 Win32 通訊 Timeout 的機制中, timeout 的性質共分為兩類, 先來看看 COMMTIMEOUTS 這個結構:

            typedef struct _COMMTIMEOUTS // ctmo
                DWORD ReadIntervalTimeout;
                DWORD ReadTotalTimeoutMultiplier;
                DWORD ReadTotalTimeoutConstant;
                DWORD WriteTotalTimeoutMultiplier;
                DWORD WriteTotalTimeoutConstant;
            }
             COMMTIMEOUTS,*LPCOMMTIMEOUTS;
              programmer 可以利用 GetCommTimeouts() 和 SetCommTimeouts() 來讀取或是設定目前的 timeout 值.
            BOOL GetCommTimeouts(
                                 HANDLE hFile, 
            // handle of communications device
                                 LPCOMMTIMEOUTS lpCommTimeouts // address of comm. time-outs structure
                                 );

            BOOL SetCommTimeouts(
                                 HANDLE hFile, 
            // handle of communications device
                                 LPCOMMTIMEOUTS lpCommTimeouts // address of communications time-out structure
                                 );
            第一種 timeout 的機制稱為 interval timeout, 從字面上的意義很容易可以理解這種 timeout 的機制是讀取字符之間的間隔時間的 timeout, 只有讀取字符時才能夠使用interval timeout. 也就是在這個結構中的 ReadIntervalTimeout, 單位為 ms, 當讀取完一個字符后, 超過了 ReadIntervalTimeout 的值, 卻還沒有讀到下一個字符時, timeout 就發生了.

              第二種 timeout 的機制稱為 total timeout, 顧名思義即是傳輸的總時間的 timeout . 在這種 timeout 的機制下, Win32 提供了一個具有彈性的方式來設定 total timeout. 以讀取的 total timeout 為例, 利用 ReadTotalTimeoutMultiplier 和 ReadTotalTimeoutConstant 構成了一個線性的上限值. 什么意思呢? 實際上的 total timeout 應該是這樣的一個式子:

              ReadTotalTimeout = ReadTotalTimeOutMultiplier * BytesToRead + ReadTotalTimeoutConstant

              WriteTotalTimeout 用同樣的公式來計算. 這樣的話, 不僅可以用一個固定的值來做為 timeout 值, 也可以用條線來做為 timeout 的值, 而隨著要讀取或是要寫的 bytes 數而變動.

              如果不想使用 timeout, 就把 COMMTIMEOUTS 里頭的資料成員都填為 0.

              如果你將 ReadIntervalTimeout 設為 MAXDWORD, 且將 ReadTotalTimeOutMultiplier 和 ReadTotalTimeoutConstant 都設為 0 的話, 那么讀取時, 如果 receive queue 里頭并沒有資料, 讀取的動作將會馬上返回, 而不會停滯在讀取的動作.

              這里有一個和 BuildCommDCB() 很像的 API 叫 BuildCommDCBAndTimeouts():


            BOOL BuildCommDCBAndTimeouts(
                                         LPCTSTR lpDef, 
            // pointer to the device-control string
                                         LPDCB lpDCB, // pointer to the device-control block
                                         LPCOMMTIMEOUTS lpCommTimeouts // pointer to comm. time-out structure
                                         );

              lpDef 一樣是控制字符串, 可以給像 BuildCommDCB() 中的 lpDef 那樣格式的字符串, 但是多了 "TO=XXX" 這個設定. 如果 "TO=ON", 這個 API 會依據 lpCommTimeouts 里頭的值來設定讀和寫的 timeout 值. 如果 "TO=OFF", 則會設定這個 device 沒有 timeout. 如果是 "ON" 和 "OFF" 之外的其它值, 則 lpCommTimeouts 的設定將會被忽略.

              對了, 在設定完 timeout 值之后, 記得要檢查 COMMPROP 里的 dwProvCapabilities 中的 PCF_INTTIMEOUTS 和 PCF_TOTALTIMEOUTS 兩個 flags 是否有被 set, 以確認 interval timeout 和 total timeout 是否有支持.
              7.讀取資料

             

              從 serial port 里頭讀取資料就跟讀取一般的檔案一樣, 使用 ReadFile() 來達成.

            BOOL ReadFile(
                          HANDLE hFile, 
            // handle of file to read
                          LPVOID lpBuffer, // address of buffer that receives data
                          DWORD nNumberOfBytesToRead, // number of bytes to read
                          LPDWORD lpNumberOfBytesRead, // address of number of bytes read
                          LPOVERLAPPED lpOverlapped // address of structure for data
                          );

              要注意的是, nNumberOfBytesToRead 設定的是一次最多的讀取量, 很有可能所讀取的值(檢查 lpNumberOfBytesRead)小于這個值. 通常在錯誤發生或是 timeout 發生時這個 API 就會返回.

              PurgeComm() 這個 API 可以用來終止目前正在進行的讀或寫的動作, 也可以 flush 掉 I/O buffer 內等待讀或寫的資料.

             

            BOOL PurgeComm(
                           HANDLE hFile, 
            // handle of communications resource
                           DWORD dwFlags // action to perform
                           );

            其中 dwFlags 共有四種 flags:

              PURGE_TXABORT: 終止目前正在進行的(背景)寫入動作
              PURGE_RXABORT: 終正目前正在進行的(背景)讀取動作
              PURGE_TXCLEAR: flush 寫入的 buffer
              PURGE_RXCLEAR: flush 讀取的 buffer
              而使用 FlushFileBuffers() 可以確保所有的資料都被送出, 這個 API 才會返回.

              另外一個有趣的 API 是 ClearCommError(), 從字面上的意思看來, 它是用來清除錯誤情況用的, 但是實際上它還可以拿來取得目前通訊設備的一些信息.

             

            BOOL ClearCommError(
                                HANDLE hFile, 
            // handle to communications device
                                LPDWORD lpErrors, // pointer to variable to receive error codes
                                LPCOMSTAT lpStat // pointer to buffer for communications status
                                );

            呼叫這個 API 之后, 關于通訊設備的一些信息會被儲存在 lpStat 中, COMSTAT 的結構如下:


            typedef 
            struct _COMSTAT // cst
                DWORD fCtsHold : 1// Tx waiting for CTS signal
                DWORD fDsrHold : 1// Tx waiting for DSR signal
                DWORD fRlsdHold : 1// Tx waiting for RLSD signal
                DWORD fXoffHold : 1// Tx waiting, XOFF char rec'd
                DWORD fXoffSent : 1// Tx waiting, XOFF char sent
                DWORD fEof : 1// EOF character sent
                DWORD fTxim : 1// character waiting for Tx
                DWORD fReserved : 25// reserved
                DWORD cbInQue; // bytes in input buffer
                DWORD cbOutQue; // bytes in output buffer
            }
             COMSTAT, *LPCOMSTAT

            藉由 fCtsHold, fDsrHold, fRlsdHold, fXoffHold, fXoffSent 可以知道目前因為什么因素而使通訊阻礙住了.( 跟 handshaking 和 flow control 有關) cbInque 和 cbOutQue 則可以顯示出還有多少 bytes 在讀取或是寫入 queue 中.

              8.寫入資料

              和讀取資料一樣, programmer 可以使用 WriteFile() 來將資料寫入 serial port.

            BOOL WriteFile(
                           HANDLE hFile, 
            // handle to file to write to
                           LPCVOID lpBuffer, // pointer to data to write to file
                           DWORD nNumberOfBytesToWrite, // number of bytes to write
                           LPDWORD lpNumberOfBytesWritten, // pointer to number of bytes written
                           LPOVERLAPPED lpOverlapped // pointer to structure needed for overlapped I/O
                           );

              關于通訊設備的寫入有三個很有趣的 API, 它們分別是 SetCommBreak(), ClearCommBreak, 和 TransmitCommChar().

             

            BOOL SetCommBreak(
                              HANDLE hFile 
            // handle of communications device
                              );

            BOOL ClearCommBreak(
                                HANDLE hFile 
            // handle to communications device
                                );

            BOOL TransmitCommChar(
                                  HANDLE hFile, 
            // handle of communications device
                                  char cChar // character to transmit
                                  );

              SetCommBreak() 是用來暫停目前的傳輸作業, 它會使 buffer 中的資料都不再被送出, 這個時候, program 可以去做些雜七雜八的事, 之后, 再利用 ClearCommBreak() 回復傳輸作業.

              TransmitCommChar() 是用來立即性的趕在所有 buffer 數據被送出去之前, 傳輸一個字符的數據出去, 即使 buffer 中還有資料. 換句話說, 這個字符有最高的優先權被送出去.

              9.事件驅動式的 I/O

              在 Win32 里頭, 對于通訊設備的 I/O 可以用像是事件驅動式的方法來達成. 主要是利用一個叫 WaitCommEvent() 的 API. 呼叫這個 API 之后, 會一直 block 到設定的事件發生之后才會返回. 我們先來看看如何設定事件, 再回過頭來看 WaitCommEvent() .

              programer 可以用 GetCommMask() 和 SetCommMask() 來取得或是設定目前設定的通訊事件.

             

            BOOL GetCommMask(
                             HANDLE hFile, 
            // handle of communications device
                             LPDWORD lpEvtMask // address of variable to get event mask
                             );

            BOOL SetCommMask(
                             HANDLE hFile, 
            // handle of communications device
                             DWORD dwEvtMask // mask that identifies enabled events
                             );

              可以設定的事件有 EV_BREAK, EV_CTS, EV_DSR, EV_ERR, EV_RING, EV_RLSD, EV_RXCHAR, EV_RXFLAG, EV_TXEMPTY.(其意義請自行參考 help), 當然, 你可以把它們 or 起來成為組合的事件.

              在設定完想要處理的事件之后, 可以使用 WaitCommEvent()

            BOOL WaitCommEvent(
                               HANDLE hFile, 
            // handle of communications device
                               LPDWORD lpEvtMask, // address of variable for event that occurred
                               LPOVERLAPPED lpOverlapped, // address of overlapped structure
                               );

              WaitCommEvent() 會一直 block 到你所設定的通訊事件發生為止. 所以當 WaitCommEvent() 返回時, 你可以由 lpEvtMask 取得究竟是那一事件發生, 再來決定要如何處理.

              舉例來說, 可以用 SetCommMask() 設定事件為 EV_RXCHAR, 那么在呼叫 WaitCommEvent() 時, 它會等到有字符可供讀取時才會返回, 那么在它返回之后, 可以檢查一下 lpEvtMask 中是否 set 了 EV_RXCHAR, 如果是的話就可以用 ReadFile() 去讀取. 這樣的話, 可以避免掉某些情形之下, 需要做 polling 所引起效率不彰的問題.

              10.錯誤的處理

              前面提過的 ClearnCommError() 可以用來取得目前發生錯誤的原因.(請參見 help)

              11.硬件的控制命令

              Win32 中提供了 EscapeCommFunction() 允許 programer 對幾個硬件訊號做控制.

            BOOL EscapeCommFunction(
                                    HANDLE hFile, 
            // handle to communications device
                                    DWORD dwFunc // extended function to perform
                                    );

              其中 dwFunc 可以是:

            CLRDTR : 讓 DTR OFF
            CLRRTS : 讓 RTS OFF
            SETDTR : 讓 DTR ON
            SETRTS : 讓 RTS ON
            SETXOFF : "仿真" 接收到 XOFF 字符
            SETXON : "仿真" 接收到 XON 字符
            SETBREAK : 和 SetCommBreak() 的意思相同
            CLRBREAK : 和 ClearCommBreak() 的意思相同

            posted on 2008-01-08 11:34 isabc 閱讀(2423) 評論(0)  編輯 收藏 引用 所屬分類: 串口通信

            廣告信息(免費廣告聯系)

            中文版MSDN:
            歡迎體驗

            中文无码久久精品| 精品久久久久久无码中文字幕| 亚洲国产成人精品女人久久久 | 久久99久久成人免费播放| 国产亚州精品女人久久久久久 | 欧美精品福利视频一区二区三区久久久精品 | 亚洲AV伊人久久青青草原| 午夜精品久久久久久中宇| 狠狠色噜噜狠狠狠狠狠色综合久久 | 精品久久久久久中文字幕人妻最新| 国产精品99久久免费观看| 久久午夜综合久久| 99999久久久久久亚洲| 国产精品亚洲综合久久| 亚洲国产精品久久久久网站| 伊人久久大香线蕉av一区| 精品国产91久久久久久久a | 国产精品久久网| 亚洲人成电影网站久久| 狠狠精品久久久无码中文字幕| 久久久精品久久久久影院| 97精品国产97久久久久久免费| 无码国内精品久久人妻蜜桃| 欧美久久久久久精选9999| 久久精品午夜一区二区福利 | 久久99国产精品久久99果冻传媒| 老司机午夜网站国内精品久久久久久久久| 久久久久久国产精品无码超碰| 污污内射久久一区二区欧美日韩| 99久久伊人精品综合观看| 国产韩国精品一区二区三区久久| 性色欲网站人妻丰满中文久久不卡| 久久综合亚洲色HEZYO国产| 久久精品国产国产精品四凭| 国产成人精品久久综合| 99麻豆久久久国产精品免费| 2022年国产精品久久久久| 成人国内精品久久久久一区| 久久99精品国产自在现线小黄鸭 | 久久久久亚洲AV成人片 | 亚洲欧美精品伊人久久|