ZFDok
驱动牛犊
驱动牛犊
  • 注册日期2005-01-05
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分110分
  • 威望20点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
阅读:3897回复:13

串口编程

楼主#
更多 发布于:2005-07-05 15:31
  我用VC6。0 API 编写对话框程序,我不会开线程,怎样等待串口接收数据?我要接收发送好几次,一个函数完成,而且要在界面上显示时间进程条,有办法吗?请回答。谢谢!
ZFDok
驱动牛犊
驱动牛犊
  • 注册日期2005-01-05
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分110分
  • 威望20点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
沙发#
发布于:2005-07-13 09:16
没有人帮忙顶一下吗?
GoalKeeper
驱动牛犊
驱动牛犊
  • 注册日期2005-07-13
  • 最后登录
  • 粉丝0
  • 关注0
  • 积分1分
  • 威望2点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
板凳#
发布于:2005-07-13 22:29
1) 使用CreateThead()可以开线程。
2)一般在串口中读取数据是在一个单独线程中。即主线程写,读线程读。
3)在发送完数据后可以通过WaitForSingleObjec(hReadEvent)t函数等待,在收到数据后使用SetEvent置事件对象的状态。
caesun
驱动小牛
驱动小牛
  • 注册日期2001-12-10
  • 最后登录2006-12-01
  • 粉丝0
  • 关注0
  • 积分290分
  • 威望30点
  • 贡献值393点
  • 好评度21点
  • 原创分0分
  • 专家分0分
地板#
发布于:2005-07-16 08:35
msdn里面有个tty的sample   不过看懂这个比较花时间
KungFu
驱动大牛
驱动大牛
  • 注册日期2001-09-27
  • 最后登录2008-04-08
  • 粉丝0
  • 关注0
  • 积分221分
  • 威望24点
  • 贡献值0点
  • 好评度19点
  • 原创分0分
  • 专家分0分
地下室#
发布于:2005-07-19 18:13
直接使用vc提供的串口控件,可以方便一些.去书店看看相关的书,也会帮助很大.
我不写驱动好多年
gxpa1
驱动中牛
驱动中牛
  • 注册日期2002-10-30
  • 最后登录2010-09-02
  • 粉丝0
  • 关注0
  • 积分8分
  • 威望11点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
5楼#
发布于:2005-07-20 15:17
串行通信与重叠I/O

  Win 32系统把
文件的概念进行了扩展。无论是文件、通信设备、命名管道、邮件槽、磁盘、还是控制台,都是用API函数CreateFile来打开或创建的。该函数的声明为:

HANDLE CreateFile(

LPCTSTR lpFileName, // 文件名  

DWORD dwDesiredAccess, // 访问模式  

DWORD dwShareMode, // 共享模式  

LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 通常为NULL

DWORD dwCreationDistribution, // 创建方式  

DWORD dwFlagsAndAttributes, // 文件属性和标志

HANDLE hTemplateFile // 临时文件的句柄,通常为NULL  

);

  如果调用成功,那么该函数返回文件的句柄,如果调用失败,则函数返回INVALID_HANDLE_VALUE。
在打开通信设备句柄后,常常需要对串行口进行一些初始化工作。这需要通过一个DCB结构来进行。DCB结构包含了诸如波特率、每个字符的数据位数、奇偶校验和停止位数等信息。在查询或配置置串行口的属性时,都要用DCB结构来作为缓冲区。

  调用GetCommState函数可以获得串口的配置,该函数把当前配置填充到一个DCB结构中。一般在用CreateFile打开串行口后,可以调用GetCommState函数来获取串行口的初始配置。要修改串行口的配置,应该先修改DCB结构,然后再调用SetCommState函数用指定的DCB结构来设置串行口。

  除了在DCB中的设置外,程序一般还需要设置I/O缓冲区的大小和超时。Windows用I/O缓冲区来暂存串行口输入和输出的数据,如果通信的速率较高,则应该设置较大的缓冲区。调用SetupComm函数可以设置串行口的输入和输出缓冲区的大小。

  在用ReadFile和WriteFile读写串行口时,需要考虑超时问题。如果在指定的时间内没有读出或写入指定数量的字符,那么ReadFile或WriteFile的操作就会结束。要查询当前的超时设置应调用GetCommTimeouts函数,该函数会填充一个COMMTIMEOUTS结构。调用SetCommTimeouts可以用某一个COMMTIMEOUTS结构的内容来设置超时。

  有两种超时:间隔超时和总超时。间隔超时是指在接收时两个字符之间的最大时延,总超时是指读写操作总共花费的最大时间。写操作只支持总超时,而读操作两种超时均支持。用COMMTIMEOUTS结构可以规定读/写操作的超时,该结构的定义为:

