[转]实战串行通讯

实战串行通讯

罗云彬

本文不是全面的讲述如何编写串行通讯程序,而是讨论一些实际遇到的问题。

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值