VC实现串口通信例程

本文介绍使用Visual C++在Windows 95环境下进行串口通信编程的方法。内容覆盖串口的打开与关闭、缓冲区设置、串口属性配置、不同读写数据模式等关键步骤。

VC实现串口通信例程

摘要:WIN95界面下的VC++串口通讯程序在WIN32下是不建议对端口进行操作的,在WIN32中所有的设备都被看成是文件,串行口也不例外也是作为文件来进行处理的。
 关键词 串行口,DWORD,缓冲区 


  WIN95界面下的VC++串口通讯程序在WIN32下是不建议对端口进行操作的,在WIN32中所有的设备都被看成是文件,串行口也不例外也是作为文件来进行处理的。这是我的一份关于串口编程的读书笔记,对于使 用VC进行编程的同行应该有一定的帮助。

1.打开串口:

  在Window 95下串行口作为文件处理,使用文件操作对串行口进行处理。使用CreateFile()打开串口,CreateFile()将返回串口的句柄。
  HANDLE CreateFile(
  LPCTSTR lpFileName, // pointer to name of the file
  DWORD dwDesiredAccess, // access (read-write) mode
  DWORD dwShareMode, // share mode
  LPSECURITY_ATTRIBUTES lpSecurityAttributes, // pointer to security attributes
  DWORD dwCreationDistribution, // how to create
  DWORD dwFlagsAndAttributes, // file attributes
  HANDLE hTemplateFile // handle to file with attributes to copy
  );
  lpFileName: 指明串口制备,例:COM1,COM2
  dwDesiredAccess: 指明串口存取方式,例:GENERIC_READ|GENERIC_WRITE
  dwShareMode: 指明串口共享方式
  lpSecurityAttributes: 指明串口的安全属性结构,NULL为缺省安全属性
  dwCreateionDistribution: 必须为OPEN_EXISTIN
  dwFlagAndAttributes: 对串口唯一有意义的是FILE_FLAG_OVERLAPPED
  hTemplateFile: 必须为NULL

2.关闭串口:

  CloseHandle(hCommDev);

3.设置缓冲区长度:

  BOOL SetupComm(
  HANDLE hFile, // handle of communications device
  DWORD dwInQueue, // size of input buffer
  DWORD dwOutQueue // size of output buffer
  );