typedef struct _COMMTIMEOUTS {  

DWORD ReadIntervalTimeout; // 读间隔超时

DWORD ReadTotalTimeoutMultiplier; // 读时间系数

DWORD ReadTotalTimeoutConstant; // 读时间常量

DWORD WriteTotalTimeoutMultiplier; // 写时间系数

DWORD WriteTotalTimeoutConstant; // 写时间常量

} COMMTIMEOUTS,*LPCOMMTIMEOUTS;

  COMMTIMEOUTS结构的成员都以毫秒为单位。总超时的计算公式是:

总超时=时间系数×要求读/写的字符数 + 时间常量

  例如,如果要读入10个字符,那么读操作的总超时的计算公式为:

读总超时=ReadTotalTimeoutMultiplier×10 + ReadTotalTimeoutConstant

  可以看出,间隔超时和总超时的设置是不相关的,这可以方便通信程序灵活地设置各种超时。

  如果所有写超时参数均为0,那么就不使用写超时。如果ReadIntervalTimeout为0,那么就不使用读间隔超时,如果ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant都为0,则不使用读总超时。如果读间隔超时被设置成MAXDWORD并且两个读总超时为0,那么在读一次输入缓冲区中的内容后读操作就立即完成,而不管是否读入了要求的字符。

  在用重叠方式读写串行口时,虽然ReadFile和WriteFile在完成操作以前就可能返回,但超时仍然是起作用的。在这种情况下,超时规定的是操作的完成时间,而不是ReadFile和WriteFile的返回时间。
在用ReadFile和WriteFile读写串行口时,既可以同步执行,也可以重叠(异步)执行。在同步执行时,函数直到操作完成后才返回。这意味着在同步执行时线程会被阻塞,从而导致效率下降。在重叠执行时,即使操作还未完成,调用的函数也会立即返回。费时的I/O操作在后台进行,这样线程就可以干别的事情。例如,线程可以在不同的句柄上同时执行I/O操作,甚至可以在同一句柄上同时进行读写操作。“重叠”一词的含义就在于此。

  ReadFile函数只要在串行口输入缓冲区中读入指定数量的字符,就算完成操作。而WriteFile函数不但要把指定数量的字符拷入到输出缓冲中,而且要等这些字符从串行口送出去后才算完成操作。

  ReadFile和WriteFile函数是否为执行重叠操作是由CreateFile函数决定的。如果在调用CreateFile创建句柄时指定了FILE_FLAG_OVERLAPPED标志,那么调用ReadFile和WriteFile对该句柄进行的读写操作就是重叠的,如果未指定重叠标志,则读写操作是同步的。

  函数ReadFile和WriteFile的参数和返回值很相似。这里仅列出ReadFile函数的声明:

BOOL ReadFile(

HANDLE hFile, // 文件句柄

LPVOID lpBuffer, // 读缓冲区

DWORD nNumberOfBytesToRead, // 要求读入的字节数  

LPDWORD lpNumberOfBytesRead, // 实际读入的字节数

LPOVERLAPPED lpOverlapped // 指向一个OVERLAPPED结构

); //若返回TRUE则表明操作成功

 

  需要注意的是如果该函数因为超时而返回,那么返回值是TRUE。参数lpOverlapped在重叠操作时应该指向一个OVERLAPPED结构,如果该参数为NULL,那么函数将进行同步操作,而不管句柄是否是由FILE_FLAG_OVERLAPPED标志建立的。

  当ReadFile和WriteFile返回FALSE时,不一定就是操作失败,线程应该调用GetLastError函数分析返回的结果。例如,在重叠操作时如果操作还未完成函数就返回,那么函数就返回FALSE,而且GetLastError函数返回ERROR_IO_PENDING。

  在使用重叠I/O时,线程需要创建OVERLAPPED结构以供读写函数使用。OVERLAPPED结构最重要的成员是hEvent,hEvent是一个事件对象句柄,线程应该用CreateEvent函数为hEvent成员创建一个手工重置事件,hEvent成员将作为线程的同步对象使用。如果读写函数未完成操作就返回,就那么把hEvent成员设置成无信号的。操作完成后(包括超时),hEvent会变成有信号的。

  如果GetLastError函数返回ERROR_IO_PENDING,则说明重叠操作还为完成,线程可以等待操作完成。有两种等待办法:一种办法是用象WaitForSingleObject这样的等待函数来等待OVERLAPPED结构的hEvent成员,可以规定等待的时间,在等待函数返回后,调用GetOverlappedResult。另一种办法是调用GetOverlappedResult函数等待,如果指定该函数的bWait参数为TRUE,那么该函数将等待OVERLAPPED结构的hEvent 事件。GetOverlappedResult可以返回一个OVERLAPPED结构来报告包括实际传输字节在内的重叠操作结果。

  如果规定了读/写操作的超时,那么当超过规定时间后,hEvent成员会变成有信号的。因此,在超时发生后,WaitForSingleObject和GetOverlappedResult都会结束等待。WaitForSingleObject的dwMilliseconds参数会规定一个等待超时,该函数实际等待的时间是两个超时的最小值。注意GetOverlappedResult不能设置等待的时限,因此如果hEvent成员无信号,则该函数将一直等待下去。

  在调用ReadFile和WriteFile之前,线程应该调用ClearCommError函数清除错误标志。该函数负责报告指定的错误和设备的当前状态。

  调用PurgeComm函数可以终止正在进行的读写操作,该函数还会清除输入或输出缓冲区中的内容。
gxpa1
驱动中牛
驱动中牛
  • 注册日期2002-10-30
  • 最后登录2010-09-02
  • 粉丝0
  • 关注0
  • 积分8分
  • 威望11点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
6楼#
发布于:2005-07-20 15:18
实战串行通讯
    
本文不是全面的讲述如何编写串行通讯程序,而是讨论一些实际遇到的问题。

1 选择通讯方式 -- 同步还是非同步

正如在《Serial communications in Microsoft Win32》等文章中提到的,同步(NonOverLapped)方式是比较简单的一种方式,编写起来代码的长度要明显少于异步(OverLapped)方式,我开始用同步方式编写了整个子程序,在 Windows98 下工作正常,但后来在 Windows2000下测试,发现接收正常,但一发送数据,程序就会停在那里,原因应该在于同步方式下如果有一个通讯 Api 在操作中,另一个会阻塞直到上一个操作完成,所以当读数据的线程停留在 WaitCommEvent 的时候,WriteFile 就停在那里。我又测试了我手上所有有关串行通讯的例子程序,发现所有使用同步方式的程序在 Windows 2000 下全部工作不正常,对这个问题我一直找不到解决的办法,后来在 Iczelion 站点上发现一篇文章提到 NT 下对串行通讯的处理和 9x 有些不同,根本不要指望在 NT 或 Windows 2000 下用同步方式同时收发数据,我只好又用异步方式把整个通讯子程序重新写了一遍。

所以对于这个问题的建议是:如果程序只打算工作在 Win9x 下,为了简单起见,可以用同步方式写程序,如果程序打算在 NT 下也可以工作的话,就必须用异步方式写。

2 Win32 通讯 API Bug 之一 --- CommConfigDialog

CommConfigDialog 是弹出系统内置串口设置对话框的 API,我们在设备管理器中设置串口参数的对话框就是这个,使用这个 API 时不用先打开端口,它并不针对一个已打开的端口,而是仅仅是把 DCB 的内容填写到对话框中,当按了 OK 后把输入的结果存回到 DCB 数据结构中,至于什么时候把结果设置到串口上,那就是你自己要做的事情了。

CommCinfigDialog 的定义如下:

