使用Visual C++进行串口通信编程

利用Visual C++在windows环境下设计异步串行通信程序可以使用不同的方法。一种方法可以使用windows系统提供的串行口API函数;另一种方法可以直接 使用Microsoft公司提供的ActiveX控件MSCOMM.OCX。利用MSCOMM.OCX控件进行串行口程序设计相对比较简单,只要对该控件 的属性、事件和方法进行设置和操作,就能完成简单的串行通信功能。而直接使用windows系统提供的串行口API函数则相对较为灵活。试验中,可根据自 己的情况任意其中一种进行编程。以下针对如何使用windows系统提供的串行口API函数进行编程做简要介绍
    在windows系统,串行口和其它通信设备都是作为文件进行处理的。串行口的打开、关闭、发送和接收所用的函数都与操作文件的函数相同。总体来说,利用 Visual C++进行异步串行通信程序设计通常可以分为4个大阶段,它们是串行口打开阶段、串行口状态值读取和属性设置阶段、串行数据的发送与接收阶段,以及串行口 关闭阶段。

(1)    打开串行口

在 对串行口进行所有的操作之前,首先要将其打开。串行口的打开可以使用CreateFile函数,CreateFile函数将返回一个句柄,在随后与该串行 口相关的各种操作中使用。与文件操作相同,在利用CreateFile打开串行口时,也可以将串行口指定为“读访问权限”、“写访问权限”或“读写访问权 限”。

HANDLE CreateFile(

LPCTSTR  lpFileName

DWORD   dwDesiredAccess

DWORD   dwSharedMode

LPSECURITY_ATTRIBUTES  lpSecurityAttributes

DWORD   dwCreationDisposition

DWORD   dwFlagsAndAttributes

HANDLE   hTemplateFile

);

在调用成功时,CreateFile返回打开文件的句柄,该句柄将在以后与该串口相关的各个调用函数中使用。如果调用失败,则CreateFile返回INVALID_HANDLE_VALUE。

(2)    串行口的状态读取和属性设置

一旦将串口打开,就可以对该串口的属性进行设置。由于串口的属性非常复杂,因此通常采用读取该串口当前状态值,然后在此基础上进行修改的方法。

n        获取串行口当前状态

windows系统使用GetCommState函数获取串行口的当前配置,GetCommState的声明如下:

BOOL  GetCommState(

      HANDLE hFile

      LPDCB  lpDCB

);

GetCommState函数的第一个参数hFile是由CreateFile函数返回指向已打开串行口的句柄。第二个参数指向设备控制块DCB。DCB是一个非常重要的数据结构,几乎所有的串行口属性和状态都存储在该结构的成员变量中。

n        对串口进行设置

windows系统利用SetCommState函数修改串行口的当前参数配置。SetCommState函数声明如下:

BOOL  SetCommState(

      HANDLE hFile

      LPDCB  lpDCB

);

GetCommState 函数的第一个参数hFile是由CreateFile函数返回指向已打开串行口的句柄。第二个参数指向设备控制块DCB。如果函数调用成功,则返回值为非 0;若函数调用失败,则返回值为0。当应用程序仅仅需要修改一部分串行口的配置值时,可以通过GetCommState函数获得当前的DCB结构,然后更 改参数,再调用SetCommState函数设置修改过的DCB来配置串行口。

n        为串口分配接收和发送缓冲区

当 一个串行口打开时,可以为该串口分配一个发送缓冲区和一个接收缓冲区。串行口发送缓冲区和接收缓冲区的配置可以由函数SetupComm实现。如果不调用 SetupComm,系统会为该串口分配默认的发送缓冲区和接收缓冲区。但是为了保证缓冲区的大小与实际需要的一致,最好调用该函数进行设置。 SetupComm函数原型如下:

BOOL  SetupComm(

     HANDLE hFile

     DWORD dwInQueue

     DWORD dwOutQueue

);

其中hFile是由CreateFile函数返回指向已打开串行口的句柄。参数dwInQueue和dwOutQueue分别指定应用程序推荐使用的接收缓冲区和发送缓冲区的大小。

n        清空接收和发送缓冲区

在进行串口所有的发送和接收数据操作之前,最好使用PurgeComm函数将串行口发送缓冲区和接收缓冲区中的数据清楚干净。PurgeComm函数原型如下:

BOOL  PurgeComm(

     HANDLE  hFile

     DWORD   dwFlages

);