4.COMMPROP结构:

  可使用GetCommProperties()取得COMMPROP结构,COMMPROP结构中记载了系统支持的各项设置。
  typedef struct _COMMPROP { // cmmp
  WORD wPacketLength; // packet size, in bytes
  WORD wPacketVersion; // packet version
  DWORD dwServiceMask; // services implemented
  DWORD dwReserved1; // reserved
  DWORD dwMaxTxQueue; // max Tx bufsize, in bytes
  DWORD dwMaxRxQueue; // max Rx bufsize, in bytes
  DWORD dwMaxBaud; // max baud rate, in bps
  DWORD dwProvSubType; // specific provider type
  DWORD dwProvCapabilities; // capabilities supported
  DWORD dwSettableParams; // changeable parameters
  DWORD dwSettableBaud; // allowable baud rates
  WORD wSettableData; // allowable byte sizes
  WORD wSettableStopParity; // stop bits/parity allowed
  DWORD dwCurrentTxQueue; // Tx buffer size, in bytes
  DWORD dwCurrentRxQueue; // Rx buffer size, in bytes
  DWORD dwProvSpec1; // provider-specific data
  DWORD dwProvSpec2; // provider-specific data
  WCHAR wcProvChar[1]; // provider-specific data
  } COMMPROP;
  dwMaxBaud:
  BAUD_075 75 bps
  BAUD_110 110 bps
  BAUD_134_5 134.5 bps
  BAUD_150 150 bps
  BAUD_300 300 bps
  BAUD_600 600 bps
  BAUD_1200 1200 bps
  BAUD_1800 1800 bps
  BAUD_2400 2400 bps
  BAUD_4800 4800 bps
  BAUD_7200 7200 bps
  BAUD_9600 9600 bps
  BAUD_14400 14400 bps
  BAUD_19200 19200 bps
  BAUD_38400 38400 bps
  BAUD_56K 56K bps
  BAUD_57600 57600 bps
  BAUD_115200 115200 bps
  BAUD_128K 128K bps
  BAUD_USER Programmable baud rates available
  dwProvSubType:
  PST_FAX 传真设备
  PST_LAT LAT协议
  PST_MODEM 调制解调器设备
  PST_NETWORK_BRIDGE 未指定的网桥
  PST_PARALLELPORT 并口
  PST_RS232 RS-232口
  PST_RS422 RS-422口
  PST_RS423 RS-432口
  PST_RS449 RS-449口
  PST_SCANNER 扫描仪设备
  PST_TCPIP_TELNET TCP/IP Telnet协议
  PST_UNSPECIFIED 未指定
  PST_X25 X.25标准
  dwProvCapabilities
  PCF_16BITMODE 支持特殊的16位模式
  PCF_DTRDSR 支持DTR(数据终端就绪)/DSR(数据设备就绪)
  PCF_INTTIMEOUTS 支持区间超时
  PCF_PARITY_CHECK 支持奇偶校验
  PCF_RLSD 支持RLSD(接收线信号检测)
  PCF_RTSCTS 支持RTS(请求发送)/CTS(清除发送)
  PCF_SETXCHAR 支持可设置的XON/XOFF
  PCF_SPECIALCHARS 支持特殊字符
  PCF_TOTALTIMEOUTS 支持总(占用时间)超时
  PCF_XONXOFF 支持XON/XOFF流控制
  标准RS-232和WINDOW支持除PCF_16BITMODE和PCF_SPECIALCHAR外的所有功能
  dwSettableParams
  SP_BAUD 可配置波特率
  SP_DATABITS 可配置数据位个数
  SP_HANDSHAKING 可配置握手(流控制)
  SP_PARITY 可配置奇偶校验模式
  SP_PARITY_CHECK 可配置奇偶校验允许/禁止
  SP_RLSD 可配置RLSD(接收信号检测)
  SP_STOPBITS 可配置停止位个数
  标准RS-232和WINDOW支持以上所有功能
  wSettableData
  DATABITS_5 5个数据位
  DATABITS_6 6个数据位
  DATABITS_7 7个数据位
  DATABITS_8 8个数据位
  DATABITS_16 16个数据位
  DATABITS_16X 通过串行硬件线路的特殊宽度路径
  WINDOWS 95支持16的所有设置

5.DCB结构:

  typedef struct _DCB {// dcb
  DWORD DCBlength; // sizeof(DCB)
  DWORD BaudRate; // current baud rate
  指定当前的波特率
  DWORD fBinary: 1; // binary mode, no EOF check
  指定是否允许二进制模式,
  WINDOWS 95中必须为TRUE
  DWORD fParity: 1; // enable parity checking
  指定奇偶校验是否允许
  DWORD fOutxCtsFlow:1; // CTS output flow control
  指定CTS是否用于检测发送控制。
  当为TRUE是CTS为OFF,发送将被挂起。
  DWORD fOutxDsrFlow:1; // DSR output flow control
  指定CTS是否用于检测发送控制。
  当为TRUE是CTS为OFF,发送将被挂起。
  DWORD fDtrControl:2; // DTR flow control type
  DTR_CONTROL_DISABLE值将DTR置为OFF, DTR_CONTROL_ENABLE值将DTR置为ON, DTR_CONTROL_HANDSHAKE允许DTR"握手",DWORD fDsrSensitivity:1; // DSR sensitivity 当该值为TRUE时DSR为OFF时接收的字节被忽略
  DWORD fTXContinueOnXoff:1; // XOFF continues Tx
  指定当接收缓冲区已满,并且驱动程序已经发
  送出XoffChar字符时发送是否停止。
  TRUE时,在接收缓冲区接收到缓冲区已满的字节XoffLim且驱动程序已经发送出XoffChar字符中止接收字节之后,发送继续进行。
  FALSE时,在接收缓冲区接收到代表缓冲区已空的字节XonChar且驱动程序已经发送出恢复发送的XonChar之后,发送继续进行。
  DWORD fOutX: 1; // XON/XOFF out flow control
  TRUE时,接收到XoffChar之后便停止发送
  接收到XonChar之后将重新开始
  DWORD fInX: 1; // XON/XOFF in flow control
  TRUE时,接收缓冲区接收到代表缓冲区满的XoffLim之后,XoffChar发送出去
  接收缓冲区接收到代表缓冲区空的XonLim之后,XonChar发送出去
  DWORD fErrorChar: 1; // enable error replacement
  该值为TRUE且fParity为TRUE时,用ErrorChar 成员指定的字符代替奇偶校验错误的接收字符
  DWORD fNull: 1; // enable null stripping
  TRUE时,接收时去掉空(0值)字节
  DWORD fRtsControl:2; // RTS flow control
  RTS_CONTROL_DISABLE时,RTS置为OFF
  RTS_CONTROL_ENABLE时, RTS置为ON
  RTS_CONTROL_HANDSHAKE时,
  当接收缓冲区小于半满时RTS为ON
  当接收缓冲区超过四分之三满时RTS为OFF
  RTS_CONTROL_TOGGLE时,
  当接收缓冲区仍有剩余字节时RTS为ON ,否则缺省为OFF
  DWORD fAbortOnError:1; // abort reads/writes on error
  TRUE时,有错误发生时中止读和写操作
  DWORD fDummy2:17; // reserved
  未使用
  WORD wReserved; // not currently used
  未使用,必须为0
  WORD XonLim; // transmit XON threshold
  指定在XON字符发送这前接收缓冲区中可允许的最小字节数
  WORD XoffLim; // transmit XOFF threshold
  指定在XOFF字符发送这前接收缓冲区中可允许的最小字节数
  BYTE ByteSize; // number of bits/byte, 4-8
  指定端口当前使用的数据位
  BYTE Parity; // 0-4=no,odd,even,mark,space
  指定端口当前使用的奇偶校验方法,可能为:
  EVENPARITY,MARKPARITY,NOPARITY,ODDPARITY
  BYTE StopBits; // 0,1,2 = 1, 1.5, 2
  指定端口当前使用的停止位数,可能为:
  ONESTOPBIT,ONE5STOPBITS,TWOSTOPBITS
  char XonChar; // Tx and Rx XON character
  指定用于发送和接收字符XON的值
  char XoffChar; // Tx and Rx XOFF character
  指定用于发送和接收字符XOFF值
  char ErrorChar; // error replacement character
  本字符用来代替接收到的奇偶校验发生错误时的值
  char EofChar; // end of input character
  当没有使用二进制模式时,本字符可用来指示数据的结束
  char EvtChar; // received event character
  当接收到此字符时,会产生一个事件
  WORD wReserved1; // reserved; do not use 未使用
  } DCB;

6.改变端口设置

  使用如下的两个方法
  BOOL GetCommState(hComm,&dcb);
  BOOL SetCommState(hComm,&dcb);

7.改变普通设置

  BuildCommDCB(szSettings,&DCB);
  szSettings的格式:baud parity data stop
  例: "baud=96 parity=n data=8 stop=1"
  简写:"96,N,8,1"
  szSettings 的有效值
  baud:
  11 or 110 = 110 bps
  15 or 150 = 150 bps
  30 or 300 = 300 bps
  60 or 600 = 600 bps
  12 or 1200 = 1200 bps
  24 or 2400 = 2400 bps
  48 or 4800 = 4800 bps
  96 or 9600 = 9600 bps
  19 or 19200= 19200bps
  parity:
  n=none
  e=even
  o=odd
  m=mark
  s=space
  data:
  5,6,7,8
  StopBit
  1,1.5,2

8.COMMCONFIG结构:

  typedef struct _COMM_CONFIG {
  DWORD dwSize;
  WORD wVersion;
  WORD wReserved;
  DCB dcb;
  DWORD dwProviderSubType;
  DWORD dwProviderOffset;
  DWORD dwProviderSize;
  WCHAR wcProviderData[1];
  } COMMCONFIG, *LPCOMMCONFIG;
  可方便的使用BOOL CommConfigDialog(
  LPTSTR lpszName,
  HWND hWnd,
  LPCOMMCONFIG lpCC);
  来设置串行口。

