用于串口通信的函数和结构在Winbase.h头文件中定义。
函数 描述
CreateFile 打开串行口
GetCommState 用指定通信设备的当前控制设置填充设备控制块(DCB结构)
SetCommState 按照DCB结构的说明配置通信设备。这个函数重新初始化所有硬件
和控制设备,但不清空I/O队列
GetCommTimeouts 获得指定通信设备上所有读/写操作的超时参数
SetCommTimeouts 设置指定通信设备上所有读/写操作的超时参数
WriteFile 向串行口写数据,这样将把数据传送给串行连接另一端的设备
ReadFile 从串行口读数据,这样将从串行连接另一端的设备接收数据
SetCommMask 指定为通信设备监视的一组事件
GetCommMask 获得指定通信设备的事件掩码值
WaitCommEvent 等待指定通信设备的事件的发生。WaitCommEvent函数监视的事件
包含在与设备句柄相关联的事件掩码中
EscapeCommFunction 指导指定通信设备执行扩展功能。通常用于将串行口设置为IR模式
ClearCommBreak 恢复指定通信设备的字符传输,并设置传输线路为不可中断状态
ClearCommError 获得通信错误数据,并报告指定通信设备的当前状态

打开端口
CreateFile函数用于打开串行口,因为硬件供应商和设备驱动程序开发者可以随意命名端口,所以应用程序应该列出所有可用端口,从而使用户能够指定要打开的端口。如果端口不存在,则CreateFile函数返回ERROR_FILE_NOT_FOUND,而且应该通知用户端口不可用。
打开串行口
1 在第一个参数lpzPortName指向的通信口后插入一个冒号。例如,指定“COM1:”为通信端口。
2 指定dwShareMode参数为0。通信端口不能像文件一样被共享。
3 在dwCreationDisposition参数中指定OPEN_EXISTING。这个标志是必须的。
4 指定dwFlagsAndAttributes参数为0。Windows CE只支持非重叠I/0.
下面的代码段说明了如何打开串行通信端口。
hPort=CreateFile(lpszPortName, //指出通信端口
GENERIC_READ|GENERIC_WRITE, //读写模式
0, //共享模式
NULL, //安全属性
OPEN_EXISTING, //如何打开服务端口
0, //端口属性
NULL); //端口属性句柄的拷贝
配置串行口
打开串行口后,一般情况下,应用程序需要改变缺省设置。用GetCommState函数可以获得缺省设置,用SetCommState函数可以设置新的端口设置。
另外,端口配置还包括用COMMTIMEOUTS结构设置读/写操作的超时值。当发生超时时,ReadFile或WriteFile函数返回成功传输的具体字符数。
配置串行口
DCB PortDCB;
PortDCB.DCBlength=sizeof(DCB);
GetCommState(hPort,&PortDCB);

PortDCB.BaudRate=9600; //波特率
PortDCB.fBinary=TRUE; //只支持二进制串行传输模式
PortDCB.fParity=TRUE; //启用奇偶校验
PortDCB.fOutxCtsFlow=FALSE; //TRUE是由CTS线来控制端口的输出
PortDCB.fOutxDsrFlow=FALSE; //TRUE是由DSR线来控制端口的输出
PortDCB.fDtrControl=DTR_CONTROL_ENABLE; //DTR_CONTROL_DISABLE:
禁用DTR(Data Terminal Ready)线并保持此状态;
DTR_CONTROL_ENABLE;
启用DTR(Data Terminal Ready)线
DTR_CONTROL_HANDSHAKE
根据接收缓冲区数据的数量告诉串行驱动程序切换DTR线状态
PortDCB.fDsrSensitivity=FALSE; //TRUE为端口将忽略任何输入的字节,除非端口DSR线被启用
PortDCB.fTXContinueOnXoff=TRUE; //TRUE为如接收缓冲区已满且驱动程序已传送XOFF字符,
将使驱动程序停止传输字符
PortDCB.fOutX=FALSE; //TRUE为指定XON/XOFF控制被用于控制串行输出
PortDCB.fInX=FALSE; //TRUE为指定XON/XOFF控制由输入串行流使用
PortDCB.fErrorChar=FALSE; //Windows CE串行驱动程序默认忽略该字段
PortDCB.fNull=FALSE; //TRUE为使串行驱动程序忽略接收到的空字节
PortDCB.fRtsControl=RTS_CONTROL_ENABLE; //RTS_CONTROL_DISABLE表示当端口打开时RTS(Request and Send)行
将禁用
//RTS_CONTROL_ENABLE表示当端口打开时RTS行将启用
//RTS_CONTROL_HANDSHAKE表示RTS线由驱动程序控制,输入缓冲区
不到半满,则RTS线将被启用,否则将被禁用
//RTS_CONTROL_TOGGLE表示如果在输出缓冲区有字节要被传输,则
驱动程序将启用RTS线,否则将禁用该线
PortDCB.fAbortOnError=FALSE; //出现读/写错误并不终止
PortDCB.ByteSize=8; //指定每字节的位数
PortDCB.Parity=NOPARITY; //奇偶字段,EVENPARITY、MARKPARITY、NOPARITY、ODDPARITY、SPACERITY
PortDCB.StopBits=ONESTOPBIT; //停止位,每字节一位(ONESTOPBIT)、一位半(ONE5STOPBITS)、
两位(TWOSTOPBITS)。
if(!SetCommState(hPort,&PortDCB))
{
MessageBox(hMainWnd,TEXT("Unable to configure the serial port"),TEXT("Error"),MB_OK);
dwError=GetLastError();
return FALSE;
}

配置超时值
每次打开通信端口时应用程序都必须用COMMTIMEOUTS结构设置通信超时值。如果不配置这个结构,则端口使用驱动程序提供的缺省超时值,或者使用上一个通信应用程序的超时值。通过假定特殊的与实际设置不同的超时设置,应用程序可以执行永远不能完成的读/写操作,或者执行完成过于频繁的读/写操作。
当读/写操作发生超时时,操作结束。而且ReadFile和WriteFile函数并不返回错误值。若想确定某个操作是否发生了超时,可以验证实际传输的字节数是否小于请求的字节数。例如,如果ReadFile函数返回TRUE,但读的字节数小于请求的字节数,则这个操作发生了超时。
配置串行口的超时值
1 调用GetCommTimeouts函数或者手工设置成员来初始化COMMTIMEOUTS结构。
2 用ReadIntervalTimeout成员指定在不发生超时的情况下两个字符间允许经过的最大毫秒数。
3 用ReadTotalTimeoutMultiplier成员指定读超时乘子。对于每个读操作,这个乘子被乘以读操作期望接收到的字节数。
4 用ReadTotalTimeoutConstant成员指定读超时常数。这个成员表示的毫秒数被加到读取的总字节数与 ReadTotalTimeoutMultiplier的乘积上。最后得到的结果是读操作发生超时前必须经过的毫秒数。
5 用WriteTotalTimeoutMultiplier成员指定写超时乘子。对于每个写操作,这个乘子被乘以写操作期望接收的字节数。
6 用WriteTotalTimeoutConstant成员指定写超时常数。这个成员表示的毫秒数被加到总字节数与WriteTotalTimeoutMultiplier的 乘积上。最后得到的结果是写操作发生超时前必须经过的毫秒数。
7 调用SetCommTimeouts函数激活端口超时设置。
为了有助于多任务处理,通常需要配置COMMTIMEOUTS结构,以便ReadFile函数能够立即返回读到的字符。要做到这一点,可以设置ReadIntervalTimeout为MAXWORD,设置ReadTotalTimeoutMultiplier和ReadTotalTimeoutMultiplier为0。

