使用Win32创建串口通讯程序
作者:konchat
翻译:PowerCPP
下载本文示例代码
介绍:
本文的目的是介绍如何在Win32中处理串口。串口通讯可以通过多种技术实现,例如:ActiveX,I/O存取和文件操作。本文介绍Win32平台下通过文件操作技术使用串口。程序员可以使用 Microsoft Visual C++ Version 6.0所提供的kernel32.lib。在Microsoft Windows(2000,Me,XP and 95/98)中,串口作为文件处理。因此可以通过Windows文件创建函数打开串口。
文章不仅介绍了串口通讯,也介绍了在串口通讯应用程序中实现多任务,多任务可以使串口通讯应用程序在同一时间处理更多的任务,例如:读数据任务,发送数据任务,GUI任务等。
以下主题描述了Win32中基本的串口操作:
初始化/打开串口通讯
接收/发送数据
设计步骤:
初始化/打开串口
打开串口的第一步是初始化或设置串口配置,目的是创建串口代理,整篇文章我们都将用文件句柄作为串口代理。
创建端口句柄
串口句柄是可以被用来存取的串口对象句柄,创建串口句柄的函数是CreateFile,如下代码所示:
handlePort_ = CreateFile(portName, // 端口设备: 默认 "COM1" GENERIC_READ | GENERIC_WRITE, // 设备打开模式: 允许读写 0, // 不共享 NULL, // 默认安全设置 OPEN_EXISTING, // 打开方式:打开已经存在的端口 0, // 默认 NULL); // 默认如图2所示,portName = "COM1": portName 示一个const char*变量,它指定想创建串口句柄的端口名称。
![](http://vckbase.com/document/journal/vckbase31/images/atCreateFile.gif)
图2:CreateFile函数
获取配置
在控制设备中获取当前配置,配置中包含了用于设置串口通讯设备的参数。
可以用 GetCommState函数得到当前设备配置并用指定通讯设备的当前配置填充设备控制块(DCB结构),如下代码所示:
// 获取串口当前配置 if (GetCommState(handlePort_,&config_) == 0) { AfxMessageBox("Get configuration port has problem."); return FALSE; }修改配置
当你已经在DCB结构中获取串口配置,你应该修改其中的参数,如下代码所示:
// 指定用户参数 config_.BaudRate = dcb.BaudRate; // 波特率 config_.StopBits = dcb.StopBits; // 停止位 config_.Parity = dcb.Parity; // 奇偶校验 config_.ByteSize = dcb.ByteSize; // 数据位
- DWORD BaudRate :
波特率 (默认 = 9600)
- BYTE StopBits :
0,1,2 = 1, 1.5, 2 (默认 = 0)
- BYTE Parity :
0-4= 无, 奇, 偶, 标志, 空格 (默认 = 0)
- BYTE ByteSize :
数据位, 4-8 (默认 = 8)
对于典型的通讯,建议程序员使用默认值。图3所示,Watch对话框显示了典型通讯使用的默认值。
图3:串口配置
保存配置
下一步是将已经修改的配置保存到设备控制中。调用SetCommState API函数保存配置。SetCommState函数设备控制块(DCB结构)配置通讯设备。该函数重新初始化所有的硬件控制设定,但不清空输入输出队列。代码如下所示:
if (SetCommState(handlePort_,&config_) == 0) { AfxMessageBox("Set configuration port has problem."); return FALSE; }设置通讯超时
开启端口的最后一步是通过使用COMMTIMEOUTS数据结构和调用SetCommTimeouts函数进行通讯超时设置。如下代码所示:
// COMMTIMEOUTS对象 COMMTIMEOUTS comTimeOut; // 接收时,两字符间最大的时延 comTimeOut.ReadIntervalTimeout = 3; // 读取每字节的超时 comTimeOut.ReadTotalTimeoutMultiplier = 3; // 读串口数据的固定超时 // 总超时 = ReadTotalTimeoutMultiplier * 字节数 + ReadTotalTimeoutConstant comTimeOut.ReadTotalTimeoutConstant = 2; // 写每字节的超时 comTimeOut.WriteTotalTimeoutMultiplier = 3; // 写串口数据的固定超时 comTimeOut.WriteTotalTimeoutConstant = 2; // 将超时参数写入设备控制 SetCommTimeouts(handlePort_,&comTimeOut);ReadIntervalTimeout
指定通讯线上两个字符到达的最大时延,以毫秒为单位。在ReadFile操作期间,时间周期从第一个字符接收到算起。如果收到的两个字符之间的间隔超过该值,ReadFile操作完毕并返回所有缓冲数据。如果ReadIntervalTimeout为0,则该值不起作用。
如果值为MAXDWORD, 并且ReadTotalTimeoutConstant和ReadTotalTimeoutMultiplier两个值都为0, 则指定读操作携带已经收到的字符立即返回,即使没有收到任何字符。
ReadTotalTimeoutMultiplier
指定以毫秒为单位的累积值。用于计算读操作时的超时总数。对于每次读操作,该值与所要读的字节数相乘。
ReadTotalTimeoutConstant
指定以毫秒为单位的常数。用于计算读操作时的超时总数。对于每次读操作,ReadTotalTimeoutMultiplier与所要读的字节数相乘后与该值相加。
如果ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant都为0,则在读操作时忽略总超时数。
WriteTotalTimeoutMultiplier
指定以毫秒为单位的累积值。用于计算写操作时的超时总数。对于每次写操作,该值与所要写的字节数相乘。
WriteTotalTimeoutConstant
指定以毫秒为单位的常数。用于计算写操作时的超时总数。对于每次写操作, WriteTotalTimeoutMultiplier与所要写的字节数相乘后与该值相加。
如果 WriteTotalTimeoutMultiplier 和 WriteTotalTimeoutConstant都为0,则在写操作时忽略总超时数。
提示:用户设置通讯超时后,如没有出错,串口已经被打开。
发送数据
串口数据发送多作为写文件处理的,程序员可以应用文件操作函数发送数据到串口。采用WriteFile函数发送数据到串口。
if (WriteFile(handlePort_, // 文件句柄 outputData, // 数据缓冲区指针 sizeBuffer, // 字节数 &length,NULL) == 0) // 接收成功发送数据长度的指针 { AfxMessageBox("Reading of serial communication has problem."); return FALSE; }提示:如果函数成功,返回非0值
接收数据
串口数据接收多作为读文件处理。程序员可以应用文件操作函数从串口接收数据。用ReadFile函数接收串口的数据。
if (ReadFile(handlePort_, // 句柄 inputData, // 数据缓冲区指针 sizeBuffer, // 字节数 &length, // 指向已经读入的字节数 NULL) == 0) // 重叠I/O结构体 { AfxMessageBox("Reading of serial communication has problem."); return FALSE; }提示:如果函数成功,返回非0值
关闭串口
可以调用CloseHandle API函数关闭串口
if(CloseHandle(handlePort_) == 0) // 调用该函数关闭串口 { AfxMessageBox("Port Closeing isn''t successed."); return FALSE; }提示:如果函数成功,返回非0值