⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 -

📁 串口 串口 串口 串口 串口 串口
💻
📖 第 1 页 / 共 2 页
字号:
            @stCC).wVersion,100h<BR>mov (COMMCONFIG ptr 
            @stCC).dwProviderSubType,1<BR>invoke CommConfigDialog,addr 
            [esi].szPortName,[esi].hWnd,addr @stCC<BR>popad<BR>ret</P>
            <P>_CommConfigDialog endp</P>
            <P><FONT color=#ff0000>3 Win32 通讯 API Bug 之二--- 
            BuildCommDCB</FONT></P>
            <P>BuildCommDCB 的功能是把一个字符串如 com1:9600,n,8,1 这样的转换到具体的数据填写到 DCB 
            中,但使用中也存在问题,我发现我用它转换象 com1:9600,e,7,1 之类的带校验位的字符串,它总是无法把这个 e 
            给我转换过去,设置好串口一看,成了 9600,n,7,1,而上面提到的 CommConfigDialog 
            返回的结果用来设置串口却是正确的,经过比较,发现问题出在 DCB.fbits.fParity 这个 bit 上,只有把这个 bit 置 
            1,校验位才是有效的,而 BuildCommDCB 恰恰是漏了这个 bit,所有如果你要使用 BuildCommDCB,别忘了补充把 
            DCB.fbits.fParity 设置回去,我用的代码是:</P>
            <P>_BuildCommDCB proc _lpszPara,_lpstDCB</P>
            <P>pushad<BR>mov esi,_lpstDCB<BR>assume esi:ptr DCB</P>
            <P>invoke RtlZeroMemory,esi,sizeof DCB<BR>invoke 
            BuildCommDCB,_lpszPara,esi<BR>;********************************************************************<BR>; 
            根据校验位补充设置 DCB 中的 DCB.fbits.fParity 
            字段<BR>;********************************************************************<BR>mov 
            dword ptr [esi].fbits,0010b</P>
            <P>cld<BR>@@:<BR>lodsb<BR>or al,al<BR>jz @F<BR>cmp al,'='<BR>jz 
            _BCD_Check<BR>cmp al,','<BR>jnz @B<BR>_BCD_Check:<BR>lodsb<BR>or 
            al,al<BR>jz @F<BR>or al,20h<BR>cmp al,'n'<BR>jnz 
            @B<BR>;********************************************************************<BR>; 
            扫描到 =n 或 ,n 
            则取消校验位<BR>;********************************************************************<BR>mov 
            esi,_lpstDCB<BR>and dword ptr [esi].fbits,not 
            0010b<BR>@@:<BR>popad<BR>ret</P>
            <P>_BuildCommDCB endp</P>
            <P><FONT color=#ff0000>4 Win32 通讯编程的一般流程</FONT></P>
            <P>由于同步方式相对比较简单,在这里讲述的是异步方式的流程,在其他的很多文章里提到了 Windows 通讯 API 
            有二十多个,它们是:</P>
            <P>BuildCommDCB<BR>BuildCommDCBAndTimeouts<BR>ClearCommBreak<BR>ClearCommError<BR>CommConfigDialog<BR>EscapeCommFunction<BR>GetCommConfig<BR>GetCommMask<BR>GetCommModemStatus<BR>GetCommProperties<BR>GetCommState<BR>GetCommTimeouts<BR>GetDefaultCommConfig<BR>PurgeComm<BR>SetCommBreak<BR>SetCommConfig<BR>SetCommMask<BR>SetCommState<BR>SetCommTimeouts<BR>SetDefaultCommConfig<BR>SetupComm<BR>TransmitCommChar<BR>WaitCommEvent</P>
            <P>我刚看到这些 API 的时候,都不知道如何使用它们,但并不是所有这些 API 都是必须用的,比如说你要检测当前串口的设置可以只用 
            SetCommState 而不用 GetCommProperties 和 
            GetCommConfig,虽然它们返回的信息可能更多。同样,如果有些值你想用缺省的,比如缓冲区的大小和超时的时间等等,那么 
            SetupComm 和 BuildCommDCBAndTimeouts、SetCommTimeouts 
            也可以不用,TransmitCommChar 是马上在发送序列中优先插入发送一个字符用的,平时也很少用到,下面讲的是必须用到的 API 
            和使用步骤:</P>
            <OL>
              <LI><FONT color=#ff6600>建立 Event -- 用 
              CreateEvent</FONT><BR><BR>invoke 
              CreateEvent,NULL,TRUE,FALSE,NULL<BR>用异步方式操作串口必须要定义 OVERLAPPED 
              结构,其中的 hEvent 必须自己建立,你要定义两个 OVERLAPPED 结构,一个用于读一个用于写,当然也必须建立两个 
              Event,把它们放入 OVERLAPPED.hEvent<BR><BR>
              <LI><FONT color=#ff6600>打开串口 -- 用 CreateFile</FONT><BR><BR>invoke 
              CreateFile,addr szPortName,GENERIC_READ or 
              GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL<BR>注意用异步方式必须指定 
              FILE_FLAG_OVERLAPPED,而文件方式必须 OPEN_EXISTING,读写必须是 GENERIC_READ or 
              GENERIC_WRITE<BR><BR>
              <LI><FONT color=#ff6600>设置串口参数 -- 用 
              SetCommState</FONT><BR><BR>invoke SetCommState,hCom,addr 
              dcbx<BR>hCom 是前面打开成功后返回的句柄,dcbx 是数据结构 
              DCB,里面包括了通讯的具体参数,至于这个参数的建立,你可以自己填写,也可以用前面提到的 BuildCommDCB 或 
              CommConfigDialog 填写<BR><BR>
              <LI><FONT 
              color=#ff6600>建立读数据的线程</FONT><BR><BR>到这里,就可以开始读数据了,一般我们是在主线程中写数据,因为写是我们可以控制的,而读的时候我们不知道数据什么时候会到,所以要建立一个线程专门用来读数据,在这个线程中,我们循环地用 
              ReadFile 读串口,同时用 WaitCommEvent 检测线路状态。<BR><BR>
              <LI>如果要<FONT color=#ff6600>检测通讯状态</FONT>,如 CTS 信号,RingIn 等等 -- 用 
              SetCommMask、WaitCommEvent、ClearCommError、GetCommModemStatus<BR><BR>invoke 
              SetCommMask,hCom,EV_BREAK or EV_CTS or EV_DSR or EV_ERR or EV_RING 
              or EV_RLSD or EV_RXCHAR or EV_RXFLAG or EV_TXEMPTY<BR>SetCommMask 
              指定 WaitCommEvent 要等待的事件名称,具体的参数请查手册<BR><BR>invoke 
              WaitCommEvent,hCom,addr dwEvent,NULL<BR>WaitCommEvent 等待一直到 
              SetCommMask 指定事件之一发生<BR><BR>invoke ClearCommError,hCom,addr 
              dwError,addr stComStat<BR>在 WaitCommEvent 以后,要用 ClearCommError 
              清除事件的 Flag,以便进行下一轮 WaitCommEvent,同时这个 API 
              可以获得更详细的事件信息<BR><BR>invoke GetCommModemStatus,hCom,addr 
              dwModemStatus<BR>同样,GetCommModemStatus 是用来获得串口线路状态的,如 CTS、RING 
              等等,当 WaitCommEvent 返回时,只是指出了如 CTS 等等状态有变化,但具体是变成 On 还是 Off 了还要靠这个 
              API 去取得更详细的信息<BR><BR>
              <LI><FONT color=#ff6600>读数据 -- 用 ReadFile</FONT><BR><BR>invoke 
              ReadFile,hCom,addr szBuffer,sizeof szBuffer,addr dwBytesRead,addr 
              stReadState<BR>最后一个参数是开头定义的 OVERLAPPED 结构的地址,指定了它就表示是用异步方式的读方式,这个 
              API 会马上返回,接下去要用<BR><BR>invoke GetOverlappedResult,hCom,addr 
              stReadState,addr dwBytesRead,FALSE<BR>将其余的数据读完<BR><BR>
              <LI><FONT color=#ff6600>结束时关闭端口 -- 停止 WaitCommEvent 的等待以及关闭端口 
              CloseHandle</FONT><BR><BR>平时程序会停留在 WaitCommEvent 
              的等待中,当要终止线程的时候,必须是程序从 WaitCommEvent 中退出来,这时候要用<BR><BR>按照 Win32 
              手册上的说明,参数为 NULL 的 SetCommMask 会使另一个线程中的 WaitCommEvent 马上返回,然后就是用 
              CloseHandle 关闭端口<BR>invoke CloseHandle,hCom </LI></OL>
            <P><FONT color=#ff0000>5 Win32 通讯 API Bug 之二--- SetCommMask 和 
            WaitCommEvent</FONT><BR><BR>严格的说这不应该是 
            Bug,而是偶然的情况,我发现有些时候我的读线程无法结束,跟踪发现是停在了 WaitCommEvent 上,这说明有时候 invoke 
            SetCommMask,hCom,NULL 并不能使 WaitCommEvent 退出,我最后使用的办法是: 在 SetCommMask 
            以后再执行 invoke SetEvent,stReadState.hEvent,把读的 OVERLAPPED 结构中的 Event 
            置位,让 WaitCommEvent 认为有 Event 发生,它就会马上返回,也许这并不是普遍的情况,但如果你的程序也是停在了 
            WaitCommEvent 的地方,不妨一试。</P>
            <P><FONT color=#ff0000>6 如何编写读线程中的循环</FONT><BR></P>
            <P>按照<A 
            href="http://asm001.home.chinaren.com/program/tip-comm-prg.htm" 
            target=_blank>《Serial communications in Microsoft 
            Win32》</A>一文中的例程,读循环可以用:</P>
            <P>#define READ_TIMEOUT 500 // milliseconds</P>
            <P>DWORD dwRes;<BR>DWORD dwRead;<BR>BOOL fWaitingOnRead = 
            FALSE;<BR>OVERLAPPED osReader = {0};</P>
            <P>// Create the overlapped event. Must be closed before 
            exiting<BR>// to avoid a handle leak.<BR>osReader.hEvent = 
            CreateEvent(NULL, TRUE, FALSE, NULL);</P>
            <P>if (osReader.hEvent == NULL)<BR>// Error creating overlapped 
            event; abort.</P>
            <P>if (!fWaitingOnRead) {<BR>// Issue read operation.<BR>if 
            (!ReadFile(hComm, lpBuf, READ_BUF_SIZE, &amp;dwRead, &amp;osReader)) 
            {<BR>if (GetLastError() != ERROR_IO_PENDING) // read not 
            delayed?<BR>// Error in communications; report 
            it.<BR>else<BR>fWaitingOnRead = TRUE;<BR>}<BR>else {<BR>// read 
            completed immediately<BR>HandleASuccessfulRead(lpBuf, 
            dwRead);<BR>}<BR>}</P>
            <P> </P>
            <P>if (fWaitingOnRead) {<BR>dwRes = 
            WaitForSingleObject(osReader.hEvent, 
            READ_TIMEOUT);<BR>switch(dwRes)<BR>{<BR>// Read completed.<BR>case 
            WAIT_OBJECT_0:<BR>if (!GetOverlappedResult(hComm, &amp;osReader, 
            &amp;dwRead, FALSE))<BR>// Error in communications; report 
            it.<BR>else<BR>// Read completed 
            successfully.<BR>HandleASuccessfulRead(lpBuf, dwRead);</P>
            <P>// Reset flag so that another opertion can be 
            issued.<BR>fWaitingOnRead = FALSE;<BR>break;</P>
            <P>case WAIT_TIMEOUT:<BR>// Operation isn't complete yet. 
            fWaitingOnRead flag isn't<BR>// changed since I'll loop back around, 
            and I don't want<BR>// to issue another read until the first one 
            finishes.<BR>//<BR>// This is a good time to do some background 
            work.<BR>break;</P>
            <P>default:<BR>// Error in the WaitForSingleObject; abort.<BR>// 
            This indicates a problem with the OVERLAPPED structure's<BR>// event 
            handle.<BR>break;<BR>}<BR>}</P>
            <P> </P>
            <P>这一段程序在 98 下正常,但非常不幸的是在 Win2000 下,ReadFile 总是返回读正确,并不返回 
            ERROR_IO_PENDING,使下面的 WaitForSingleObject 的循环形同虚设,要命的是,ReadFile 
            返回读正确却每次只读一个字节,结果程序工作得很奇怪,即使缓冲区中有很多的字符,程序也每次只能读一个字符,要等到发送字符或做其他的操作使线路状态改变了,才能读下一个字符,我不知道这个奇怪的现象是如何发生的,反正我解决的办法是在 
            ReadFile 前加 WaitCommEvent,真正等到 EV_RXCHAR 以后才去 
            ReadFile,到最后,我用的循环是这样的,虽然没有一篇文章中的例子是这样的,但它却同时在 windows9x 和 
            windows2000 下工作得很好:</P>
            <P>.while dwFlag &amp; 
            IF_CONNECT<BR>;********************************************************************<BR>; 
            检测其它的通信事件<BR>; 如果检测到且定义了 lpProcessEvent 则调用 
            lpProcessEvent<BR>;********************************************************************<BR>invoke 
            WaitCommEvent,hCom,addr @dwEvent,NULL ;addr stReadState<BR>push 
            eax<BR>invoke ClearCommError,hCom,addr @dwError,addr 
            @stComStat<BR>pop eax<BR>.if eax == 0<BR>invoke GetLastError<BR>.if 
            eax == ERROR_IO_PENDING<BR>or 
            dwFlag,IF_WAITING<BR>.endif<BR>.else<BR>;这里是线路状态的处理<BR>.endif<BR>;********************************************************************<BR>; 
            如果没有在等待异步读的过程中,则读端口<BR>;********************************************************************<BR>.if 
            ! (dwFlag &amp; IF_WAITING)<BR>mov @dwBytesRead,0<BR>invoke 
            ReadFile,hCom,addr @szBuffer,sizeof @szBuffer,\<BR>addr 
            @dwBytesRead,addr stReadState<BR>.if eax == FALSE<BR>or 
            dwFlag,IF_WAITING<BR>invoke GetLastError<BR>.if eax != 
            ERROR_IO_PENDING<BR>;这里是错误处理<BR>.endif<BR>.else<BR>and dwFlag,not 
            IF_WAITING<BR>mov eax,@dwBytesRead<BR>.if eax != 
            0<BR>;这里是接收到的数据处理<BR>.endif<BR>.endif<BR>.endif<BR>;********************************************************************<BR>; 
            如果在异步读端口中,则等待一段时间<BR>;********************************************************************<BR>.if 
            dwFlag &amp; IF_WAITING<BR>invoke 
            WaitForSingleObject,stReadState.hEvent,200<BR>.if eax == 
            WAIT_OBJECT_0<BR>and dwFlag,not IF_WAITING<BR>invoke 
            GetOverlappedResult,hCom,addr stReadState,\<BR>addr 
            @dwBytesRead,FALSE<BR>.if eax != 0<BR>mov eax,@dwBytesRead<BR>.if 
            eax != 0<BR>;这里是接收到的数据处理<BR>.endif<BR>.else<BR>;这里是错误处理<BR>invoke 
            ClearCommError,hCom,addr @dwError,addr 
            @stComStat<BR>.endif<BR>.else<BR>;这里是错误处理<BR>.endif<BR>.endif<BR>.endw<BR><BR><FONT 
            color=#ff0000>7 流控制的问题</FONT></P>
            <P>在流控制方式为“无”和“软件控制”的情况下,基本上没有什么问题,但在“硬件控制”下,win32 手册中说明 
            RTS_CONTROL_HANDSHAKE 控制方式的含义是:</P>
            <P>Enables RTS handshaking. The driver raises the RTS line when the 
            "type-ahead" (input) buffer is less than one-half full and lowers 
            the RTS line when the buffer is more than three-quarters full. If 
            handshaking is enabled, it is an error for the application to adjust 
            the line by using the EscapeCommFunction function.</P>
            <P>也就是说,当缓冲区快满的时候 RTS 会自动 OFF 通知对方暂停发送,当缓冲区重新空出来的时候, RTS 会自动 
            ON,但我发现当 RTS 变 OFF 以后即使你已经清空了缓冲区, RTS 也不会自动的 
            ON,造成对方停在那里不发送了,所以,如果要用硬件流控制的话,还要在接收后最好加上检测缓冲区大小的判断,具体是使用 
            ClearCommError 后返回的 COMSTAT.cbInQue,当缓冲区已经空出来的时候,要使用 invoke 
            EscapeCommFunction,hCom,SETRTS 重新将 RTS 设置为 
      ON。</P></TD></TR></TBODY></TABLE>
      <TABLE border=0 cellPadding=1 cellSpacing=1 width=550>
        <TBODY>
        <TR>
          <TD>
            <DIV align=center>
            <HR align=center color=#99cccc SIZE=2 width=500>
            </DIV></TD></TR>
        <TR>
          <TD vAlign=top width=424>
            <P align=center><IMG border=0 height=74 src="实战串行通讯.files/coffe.gif" 
            width=57> </P></TD></TR>
        <TR>
          <TD>
            <DIV align=center>
            <HR align=center color=#99cccc SIZE=2 width=500>
            </DIV></TD></TR>
        <TR>
          <TD vAlign=top width=424></TD></TR></TBODY></TABLE> <BR></TD>
    <TD align=right background=实战串行通讯.files/zhe.gif height=443 rowSpan=2 
    vAlign=top width=10>&nbsp; </TD>
    <TD align=middle bgColor=#cbe4e4 vAlign=top width=150> </TD></TR>
  <TR>
    <TD align=middle bgColor=#cbe4e4 height=18 vAlign=top 
  width=150> </TD></TR></TBODY></TABLE>
<TABLE align=center border=0 cellPadding=0 cellSpacing=0 height=28 width=758>
  <TBODY>
  <TR>
    <TD background=实战串行通讯.files/looker.gif height=16 vAlign=center>
      <P 
      align=right>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&amp;nb</P></TR></TBODY></TABLE></BODY></HTML>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -