串行通讯

1硬件连接    1

1.1 RS232接口    1

1.1.1 DB9    1

1.1.2 DB25    2

1.2 连线及说明    2

1.3 代码控制    5

1.3.1 输出信号    5

1.3.2 输入信号    6

1.4 连线实例    6

1.4.1 PC-E500    6

1.4.2 DSNP GPS接收机(5000系列)    7

1.4.3 麦哲伦SP24手持机    7

2流控制    9

2.1 Quick Basic    9

2.2 Visual Basic 6.0    10

2.3 读取超时    11

2.4 串口驱动缓存    12

 

1硬件连接

1.1 RS232接口

串行通讯中,RS232的应用最为广泛。其通讯接口一般为DB9DB25,其中的925表示通讯线的个数。

1.1.1 DB9

DB99条通讯线,每条通讯线的编号请见下图。注意:公头和母头的编号有所区别。

图1.1

各条通讯线的含义:

编号

输入/输出

1 

DCD    Data Carrier Detect

CD        Carrier Detect

RLSD    Receive Line Signal Detect

载波检测

in 

2 

RXD    接收数据

in 

3 

TXD    发送数据

out 

4 

DTR    data-terminal-ready

设备就绪就设置为高电平,这样对方可以发送数据了

out 

5 

SG        信号地线

 

6 

DSR    data-set-ready

高电平说明对方已准备就绪,可以给对方发送数据了

in 

7 

RTS    request-to-send

半双工:发送状态置为高电平,接收状态置为低电平

全双工:用于硬件流控制

out 

8 

CTS     clear-to-send

半双工:高/低电平说明对方处于发送/接收状态

全双工:用于硬件流控制

in 

9 

RI        振铃指示,通知对方通信线路已接通

in 

1.1.2 DB25

DB2525条通讯线。每条通讯线的编号与图1.1的编号类似:第一行的编号从113,第二行的编号从1425

各条通讯线的含义:

编号

输入/输出

1 

GND    设备地线

 

2 

TXD    发送数据

out 

3 

RXD    接收数据

in 

4 

RTS    request-to-send

半双工:发送状态置为高电平,接收状态置为低电平

全双工:用于硬件流控制

out 

5 

CTS     clear-to-send

半双工:高/低电平说明对方处于发送/接收状态

全双工:用于硬件流控制

in 

6 

DSR    data-set-ready

高电平说明对方已准备就绪,可以给对方发送数据了

in 

7 

SG        信号地线

 

8 

DCD    Data Carrier Detect

CD        Carrier Detect

RLSD    Receive Line Signal Detect

载波检测

in 

9 

发送电流+

 

11 

发送电流-

 

18 

接收电流+

 

20 

DTR    data-terminal-ready

设备就绪就设置为高电平,这样对方可以发送数据了

out 

22 

RI        振铃指示,通知对方通信线路已接通

in 

25 

接收电流-

 

其它

未用,具体有十一个:1012-1719212324

 

1.2 连线及说明

如果只是延长通讯线,就采用顺接的方法,即:DCDDCDDTRDTRCTSCTS……

如果是两个串口设备相互通讯,就必须采用交叉的方式连线,如下图所示:

图1.2

说明如下:

1RXDTXDSG这三条线是必需的,用来传输数据;

2、设备A在给设备B发送数据前,需要知道设备B是否已经开机。因此,它会检查DSRDCDRI是否为高电平,如果是高电平说明设备B已经准备就绪,可以通讯了。串口设备在开机后或PC的串口程序打开串口后一般都会设置自己的DTR为高电平,这样对方的DSRDCDRI都变成了高电平,就可以正常通讯了。DTR除了通知对方准备就绪外,还可用于硬件流控制,其原理与RTS硬件流控制相同;

3RTSCTS的设计初衷是用来协调半双工通讯的,在全双工通讯普及的现在它们的主要作用是用来进行硬件流控制。

啥叫半双工、全双工?对讲机就是半双工——按下按钮,只能讲不能听;松开按钮,只能听不能讲。电话、手机就是全双工——接通电话后,可以同时讲、听。

半双工通讯时,设备A给设备B发送数据前,需要查询CTS的状态。如果是高电平说明设备B正处于发送状态(设备BRTS为高电平)。为此,设备A将自己的RTS设置为高电平,设备BCTS也随之变为高电平,设备B察觉到CTS由低电平变为高电平后会将状态切换为接收数据,然后设置自己的RTS为低电平。这样,设备ACTS就由高电平变成了低电平。好了,现在设备A可以给设备B发送数据了。发送完数据,设备A会切换状态为接收并设置RTS为低电压,这样设备B就可以给它发送数据了。

全双工通讯时代,不再需要切换发送或接收状态,RTS/CTS的作用也发生了改变——那就是用于硬件流控制。如:设备A给设备B发送数据,其中设备B的反应速度比较慢。短时间内,在设备A给设备B发送大量数据的前提下,设备B可能会丢失一些数据,甚至导致通讯中断。为此,可以使用RTS硬件流控制。其步骤为:设备ABRTS初始状态均为高电平,表明大家现在都很闲,可以发送数据过来。设备A给设备B发送的数据被存放在设备B的串口数据缓冲区内,当设备B发现缓冲区内的数据太多,如超过75%时,会设置RTS为低电压,告诉设备A我忙不过来了。此时,设备ACTS就由高电压变成了低电压它就知道设备B反应不过来了,那就等一会儿吧。设备B处理缓冲区内的数据,当缓冲区内的数据被处理得差不多的时候,如缓冲区内的数据不足25%的时候,会再次设置自身的RTS为高电平,用以表明我现在缓过劲来了,可以继续发送数据过来了。设备ACTS由低电平变为高电平后,就会继续发送数据给设备B

这里有一个关键点:那就是设备B正忙的时候,设备A需要等待多长时间?在Windows下,这个时间可以进行设置。具体的就是使用SetCommTimeouts函数。其声明如下:

BOOL SetCommTimeouts(HANDLE hFile,LPCOMMTIMEOUTS pTimeout);

2个参数是一个结构,其定义如下

typedef struct _COMMTIMEOUTS {

DWORD ReadIntervalTimeout;

DWORD ReadTotalTimeoutMultiplier;

DWORD ReadTotalTimeoutConstant;

DWORD WriteTotalTimeoutMultiplier;

DWORD WriteTotalTimeoutConstant;

} COMMTIMEOUTS,*LPCOMMTIMEOUTS;

最关键的就是 WriteTotalTimeoutMultiplier WriteTotalTimeoutConstant。它们两确定了等待时间,具体公式如下:

上式中的Bytes是将要发送的字节数。

1.3 代码控制

1.3.1 输出信号

DTRRTS属于输出信号,它们的状态(高电平、低电平)可以使用代码(VC++)进行控制。

方法一:调用函数SetCommState

2个参数是一个DCB结构。DCB重要的成员如下:

fOutxDsrFlow

    是否启用DTR/DSR硬件流控制。

    启用后,发送数据前会检查DSR是否为高电平

fDtrControl

    DTR_CONTROL_DISABLE        设置DTR为低电平

    DTR_CONTROL_ENABLE        设置DTR为高电平

    DTR_CONTROL_HANDSHAKE    根据接收缓冲区的数据量设置DTR

fOutxCtsFlow

    是否启用RTS/CTS硬件流控制

    启用后,发送数据前会检查CTS是否为高电平

fRtsControl

    RTS_CONTROL_DISABLE        设置RTS为低电平

    RTS_CONTROL_ENABLE            设置RTS为高电平

    RTS_CONTROL_HANDSHAKE        根据接收缓冲区的数据量设置RTS

    RTS_CONTROL_TOGGLE            发送数据时高电平,发送完低电平。

                            用于半双工通讯

方法二:调用函数EscapeCommFunction

给第2个参数传递如下宏

SETDTR     设置DTR为高电平

CLRDTR    设置DTR为低电平

SETRTS         设置RTS为高电平

CLRRTS     设置RTS为低电平

显然,如果SetCommStatefDtrControlDTR_CONTROL_HANDSHAKE,那么DTR的状态由Windows来维护。此时就不能使用EscapeCommFunction修改DTR的状态。同样要注意RTS_CONTROL_HANDSHAKERTS_CONTROL_TOGGLE

1.3.2 输入信号

DSR CTSDCD(也叫RLSD)、RI属于输入信号,它们的状态(高电平、低电平)可以使用代码查询到。

请使用函数GetCommModemStatus,获得的第2个参数就是查询到的状态dwStat

dwStat & MS_DSR_ON    DSR    是否为高电压

dwStat & MS_CTS_ON    CTS    是否为高电压

dwStat & MS_RLSD_ON    DCD是否为高电压