参 数hFile是由CreateFile函数返回指向已打开串行口的句柄,参数dwFlags指明执行的动作。如果dwFlags为 PURGE_TXCLEAR,则通知系统清空发送缓冲区;如果dwFlags为PURGE_RXCLEAR,则通知系统清空接收缓冲区;如果需要将发送缓 冲区和接收缓冲区全部清空,可以把dwFlags设置为PURGE_TXCLEAR|PURGE_RXCLEAR。如果PurgeComm函数调用成功, 则返回值为非0;若函数调用失败,则返回值为0。

(3)    串行数据的发送和接收

与普通的文件操作相同,在对串行口进行操作时,通常利用ReadFile函数读取串行口收到的数据,利用WriteFile将需要发送的数据写如串行口。

n        串行数据的接收

利用ReadFile函数可以读取将串行口接收到的数据。ReadFile函数原型如下:

BOOL  ReadFile(

HANDLE  hFile

LPVIOD   lpBuffer

DWORD   nNumberOfBytesToRead

LPDWORD   lpNumberOfBytesRead

LPOVERLAPPED  lpOverlapped

);

其 中参数hFile指向已经打开的串行口句柄;lpBuffer指向一个读取数据缓冲区;nNumberOfBytesToRead指定要从串行设备中读取 的字节数;lpNumberOfBytesRead指明实际从串行口中读出的字节数;lpOverlapped指向一个OVERLAPPED结构变量,该 结构变量中包含一个同步事件。

通常如果调用成功,ReadFile返回非0值; 否则返回值为0。但是对于接收操作在后台进行的串口来说,返回值为0不一定说明函数调用失败。此时可以调用GetLastError函数获取进一步的信 息。如果GetLastError返回值为ERROR_IO_PENDING,则说明该读取串口的操作仍然处于后台等待状态,而非一个真正意义上的错误。

n        串行数据的发送

利用WriteFile函数可以向串行口写入数据。WriteFile函数原型如下:

BOOL  WriteFile(

HANDLE  hFile

LPVIOD   lpBuffer

DWORD   nNumberOfBytesToWrite

LPDWORD   lpNumberOfBytesWritten

LPOVERLAPPED  lpOverlapped

);

其 中参数hFile指向已经打开的串行口句柄;lpBuffer指向一个发送数据缓冲区;nNumberOfBytesToRead指定要从串行设备中发送 的字节数;lpNumberOfBytesRead指明实际从串行口中发送的字节数;lpOverlapped指向一个OVERLAPPED结构变量,该 结构变量中包含一个同步事件。

通常如果调用成功,WriteFile返回非0 值;否则返回值为0。但是对于发送操作在后台进行的串口来说,返回值为0不一定说明函数调用失败。此时可以调用GetLastError函数获取进一步的 信息。如果GetLastError返回值为ERROR_IO_PENDING,则说明该写入串口的操作仍然处于后台等待状态,而非一个真正意义上的错 误。

(4)    关闭串行口

在用完串行口后通常要将其关闭。如果忘记关闭,该串口会始终处于打开状态,其它的应用程序就不能打开或使用它。

关闭串口可以使用函数CloseHandle,其函数原型如下:

BOOL  CloseHandle(

    HANDLE  hObject

);

CloseHandle函数非常简单,其中hObject为该打开串口的句柄。如果该函数调用成功,则返回值为非0;否则返回值为0。

 

 

Win32串口操作的技巧

1.开启一个 Serial Port:

  利用一般开启档案的 CreatFile() 即可开启 serial port device用 CreateFile() API.


使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客HANDLE CreateFile(
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                  LPCTSTR lpFileName, // pointer to name of the file
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                  DWORD dwDesiredAccess, // access (read-write) mode
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                  DWORD dwShareMode, // share mode
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                  LPSECURITY_ATTRIBUTES lpSecurityAttributes, // pointer to security attributes
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                  DWORD dwCreationDistribution, // how to create
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                  DWORD dwFlagsAndAttributes, // file attributes
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                  HANDLE hTemplateFile // handle to file with attributes to copy
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                  );
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客
        lpFileName 为 "COM1" 或是 "COM2"
  dwDersiredAccess 一般为 GENERIC_READ|GENERIC_WRITE
  dwShareMode "必须"为 0, 即不能共享, 但同一个 process 中的不同 thread 在一开启之后就可以共享.
  lpSecurityAttributes 一般为 NULL
  dwCreateionDistributon 在这里"必须"为 OPEN_EXISTING
  dwFlagsAndAttributes 定义了开启的属性, 若是设成 FILE_FLAG_OVERLAPPED 则可使用异步的 I/O.
  hTemplateFile "必须"为 NULL
  传回档案 handle
  设定 Serial Port 传送及接收缓冲区的大小

  在开启完 serial port 之后, 可以藉由呼叫 SetupComm() 来进行配置传送时的缓冲区及接收时的缓冲区. 如果没有呼叫 SetupComm() 的话, Win95 会配置内定的缓冲区