写串行口
WriteFile函数通过串行连接向另一台设备传输数据。调用这个函数之前,应用程序必须打开和配置串行口。
因为Windows CE不支持重叠I/O(也称为异步I/O),所以主线程或者任何创建窗口的线程都不应该试图向串行口写大量数据。这样的线程将被阻塞,因而不能管理消息队列。应用程序可以通过创建多个线程处理读/写操作以模拟重叠I/O。为了协调多个线程,应用程序可以调用WaitCommEvent函数阻塞线程,直至发生特定的通信事件。
写串行口
1 用CreateFile函数返回的句柄。
2 用lpBuffer参数指定要写的数据的指针。这一数据通常是二进制数据或者字符数据。
3 用nNumberOfBytesToWrite参数指定要写的字符数。对于基于Windows CE的设备,通常写一个字符,因为应用程序必须将Unicode 字符转换为ASCII字符,以便将文本传输到串行连接另一端的设备。
4 用lpNumberOfBytesWritten参数指定实际写的字节数的指针。WriteFile函数填充这个变量,以便应用程序能够确定数据是否已 被传输。
5 lpOverlapped参数必须是NULL。
读串行口
ReadFile函数从串行连接另一端的设备获取数据。参数与WriteFile相同。
通常情况下,读操作是一个独立的线程,他总是随时准备处理到达串行口的数据。通信事件通知读线程串行口有数据可读。读线程通常一次读一个字节(每读一个字节调用一次ReadFile函数),直至读完所有数据,然后读线程等待下一个通信事件。

使用通信事件
通信事件是当发生重要事件时Windows CE向应用程序发送的通知:应用程序可以用WaitCommEvent函数阻塞线程,直至特定事件发生;用SetCommMask函数可以指定继续处理前必须发生的事件。如果指定了多个事件,则任何一个指定事件的发生将导致WaitCommEvent函数返回。
例如,这种机制使应用程序能够知道数据何时到达串行口。通过等待表示数据到达的通信事件,应用程序可以避免因调用ReadFile函数等待数据到达而阻塞串行口。只有当有数据可读时才应该调用ReadFile函数。
事件 描述
EV_BREAK 输入中发生中断
EV_CTS CTS信号状态发生变化
EV_DSR DSR信号状态发生变化
EV_ERR 发生线路状态错误,线路状态错误包括CE_FRAME,CE_OVERRUN和CE_RXPARITY
EV_RING 检测到振铃指示
EV_RLSD 接收线路信号检测的信号状态发生变化
EV_RXCHAR 接收到字符,并置于输入缓冲区中
EV_RXFLAG 接收到事件字符,并置于输入缓冲区中
EV_TXEMPTY 输出缓冲区中的最后一个字符已被发送

使用通信事件
1 调用SetCommMask函数指定要查找的事件。
2 调用WaitCommEvent函数,并指定导致这个函数返回的事件。当应用程序指定多个事件时,lpEvtMask参数指向表示导致 WaitCommEvent函数返回的事件的变量。
3 WaitCommEvent函数返回后,循环调用ReadFile函数,直至读完所有接收到的数据。
4 再次调用SetCommMask函数,指定要查找的事件。
SetCommMask函数通常是应用程序在监视串行口和读数据的循环中第一个调用的函数。下面的代码说明了如何将通信事件用于这个目的。
BYTE Byte;
DWORD dwBytesTransferred;

SetCommMask(hPort,EV_RXCHAR|EV_CTS|EV_DSR|EV_RLSD|EV_RING);
while(hPort!=INVALID_HANDLE_VALUE)
{
WaitCommEvent(hPort,&dwCommModemStatus,0);
SetCommMask(hPort,EV_RXCHAR|EV_CTS|EV_DSR|EV_RING);
if(dwCommModemStatus & EV_RXCHAR)
{
do
{
ReadFile(hPort,&Byte,1,&dwBytesTransferred,0);
if(dwBytesTransferred==1)
ProcessChar(Byte);
}while(dwBytesTransferred==1);
}
}

最后关闭串行口
CloseHandle(hPort);