BOOL CommConfigDialog(

LPTSTR lpszName, // pointer to device name string
HWND hWnd, // handle to window
LPCOMMCONFIG lpCC // pointer to comm. configuration structure
);

但在使用中发现,对话框有时能出来,有时出不来,最后总结的经验是问题出在 COMMCONFIG 结构的 dwSize 字段上,COMMCONFIG 的定义如下:

typedef struct _COMM_CONFIG {
DWORD dwSize;
WORD wVersion;
WORD wReserved;
DCB dcb;
DWORD dwProviderSubType;
DWORD dwProviderOffset;
DWORD dwProviderSize;
WCHAR wcProviderData[1];
} COMMCONFIG, *LPCOMMCONFIG;

在参数中,wVersion 要填 100h,dwProviderSubType 要填 1,但 dwSize 就不能填 sizeof COMMCONFIG 了,我发现好象一定要把 dwSize 设置为比 sizeof COMMCONFIG 对话框才能出来,所以我用的代码中定义了一个足够大的缓冲区作为结构的地址:

_CommConfigDialog proc
local @stCC[256]:BYTE

pushad
invoke RtlZeroMemory,addr @stCC,sizeof @stCC
mov (COMMCONFIG ptr @stCC).dwSize,256
mov (COMMCONFIG ptr @stCC).wVersion,100h
mov (COMMCONFIG ptr @stCC).dwProviderSubType,1
invoke CommConfigDialog,addr [esi].szPortName,[esi].hWnd,addr @stCC
popad
ret

_CommConfigDialog endp

3 Win32 通讯 API Bug 之二--- BuildCommDCB

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 设置回去,我用的代码是:

_BuildCommDCB proc _lpszPara,_lpstDCB

pushad
mov esi,_lpstDCB
assume esi:ptr DCB

invoke RtlZeroMemory,esi,sizeof DCB
invoke BuildCommDCB,_lpszPara,esi
;********************************************************************
; 根据校验位补充设置 DCB 中的 DCB.fbits.fParity 字段
;********************************************************************
mov dword ptr [esi].fbits,0010b

cld
@@:
lodsb
or al,al
jz @F
cmp al,'='
jz _BCD_Check
cmp al,','
jnz @B
_BCD_Check:
lodsb
or al,al
jz @F
or al,20h
cmp al,'n'
jnz @B
;********************************************************************
; 扫描到 =n 或 ,n 则取消校验位
;********************************************************************
mov esi,_lpstDCB
and dword ptr [esi].fbits,not 0010b
@@:
popad
ret

_BuildCommDCB endp

4 Win32 通讯编程的一般流程

由于同步方式相对比较简单,在这里讲述的是异步方式的流程,在其他的很多文章里提到了 Windows 通讯 API 有二十多个,它们是:

BuildCommDCB
BuildCommDCBAndTimeouts
ClearCommBreak
ClearCommError
CommConfigDialog
EscapeCommFunction
GetCommConfig
GetCommMask
GetCommModemStatus
GetCommProperties
GetCommState
GetCommTimeouts
GetDefaultCommConfig
PurgeComm
SetCommBreak
SetCommConfig
SetCommMask
SetCommState
SetCommTimeouts
SetDefaultCommConfig
SetupComm
TransmitCommChar
WaitCommEvent

我刚看到这些 API 的时候,都不知道如何使用它们,但并不是所有这些 API 都是必须用的,比如说你要检测当前串口的设置可以只用 SetCommState 而不用 GetCommProperties 和 GetCommConfig,虽然它们返回的信息可能更多。同样,如果有些值你想用缺省的,比如缓冲区的大小和超时的时间等等,那么 SetupComm 和 BuildCommDCBAndTimeouts、SetCommTimeouts 也可以不用,TransmitCommChar 是马上在发送序列中优先插入发送一个字符用的,平时也很少用到,下面讲的是必须用到的 API 和使用步骤:

建立 Event -- 用 CreateEvent

invoke CreateEvent,NULL,TRUE,FALSE,NULL
用异步方式操作串口必须要定义 OVERLAPPED 结构,其中的 hEvent 必须自己建立,你要定义两个 OVERLAPPED 结构,一个用于读一个用于写,当然也必须建立两个 Event,把它们放入 OVERLAPPED.hEvent


打开串口 -- 用 CreateFile

invoke CreateFile,addr szPortName,GENERIC_READ or GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL
注意用异步方式必须指定 FILE_FLAG_OVERLAPPED,而文件方式必须 OPEN_EXISTING,读写必须是 GENERIC_READ or GENERIC_WRITE


设置串口参数 -- 用 SetCommState

invoke SetCommState,hCom,addr dcbx
hCom 是前面打开成功后返回的句柄,dcbx 是数据结构 DCB,里面包括了通讯的具体参数,至于这个参数的建立,你可以自己填写,也可以用前面提到的 BuildCommDCB 或 CommConfigDialog 填写


建立读数据的线程

到这里,就可以开始读数据了,一般我们是在主线程中写数据,因为写是我们可以控制的,而读的时候我们不知道数据什么时候会到,所以要建立一个线程专门用来读数据,在这个线程中,我们循环地用 ReadFile 读串口,同时用 WaitCommEvent 检测线路状态。


如果要检测通讯状态,如 CTS 信号,RingIn 等等 -- 用 SetCommMask、WaitCommEvent、ClearCommError、GetCommModemStatus

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
SetCommMask 指定 WaitCommEvent 要等待的事件名称,具体的参数请查手册

invoke WaitCommEvent,hCom,addr dwEvent,NULL
WaitCommEvent 等待一直到 SetCommMask 指定事件之一发生

invoke ClearCommError,hCom,addr dwError,addr stComStat
在 WaitCommEvent 以后,要用 ClearCommError 清除事件的 Flag,以便进行下一轮 WaitCommEvent,同时这个 API 可以获得更详细的事件信息

invoke GetCommModemStatus,hCom,addr dwModemStatus
同样,GetCommModemStatus 是用来获得串口线路状态的,如 CTS、RING 等等,当 WaitCommEvent 返回时,只是指出了如 CTS 等等状态有变化,但具体是变成 On 还是 Off 了还要靠这个 API 去取得更详细的信息


读数据 -- 用 ReadFile

invoke ReadFile,hCom,addr szBuffer,sizeof szBuffer,addr dwBytesRead,addr stReadState
最后一个参数是开头定义的 OVERLAPPED 结构的地址,指定了它就表示是用异步方式的读方式,这个 API 会马上返回,接下去要用

invoke GetOverlappedResult,hCom,addr stReadState,addr dwBytesRead,FALSE
将其余的数据读完


结束时关闭端口 -- 停止 WaitCommEvent 的等待以及关闭端口 CloseHandle

平时程序会停留在 WaitCommEvent 的等待中,当要终止线程的时候,必须是程序从 WaitCommEvent 中退出来,这时候要用

按照 Win32 手册上的说明,参数为 NULL 的 SetCommMask 会使另一个线程中的 WaitCommEvent 马上返回,然后就是用 CloseHandle 关闭端口
invoke CloseHandle,hCom
5 Win32 通讯 API Bug 之二--- SetCommMask 和 WaitCommEvent

严格的说这不应该是 Bug,而是偶然的情况,我发现有些时候我的读线程无法结束,跟踪发现是停在了 WaitCommEvent 上,这说明有时候 invoke SetCommMask,hCom,NULL 并不能使 WaitCommEvent 退出,我最后使用的办法是: 在 SetCommMask 以后再执行 invoke SetEvent,stReadState.hEvent,把读的 OVERLAPPED 结构中的 Event 置位,让 WaitCommEvent 认为有 Event 发生,它就会马上返回,也许这并不是普遍的情况,但如果你的程序也是停在了 WaitCommEvent 的地方,不妨一试。

6 如何编写读线程中的循环


按照《Serial communications in Microsoft Win32》一文中的例程,读循环可以用:

#define READ_TIMEOUT 500 // milliseconds

DWORD dwRes;
DWORD dwRead;
BOOL fWaitingOnRead = FALSE;
OVERLAPPED osReader = {0};

// Create the overlapped event. Must be closed before exiting
// to avoid a handle leak.
osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

if (osReader.hEvent == NULL)
// Error creating overlapped event; abort.