使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客BOOL SetupComm(
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客               HANDLE hFile, // handle of communications device
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客               DWORD dwInQueue, // size of input buffer
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客               DWORD dwOutQueue // size of output buffer
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客               );

  2.关闭 Serial Port file

  利用一般的 CloseHandle() 即可.


使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客BOOL CloseHandle(
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                 HANDLE hObject // handle to object to close
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                 )
  3.取得 Seial Port 的信息

  在 Win32 里头, 将一些通讯时会用到的信息用 COMMPROP 这个结构来表示. (当然不仅仅是 Serial Port) 可以用 GetCommProperties() 来取得:


使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客BOOL GetCommProperties(
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                       HANDLE hFile, // handle of communications device
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                       LPCOMMPROP lpCommProp // address of communications properties structure
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                       );
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客
      COMMPROP 长的像这个样子:
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客typedef struct _COMMPROP 使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客{ // cmmp
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    WORD wPacketLength; // packet size, in bytes
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    WORD wPacketVersion; // packet version
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD dwServiceMask; // services implemented
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD dwReserved1; // reserved
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD dwMaxTxQueue; // max Tx bufsize, in bytes
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD dwMaxRxQueue; // max Rx bufsize, in bytes
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD dwMaxBaud; // max baud rate, in bps
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD dwProvSubType; // specific provider type
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD dwProvCapabilities; // capabilities supported
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD dwSettableParams; // changable parameters
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD dwSettableBaud; // allowable baud rates
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    WORD wSettableData; // allowable byte sizes
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    WORD wSettableStopParity; // stop bits/parity allowed
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD dwCurrentTxQueue; // Tx buffer size, in bytes
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD dwCurrentRxQueue; // Rx buffer size, in bytes
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD dwProvSpec1; // provider-specific data
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD dwProvSpec2; // provider-specific data
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    WCHAR wcProvChar[1]; // provider-specific data
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客} COMMPROP;
   在这里, lpCommProp 需要 programmer 自行配置空间. 有趣的问题是, 系统在这个结构之后会需要额外的空间. 但是配置者也就是 programmer 却不知道系统会需要多少. 很简单的做法是配置一大块绝对会够的空间. 另一个聪明的做法是执行两次 GetCommProperties() , 第一次只配置 sizeof(COMMPROP) 这么大的空间, 因为还没有开始执行一些动作, 所以系统并不会尝试着在后面填东西, 所以不会出问题. 接着执行第一次的 GetCommProperties(), 得到结果, 取出结构中的 wPacketLength, 这个 member 代表实际上需要的大小, 然后依据这个大小重新配置一个新的. 这样的话 , 就不会有浪费任何空间的问题了.

  至于上述 COMMPROP 结构的成员所代表的意思, on-line help 中应该写的都满清楚的 .

  4.设定及取得通讯状态

 

  你可以利用 COMMPROP 来取得一些状态, 但是当你想改变目前的设定时你需要两个 API 来完成:


使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客BOOL GetCommState(
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                  HANDLE hFile, // handle of communications device
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                  LPDCB lpDCB // address of device-control block structure
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                  );
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客BOOL SetCommState(
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                  HANDLE hFile, // handle of communications device
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                  LPDCB lpDCB // address of device-control block structure
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                  );

  你可以用 GetCommState() 来取得目前 Serial Port 的状态, 也可以用 SetCommState() 来设定 Serial Port 的状态.

  DCB 的结构就请自行翻阅 help 啰.

  另外, programmer 最常控制的几个设定就是 baud rate, parity method, data bits, 还有 stop bit. BuildCommDCB() 提供了对于这几个常见设定的控制.