9.超时设置:

  可通过COMMTIMEOUTS结构设置超时,
  typedef struct _COMMTIMEOUTS {
  DWORD ReadIntervalTimeout;
  DWORD ReadTotalTimeoutMultiplier;
  DWORD ReadTotalTimeoutConstant;
  DWORD WriteTotalTimeoutMultiplier;
  DWORD WriteTotalTimeoutConstant;
  } COMMTIMEOUTS,*LPCOMMTIMEOUTS;
  区间超时仅对从端口中读取数据有用)它指定在读取两个字符之间要经历的时间
  总超时: 当读或写特定的字节数需要的总时间超过某一阈值时,超时触发.
  超时公式:
  ReadTotalTimeout = (ReadTotalTimeoutMultiplier * bytes_to_read)
  + ReadToTaltimeoutConstant
  WriteTotalTimeout = (WriteTotalTimeoutMuliplier * bytes_to_write)
  + WritetoTotalTimeoutConstant
  NOTE:在设置超时时参数0为无限等待,既无超时
  参数MAXDWORD为立即返回
  超时设置:
  GetCommTimeouts(hComm,&timeouts);
  SetCommTimeouts(hComm,&timeouts);

10.查询方式读写数据

  例程:
  COMMTIMEOUTS to;
  DWORD ReadThread(LPDWORD lpdwParam)
  {
  BYTE inbuff[100];
  DWORD nBytesRead;
  if(!(cp.dwProvCapabilities&PCF_INTTIMEOUTS))
  return 1L;
  memset(&to,0,sizeof(to));
  to.ReadIntervalTimeout = MAXDWORD;
  SetCommTimeouts(hComm,&to);
  while(bReading)
  {
  if(!ReadFile(hComm,inbuff,100,&nBytesRead,NULL))
  locProcessCommError(GetLastError());
  else
  if(nBytesRead)
  locProcessBytes(inbuff,nBytesRead);
  }
  PurgeComm(hComm,PURGE_RXCLEAR);
  return 0L;
  }
  NOTE:
  PurgeComm()是一个清除函数,它可以中止任何未决的后台读或写,并且可以冲掉I/O缓冲区.
  BOOL PurgeComm(HANDLE hFile,DWORD dwFlags);
  dwFlages的有效值:
  PURGE_TXABORT: 中止后台写操作
  PRUGE_RXABORT: 中止后台读操作
  PRUGE_TXCLEAR: 清除发送缓冲区
  PRUGE_RXCLEAR: 清除接收缓冲区
  技巧:
  可通过ClearCommError()来确定接收缓区中处于等待的字节数。
  BOOL ClearCommError(
  HANDLE hFile, // handle to communications device
  LPDWORD lpErrors, // pointer to variable to receive error codes
  LPCOMSTAT lpStat // pointer to buffer for communications status
  );
  ClearCommError()将返回一个COMSTAT结构:
  typedef struct _COMSTAT { // cst
  DWORD fCtsHold : 1; // Tx waiting for CTS signal
  DWORD fDsrHold : 1; // Tx waiting for DSR signal
  DWORD fRlsdHold : 1; // Tx waiting for RLSD signal
  DWORD fXoffHold : 1; // Tx waiting, XOFF char rec`d
  DWORD fXoffSent : 1; // Tx waiting, XOFF char sent
  DWORD fEof : 1; // EOF character sent
  DWORD fTxim : 1; // character waiting for Tx
  DWORD fReserved : 25; // reserved
  DWORD cbInQue; // bytes in input buffer
  DWORD cbOutQue; // bytes in output buffer
  } COMSTAT, *LPCOMSTAT;
  其中的cbInQue和cbOutQue中即为缓冲区字节。

11.同步I/O读写数据

  COMMTIOMOUTS to;
  DWORD ReadThread(LPDWORD lpdwParam)
  {
  BYTE inbuff[100];
  DWORD nByteRead,dwErrorMask,nToRead;
  COMSTAT comstat;
  if(!cp.dwProvCapabilities&PCF_TOTALTIMEOUTS)
  return 1L;
  memset(&to,0,sizeof(to));
  to.ReadTotalTimeoutMultiplier = 5;
  to.ReadTotalTimeoutConstant = 50;
  SetCommTimeouts(hComm,&to);
  while(bReading)
  {
  ClearCommError(hComm,&dwErrorMask,&comstat);
  if(dwErrorMask)
  locProcessCommError(dwErrorMask);
  if(comstat.cbInQue >100)
  nToRead = 100;
  else
  nToRead = comstat.cbInQue;
  if(nToRead == 0)
  continue;
  if(!ReadFile(hComm,inbuff,nToRead,&nBytesRead,NULL))
  locProcessCommError(GetLastError());
  else
  if(nBytesRead)
  locProcessBytes(inbuff,nBytesRead);
  }
  return 0L;
  }

12.异步I/O读写数据

  当CreateFile()中的fdwAttrsAndFlags参数为FILE_FLAG_OVERLAPPEN时, 端口是为异步I/O打开的,此时可以在ReadFile的最后一个参数中指定一个OVERLAPPED结构,使数据的读操作在后台进行。WINDOWS 95包括了异步I/O的许多变种。
  typedef struct _OVERLAPPED {
  DWORD Internal;
  DWORD InternalHigh;
  DWORD Offset;
  DWORD OffsetHigh;
  HANDLE hEvent;
  } OVERLAPPED;
  对于串行口仅hEvent成员有效,其于成员必须为0。
  例程:
  COMMTIMEOUTS to;
  ...
  DWORD ReadThread((LPDWORD lpdwParam)
  {
  BYTE inbuff[100];
  DWORD nRytesRead,endtime,lrc;
  static OVERLAPPED o;
  if(!cp.dwProvCapabilities & PCF_TOTALTIMEOUTS)
  return 1L;
  memset(&to,0,sizeof(to));
  to.ReadTotalTimeoutMultiplier = 5;
  to.ReadTotalTimeoutConstant = 1000;
  SetCommTimeouts(hComm,&to);
  o.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
  while(bReading)
  {
  if(!ReadFile(hComm,inbuff,10,&nBytesRead,&o))
  {
  nBytesRead = 0;
  if(lrc=GetLastError() == ERROR_IO_PENDING)
  {
  endtime = GetTickCount() + 1000;
  while(!GetOverlappedResult(hComm,&o,&nBytesRead,FALSE))
  if(GetTickCount() > endtime) break;
  }
  if(nBytesRead) locProcessBytes(inbuff,nBytesRead);
  }
  else
  {
  if(nBytesRead) locProcessBytes(inbuff,nBytesRead);
  ResetEvent(o.hEvent);
  }
  }
  PurgeComm(hComm,PURGE_RXCLEAR);
  return 0L;
  }
  这一例程是对一开始读缓冲区就读到所需的字节时的处理:
  while(bReading)
  {
  if(!ReadFile(hComm,inbuff,10,&nBytesRead,&o))
  {
  if((lrc=GetLastError()) ==ERROR_IO_PENDING)
  {
  if(GetOverlappedResult(hComm,&o,&nBytesRead,TRUE))
  {
  if(nBytesRead)
  locProcessBytesa(inbuff,nBytesRead);
  }
  else
  locProcessCommError(GetLastError());
  }
  else
  locProcessCommError(GetLastError));
  }
  else
  if(nBytesRead) locProcessBytes(inbuff,nBytesRead);
  ResetEvent(o.hEvent);
  }

13.事件驱I/O读写:

  GetCommMask(hComm,&dwMask)
  Windows 95报告给应用程序的事件由此方法返回。
  SetCommMasl(hComm,&dwMask)
  添加或修改Windows 95所报告的事件列表。
  事件掩码如下:
  EV_BREAK 检测到输入为止
  EV_CTS CTS(清除发送)信号改变状态
  EV_DSR DSR(数据设置就绪)信号改变状态
  EV_ERR 发生了线路状态错误.
  线路状态错误为:
  CE_FRAME(帧错误)
  CE_OVERRUN(接收缓冲区超限)
  CE_RXPARITY(奇偶校验错误)
  EV_RING 检测到振铃
  EV_RLSD RLSD(接收线路信号检测)信号改变状态
  EV_EXCHAR 接收到一个字符,并放入输入缓冲区
  EV_RXFLAG 接收到事件字符(DCB成员的EvtChar成员),度放入输入缓冲区
  EV_TXEMPTY 输出缓冲区中最后一个字符发送出去
  在用SetCommMask指定了有用的事件后,应用程序可调用WaitCommEvent()来等待事件发生.
  BOOL WaitCommEvent(
  HANDLE hFile, // handle of communications device
  LPDWORD lpEvtMask, // address of variable for event that occurred
  LPOVERLAPPED lpOverlapped, // address of overlapped structure
  );
  此方法可以以同步或异步方式操作
  例程:
  COMMTIMEOUTS to;
  ...
  DWORD ReadTherad(LPDWORD lpdwParam)
  {
  BYTE binbuff[100];
  DWORD nBytesRead,dwEvent,dwError;
  COMSTAT cs;
  SetCommMask(hComm,EV_RXHAR);
  while(bReading)
  {
  if(WaitCommEvent(hComm,&dwEvent,NULL))
  {
  ClearCommError(hComm,&dwError,&cs);
  if((dwEvent&EV_RXCHAR)&&cs.cbInQue)
  {
  if(!ReadFile(hComm,inbuff,cs.cbInQue,&nBytesRead,NULL)
  locProcessCommError(GetLastError());
  }
  else
  {
  if(nByteRead)
  locProcessBytes(inbuff,nBytesRead);
  }
  else
  locProcessCommError(GetLastError());
  }
  PurgeComm(hComm,PURGE_RXCLEAR);
  return 0L;
  }
  NOTE: SetCommMask(hComm,0)可使WaitCommEvent()中止.
  可使用GetCommmodemStatus()方法,例程:
  if(cp.dwProvCapabilities&PCF_RTSCTS)
  {
  SetCommMask(hComm,EV_CTS);
  WaitCommEvent(hComm,&dwMask,NULL);
  if(dwMask&EV_CTS)
  {
  GetCommModemStatus(hComm,&dwStatus)
  if(dwStatus&MS_CTS_ON) /* CTS stransition OFF-ON */
  else /* CTS stransition ON-OFF */
  }
  }
  MS_CTS_ON CTS为ON
  MS_DSR_ON DSR为ON
  MS_RING_ON RING为ON
  MS_ELSD_ON RLSD为ON

14.错误

  当发生错误时应用方法ClearCommError(hComm,&dwErrorMask,&constat)得到错误掩码。
  CE_BREAK 中止条件
  CE_FRAME 帧错误
  CW_IOE 一般I/O错误,常伴有更为详细的错误标志
  CE_MODE 不支持请求的模式
  CE_OVERRUN 缓冲区超限下一个字符将丢失
  CE_RXOVER 接收缓冲区超限
  CE_RXPARITY 奇偶校验错误
  CE_TXFULL 发送缓冲区满
  CE_DNS 没有选择并行设备
  CE_PTO 并行设备发生超时
  CE_OOP 并行设备缺纸

15.控制命令

  EscapeCommFunction()可将硬件信号置ON或OFF,模拟XON或XOFF

  BOOL EscapeCommFunction(
  HANDLE hFile, // handle to communications device
  DWORD dwFunc // extended function to perform
  );
  dwFunc的有效值(可用'|'同时使用多个值)
  CLRDTR DTR置OFF
  CLRRTS RTS置OFF
  SETDTR STR置ON
  SETRTS TRS置ON
  SETXOFF 模拟XOFF字符的接收
  SETXON 模拟XON字符的接收
  SETBREAK 在发送中产生一个中止
  CLRBREAK 在发送中清除中止
 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值