dwStat & MS_RING_ON    RI是否为高电压

1.4 连线实例

1.4.1 PC-E500

PC-E500 

PCDB9

1 

GND设备地线

壳(可选)

2 

TXD发送

2 RXD 

3 

RXD接收

3 TXD 

4 

RTS 

 

5 

CTS 

4 DTR 

8 

DCD 

7 

SG信号地线

5 SG 

10,13 

SG之间的电压应为+6V,实际与电池电压有关。用来给外部供电

 

11 

Receive Ready 

8 CTS 

14 

DTR 

6 DSR

1 DCD(可选)

9 RI(可选)

说明:

1PC-E500的针脚编号是从上到下的,即由DEL键到INS键;

2PC-E5004号针为RTS。但是打开串口后,它不是高电平,因此无法进行RTS硬件流控制。反而是11号针,打开串口后,它是高电平,而且在接收串口数据忙不过来时它会设置为低电平。因此,11号针可以充当RTS的角色进行硬件流控制,所以它被接到电脑的CTS上;

3PC-E5005号针(CTS)按理应该接到PC7号针(RTS),不过连接到4号针(DTR)也是可以的。因为PC上打开串口后DTR一般都是高电平,而RTS不一定高电平。这种连法通用性更强。

1.4.2 DSNP GPS接收机(5000系列)

接收机A口是RS232接口,共有7根针,如下图所示:

图1.3

各个针头的含义,及与PCDB9公头的连接请参考下表

针号

输入/输出

PCDB9

1 

+12V 

 

9 RI 

2 

TXD 

out 

2 RXD 

3 

RXD 

in 

3 TXD 

4 

DSR

 

4 DTR 

5 

SG 

 

5 SG 

6 

CTS 

out 

7 RTS 

7 

RTS 

in 

8 CTS 

1.4.3 麦哲伦SP24手持机

SP24手持机背面有5根针,如下图所示:

图1. 4

各个针头的含义,及与PCDB9公头的连接请参考下表

针号

线色

PCDB9

1 

向天线供电

 

2 