使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客BOOL BuildCommDCB(
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                  LPCTSTR lpDef, // pointer to device-control string
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                  LPDCB lpDCB // pointer to device-control block
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                  );
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客
lpDef 长的像这样: "baud=2400 parity=N data=8 stop=1"

  5.通讯设定对话盒

   Win32 API 中提供了一个开启通讯设定对话盒的 API: CommConfigDialog(), 当呼叫这个 API 时, 会蹦现一个可供设定 Baud Rate, Data Bits, Parity .. 等信息的对话盒, programmer 可以利用它来让使用者设定一些信息, 并且取得结果.


使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客BOOL CommConfigDialog(
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                      LPTSTR lpszName, // pointer to device name string
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                      HWND hWnd, // handle to window
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                      LPCOMMCONFIG lpCC // pointer to comm. configuration structure
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                      );
  其中 lpCC 被用来存放设定值的结果.
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客typedef struct _COMM_CONFIG 使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客{
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD dwSize;
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    WORD wVersion;
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    WORD wReserved;
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DCB dcb;
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD dwProviderSubType;
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD dwProviderOffset;
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD dwProviderSize;
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    WCHAR wcProviderData[1];
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客} COMMCONFIG, *LPCOMMCONFIG;

   在我们呼叫 CommConfigDialog() 之前, dwSize 要设为 sizeof(COMMCONFIG), wVersion 的值在这边似乎不重要(我不清楚, VC5 的 on-line help 说可以设为 1, 我手中的 book 的范例是设为 0x100), 呼叫完 CommConfigDialog() 之后, 成员 dcb 中的 BaudRate, ByteSize, StopBits, Parity 就是使用者的设定.

  6.Timeout 的机制

   因为传输时并不会维持一个绝对稳定的速率. 因为传输品质的关系, programer 会需要 timeout 的机制来协助他们做一些控制. 在 Win32 通讯 Timeout 的机制中, timeout 的性质共分为两类, 先来看看 COMMTIMEOUTS 这个结构:


使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客typedef struct _COMMTIMEOUTS 使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客{ // ctmo
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD ReadIntervalTimeout;
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD ReadTotalTimeoutMultiplier;
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD ReadTotalTimeoutConstant;
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD WriteTotalTimeoutMultiplier;
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD WriteTotalTimeoutConstant;
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客} COMMTIMEOUTS,*LPCOMMTIMEOUTS;
  programmer 可以利用 GetCommTimeouts() 和 SetCommTimeouts() 来读取或是设定目前的 timeout 值.
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客BOOL GetCommTimeouts(
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                     HANDLE hFile, // handle of communications device
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                     LPCOMMTIMEOUTS lpCommTimeouts // address of comm. time-outs structure
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                     );
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客BOOL SetCommTimeouts(
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                     HANDLE hFile, // handle of communications device
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                     LPCOMMTIMEOUTS lpCommTimeouts // address of communications time-out structure
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                     );
第 一种 timeout 的机制称为 interval timeout, 从字面上的意义很容易可以理解这种 timeout 的机制是读取字符之间的间隔时间的 timeout, 只有读取字符时才能够使用interval timeout. 也就是在这个结构中的 ReadIntervalTimeout, 单位为 ms, 当读取完一个字符后, 超过了 ReadIntervalTimeout 的值, 却还没有读到下一个字符时, timeout 就发生了.

  第二种 timeout 的机制称为 total timeout, 顾名思义即是传输的总时间的 timeout . 在这种 timeout 的机制下, Win32 提供了一个具有弹性的方式来设定 total timeout. 以读取的 total timeout 为例, 利用 ReadTotalTimeoutMultiplier 和 ReadTotalTimeoutConstant 构成了一个线性的上限值. 什么意思呢? 实际上的 total timeout 应该是这样的一个式子:

  ReadTotalTimeout = ReadTotalTimeOutMultiplier * BytesToRead + ReadTotalTimeoutConstant

  WriteTotalTimeout 用同样的公式来计算. 这样的话, 不仅可以用一个固定的值来做为 timeout 值, 也可以用条线来做为 timeout 的值, 而随着要读取或是要写的 bytes 数而变动.

  如果不想使用 timeout, 就把 COMMTIMEOUTS 里头的资料成员都填为 0.

   如果你将 ReadIntervalTimeout 设为 MAXDWORD, 且将 ReadTotalTimeOutMultiplier 和 ReadTotalTimeoutConstant 都设为 0 的话, 那么读取时, 如果 receive queue 里头并没有资料, 读取的动作将会马上返回, 而不会停滞在读取的动作.

  这里有一个和 BuildCommDCB() 很像的 API 叫 BuildCommDCBAndTimeouts():


使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客BOOL BuildCommDCBAndTimeouts(
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                             LPCTSTR lpDef, // pointer to the device-control string
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                             LPDCB lpDCB, // pointer to the device-control block
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                             LPCOMMTIMEOUTS lpCommTimeouts // pointer to comm. time-out structure
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                             );

   lpDef 一样是控制字符串, 可以给像 BuildCommDCB() 中的 lpDef 那样格式的字符串, 但是多了 "TO=XXX" 这个设定. 如果 "TO=ON", 这个 API 会依据 lpCommTimeouts 里头的值来设定读和写的 timeout 值. 如果 "TO=OFF", 则会设定这个 device 没有 timeout. 如果是 "ON" 和 "OFF" 之外的其它值, 则 lpCommTimeouts 的设定将会被忽略.

  对了, 在设定完 timeout 值之后, 记得要检查 COMMPROP 里的 dwProvCapabilities 中的 PCF_INTTIMEOUTS 和 PCF_TOTALTIMEOUTS 两个 flags 是否有被 set, 以确认 interval timeout 和 total timeout 是否有支持.
  7.读取资料

 

  从 serial port 里头读取资料就跟读取一般的档案一样, 使用 ReadFile() 来达成.


使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客BOOL ReadFile(
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客              HANDLE hFile, // handle of file to read
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客              LPVOID lpBuffer, // address of buffer that receives data
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客              DWORD nNumberOfBytesToRead, // number of bytes to read
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客              LPDWORD lpNumberOfBytesRead, // address of number of bytes read
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客              LPOVERLAPPED lpOverlapped // address of structure for data
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客              );

  要注意的是, nNumberOfBytesToRead 设定的是一次最多的读取量, 很有可能所读取的值(检查 lpNumberOfBytesRead)小于这个值. 通常在错误发生或是 timeout 发生时这个 API 就会返回.

  PurgeComm() 这个 API 可以用来终止目前正在进行的读或写的动作, 也可以 flush 掉 I/O buffer 内等待读或写的资料.

 


使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客BOOL PurgeComm(
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客               HANDLE hFile, // handle of communications resource
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客               DWORD dwFlags // action to perform
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客               );

其中 dwFlags 共有四种 flags:

  PURGE_TXABORT: 终止目前正在进行的(背景)写入动作
  PURGE_RXABORT: 终正目前正在进行的(背景)读取动作
  PURGE_TXCLEAR: flush 写入的 buffer
  PURGE_RXCLEAR: flush 读取的 buffer
  而使用 FlushFileBuffers() 可以确保所有的资料都被送出, 这个 API 才会返回.

  另外一个有趣的 API 是 ClearCommError(), 从字面上的意思看来, 它是用来清除错误情况用的, 但是实际上它还可以拿来取得目前通讯设备的一些信息.

 


使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客BOOL ClearCommError(
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                    HANDLE hFile, // handle to communications device
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                    LPDWORD lpErrors, // pointer to variable to receive error codes
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                    LPCOMSTAT lpStat // pointer to buffer for communications status
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                    );

呼叫这个 API 之后, 关于通讯设备的一些信息会被储存在 lpStat 中, COMSTAT 的结构如下:


使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客typedef struct _COMSTAT 使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客{ // cst
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD fCtsHold : 1; // Tx waiting for CTS signal
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD fDsrHold : 1; // Tx waiting for DSR signal
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD fRlsdHold : 1; // Tx waiting for RLSD signal
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD fXoffHold : 1; // Tx waiting, XOFF char rec'd
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD fXoffSent : 1; // Tx waiting, XOFF char sent
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD fEof : 1; // EOF character sent
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD fTxim : 1; // character waiting for Tx
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD fReserved : 25; // reserved
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD cbInQue; // bytes in input buffer
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客    DWORD cbOutQue; // bytes in output buffer
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客} COMSTAT, *LPCOMSTAT

藉 由 fCtsHold, fDsrHold, fRlsdHold, fXoffHold, fXoffSent 可以知道目前因为什么因素而使通讯阻碍住了.( 跟 handshaking 和 flow control 有关) cbInque 和 cbOutQue 则可以显示出还有多少 bytes 在读取或是写入 queue 中.

  8.写入资料

  和读取资料一样, programmer 可以使用 WriteFile() 来将资料写入 serial port.


使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客BOOL WriteFile(
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客               HANDLE hFile, // handle to file to write to
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客               LPCVOID lpBuffer, // pointer to data to write to file
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客               DWORD nNumberOfBytesToWrite, // number of bytes to write
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客               LPDWORD lpNumberOfBytesWritten, // pointer to number of bytes written
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客               LPOVERLAPPED lpOverlapped // pointer to structure needed for overlapped I/O
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客               );

  关于通讯设备的写入有三个很有趣的 API, 它们分别是 SetCommBreak(), ClearCommBreak, 和 TransmitCommChar().

 


使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客BOOL SetCommBreak(
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                  HANDLE hFile // handle of communications device
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                  );
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客BOOL ClearCommBreak(
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                    HANDLE hFile // handle to communications device
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                    );
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客BOOL TransmitCommChar(
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                      HANDLE hFile, // handle of communications device
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                      char cChar // character to transmit
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                      );

  SetCommBreak() 是用来暂停目前的传输作业, 它会使 buffer 中的资料都不再被送出, 这个时候, program 可以去做些杂七杂八的事, 之后, 再利用 ClearCommBreak() 回复传输作业.

  TransmitCommChar() 是用来立即性的赶在所有 buffer 数据被送出去之前, 传输一个字符的数据出去, 即使 buffer 中还有资料. 换句话说, 这个字符有最高的优先权被送出去.

  9.事件驱动式的 I/O

   在 Win32 里头, 对于通讯设备的 I/O 可以用像是事件驱动式的方法来达成. 主要是利用一个叫 WaitCommEvent() 的 API. 呼叫这个 API 之后, 会一直 block 到设定的事件发生之后才会返回. 我们先来看看如何设定事件, 再回过头来看 WaitCommEvent() .

  programer 可以用 GetCommMask() 和 SetCommMask() 来取得或是设定目前设定的通讯事件.

 


使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客BOOL GetCommMask(
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                 HANDLE hFile, // handle of communications device
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                 LPDWORD lpEvtMask // address of variable to get event mask
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                 );
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客BOOL SetCommMask(
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                 HANDLE hFile, // handle of communications device
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                 DWORD dwEvtMask // mask that identifies enabled events
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                 );

   可以设定的事件有 EV_BREAK, EV_CTS, EV_DSR, EV_ERR, EV_RING, EV_RLSD, EV_RXCHAR, EV_RXFLAG, EV_TXEMPTY.(其意义请自行参考 help), 当然, 你可以把它们 or 起来成为组合的事件.

  在设定完想要处理的事件之后, 可以使用 WaitCommEvent()


使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客BOOL WaitCommEvent(
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                   HANDLE hFile, // handle of communications device
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                   LPDWORD lpEvtMask, // address of variable for event that occurred
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                   LPOVERLAPPED lpOverlapped, // address of overlapped structure
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                   );

  WaitCommEvent() 会一直 block 到你所设定的通讯事件发生为止. 所以当 WaitCommEvent() 返回时, 你可以由 lpEvtMask 取得究竟是那一事件发生, 再来决定要如何处理.

   举例来说, 可以用 SetCommMask() 设定事件为 EV_RXCHAR, 那么在呼叫 WaitCommEvent() 时, 它会等到有字符可供读取时才会返回, 那么在它返回之后, 可以检查一下 lpEvtMask 中是否 set 了 EV_RXCHAR, 如果是的话就可以用 ReadFile() 去读取. 这样的话, 可以避免掉某些情形之下, 需要做 polling 所引起效率不彰的问题.

  10.错误的处理

  前面提过的 ClearnCommError() 可以用来取得目前发生错误的原因.(请参见 help)

  11.硬件的控制命令

  Win32 中提供了 EscapeCommFunction() 允许 programer 对几个硬件讯号做控制.


使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客BOOL EscapeCommFunction(
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                        HANDLE hFile, // handle to communications device
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                        DWORD dwFunc // extended function to perform
使用Visual C++进行串口通信编程 - dhd1999@126 - dhd1999@126的博客                        );

  其中 dwFunc 可以是:

CLRDTR : 让 DTR OFF
CLRRTS : 让 RTS OFF
SETDTR : 让 DTR ON
SETRTS : 让 RTS ON
SETXOFF : "仿真" 接收到 XOFF 字符
SETXON : "仿真" 接收到 XON 字符
SETBREAK : 和 SetCommBreak() 的意思相同
CLRBREAK : 和 ClearCommBreak() 的意思相同

你可能感兴趣的:(编程)