📄 win32串口编程技术.htm
字号:
<HTML><HEAD><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=gb_2312-80"><META NAME="Generator" CONTENT="Microsoft Word 97"><TITLE>WIN32串口通信技术</TITLE></HEAD><BODY bgcolor="#CCCCFF"><B><FONT FACE="宋体" LANG="ZH-CN"> <P ALIGN="JUSTIFY">Win32串口编程技术</P></font></B><FONT FACE="宋体" LANG="ZH-CN" SIZE=3> <P ALIGN="JUSTIFY"> <b><font face="宋体" lang="ZH-CN" size="2">金贝贝</font><font size="2"> </font></b> </P><P ALIGN="JUSTIFY">串口通信是经典的数据通信,从单片机串口通信DOS下串口通信到WIN32串口通信,发展变换很大,WIN32下的串口通信与其他串口通信有很大的区别,在WIN32下的串口通信编程方式主要有两种,一种是采用WIN32 API函数调用,另外一种方式是使用ActiveX控件。该综述的第二部分介绍了WIN32下的串口通信。主要是利用多线程技术,调用WIN32 API函数,实现同步或异步的串口通信,根据不同的硬件握手方式和串口连线方式,WIN32下的串口通信方式十分灵活。作者利用该串口通信技术,采用VC6.0编程,在Windows98环境下开发了一套“公安350兆无线集群调度系统”,实时传递集群系统的电台的数据和计算机的指令,成功的实现了集群系统的计算机调度,工作情况良好。</P><B><P ALIGN="JUSTIFY"> </P><P ALIGN="JUSTIFY">一.Win32串口通信基本知识</P></B><P ALIGN="JUSTIFY"> </P></FONT><P ALIGN="JUSTIFY"><FONT SIZE=3>Win32</font><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>下串口通信与</FONT><FONT SIZE=3>16</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>位串口通信有很大的区别。在</FONT><FONT SIZE=3>Win32</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>下,可以使用两种编程方式实现串口通信,其一是调用的</FONT><FONT SIZE=3>Windows</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>的</FONT><FONT SIZE=3>API</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>函数,其二是使用</FONT><FONT SIZE=3>ActiveX</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>控件。使用</FONT><FONT SIZE=3>API</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>调用,可以清楚地掌握串口通信的机制,熟悉各种配置和自由灵活采用不同的流控进行串口通信。下面介绍一下串口操作的基本知识。</font></P><OL><FONT FACE="宋体" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY"></font><LI><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>打开串口:使用</font><FONT SIZE=3>CreateFile()</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>函数,可以打开串口,有两种方法可以打开串口,一种是同步方式(</FONT><FONT SIZE=3>NonOverlapped</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>)</FONT><FONT SIZE=3>,</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>另外一种异步方式(</FONT><FONT SIZE=3>overlapped</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>)。使用</FONT><FONT SIZE=3>Overlapped</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>打开时,适当的方法是:</font></LI><FONT FACE="宋体" LANG="ZH-CN" SIZE=3><p></P></FONT><FONT SIZE=3><P ALIGN="JUSTIFY">HANDLE hComm;</P><P ALIGN="JUSTIFY">hComm = CreateFile( gszPort, </P><P ALIGN="JUSTIFY"> GENERIC_READ | GENERIC_WRITE, </P><P ALIGN="JUSTIFY"> 0, </P><P ALIGN="JUSTIFY"> 0, </P><P ALIGN="JUSTIFY"> OPEN_EXISTING,</P><P ALIGN="JUSTIFY"> FILE_FLAG_OVERLAPPED,</P><P ALIGN="JUSTIFY"> 0);</P><P ALIGN="JUSTIFY">if (hComm == INVALID_HANDLE_VALUE)</P><P ALIGN="JUSTIFY"> // error opening port; abort</P></FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY"><LI>配置串口:</LI><p></P></font></OL><DIR><P ALIGN="JUSTIFY"><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>(</font><FONT SIZE=3>1</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>)</FONT><FONT SIZE=3>DCB</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>配置:</FONT> </P><DIR><DIR><P ALIGN="JUSTIFY"><FONT SIZE=3>DCB</font><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>(</FONT><FONT SIZE=3>Device Control Block</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>)结构定义了串口通信设备的控制设置。许多重要设置都是在</FONT><FONT SIZE=3>DCB</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>结构中设置的,有三种方式可以初始化</FONT><FONT SIZE=3>DCB</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>。</font></P></DIR></DIR><P ALIGN="JUSTIFY"><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>其一是:通过</font><FONT SIZE=3>GetCommState()</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>函数得</FONT><FONT SIZE=3>DCB</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>的初始值,其使用方式为:</font></P><FONT SIZE=3><P ALIGN="JUSTIFY"> DCB dcb = {0};</P><P ALIGN="JUSTIFY"> if (!GetCommState(hComm, &dcb))</P><P ALIGN="JUSTIFY"> // Error getting current DCB settings</P><P ALIGN="JUSTIFY"> else</P><P ALIGN="JUSTIFY"> // DCB is ready for use.</P></FONT><DIR><DIR><FONT SIZE=3></font><P ALIGN="JUSTIFY"><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>其二是:用</font><FONT SIZE=3>BuildCommDCB()</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>函数初始化</FONT><FONT SIZE=3>DCB</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>结构,该函数填充</FONT><FONT SIZE=3>DCB</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>的波特率,奇偶校验类型,数据位,停止位,和数据位。对于流控成员函数设置了缺省值。其用法是:</font></P></DIR></DIR><FONT SIZE=3><P ALIGN="JUSTIFY"> DCB dcb;</P><P ALIGN="JUSTIFY"> FillMemory(&dcb, sizeof(dcb), 0);</P><P ALIGN="JUSTIFY"> dcb.DCBlength = sizeof(dcb);</P><P ALIGN="JUSTIFY"> if (!BuildCommDCB("9600,n,8,1", &dcb)) { </P><P ALIGN="JUSTIFY"> // Couldn't build the DCB. Usually a problem</P><P ALIGN="JUSTIFY"> // with the communications specification string.</P><P ALIGN="JUSTIFY"> return FALSE;</P><P ALIGN="JUSTIFY"> }</P><P ALIGN="JUSTIFY"> else</P><P ALIGN="JUSTIFY"> // DCB is ready for use.</P></FONT><DIR><FONT SIZE=3></font><P ALIGN="JUSTIFY"><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>其三为:用</font><FONT SIZE=3>SetCommState()</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>函数手动设置</FONT><FONT SIZE=3>DCB</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>初值。用法如下:</font></P></DIR></DIR><FONT SIZE=3><P ALIGN="JUSTIFY"> DCB dcb;</P><P ALIGN="JUSTIFY"> FillMemory(&dcb, sizeof(dcb), 0);</P><P ALIGN="JUSTIFY"> if (!GetCommState(hComm, &dcb)) // get current DCB</P></FONT><P ALIGN="JUSTIFY"><FONT SIZE=3> </font><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>	</FONT><FONT SIZE=3> // Error in GetCommState</font></P><FONT SIZE=3><P ALIGN="JUSTIFY"> 	return FALSE;</P><DIR><P ALIGN="JUSTIFY">// Update DCB rate.</P></DIR></FONT><P ALIGN="JUSTIFY"><FONT SIZE=3> </font><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>	</FONT><FONT SIZE=3> dcb.BaudRate = CBR_9600 ;</font></P><P ALIGN="JUSTIFY"><FONT SIZE=3> </font><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>	</FONT><FONT SIZE=3>// Set new state.</font></P><FONT SIZE=3><P ALIGN="JUSTIFY"> if (!SetCommState(hComm, &dcb))</P><P ALIGN="JUSTIFY"> // Error in SetCommState. Possibly a problem with the communications </P><P ALIGN="JUSTIFY"> // port handle or a problem with the DCB structure itself.</P></FONT><P ALIGN="JUSTIFY"><FONT SIZE=3> </font><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>手动设置</FONT><FONT SIZE=3>DCB</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>值时,</FONT><FONT SIZE=3>DCB</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>的结构的各成员的含义,可以参看</FONT><FONT SIZE=3>MSDN</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>帮助。</font></P><P ALIGN="JUSTIFY"><FONT SIZE=3> </font><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>(</FONT><FONT SIZE=3>2</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>)流控设置:</font></P><OL TYPE="A"><DIR><DIR><OL TYPE="A"><FONT FACE="宋体" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY"></font><LI><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>硬件流控:串口通信中的硬件流控有两种,</font><FONT SIZE=3>DTE/DSR</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>方式和</FONT><FONT SIZE=3>RTS/CTS</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>方式,这与</FONT><FONT SIZE=3>DCB</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>结构的初始化有关系,</FONT><FONT SIZE=3>DCB</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>结构中的</FONT><FONT SIZE=3>OutxCtsFlow</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>、</FONT><FONT SIZE=3>fOutxDsrFlow</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>、</FONT><FONT SIZE=3>fDsrSensitivity</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>、</FONT><FONT SIZE=3>fRtsControl</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>、</FONT><FONT SIZE=3>fDtrControl</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>几个成员的初始值很关键,不同的值代表不同流控,也可以自己设置流控。但建议采用标准流行的流控方式。采用硬件流控时,</FONT><FONT SIZE=3>DTE</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>、</FONT><FONT SIZE=3>DSR</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>、</FONT><FONT SIZE=3>RTS</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>、</FONT><FONT SIZE=3>CTS</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>的逻辑位直接影响到数据的读写及收发数据的缓冲区控制。</font></LI><FONT FACE="宋体" LANG="ZH-CN" SIZE=3><p></P><P ALIGN="JUSTIFY"></FONT><LI><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>软件流控:串口通信中采用特殊字符</font><FONT SIZE=3>XON</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>和</FONT><FONT SIZE=3>XOFF</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>作为控制串口数据的收发。与此相关的</FONT><FONT SIZE=3>DCB</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>成员是:</FONT><FONT SIZE=3>fOut</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>、</FONT><FONT SIZE=3>fInX</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>、</FONT><FONT SIZE=3>XoffChar</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>、</FONT><FONT SIZE=3>XonChar</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>、</FONT><FONT SIZE=3>XoffLim</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>和</FONT><FONT SIZE=3>XonLim</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>。</font></LI><FONT FACE="宋体" LANG="ZH-CN" SIZE=3><p></P></font></OL></DIR></DIR></OL><P ALIGN="JUSTIFY"><FONT SIZE=3> </font><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>具体含义参见</FONT><FONT SIZE=3>MSDN</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>帮助。</font></P><FONT FACE="宋体" LANG="ZH-CN" SIZE=3><OL><P ALIGN="JUSTIFY"><LI>串口读写操作:</LI><p></P></OL></FONT><DIR><P ALIGN="JUSTIFY"><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>串口读写有两种方式:同步方式(</font><FONT SIZE=3>NonOverlapped</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>)和异步方式(</FONT><FONT SIZE=3>Overlapped</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>)。同步方式是指必需完成了读写操作,函数才返回,这可能造成程序死掉,因为如果在读写时发生了错误,永远不返回就会出错,可能线程将永远等待在那儿。而异步方式则灵活得多,一旦读写不成功,就将读写挂起,函数直接返回,可以通过</FONT><FONT SIZE=3>GetLastError</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>函数得到读写未成功的原因。所以常常采用异步方式操作。</font></P></DIR><OL><FONT FACE="宋体" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY"><LI>读操作:</LI><p></P></font><P ALIGN="JUSTIFY"><FONT SIZE=3>ReadFile()</font><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>函数用于完成读操作。异步方式的读操作为:</font></P><FONT SIZE=3><P ALIGN="JUSTIFY">DWORD dwRead;</P><P ALIGN="JUSTIFY">BOOL fWaitingOnRead = FALSE;</P><P ALIGN="JUSTIFY">OVERLAPPED osReader = {0};</P><P ALIGN="JUSTIFY">// Create the overlapped event. Must be closed before exiting</P><P ALIGN="JUSTIFY">// to avoid a handle leak.</P><P ALIGN="JUSTIFY">osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);</P><P ALIGN="JUSTIFY">if (osReader.hEvent == NULL)</P><P ALIGN="JUSTIFY"> // Error creating overlapped event; abort.</P><P ALIGN="JUSTIFY">if (!fWaitingOnRead) {</P><P ALIGN="JUSTIFY"> // Issue read operation.</P><P ALIGN="JUSTIFY"> if (!ReadFile(hComm, lpBuf, READ_BUF_SIZE, &dwRead, &osReader)) {</P><P ALIGN="JUSTIFY"> if (GetLastError() != ERROR_IO_PENDING) // read not delayed?</P><P ALIGN="JUSTIFY"> // Error in communications; report it.</P><P ALIGN="JUSTIFY"> else</P><P ALIGN="JUSTIFY"> fWaitingOnRead = TRUE;</P><P ALIGN="JUSTIFY"> }</P><P ALIGN="JUSTIFY"> else { </P><P ALIGN="JUSTIFY"> // read completed immediately</P><P ALIGN="JUSTIFY"> HandleASuccessfulRead(lpBuf, dwRead);</P><P ALIGN="JUSTIFY"> }</P><P ALIGN="JUSTIFY">}</P></FONT><P ALIGN="JUSTIFY"><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>如果读操作被挂起,可以调用</font><FONT SIZE=3>WaitForSingleObject()</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>函数或</FONT><FONT SIZE=3>WaitForMuntilpleObjects()</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>函数等待读操作完成或者超时发生,再调用</FONT><FONT SIZE=3>GetOverlappedResult()</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>得到想要的信息。</font></P><FONT FACE="宋体" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY"></FONT><LI><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>写操作:与读操作相似,故不详述,调用的</font><FONT SIZE=3>API</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>函数是:</FONT><FONT SIZE=3>WriteFile</FONT><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>函数。</font></LI><FONT FACE="宋体" LANG="ZH-CN" SIZE=3><p></P></font></OL><FONT FACE="宋体" LANG="ZH-CN" SIZE=3>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -