RS232串口通信原理詳解見:hi.baidu.com/gilbertjuly/blog/item/902a3f11d4b42b0b203f2e39.html
Gilbert在串口通信中使用CSerialPort時,發(fā)現(xiàn)連續(xù)發(fā)送有問題,比如,連續(xù)發(fā)了三個數(shù)組:
m_serial.WriteToPort(chSend1); m_serial.WriteToPort(chSend2); m_serial.WriteToPort(chSend2); //chSend1,chSend2,chSend3是數(shù)組首地址.
只有最后一個數(shù)組發(fā)送成功,開始以為這個類不能連續(xù)發(fā)送數(shù)據(jù),但是后來發(fā)現(xiàn)如果寫成這個樣子:
m_serial.WriteToPort(chSend1); AfxMessageBox(""); m_serial.WriteToPort(chSend2); AfxMessageBox(""); m_serial.WriteToPort(chSend3); AfxMessageBox("");
三個數(shù)組的數(shù)據(jù)就都能發(fā)出去,這是為什么呢?
========================================= 這個要看CSerialPort是怎么做出來的,也就是從Windows的串口編程說起。在Win32下,可以使用兩種編程方式實現(xiàn)串口通信,其一是使用ActiveX控件,這種方法程序簡單,但是控件只能在對話框中使用。其二是調用Windows的API函數(shù),這種方法可以清楚地掌握串口通信的機制,并且自由靈活。CSerialPort類就是對串口的API函數(shù)封裝。
與以往DOS下串行通信程序不同的是,Windows不提倡應用程序直接控制硬件,而是通過Windows操作系統(tǒng)提供的設備驅動程序來進行數(shù)據(jù)傳遞。串行口在Win 32中是作為文件來進行處理的,而不是直接對端口進行操作,對于串行通信,Win 32 提供了相應的文件I/O函數(shù)與通信函數(shù),通過了解這些函數(shù)的使用,可以編制出符合不同需要的通信程序。
由于是作為文件來處理,我們看看windows API中打開串口的函數(shù): HANDLE CreateFile( LPCTSTR lpFileName, //將要打開的串口邏輯名,如“COM1”; DWORD dwDesiredAccess, //指定串口訪問的類型,可以是讀取、寫入或二者并列; DWORD dwShareMode, //指定共享屬性,由于串口不能共享,該參數(shù)必須置為0; LPSECURITY_ATTRIBUTES lpSecurityAttributes, //引用安全性屬性結構,缺省值為NULL; DWORD dwCreationDistribution, //創(chuàng)建標志,對串口操作該參數(shù)必須置為OPEN_EXISTING; DWORD dwFlagsAndAttributes, //屬性描述,用于指定該串口進行異步或同步操作; HANDLE hTemplateFile); //對串口而言該參數(shù)必須置為NULL;
========================================= 請務必注意倒數(shù)第二個參數(shù)dwFlagsAndAttributes,它指示了串口進行異步或同步操作。還請注意這里的同步和異步不是指的數(shù)據(jù)通信里時鐘的同步和異步。這里的同步和異步指的是:
同步操作時,API函數(shù)會阻塞直到操作完成以后才能返回(在多線程方式中,雖然不會阻塞主線程,但是仍然會阻塞監(jiān)聽線程);而異步(重疊)操作方式,API函數(shù)會立即返回,操作在后臺進行,避免線程的阻塞。
dwFlagsAndAttributes該值為FILE_FLAG_OVERLAPPED,表示使用異步的I/O;該值為0,表示同步I/O操作。
既然CSerialPort類是對API函數(shù)的封裝,我們有理由相信在它的實現(xiàn)里面必定有CreateFile函數(shù),打開它的cpp來看,果然可以搜索的到,再看看它是怎么設置的:
m_hComm = CreateFile(szPort, // communication port string (COMX) GENERIC_READ | GENERIC_WRITE, // read/write types 0, // comm devices must be opened with exclusive access NULL, // no security attributes OPEN_EXISTING, // comm devices must use OPEN_EXISTING FILE_FLAG_OVERLAPPED, // Async I/O 0); // template must be 0 for comm devices
可以看到倒數(shù)第二個參數(shù)設為FILE_FLAG_OVERLAPPED,即異步發(fā)送。
========================================= 其實問題就在這里,異步發(fā)送的話就造成文章一開始說的兩個現(xiàn)象。
先說第一個現(xiàn)象:為什么三個數(shù)組中只發(fā)送了最后一個數(shù)組? 因為采用了異步操作,在執(zhí)行到m_serial.WriteToPort(chSend1); 時,并不馬上發(fā)送串口數(shù)據(jù),而是要等進入CSerialPort的線程之后再發(fā)送(如果是同步操作,則程序停在那里等發(fā)送完成)。 你可以跟蹤一下程序,看是什么時候進入它的線程的,三句WriteToPort都會進入這一個線程,而不是三個線程,在這個線程中只發(fā)送一次數(shù)據(jù),數(shù)據(jù)的來源就是形參最后的更新,所以就是第三個數(shù)組了。
第二個現(xiàn)象:為什么加了AfxMessageBox(""); 就都可以發(fā)送? 因為AfxMessageBox(""); 使程序的進程被掛起,這樣CSerialPort的線程就得以運行,所以就發(fā)送了。
========================================= 看到這里你可能會想:那能不能將dwFlagsAndAttributes改成0設為同步發(fā)送? 答案是好像不可以,可能還需要改其他地方,光這里改成同步的話,串口什么數(shù)據(jù)也發(fā)不了。 如果你發(fā)現(xiàn)能簡易的將這個類改成同步的方法,歡迎聯(lián)系我gilbertjuly@gmail.com。
========================================= 那我們要同步發(fā)送怎么辦? 1.將chSend1,chSend2,chSend3放在一起組建成一個更大的數(shù)組一次發(fā)送 這樣如果接收端處理串口數(shù)據(jù)較慢的話,可能要在每個數(shù)組當中插入些無用的數(shù)據(jù)。 2.使用其他具有同步操作功能的類,如: blog./u/32550/showart_365425.html 3.學習windows api的同學們還可以自己寫串口編程的程序,可以參考: http://www./document/viewdoc/?id=1734#%E8%AF%BB%E5%86%99%E4%B8%B2%E5%8F%A3 |
|