本章将介绍Windows Embedded CE 7.0的串口通信。一些基于Windows CE的设备可以与其他计算机、打印机、调制解调器,或全球定位系统(GPS)卫星通信由串行连接方式。
串行I / O是Windows CE支持的最简单的通信方式。两个设备之间建立一个直接的、一对一的连接。串行I / O可以通过多种方式的硬件连接,但是,大多数基于Windows CE的设备使用串行电缆或PC卡设备,如调制解调器或红外(IR)收发器。串行电缆之间的数据交换类似于文件的读取或写入。
Windows CE支持基于标准的基于Windows桌面的串行通讯函数。这些函数可用于打开、关闭、并操纵串行端口、发送和接收数据,并管理连接。
虽然目前很多设备都没有外置串口,因为有更快更稳定的接口代替了串口。但是基于Windows CE的设备仍然保留着串口,因为目前常用的基于Windows CE的设备都具体导航、电话等功能,而GPS、GSM/GPRS的模块都是外置串口的终端设备。因此,串口仍会作为Windows CE设备中不可缺少的部分。
9.1 设计说明
在Windows CE下的串口编程不同于MS-DOS下的编程。每个Windows CE设备都拥有各自不同的物理内存映射表。即使能够查找到串口设备对应的基地址,也不能直接对寄存器进行编程。因为串行硬件与串口端口不能保证完全兼容,程序对寄存器的操作并不一定能够成功或正确写入到相应的串行硬件中。
一个与串口进行交互的应用程序,涉及到打开串行设备驱动程序以及与其通信。与大部分的现代操作系统一样,Windows CE通过文件系统的一系列API来访问设备驱动程序。串口通信中常用的API既包括了设备基本通信函数,如CreateFile, ReadFile, WriteFile, CloseHandle函数分别用于串口的建立、,数据传输以及关闭,也包括了设备控制函数,如GetCommState/SetCommState,GetCommTimeouts/SetCommTimeouts,GetCommMask/SetCommMask,以及WatiCommEvent函数分别用于串口状态、参数以及事件条件等的查询和设定。
9.2 开启序列通信端口
开启序列通信端口通过调用CreateFile函数来打开。因为硬件供应商和设备驱动程序开发者可以给端口指定任意的名字,应用程序应该列出可用的端口,并允许用户指定需要打开的端口。如果指定的端口不存在,CreateFile函数返回ERROR_FILE_NOT_FOUND,并应通知用户端口不可用。
CreateFile函数的功能在于创建、打开或截断文件、通信资源,磁盘设备或控制台。它返回一个可以用来访问对象的句柄,也可以打开并返回目录的句柄。
CreateFile函数的原型如下:
HANDLE CreateFile(
LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDispostion ,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile);
参数lpFileName是一个指向空结束字符串的指针,这个字符串描述了需要创建或打开的对象的名称(文件,控制台,磁盘设备,通信资源,或目录)。如果参数lpFileName的值是文件路径,那么默认的字符串长度限制为MAX_PATH;如果参数lpFileName指向了通信资源的对象,那么在名称的末尾必须加上“:”,如“COM1:”。
参数dwDesiredAccess指定访问对象的类型。应用程序可以获取读访问,写访问,读写访问,或设备查询的访问。这个参数可以是下列值的任意组合:如表9-1所示
描述 | |
0 | 允许设备对象被查询访问。应用程序可以不用访问设备而能够查询到设备的属性 |
GENERIC_READ | 允许对象被读取访问。可以从文件中读取数据,也可以移动文件指针。结合属性GENERIC_WRITE允许读写访问 |
GENERIC_WRITE | 允许对象被写入访问。可以向文件中写入数据,也可以移动文件指针。结合属性GENERIC_ READ允许读写访问 |
表9-1对象类型说明
参数dwShareMode指定对象被共享的方式。如果dwShareMode是0,对象不能共享。第一次之后打开对象的操作将失败,直到句柄被关闭。这个参数可以是下列值的任意组合:如表9-2所示
值 | 描述 |
FILE_SHARE_READ | 如果请求是读访问,以后每次打开对象的操作会成功。 |
FILE_SHARE_WRITE | 如果请求是写访问,以后每次打开对象的操作会成功。 |
表9-2对象返回值说明
参数lpSecurityAttributes被忽略,值为NULL。
参数dwCreationDispostion指定当文件存在以及不存在时分别采取的动作此参数必须是以下值之一:如表9-3所示。
值 | 描述 |
CREATE_NEW | 创建一个新文件。如果指定的文件已经存在,该函数将失败。 |
CREATE_ALWAYS | 创建一个新文件。如果该文件存在,函数将覆盖该文件,并清除现有的属性。 |
OPEN_EXISTING | 打开该文件。如果该文件不存在,该函数将失败 |
OPEN_ALWAYS | 如果文件存在,则打开它;如果该文件不存在,该函数创建新文件,就像dwCreationDisposition参数的值是 CREATE_NEW |
TRUNCATE_EXISTING | 打开该文件。一旦打开,该文件被截断,因此,它的大小为零字节。调用程序至少需要用GENERIC_WRITE属性来打开文件。如果该文件不存在,该函数将失败 |
表9-3参数说明
参数dwFlagsAndAttributes指定文件的文件属性和标志。下列属性的任意组合都是dwFlagsAndAttributes参数的可选值,除非其他文件属性覆盖FILE_ATTRIBUTE_NORMAL。如表9-4所示。
值 | 描述 |
FILE_ATTRIBUTE_ARCHIVE | 应当归档的文件。应用程序使用此属性标记备份或删除的文件 |
FILE_ATTRIBUTE_HIDDEN | 该文件是隐藏的。它不被普通的目录列表包括 |
FILE_ATTRIBUTE_NORMAL | 文件有没有设置其他的属性。仅当此属性单独使用才有效 |
FILE_ATTRIBUTE_READONLY | 该文件是只读的。应用程序可以读取该文件,但不能写入或删除它 |
FILE_ATTRIBUTE_SYSTEM | 该文件是操作系哦他能够的一部分,或是被操作系统所独占 |
表9-4返回值说明
下列标志的任意组合dwFlagsAndAttributes参数是可以接受的。如表9-5所示。
描述 | |
FILE_FLAG_WRITE_THROUGH | 指示系统对中间缓存执行写穿透策略,直接写入到磁盘。系统仍然可以缓存写入操作,但不能采取懒惰方式将数据刷入。 |
FILE_FLAG_OVERLAPPED | 这个标志不支持,但是,多个读/写设备等候在同一时刻是被允许的。 |
FILE_FLAG_RANDOM_ACCESS | 表示该文件是随机访问。该系统可以作为一个提示使用此优化文件缓存。 |
表9-5返回值说明
参数hTemplateFile被忽视;因此, CreateFile不会复制到新文件的扩展属性。
函数执行成功返回句柄。如果在函数调用之前指定的文件存在,而且参数dwCreationDisposition的值是 CREATE_ALWAYS或OPEN_ALWAYS,调用GetLastError返回ERROR_ALREADY_EXISTS,即使功能已成功。如果文件在调用前不存在,GetLastError返回零。 INVALID_HANDLE_VALUE表示失败。
要打开一个串口,基本的操作如下:
1. 在第一个参数lpzPortName后插入一个冒号。例如,指定“COM1:”作为通信端口。
2. 设置参数dwShareMode的值为零,除非使用的驱动程序允许被打开多次。只有一个设备可以执行读/写或改变端口行为的操作;其他应用程序可以执行例如端口监控,控制线的状态。
3. 设置dwCreationDisposition参数的值为OPEN_EXISTING。这个标志是必需的。
4. 设置dwFlagsAndAttributes参数的值为零。 Windows CE支持只nonoverlapped I / O。
下面的例子给出了如何打开一个序列通信端口:
// Open the serial port.
hPort = CreateFile (lpszPortName, // Pointer to the name of the port
GENERIC_READ | GENERIC_WRITE,
// Access (read-write) mode
0, // Share mode
NULL, // Pointer to the security attribute
OPEN_EXISTING,// How to open the serial port
0, // Port attributes
NULL); // Handle to port with attribute
// to copy
在从端口写入或读取数据前,必须先配置端口。当一个应用程序打开一个端口,它使用默认的配置设置,可能不适合连接的另一端。
9.3 设定序列通信端口
在串行通信编程的最关键的步骤是通过DCB结构体对端口进行设置。错误地初始化DCB结构,是一个普遍问题。当一个串行通信功能没有产生预期的结果,DCB结构可能会犯错。
DCB结构体定义了一个串行通信设备的控制设置。结构体的定义如下:
typedef struct _DCB {
DWORD DCBlength;
DWORD BaudRate;
DWORD fBinary:1;
DWORD fParity:1;
DWORD fOutxCtsFlow:1;
DWORD fOutxDsrFlow:1;
DWORD fDtrControl:2;
DWORD fDsrSensitivity:1;
DWORD fTXContinueOnXoff:1;
DWORD fOutX: 1;
DWORD fInX: 1;
DWORD fErrorChar:1;
DWORD fNull:1;
DWORD fRtsControl:2;
DWORD fAbortOnError:1;
DWORD fDummy2:17;
WORD wReserved;
WORD XonLim;
WORD XoffLim;
BYTE ByteSize;
BYTE Parity;
BYTE StopBits;
char XonChar;
char XoffChar;
char ErrorChar;
char EofChar;
char EvtChar;
WORD wReserved1;
} DCB;
DCB结构体涵括了与一个串行通信设备相关的所有基本参数与信息,例如数据传输速度、数据包的格式、传输协议、流量控制以及信号处理等。
调用CreateFile函数利用默认端口设置来打开一个串行端口。通常,应用程序需要更改默认值。使用GetCommState函数来检索的默认设置,使用SetCommState函数来设置新的端口设置。GetCommState函数的原型为:
BOOL GetCommState(
HANDLE hFile,
LPDCB lpDCB);
SetCommState函数的原型为:
BOOL SetCommState(
HANDLE hFile,
LPDCB lpDCB);
两个函数的参数相同。参数hFile代表进行通信的设备的句柄,而lpDCB是存放或写入DCB的结构体。
此外,端口配置使用COMMTIMEOUTS结构设置读/写操作的超时值。当发生超时,ReadFile或WriteFile函数返回成功传输的字符的具体数目。
设定串口的步骤如下:
1. 初始化DCB结构体DCBlength的成员结构的大小。在使用DCB结构体之前,DCBlength成员的值必须完成初始化。DCB结构必须是完整的,包括硬件和软件流控制。
2. 调用GetCommState函数来查询用CreateFile函数打开的端口的默认设置。利用CreateFile函数返回的句柄来识别端口。
3. 根据需要修改DCB结构体成员的值。下表显示了DCB结构成员。如表9-6所示。
成员 | 描述 |
DCBlength | 调用GetCommState函数之前,利用这个成员来设置DCB结构的长度。不设置正确的长度,可能会导致失败或返回错误的数据。 |
BaudRate | 指定设备的通讯速率。成员的值或者是实际的波特率,或者是指向CBR_constan常量的索引。 |
fBinary | 指定是否启用二进制模式。 Win32 API中不支持非二进制模式传输,因此成员的值必须为TURE,使用FALSE将无法工作。 |
fParity | 指定是否启用奇偶校验。如果这个成员是TRUE,执行奇偶校验,并报告错误。 |
fOutxCtsFlow | 打开或关闭CTS流量控制开关。要使用RTS / CTS流量控制,该成员的值指定为TRUE,而且指定fRtsControl成员的值为RTS_CONTROL_HANDSHAKE TRUE。 |
fOutxDsrFlow | 打开或关闭DSR流控制开关。 DSR流控制很少使用;在使能DTR线时,该成员一般被设置为FALSE。 |
fDtrControl | 设定DTR流量控制。 DTR_CONTROL_ENABL打开DTR线; DTR_CONTROL_HANDSHAKE打开DTR握手; DTR_CONTROL_DISABLE关闭DTR线。 |
fDsrSensitivity | 在通讯驱动程序对DSR信号状态比较敏感时,设置这个成员。如果这个成员为TRUE,则驱动程序忽略任何接收字节,除非DSR modem输入线为高。 |
fTXContinueOnXoff | 在输入缓冲区已满时,而且驱动已经发送了XoffChar字符,该成员指定是否停止传输。 |
fOutX | 指定是否在传输过程中使用XON/ XOFF流量控制。如果这个成员是TRUE,在接收到XoffChar字符时传输停止;当接收到XonChar字符时,再次启动时传输。 |
fInX | 指定是否在接受过程中使用XON/ XOFF流量控制。如果这个成员是TRUE,当输入缓冲区内剩余XoffLim字节时,发送XoffChar字符;当输入缓冲区内只剩XonLim字节时,发送XonChar字符。 |
fErrorChar | 指定是否将发生奇偶校验错误的字节替换为ErrorChar成员指定的字符。如果这个成员是TRUE而且fParity成员为TRUE,允许替换。 |
fNull | 指定是否丢弃空字节。 |
fRtsControl | 打开或关闭RTS流控制。 RTS_CONTROL_ENABLE在连接过程中打开RTS线; RTS_CONTROL_HANDSHAKE打开RTS握手; RTS_CONTROL_DISABLE轮流关闭RTS线。;RTS_CONTROL_TOGGLE在可以传输数据时指定RTS线为高。如果所有缓冲的字节都被发送,RTS线设置为低。 |
fAbortOnError | 指定在出错错误时,是否终止读取和写入操作。如果这个成员是TRUE,驱动程序在出现错误的时候将终止所有读写操作。驱动程序将不接受任何通信,直至应用程序调用ClearCommError函数进行确认。 |
fDummy2 | 保留,不可用。 |
wReserved | 不可用,设置为0。 |
XonLim | 指定发送XON字符之前,输入缓冲区中最少的字节数。 |
XoffLim | 指定发送XOFF字符之前,输入缓冲区中最多能接收的字节数。 |
ByteSize | 指定发送和接收的字节所包含的位数。 |
Parity | 指定的奇偶机制。不要与打开或关闭奇偶校验的fParity成员混淆。因为很少使用奇偶,这个成员通常设置为NOPARITY。 |
StopBits | 指定停止位的数目。 ONESTOPBIT是最常用的设置。 |
XonChar | 指定传输和接收时XON字符的值。 |
XoffChar | 指定传输和接收时XOFF字符的值。 |
ErrorChar | 指定了在发生奇偶错误用于替换的字符的值。 |
EofChar | 指定了用于标识数据结束的字符的值。 |
EvtChar | 指定了用于标识发生事件的字符的值。 |
WReserved1 | 保留,不可用。 |
表9-6 DCB结构说明
4. 调用SetCommState函数设置新的端口设置。
下面的例子给出了如何设定序列通信端口:
DCB PortDCB;
// Initialize the DCBlength member.
PortDCB.DCBlength = sizeof (DCB);
// Get the default port setting information.
GetCommState (hPort, &PortDCB);
// Change the DCB structure settings.
PortDCB.BaudRate = 9600; // Current baud
PortDCB.fBinary = TRUE; // Binary mode; no EOF check
PortDCB.fParity = TRUE; // Enable parity checking
PortDCB.fOutxCtsFlow = FALSE; // No CTS output flow control
PortDCB.fOutxDsrFlow = FALSE; // No DSR output flow control
PortDCB.fDtrControl = DTR_CONTROL_ENABLE;
// DTR flow control type
PortDCB.fDsrSensitivity = FALSE; // DSR sensitivity
PortDCB.fTXContinueOnXoff = TRUE; // XOFF continues Tx
PortDCB.fOutX = FALSE; // No XON/XOFF out flow control
PortDCB.fInX = FALSE; // No XON/XOFF in flow control
PortDCB.fErrorChar = FALSE; // Disable error replacement
PortDCB.fNull = FALSE; // Disable null stripping
PortDCB.fRtsControl = RTS_CONTROL_ENABLE;
// RTS flow control
PortDCB.fAbortOnError = FALSE; // Do not abort reads/writes on
// error
PortDCB.ByteSize = 8; // Number of bits/byte, 4-8
PortDCB.Parity = NOPARITY; // 0-4=no,odd,even,mark,space
PortDCB.StopBits = ONESTOPBIT; // 0,1,2 = 1, 1.5, 2
// Configure the port according to the specifications of the DCB
// structure.
if (!SetCommState (hPort, &PortDCB))
{
// Could not configure the serial port.
MessageBox (hMainWnd, TEXT("Unable to configure the serial port"),
TEXT("Error"), MB_OK);
dwError = GetLastError ();
return FALSE;
}