串行通信接口标准有多个版本,但是基本上都是在RS-232标准的基础上发展而来。RS-232C标准时美国EIA和BELL等公司一起开发并于1969年公布的通信协议。1997年TIA发不了最新的一个版本,命名为TIA/EIA-232-F。ITU和CCITT发布了一个类似的标准-V.28。RS-232C标准最初是为远程通信连接数据终端设备DTE和数据通信设备DCE而制定的。因此RS-232C标准中所提到的发送和接收都是站在DTE立场上,而不是站在DCE的立场上来定义的。
RS-232采用负逻辑,即+3V~+15V表示逻辑0,而-3V~-15V表示逻辑1。而在RTS/CTS/DSR/DTR/DCD等控制线上,+3V~+15V表示信号有效,-3V~-15V表示信号无效。
RS-232C并未定义连接器的物理特性,因此出现了DB-25、DB-15和DB-9各种类型的连接器,其引脚的定义也各不相同,DB9定义如图1所示。通信速率低于20kbps时,且码元畸变小于4%时,要求驱动器的负载电容小于2500pF,支持最长距离为15m。
图1 RS232 DB9引脚定义
PC机的每个串口都保留了一序列的端口资源,大多数都有一个指定的中断请求IRQ或中断请求级别,端口被命名为COM1、COM2等。在Widnows中可以在控制面板中浏览端口资源。端口可以使用硬件支持的任何地址和IRQ号,每个端口保留8个连续地址,从基址开始的寄存器。串行接口包括4个主要寄存器,即控制寄存器、状态寄存器、数据输入寄存器及数据输出寄存器。
串行通信协议分为同步协议和异步协议
(1)面向字符的同步协议,这种协议的典型代表是IBM公司开发的BSC协议。它的特点是一次传送由若干字符组成的数据块,而不是只传送一个字符,并规定了10个字符作为这个数据块的开头和结束标志以及整个传输过程的控制信息,也叫做通信控制字。此种协议涉及到“数据透明”和“字符填充技术”。
(2)面向比特的同步协议HDLC,不是靠特定字符来标识帧的开始和结束,故称“面向比特”的协议。包含有标识场、地址场、控制场、信息场和帧校验信息。
(3)起止式异步协议,一个字符一个字符地传输,并且传送一个字符总是以起始比特开始,以停止位结束,字符之间没有固定的时间间隔要求。起始比特是用来通知收方,以此来重新核对收发双方同步。若接收设备和发送设备两者的时钟频率略有偏差,也不会因偏差的累积而导致错位。但是由于需要起始位和停止比特位这样一些附加位,使得传输效率只有约80%。因此这种协议一般用在数据速率较慢的场合,而高速传送时,一般要采用同步协议。
Widnows系统下进行串口编程有多种方法:1)API方式:CreateFile,GetCommState,SetCommState,ReadFile,WriteFile。2)ActiveX方式:MSComm控件。3)直接嵌入汇编方式:Windows98以前使用。4)VXD或者WDM实现。
MSComm控件通过串行传输和接收数据,并且在VC,CB以及Delphi等语言中都可以使用。MSComm提供了两种处理通信问题的方法:一是事件驱动方法,一是查询法。
1.事件驱动方式
当发生特定事件,比如接收到一个字符或者一个RTS/CD事件时,就会发生OnComm事件,而且还可以检查和处理通信错误。在编程过程中,可以在OnComm事件处理函数中加入自己的处理代码。这种方法的优点是程序响应及时,可靠性高。每个MSComm控件对应着一个串行端口,如果应用程序要访问多个换行端口,必须使用多个MSComm控件。
2.查询方式
查询方式本质上还是属于事件驱动,通过检查CommEvent属性的值来检查事件和错误,只要CommEvent属性的值有了变化,就表明一个通信事件或一个错误发生。如果应用程序较小,且自成一体,这种方法是比较方便的。
MSComm控件的属性:
(1)CommPort属性
void SetCommPort(short nNewValue);
short GetCommPort();
此属性用于设置和返回连接的串行端口号,必须在打开端口之前设置CommPort属性。设置时,nNewValue可以设置成1~16的任何数(默认为1)。但是如果用PortOpen方法打开一个并不存在的端口时,MSComm控件会产生错误68(设备无效)。
(2)Settings属性
void SetSettings(LPCTSTR lpszNewValue);
String GetSettings();
该属性包含数据传输速率、奇偶校验、数据比特、停止比特位参数,打开端口前必须设置。当端口打开时,如果value非法,则控件产生错误380(非法属性值)。其中lpszNewValue用字符串表示,由四个设置值组成,有如下的组成格式:
“BBBB,P,D,S”
BBBB为数据传输速率,P为就校验,D为数据比特数,S为停止比特数。
(3)RThreshold属性
void SetRThreshold(short nNewValue);
short GetRThreshold();
控件设置CommEvent属性为comEvReceive并产生OnComm之前要接收的字符数。当接收字符后,如果RThreshold属性为0,则不会产生OnComm事件。否则当接收缓冲区内字节个数达到或超过该值就会产生OnComm事件。
(4)SThreshold属性
void SetSThreshold(short nNewValue);
short GetSThreshold();
控件设置CommEvent属性为comEvSend并产生OnComm事件之前,设置并返回传输缓冲区中允许的最小字符数。nNewValue代表在OnComm事件产生之前在传输缓冲区中的最小字符数。如果设置SThreshold属性为0(默认值),数据传输事件不会产生OnComm事件。若设置SThreshold属性为1,当传输缓冲区完全空时,MSCOmm空间产生OnComm事件。如果在传输缓冲区的字符数小于Value,CommEvent属性设置为comEvSend,并产生OnComm事件。CommEvSend事件仅当字符数与SThreshold交叉时被激活一次。
(5)InputMode属性
void SetInputMode(long nNewValue);
long GetInputMode();
该属性用于设置或者返回传输数据的类型,即以文本或二进制方式取回数据
(6)InputLen属性
void SetInputLen(short nNewVlaue);
short GetInputLen();
该属性用于设置并返回Input属性从接受缓冲区读取的字符数。如果设置为0,则Input将读取接受缓冲区的所有数据。在使用Input前,用户可以选择检查InBufferCount属性来确定缓冲区中是否已有需要数目的字符,该属性在从输出格式为定常数据的机器读取数据时非常有用。
(7)InBufferSize属性
void SetInBufferSize(short nNewValue);
short GetInBufferSize();
设置和返回输入缓冲区的大小,默认值为1024字节
(8)InBufferCount属性
void setInBufferCount(short nNewValue);
short GetInBufferCount();
返回输入缓冲区内的等待读取的字节个数,可以通过该属性值为0来清除接收缓冲区
(9)Input属性
VARIANT GetInput();
表示从接收缓冲区移走一串字符,将缓冲区接收到的数据读入变量,属性值为Variant型变量,即当InputMode属性值为0(文本模式)时,变量中含String型数据。当InputMode属性值为1(二进制模式)时,变量中含有Byte型数组数据。该属性在端口未打开时不可用,在运行时是只读的。
(10)PortOpen属性
void SetPortOpen(BOOL bNewValue);
BOOL GetPortOpen();
打开或关闭端口,通常需要显式打开或关闭串口。
(11)OutBuffersize属性
void SetOutBufferSize(short nNewValue);
设置或者返回发送缓冲区的大小,默认为512字节。发送缓冲区设置越大,应用程序可用内存就越小,然而如果设置太小,缓冲区将会溢出,除非使用握手协议。
(12)OutBufferCounter属性
void SetOutBufferCounter(short nNewValue);
short GetOutBufferSize();
返回发送缓冲区内等待发送的字节数,可以用来清空发送缓冲区。
(13)OutPut属性
void SetOutput(const VARIANT &newValue);
向发送缓冲区写数据流,属性为Variant变量。在端口未打开时不可用,在运行时是只写的。Output属性可以发送文本数据或二进制数据,通常发送ANSI字符串可以以文本方式发送,如果数据包含了内嵌控制字符,Null字符等,必须将其作为二进制传输。
(14)DTREnable属性
void SetDTREnable(BOOL bNewValue);
BOOL GetDTREnable();
设置DTR线是否有效
(15)RTSEnable属性
void SetRTSEnable(BOOL bNewValue);
BOOL GetRTSEnable();
设置RST线是否有效。
(16)DSRHolding属性
判断DSR线是否有效
(17)CDHolding属性
判断CD线的状态是否有效
(18)CTSHolding属性
用于判断CTS先的状态
API串口通信编程
在WIN32 API中,串口使用文件方式进行访问,其操作的API基本上与文件操作的API一致。
打开串口:
Win32中用于打开串口的API函数为CreateFile,其原型为:
HANDLE CreateFile (
LPCTSTR lpFileName, //将要打开的串口逻辑名,如COM1 或COM2
DWORD dwAccess, //指定串口访问的类型,可以是读取、写入或两者并列
DWORD dwShareMode, //指定共享属性,由于串口不能共享,该参数必须置为0
LPSECURITY_ATTRIBUTES lpsa, //引用安全性属性结构,缺省值为NULL
DWORD dwCreate, //创建标志,对串口操作该参数必须置为OPEN EXISTING
DWORD dwAttrsAndFlags, //属性描述,用于指定该串口是否可进行异步操作,
//FILE_FLAG_OVERLAPPED:可使用异步的I/O
HANDLE hTemplateFile //指向模板文件的句柄,对串口而言该参数必须置为NULL
);
windows文件操作分为同步I/O和重叠I/O(Overlapped I/O)两种方式,在同步I/O方式中,API会阻塞直到操作完成以后才能返回(在多线程方式中,虽然不会阻塞主线程,但是仍然会阻塞监听线程);而在重叠I/O方式中,API会立即返回,操作在后台进行,避免线程的阻塞。重叠I/O非常灵活,它也可以实现阻塞(比如可以设置一定要读取到一个数据才能进行到下一步操作)。如果进行I/O操作的API在没有完成操作的情况下返回,我们可以通过调用GetOverLappedResult()函数阻塞到I/O完成后返回。
配置串口:
配置串口是通过改变设备控制块DCB(Device Control Block)的成员变量来实现的,接收缓冲区和发送缓冲区的大小可通过SetupComm函数来设置。
DCB结构体定义为:
typedef struct _DCB { // dcb
DWORD DCBlength; // sizeof(DCB)
DWORD BaudRate; // current baud rate
DWORD fBinary: 1; // binary mode, no EOF check
DWORD fParity: 1; // enable parity checking
DWORD fOutxCtsFlow:1; // CTS output flow control
DWORD fOutxDsrFlow:1; // DSR output flow control
DWORD fDtrControl:2; // DTR flow control type
DWORD fDsrSensitivity:1; // DSR sensitivity
DWORD fTXContinueOnXoff:1; // XOFF continues Tx
DWORD fOutX: 1; // XON/XOFF out flow control
DWORD fInX: 1; // XON/XOFF in flow control
DWORD fErrorChar: 1; // enable error replacement
DWORD fNull: 1; // enable null stripping
DWORD fRtsControl:2; // RTS flow control
DWORD fAbortOnError:1; // abort reads/writes on error
DWORD fDummy2:17; // reserved
WORD wReserved; // not currently used
WORD XonLim; // transmit XON threshold
WORD XoffLim; // transmit XOFF threshold
BYTE ByteSize; // number of bits/byte, 4-8
BYTE Parity; // 0-4=no,odd,even,mark,space
BYTE StopBits; // 0,1,2 = 1, 1.5, 2
char XonChar; // Tx and Rx XON character
char XoffChar; // Tx and Rx XOFF character
char ErrorChar; // error replacement character
char EofChar; // end of input character
char EvtChar; // received event character
WORD wReserved1; // reserved; do not use
} DCB;
而SetupComm函数的原型则为:
BOOL SetupComm(
HANDLE hFile, // handle to communications device
DWORD dwInQueue, // size of input buffer
DWORD dwOutQueue // size of output buffer
);