if (!fWaitingOnRead) {
// Issue read operation.
if (!ReadFile(hComm, lpBuf, READ_BUF_SIZE, &dwRead, &osReader)) {
if (GetLastError() != ERROR_IO_PENDING) // read not delayed?
// Error in communications; report it.
else
fWaitingOnRead = TRUE;
}
else {
// read completed immediately
HandleASuccessfulRead(lpBuf, dwRead);
}
}

 

if (fWaitingOnRead) {
dwRes = WaitForSingleObject(osReader.hEvent, READ_TIMEOUT);
switch(dwRes)
{
// Read completed.
case WAIT_OBJECT_0:
if (!GetOverlappedResult(hComm, &osReader, &dwRead, FALSE))
// Error in communications; report it.
else
// Read completed successfully.
HandleASuccessfulRead(lpBuf, dwRead);

// Reset flag so that another opertion can be issued.
fWaitingOnRead = FALSE;
break;

case WAIT_TIMEOUT:
// Operation isn't complete yet. fWaitingOnRead flag isn't
// changed since I'll loop back around, and I don't want
// to issue another read until the first one finishes.
//
// This is a good time to do some background work.
break;

default:
// Error in the WaitForSingleObject; abort.
// This indicates a problem with the OVERLAPPED structure's
// event handle.
break;
}
}

 

这一段程序在 98 下正常,但非常不幸的是在 Win2000 下,ReadFile 总是返回读正确,并不返回 ERROR_IO_PENDING,使下面的 WaitForSingleObject 的循环形同虚设,要命的是,ReadFile 返回读正确却每次只读一个字节,结果程序工作得很奇怪,即使缓冲区中有很多的字符,程序也每次只能读一个字符,要等到发送字符或做其他的操作使线路状态改变了,才能读下一个字符,我不知道这个奇怪的现象是如何发生的,反正我解决的办法是在 ReadFile 前加 WaitCommEvent,真正等到 EV_RXCHAR 以后才去 ReadFile,到最后,我用的循环是这样的,虽然没有一篇文章中的例子是这样的,但它却同时在 windows9x 和 windows2000 下工作得很好:

.while dwFlag & IF_CONNECT
;********************************************************************
; 检测其它的通信事件
; 如果检测到且定义了 lpProcessEvent 则调用 lpProcessEvent
;********************************************************************
invoke WaitCommEvent,hCom,addr @dwEvent,NULL ;addr stReadState
push eax
invoke ClearCommError,hCom,addr @dwError,addr @stComStat
pop eax
.if eax == 0
invoke GetLastError
.if eax == ERROR_IO_PENDING
or dwFlag,IF_WAITING
.endif
.else
;这里是线路状态的处理
.endif
;********************************************************************
; 如果没有在等待异步读的过程中,则读端口
;********************************************************************
.if ! (dwFlag & IF_WAITING)
mov @dwBytesRead,0
invoke ReadFile,hCom,addr @szBuffer,sizeof @szBuffer,\
addr @dwBytesRead,addr stReadState
.if eax == FALSE
or dwFlag,IF_WAITING
invoke GetLastError
.if eax != ERROR_IO_PENDING
;这里是错误处理
.endif
.else
and dwFlag,not IF_WAITING
mov eax,@dwBytesRead
.if eax != 0
;这里是接收到的数据处理
.endif
.endif
.endif
;********************************************************************
; 如果在异步读端口中,则等待一段时间
;********************************************************************
.if dwFlag & IF_WAITING
invoke WaitForSingleObject,stReadState.hEvent,200
.if eax == WAIT_OBJECT_0
and dwFlag,not IF_WAITING
invoke GetOverlappedResult,hCom,addr stReadState,\
addr @dwBytesRead,FALSE
.if eax != 0
mov eax,@dwBytesRead
.if eax != 0
;这里是接收到的数据处理
.endif
.else
;这里是错误处理
invoke ClearCommError,hCom,addr @dwError,addr @stComStat
.endif
.else
;这里是错误处理
.endif
.endif
.endw

7 流控制的问题

在流控制方式为“无”和“软件控制”的情况下,基本上没有什么问题,但在“硬件控制”下,win32 手册中说明 RTS_CONTROL_HANDSHAKE 控制方式的含义是:

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.