输入电压(10~20V

 

3 

TXD 

2 RXD 

4 

RXD 

3 TXD 

5 

SG 

5 SG 

2流控制

啥叫流控制?串行通讯的双方,如果有一方反应较对方慢,就可能来不及处理对方发来的串口数据,导致串口数据丢失甚至通讯失败。因此,需要对串口数据流进行控制,使其准确无误的相互传输。

流控制也被称之为握手,分为软件控制(XON/XOFF)和硬件控制(DTRRTS)。本章重点讲述如何编码实现RTS硬件流控制。

2.1 Quick Basic

1998年,笔者就使用Quick Basic编写了PCPC-E500的串行通讯程序。当时对RTS硬件流控制理解不够深刻,编写出来的程序在向PC-E500发送数据时,经常发生通讯错误。后来,采用了PC发一行数据就等待1秒的笨方法缓解了这一错误。但这一问题始终没有根除。理解了RTS硬件流控制,这一问题就迎刃而解了。

关键的代码在于打开串口,以前的代码是这样的:

OPEN "COM1:1200,N,8,1,DS,RS" FOR OUTPUT AS 1

改成这样就行了:

OPEN "COM1:1200,N,8,1,DS,CS60000" FOR OUTPUT AS 1

重要参数的说明:

1DS

使用OPEN "COM1:1200,N,8,1"打开串口时,QB会检查串口的DSR是否为高电平,即对方的DTR是否为高电平。如果不是高电平就会提示错误。如果PC-E500已经开机,并且已经打开了串口(运行LOAD"COM:"),那么它的DTR就是高电平,OPEN语句就能正常运行,否则就会出错。为了在打开串口时不检查DSR,请使用DS0DS

2RS

使用OPEN "COM1:1200,N,8,1"打开串口时,RTS硬件流控制即被启用,即:打开串口时会设置RTS为高电平,同时发送数据时(如:PRINT#1,"123";会检查CTS是否为高电平,不是高电平表明对方较忙我得等一会儿。最多等待1秒,如果CTS还是低电平,发送数据的PRINT#1语句就会出错。

OPEN "COM1:1200,N,8,1,RS"语句里增加了RS参数,其含义为:禁用RTS硬件流控制,即:打开串口时设置RTS为低电平,同时发送数据时不再检查CTS是否为高电平。以前增加RS参数应该是为了避免PRINT#1时出错,其实设置好等待时间问题就解决了。

3CS60000

CS60000表示等待CTS信号为高电平时最多等待60000毫秒(即60秒),这个数值最大只能为65535

CS0CS表示不等待CTS信号为高电平,亦即禁用RTS硬件流控制。

总结:

1QBOPEN语句里通过参数RS来控制是否使用RTS硬件流控制;

2、使用RTS硬件流控制时,应该在OPEN语句里设置等待时间,即CS参数。

2.2 Visual Basic 6.0

VB6.0一般使用MSCOMM控件进行串行通讯。控制MSCOMM是否启用RTS硬件流控制太简单了:只要设置它的HandshakingcomRTS即可。

那么MSCOMM控件等待CTS为高电平的时间是多少呢?请参考代码:

Private Type COMMTIMEOUTS

ReadIntervalTimeout As Long

ReadTotalTimeoutMultiplier As Long

ReadTotalTimeoutConstant As Long

WriteTotalTimeoutMultiplier As Long

WriteTotalTimeoutConstant As Long

End Type

 

Private Declare Function GetCommTimeouts Lib "kernel32" (ByVal hFile As Long, lpCommTimeouts As COMMTIMEOUTS) As Long

 

Private Sub Command1_Click()

MSComm1.CommPort = 1

MSComm1.PortOpen = True '打开COM1

Dim tm As COMMTIMEOUTS

GetCommTimeouts MSComm1.CommID, tm '获得等待时间的设置

End Sub

关键代码就一行:GetCommTimeouts MSComm1.CommID, tm。它调用了API函数GetCommTimeouts。第一个参数是打开串口的句柄,传递MSCOMM控件的CommID即可;第二个参数用来获得等待时间信息,结果如下:

图2.1

也就是说MSCOMM控件在等待CTS信号时,最多等待5秒。修改等待时间可调用SetCommTimeouts函数。

2.3 读取超时

上图中的ReadIntervalTimeoutReadTotalTimeoutMultiplierReadTotalTimeoutConstant用来控制读取串口数据时的等待时间。

ReadIntervalTimeout 用来设置两个字符之间的等待时间。如:设置ReadIntervalTimeout10,则ReadFile在读取下一个字符时最多等待10ms。超过这个时间ReadFile将返回。

ReadTotalTimeoutConstantReadTotalTimeoutMultiplier用来设置总的等待时间。总等待时间首先被设置为ReadTotalTimeoutConstant,读取到n个字符后总等待时间将加上n×ReadTotalTimeoutMultiplier

最常见的设置方法为(上图也是这么设置的):

ReadIntervalTimeout        =    MAXDWORD;

ReadTotalTimeoutMultiplier    =    0;

ReadTotalTimeoutConstant    =    0;

它的含义为:ReadFile只会读取串口缓冲区内的数据,不会做任何等待。当串口缓冲区内没有数据时,ReadFile也会立即返回。

2.4 串口驱动缓存

有些串口的驱动程序没有提供缓存,而有些串口的驱动程序提供了缓存。发送数据时,这两种串口的表现是不一致的。

笔者的电脑上有两种串口:COM1PC自带的,其驱动为微软的标准串口驱动;COM17USB转串口,其驱动是生产厂家开发的。用这两种串口发送数据时,等待CTS信号的流程有所不同。

对于COM1而言,它会立即检查CTS信号是否为高电平。如果是低电平就会等待,等待时间按下式计算:

上式中的Bytes就是WriteFile要写入的字节数。

对于COM17而言,它会首先检查驱动提供的缓冲区是否已满?如果未满就把待写入的数据直接复制到缓冲区内。如果缓冲区已满,WriteFile就会检查CTS是否为高电平,其后的处理流程与COM1的一致。

同样的代码,COM1COM17的表现是不一致的:

DWORD dwWrite = 0;

WriteFile(hComm,"123...",4096,&dwWrite,NULL);

WriteFile(hComm,"123...",2,&dwWrite,NULL);

第一个WriteFile写入4096字节。对于COM1而言会检查CTS信号,可能会等待较长时间;对于COM17而言会立即返回,同时4096字节的数据将填满驱动提供的发送缓冲区。

第二个WriteFile写入2字节。COM17的发送缓冲区已满,因此它与COM1的表现就没有区别的,两者等待的时间相同。

posted @ 2016-11-03 21:06  hanford  阅读(3330)  评论(0编辑  收藏  举报