一个由印度人编写的VC串口类(也是一种VC串口控件),他还配合这个类写了VC 串口通信方面的一些基础知识,如怎么用VC打开串口,如何对串口进行配置,读串口、写串口等。
这个类有点特别,它没有使用事件驱动原理,它是以查询方式工作的。
简介:
对没有接触过串口通信的VC程序员来说显得非常困难,很久以前我在 codeguru.com 上搜索过串口通信相关信息得到了非常大的帮助,从那时起能编写一个简单易用的VC 串口类是我的梦想。
经过七个月在串口通信编程方面实践经验后,我编写了一个基于API实现的简单串口类,在介绍此串口类之前先介绍一下VC 串口通信方面的基础知识。
串口通信基础:
串口通信每个字节的传输以串行的方式进行,传输时低位先被发送,一个“包”由“开始位”+“数据位”+“奇偶校验位”(不是必需)+“停止位” 组成。
奇偶校验位是可选的,它用来进行错误检测,您可以在软件里设置是否启用奇偶校验,而且还可以选择启用哪种校验方式,如“奇”校验(ODD)或“偶”校验(EVEN)。
PC机通过串口发送和接收数据的流程如下:
1、 | 打开串口 |
|
2、 | 配置串口通信参数,如:波特率、校验方式、数据位数等 | |
3、 | 设置通信超时时间 | |
4、 | 写数据 | |
5、 | 读数据 | |
6、 | 关闭串口 |
用VC打开串口:
打开串口可以用API函数 CreateFile() 来实现,打开串口有两种方式,分别为重叠I/O(OVERLAPPED)和非重叠(NON-OVERLAPPED)方式(其实这两种方式分别对应串口的异步和同步通信方式-VC串口通信技术网注)。CSerialCom 类工作于非重叠(NON-OVERLAPPED)模式(即同步通信模式),有关更多 OVERLAPPED 和 NON-OVERLAPPED 方面的消息可查询MSDN。
串口配置:
VC 串口通信程序编写最重要的课题就是如何利用 DCB 结构来配置串口,对 DCB 结构填充不正确是大多数人常反犯的毛病,通常串口通信程序编写好后出现这样那样的问题就是因为这个结构没有正确填充所致。在利用 CreateFile() 函数打开串口时就需要我们对串口的波特率、校验方式、数据位、停止位等进行配置。
设置超时时间:
每次打开串口时都必须利用 COMMTIMEOUTS 结构设置超时时间,如果未设置此结构,通信将以默认或上次打开此串口时设置的超时时间为准。
写串口:
WriteFile() 函数可实现这一功能,在执行这一动作之前必须先打开并配置好串口。
读串口:
利用 ReadFile() 可实现这一功能,同样,在执行这一动作之前必须先打开并配置好串口。
关闭串口:
不再使用已经打开的串口后必须关闭它以将此串口资源释放出来,这样其它程序才可能再使用此串口资源,外于非重叠I/O(NON-OVERLAPPED)模式下的串口在打开期间是不能被其它程序或同一程序内的其它线程访问的。使用 CloseHandle() 函数可以关闭串口, CloseHandle() 函数只有一个参数,它是由 CreateFile() 打开串口时返回的设置句柄。
CSerialCom 串口类
CSerialCom 类使用六个函数来实现上面提到的功能,它们分别是:
- BOOL CSerialCom::OpenPort(CString portname)
- {
- portname= "//./" + portname;
- hComm = CreateFile(portname,
- GENERIC_READ | GENERIC_WRITE,
- 0,
- 0,
- OPEN_EXISTING,
- 0,
- 0);
- if(hComm==INVALID_HANDLE_VALUE){
- return false;}
- else
- return true;
- }
OpenPort()成员函数用来打开串口,它需要的参数只有一个,就是串口名,如“COM1”,“COM2” 。
- BOOL CSerialCom::ConfigurePort(DWORD BaudRate, BYTE ByteSize,
- DWORD fParity, BYTE Parity, BYTE StopBits)
- {
- if((m_bPortReady = GetCommState(hComm, &m_dcb))==0)
- {
- MessageBox("GetCommState Error","Error",MB_OK+MB_ICONERROR);
- CloseHandle(hComm);
- return false;
- }
- m_dcb.BaudRate =BaudRate;
- m_dcb.ByteSize = ByteSize;
- m_dcb.Parity =Parity ;
- m_dcb.StopBits =StopBits;
- m_dcb.fBinary=TRUE;
- m_dcb.fDsrSensitivity=false;
- m_dcb.fParity=fParity;
- m_dcb.fOutX=false;
- m_dcb.fInX=false;
- m_dcb.fNull=false;
- m_dcb.fAbortOnError=TRUE;
- m_dcb.fOutxCtsFlow=FALSE;
- m_dcb.fOutxDsrFlow=false;
- m_dcb.fDtrControl=DTR_CONTROL_DISABLE;
- m_dcb.fDsrSensitivity=false;
- m_dcb.fRtsControl=RTS_CONTROL_DISABLE;
- m_dcb.fOutxCtsFlow=false;
- m_dcb.fOutxCtsFlow=false;
- m_bPortReady = SetCommState(hComm, &m_dcb);
- if(m_bPortReady ==0)
- {
- MessageBox("SetCommState Error","Error",MB_OK+MB_ICONERROR);
- CloseHandle(hComm);
- return false;
- }
- return true;
- }
ConfigurePort() 函数对串口地配置,它所需参数如下所示:
DWORD BaudRate
DWORD BaudRate 用来描述串口通信使用的波特率,如此参数为 CBR_9600 时,波特率为9600bps,PC机支持的标准波特率有:CBR_110 ,CBR_300 ,CBR_600 ,CBR_1200,CBR_2400,CBR_4800,CBR_9600,CBR_14400, CBR_19200,CBR_38400,CBR_56000,CBR_57600,CBR_115200,CBR_128000,CBR_256000
BYTE ByteSize
此参数描述数据位位数,标准值为8或4。
DWORD fParity
奇偶校验开关,如果此参数为真(TRUE)则开启奇偶校验,为假(FALSE)则关闭奇偶校验。
BYTE Parity
校验方式,可选方式如下:
● | EVENPARITY |
|
● | MARKPARITY | |
● | NOPARITY | |
● | ODDPARITY | |
● | SPACEPARITY |
BYTE StopBits
停止位位数,可取值如下:
● | ONESTOPBIT |
|
● | ONE5STOPBITS | |
● | TWOSTOPBITS |
备注:ConfigurePort() 函数是假定串口的流控制是由硬件来完成的,软件在收发数据过程中不检测CTS/RTS 和 Xon/Xoff 状态,您可以修改DCB结构来启用软件流控制。
- BOOL CSerialCom::SetCommunicationTimeouts(DWORD ReadIntervalTimeout,
- DWORD ReadTotalTimeoutMultiplier,
- DWORD ReadTotalTimeoutConstant,
- DWORD WriteTotalTimeoutMultiplier,
- DWORD WriteTotalTimeoutConstant)
- {
- if((m_bPortReady = GetCommTimeouts (hComm, &m_CommTimeouts))==0)
- return false;
- m_CommTimeouts.ReadIntervalTimeout =ReadIntervalTimeout;
- m_CommTimeouts.ReadTotalTimeoutConstant =ReadTotalTimeoutConstant;
- m_CommTimeouts.ReadTotalTimeoutMultiplier =ReadTotalTimeoutMultiplier;
- m_CommTimeouts.WriteTotalTimeoutConstant = WriteTotalTimeoutConstant;
- m_CommTimeouts.WriteTotalTimeoutMultiplier =WriteTotalTimeoutMultiplier;
- m_bPortReady = SetCommTimeouts (hComm, &m_CommTimeouts);
- if(m_bPortReady ==0)
- {
- MessageBox("StCommTimeouts function failed",
- "Com Port Error",MB_OK+MB_ICONERROR);
- CloseHandle(hComm);
- return false;
- }
- return true;
- }
SetCommunicationTimeouts() 成员函数用来设置读/写超时时间,它所需参数如下:
DWORD ReadIntervalTimeout
设置读串口时,收到两个字符的最大时间间隔,单位为毫秒。在执行ReadFile() 期间,一但收到两个字符的时间间隔超过这一设定值,ReadFile()立即返回。如果这一参数设置为零,则表明这一特性未启用。
ReadTotalTimeoutConstant
常量,用来计算读操作总超时时间。每个读操作过程中,这个参数被加到ReadTotalTimeoutMultiplier。如果ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant两个参数都为零则表明总超时计算功能没有启用。
ReadTotalTimeoutMultiplier
用来保存读操作总超时时间的
WriteTotalTimeoutConstant
常量,用来计算写操作总超时时间。与ReadTotalTimeoutConstant差不多。
WriteTotalTimeoutMultiplier
用来保存写操作总超时时间的,如果WriteTotalTimeoutMultiplier 和 WriteTotalTimeoutConstant两参数均为零,表明写操作总超时时间特性不被启用。
例如,您需要传送一块数据包,设定了写操作超时时间为500ms(每个字符间发送时间最大间隔),您可以用设置超时成员函数来设定,内容为:SetCommunicationTimeouts(0,500,0,0,0);如果设置成功,函数返回真,否则返回假。
- BOOL CSerialCom::WriteByte(BYTE bybyte)
- {
- iBytesWritten=0;
- if(WriteFile(hComm,&bybyte,1,&iBytesWritten,NULL)==0)
- return false;
- else
- return true;
- }
WriteByte() 成员函数用来向串口写数据。
- BOOL CSerialCom::ReadByte(BYTE &resp)
- {
- BYTE rx;
- resp=0;
- DWORD dwBytesTransferred=0;
- if (ReadFile (hComm, &rx, 1, &dwBytesTransferred, 0))
- {
- if (dwBytesTransferred == 1)
- {
- resp=rx;
- return true;
- }
- }
- return false;
- }
ReadByte() 成员函数实现读串口功能,如果您知道有数据发送来了,可能使用 ReadByte() 来读取数据,如果不知道何时有数据发送过来,可以周期性调用此函数,当没收到数据超过超时时间后, ReadByte() 自动返回。在实际应用中,通信双方可能会以某种协议进行的,此时一般有表明通信数据包结束的标志,如3964协议的结束符是‘ETX’,您可能通过它来判断是否传输结束。
- void CSerialCom::ClosePort()
- {
- CloseHandle(hComm);
- return;
- }
ClosePort() 成员函数用来关闭一个已经打开的串口。
如何使用CSerialCom类
使用CSerialCom类前的准备工作:
1、 | 复制SerialCom.h 和 SerialCom.cpp两个文件到工程所在目录 |
|
2、 | 在VC工程中导入这两个文件 | |
3、 | 加入#include "SerialCom.h"句代码到程序适当位置 | |
4、 | 创建CSerialCom类的一个实例 |
现在就可以调用CSerialCom类成员函数来进行串口通信了,操作流程如下:
- // 打开串口。 需要检查函数返回值来判断串口是否正确打开
- port.OpenPort( );
- // 配置串口。需要检查函数返回值来判断否正确配置
- port.ConfigurePort( );
- // 设置超时时间。需要检查函数返回值来判断否正确配置
- port.SetCommunicationTimeouts( );
- // 写串口,由于一次只能写一个字节,一般需要多次调用此函数才能将需要发送的字符串发送完
- port.WriteByte();
- // 读串口,由于一次只能读一个字节,一般需要多次调用此函数才能将需要读取的字符串读完
- port.ReadByte( );
- // 关闭串口
- port.ClosePort();