也就是说,当缓冲区快满的时候 RTS 会自动 OFF 通知对方暂停发送,当缓冲区重新空出来的时候, RTS 会自动 ON,但我发现当 RTS 变 OFF 以后即使你已经清空了缓冲区, RTS 也不会自动的 ON,造成对方停在那里不发送了,所以,如果要用硬件流控制的话,还要在接收后最好加上检测缓冲区大小的判断,具体是使用 ClearCommError 后返回的 COMSTAT.cbInQue,当缓冲区已经空出来的时候,要使用 invoke EscapeCommFunction,hCom,SETRTS 重新将 RTS 设置为 ON。
gxpa1
驱动中牛
驱动中牛
  • 注册日期2002-10-30
  • 最后登录2010-09-02
  • 粉丝0
  • 关注0
  • 积分8分
  • 威望11点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
7楼#
发布于:2005-07-20 15:19
声明:网上找的
jack352
驱动牛犊
驱动牛犊
  • 注册日期2005-05-05
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分4分
  • 威望1点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
8楼#
发布于:2005-07-20 16:13
买本龚建伟的<<vc++/turboc串口通讯>>书都有了
SniperNN
驱动牛犊
驱动牛犊
  • 注册日期2002-02-02
  • 最后登录2006-04-12
  • 粉丝0
  • 关注0
  • 积分131分
  • 威望14点
  • 贡献值0点
  • 好评度13点
  • 原创分0分
  • 专家分0分
9楼#
发布于:2005-07-20 17:11
nt 2k 以上必须用OVERLAPPED方式
lzl
lzl
驱动大牛
驱动大牛
  • 注册日期2001-09-11
  • 最后登录2010-06-22
  • 粉丝1
  • 关注0
  • 积分127分
  • 威望48点
  • 贡献值0点
  • 好评度14点
  • 原创分0分
  • 专家分0分
10楼#
发布于:2005-07-20 19:40
下面是引用SniperNN于2005-07-20 17:11发表的:
nt 2k 以上必须用OVERLAPPED方式

2k xp串口 并口都要驱动
l=z=l 我听说,灌水是使分数快速增长的有效方法
lzl
lzl
驱动大牛
驱动大牛
  • 注册日期2001-09-11
  • 最后登录2010-06-22
  • 粉丝1
  • 关注0
  • 积分127分
  • 威望48点
  • 贡献值0点
  • 好评度14点
  • 原创分0分
  • 专家分0分
11楼#
发布于:2005-07-20 19:40
下面是引用SniperNN于2005-07-20 17:11发表的:
nt 2k 以上必须用OVERLAPPED方式

2k xp串口 并口都要驱动
l=z=l 我听说,灌水是使分数快速增长的有效方法
ZFDok
驱动牛犊
驱动牛犊
  • 注册日期2005-01-05
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分110分
  • 威望20点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
12楼#
发布于:2005-07-21 09:53
gxpa1 老兄长写的很好,非常感谢,即使是在网上找的也让你费心了,以后还请多赐教!
zhoujiamurong
驱动小牛
驱动小牛
  • 注册日期2006-03-20
  • 最后登录2009-05-06
  • 粉丝4
  • 关注0
  • 积分1081分
  • 威望360点
  • 贡献值0点
  • 好评度215点
  • 原创分0分
  • 专家分0分
13楼#
发布于:2007-09-24 14:01
我发现有些时候我的读线程无法结束,跟踪发现是停在了 WaitCommEvent 上,这说明有时候 invoke SetCommMask,hCom,NULL 并不能使 WaitCommEvent 退出,我最后使用的办法是: 在 SetCommMask 以后再执行 invoke SetEvent,stReadState.hEvent,把读的 OVERLAPPED 结构中的 Event 置位,让 WaitCommEvent 认为有 Event 发生,它就会马上返回,也许这并不是普遍的情况,但如果你的程序也是停在了 WaitCommEvent 的地方,不妨一试。

这个我也确实困惑,每次关闭不了,我每次解决的办法是杀进程!!o(∩_∩)o...哈哈
游客

返回顶部