VC实现串口通信例程 作者:阮帮秋

    技术2022-05-11  125

    VC实现串口通信例程 作者:阮帮秋 发布时间:2001/04/19
     
    文章摘要:
      WIN95界面下的VC++串口通讯程序在WIN32下是不建议对端口进行操作的,在WIN32中所有的设备都被看成是文件,串行口也不例外也是作为文件来进行处理的。  关键词 串行口,DWORD,缓冲区
           

    正文: 

    VC实现串口通信例程 

      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 在发送中清除中止

    作者会员名:ruan_bangqiu

     

    最新回复(0)