- 1 -
VC++实现串口通信的应用程序设计
摘 要:本文介绍了结合了Win32 的API 函数和C++类的基本思想封装的一个串行通信类
CSerialPort 类。并通过具体的实例详细介绍其在VC++6.0 下的实现串口通信方法和过程。
关键词:VC++,串口通信,CSerialPort 类
1. 引言
在控制领域的研发中,PC 机与外围控制设备的通讯方式有多种,如串行通信口、并行
通信口、一台网口(需要网卡支持)、USB 通信口或自制危机接口卡。而串口通信则是在
数据通信速率部是很高的情况下,开发者所采用的最常见、最简单、最通用的通信方式。并
且考虑到现在的PC 机标准配置都具备两个串口,因此利用串口资源来开发PC 机与外围控
制设备之间的通信应该是最方便快捷的[3]。
在目前常用的各种开发环境(如VB、VC 和Delphi 等)下实现串口通信的方式有很多
种:
首先,最简便的方法是采用Microsofe 公司提供的ActiveX 控件实现串口通信,即
MSComm 控件。实现串口通信的编程方法原理上比较简单,容易实现,但编程的灵活性稍差,
且应用程序运行时必须同时拷入与通信控件相关的文件。
其次,使用Win32 的API 函数实现串口通信亦是很好的方法。利用 API 函数实现串口
通信的编程方法功能强大 ,灵活性好 ,但原理上比较复杂 ,需要编程人员对串口的硬件工作
原理有较深入的了解。
最后,可以利用VC 运行时库的标准通信函数实现串口通信。其编程原理简单,但其使
用性和灵活性较差。
总之,各种串口通信的实现方式在使用条件、灵活性和复杂程度等方面各有优缺点,分
别适合于各自不同的应用场合。而本文介绍一个结合了Win32 的API 函数和C++类的基本
思想封装了一个串行通信类CSerialPort 类,通过一个实例介绍,我们发现这个自定义的串
口通信类能够使得串口通信的实现更加灵活、方便。
2. 串口通信的实现
虽然实现串口通信方法很多,但是一般来说,串行通信程序的工作过程一般分四步[2]:
(1)打开串口:在此步骤中,我们可以实现打开通信资源、设置通信参数、设置通信
事件、创建读写事件以及等待串口接收中断等操作。
(2)读串口:在此步骤中,当有字符或干扰到达串口时发生EV_ RXCHAR 消息的发
送 ,响应函数接收到此消息后读取串口接收缓冲区。
(3)写串口:该步骤是负责将要发送的字符写入发送串口缓冲区。
(4)关闭串口。
2.1 CSerialPort 通信类的介绍
http://www.paper.edu.cn
- 2 -
2.1.1 CSerialPort 类的定义声明
class CSerialPort
{
public:
void ClosePort();
CSerialPort();
virtual ~CSerialPort();
BOOL InitPort(CWnd* pPortOwner, UINT portnr = 1, UINT baud = 19200, char parity =
' N', UINT databits = 8, UINT stopbits = 1, DWORD dwCommEvents =
EV_RXCHAR, UINT writebuffersize = 1024);
HANDLE m_hComm;
BOOL StartMonitoring();
BOOL RestartMonitoring();
BOOL StopMonitoring();
DWORD GetWriteBufferSize();
DWORD GetCommEvents();
DCB GetDCB();
void WriteToPort(char* string);
Void WriteToPort(char* string,int n);
Void WriteToPort(LPCTSTR string);
void WriteToPort(LPCTSTR string,int n);
protected:
void ProcessErrorMessage(char* ErrorText);
static UINT CommThread(LPVOID pParam);
static void ReceiveChar(CSerialPort* port, COMSTAT comstat);
static void WriteChar(CSerialPort* port);
CWinThread* m_Thread;
CRITICAL_SECTION m_csCommunicationSync;
BOOL m_bThreadAlive;
HANDLE m_hWriteEvent;
HANDLE m_hShutdownEvent;
HANDLE m_hEventArray[3];
OVERLAPPED m_ov;
COMMTIMEOUTS m_CommTimeouts;
DCB m_dcb;
CWnd* m_pOwner;
UINT m_nPortNr;
char* m_szWriteBuffer;
DWORD _dwCommEvents;
DWORD m_nWriteBufferSize;
};
2.1.2 CSerialPort 类的函数说明
在构造函数 CSerialPort 中对该类的数据成员进行了初始化。InitPort 函数完成串口的初
始化工作,包括打开串口、设置 SPC 的参数、设置通信事件的时间、建立串口的设备控制
块。由于串口是一种临界资源,不允许多个线程或用户同时访问,因此在该函数中调用了两
http://www.paper.edu.cn
- 3 -
个Windows API 函数EnterCriticalSection 和LeaveCriticalSection,以实现对串口的互斥访问。
A,串口操作函数:
StartMonitoring 函数调用Windows API 函数:AfxBeginThread 启动一个SPC 线程,打
开串口。
RestartMonitoring 函数调用CwinThread 的成员函数:ResumeThread 重启SPC 线程,
重起串口;
StopMonitoring 函数调用CwinThread 的成员函数:SuspendThread 停止SPC 线程,关
闭串口。
B,串口读写函数:
WriteChar 函数调用Windows API 函数:WriteFile 向串口写数据;
ReceiveChar 函数调用 Windows API 函数:ReadFile 从串口读取数据。
WriteToPort 函数:另外扩充函数,通过不同的变量参数,适应不同的写串口的需求。
CommThread 函数是SPC 的线程,完成SPC 的消息循环。该函数调用了WaitCommEvent
函数来等待事件消息,然后进行消息的处理和分发。在消息的处理中调用了WriteChar 函数
和ReceiveChar 函数。另外在这个SPC 线程函数中还进行了必要的异常处理。
C,其他的相关函数
其它的几个函数 ProcessErrorMessage,GetDCB 和GetWriteBufferSize 则完成一些辅助
的功能。
2.2 CSerialPort 串口通信类的具体应用
前面介绍了CSerialPort 类的声明以及其中一些重要的函数。下面介绍在VC++6.0 环境
下利用MFC 编写一个基于对话框的串口通信测试的应用程序[1]。定义实现串口通信的类为
CSerialPort。
2.2.1 串口初始化
在我们的例子中,我们在CSerialPort.h 中声明一个串口类的对象:
CSerialPort m_SerialPort;
要实现串口首先需要对串口进行设置和初始化。打开串口后,才能对串口的状态进行监
控。其串口初始化函数如下:
InitPort(this, nPort, band, checkbit, databits, stopbits,EV_RXFLAG | EV_RXCHAR,512);
初始化串口的状态,每次只能初始化一个串口。其中参数nPort:端口号, band:波特率,
checkbit:校验位, databits:数据个数, stopbits:停止位均可以通过界面设置,也可以在初始
化串口时直接定义。
初始化串口完毕后,需要通过StartMonitoring()启动串口通信线程函数CommThread,
即可立即对所定义的串口进行监听。
2.2.2 处理函数的定义
在文件CSerialPort.h 类CSerialPort 的定义中声明自定义处理函数:
afx_msg void OnComm(WPARAM ch, LPARAM port);
在 CSerialPort.cpp 内添加函数OnComm()。实现了对接受到的数据的处理代码。
在文件 CSerialPort.cpp 消息映射的BEGIN_MESSAGE_MAP 和END_MESSAGE_MAP
之间添加自定义消息映射宏ON_MESSAGE(WM_COMM_RXCHAR, OnComm),即在接受
http://www.paper.edu.cn
- 4 -
到字节的情况下触发函数。这样就可以在串口上存在一个字节的时候就可以触发OnComm
()函数来处理消息。
2.2.3 写串口
在处理完接受的消息的后,就需要将反馈的消息再次写到串口发送出来,从而完成串口
通信。在这里我们使用m_SerialPort.WriteToPort(char* string,int n)来完成写串口,其中定义
了写到串口的字符串和字符的个数。在CSerialPort 类中,我们补充了其他的WriteToPort 的
函数以满足其他情况下的需要,在使用的过程中大家可以根据需要选择合适的写串口函数,
也可以根据自己的需要来扩充其他的函数。
2.2.4 关闭串口
在串口通信完毕用m_SerialPort.ClosePort()来关闭串口。
3. 测试
使用“串口调试工具”对编写的串口测试工具进行调试,用串口线将PC 机上的串口1 和
串口2 相连。将串口调试工具和串口测试工具的参数设置如下所示,其比特率,交验位,
数据位,停止位设置必须相同,其两个端口号分别选择为端口1,端口2。当两个端口同时
打开的时候,PC 机上的串口连接线将两个串口导通。当“串口调试工具”发送“this is a test
program!”信息时,在串口测试工具接受到该条信息。测试结果如下图所示:图1:串口调试
器发送信息;图2:串口测试工具接受信息。
图 1 串口调试器发送信息
图 2 串口测试工具接受信息
4. 结束语
本文应用Win32 的API 函数和C++类的基本思想把对串口的操作封装成一个串口通信
类的CSeriaPort,采用该串口通信类对串口设备的“屏蔽”,增加对串口操作的透明度,提高
串口传输数据的可靠性,方便程序员的编程。另外,还可以根据自己的需求对CSeriaPort
类进行功能扩充。
http://www.paper.edu.cn
参考文献
[1] Kruglinski.《Visual C++ 技术内幕》[M].北京:电子工业出版社,1999.
[2] 李现勇.《Visual C+ +串口通信技术与工程实践》[M].北京:人民邮电出版社.
[3] 王颖.《用 VC6.0 实现串行通信的三种方法》[J].VC 知识库在线帮助期,http://www.vckbase.com/,
2000 第9 期:18-20.
Application design of serial communication based on VC++
Wang Zhen, Chen Yongtai
school of information engineering,Wuhan University of Technology, Wuhan, PRC (430070)
Abstract
This paper introduces a new Serial Port class, which is named CSerialPort and is comprised based on
Win32 API and some C++ classes. By introducing one example, we analysis the application of the
CSerialPort class in the serial communication based on VC++.
Keywords:VC++,Serial Communication,Class CSerialPort