用API函数实现串行通讯
副标题#e#
以往的DOS系统是通过DOS间断和BIOS间断向用户提供串行接口的通讯本领。在Windows情况下,C++的开拓东西既没有提供象DOS和BIOS中那样专门的串行通讯节制要领,也不答允用户直接节制串口的间断。
为了担保资源共享,Windows系统完全经受了各类硬件资源,利用间断来节制端口将粉碎系统的多任务性,使系统的不变性受到影响。但Windows同时也提供了成果强大的API函数利用户能间接的节制串行通讯。
1、实现串行通讯的相关API函数
API函数不只提供了打开和读写通讯端口的操纵要领,还提供了款式繁多的函数以支持对串行通讯的各类操纵。常用函数及浸染如表5-1所示。
表5-1 常用串行通讯API函数及其浸染
函数名 浸染
CreateFile 打开串口
GetCommState 检测串口配置
SetCommState 配置串口
BuilderCommDCB 用字符串中的值来填充设备节制块
GetCommTimeouts 检测通信超时配置
SetCommTimeouts 配置通信超时参数
SetCommMask 设定被监控事件
WaitCommEvent 期待被监控事件产生
WaitForMultipleObjects 期待多个被监测工具的功效
WriteFile 发送数据
ReadFile 吸收数据
GetOverlappedResult 返回最后重叠(异步)操纵功效
PurgeComm 清空串口缓冲区,退出所有相关操纵
ClearCommError 更新串口状态布局体,并排除所有串口硬件错误
CloseHandle 封锁串行口
2、打开串口
函数CreateFile原本用于打开文件,但它同样可用于打开一个通信端口。与系统中其他工具一样,通信端口也是用句柄来标识的。CreateFile函数返回被操纵的通信端口句柄,其挪用要领如下:
HANDLE CreateFile (
#p#副标题#e#
LPCTSTR lpFileName, //指向文件名字符串的指针
DWORD dwDesireAccess, //操纵模式
DWORD dwShareMode, //共享方法
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
//指向安详属性的指针
DWORD dwCreationDistribution,//文件成立方法
DWORD dwFlagsAndAttributes //文件属性
HANDLE hTemplateFile) //模板文件句柄
lpFileName:指向一个以NULL竣事的字符串,该串指定了要建设、打开或截断的文件、管道、通信源、磁盘设备或节制台的名字。当用CreateFile打开串口时,这个参数可用“COM1”指定串口1,用“COM2”指定串口2,依此类推。
dwDesireAccess: 指定对文件会见的范例,该参数可觉得GENERIC_READ(指定对该文件的读会见权)或ENERIC_WRITE(指定该文件的写会见权)两个值之一或同时为为这两个值。用ENERIC_READ|GENERIC_WRITE则指定可对串口举办读写;
dwShareMode:指定此文件可以奈何被共享。因为串行口不支持任何共享模式,所以dwShareMode必需设为0;
lpSecurityAttributes界说安详属性,一般不消,可设为NULL。Win 9x下该参数被忽略;
dwCreationDistribution界说文件建设方法, 对串口必需设为OPEN_EXISTING,暗示打开已经存在的文件;
dwFlagsAndAttributes为该文件指定界说文件属性和符号,这个措施中设为FILE_FLAG_OVERLAPPED,暗示异步通信方法;
hTemplateFile 指向一个模板文件的句柄,串口无模板可言,设为NULL。在 Windows 9x下该参数必需为NULL。
用异步读写方法打开串口1的函数挪用如下:
m_hComm = CreateFile(“COM1”, //打开串口1
GENERIC_READ | GENERIC_WRITE,//读写方法
0, //不能共享
NULL, //不消安详布局
OPEN_EXISTING, //打开已存在的设备
FILE_FLAG_OVERLAPPED, //异步方法
0); //无模板
串口被乐成打开时,返回其句柄,不然返回INVALID_HANDLE_VALUE(0XFFFFFFFF)。
3、串口配置
第一次打开串口时,串口配置为系统默认值,函数GetCommState和SetCommState可用于检索和设定端口配置的DCB(设备节制块)布局,该布局中BaudRate、ByteSize、StopBits和Parity字段含有串口波特率、数据位数、遏制位和奇偶校验节制等信息。措施中可先用GetCommState检索端口的当前配置,修改个中的部门字段后再用SetCommState举办端口设定。这样可不必结构一个完整的DCB布局。
下面先容几个主要的函数和布局体:
(1)GetCommState
BOOL GetCommState( hCommDev, lpdcb);
参数hCommDev标识通信设备,应利用CreateFile返回的句柄。Lpdcb是指向DCB布局的指针,函数挪用后当前串口设置信息将被生存在这个布局内。假如函数乐成返回值为TRUE;不然返回值为FALSE。SetCommState用法与GetCommState相似,在此不再反复。DCB布局界说如下(只先容主要的几项):
#p#分页标题#e#
typedef struct _ DCB{
……
DWORD BardRate; //波特率的配置
BYTE ByteSize; //数据位的个数
BYTE Parity; //是否有奇偶校验位
BYTE StopBits; //遏制位的个数
……
}DCB;
(2)SetCommTimeouts
BOOL SetCommTimeouts( hCommDev, lpctmo );
Lpctmo指向包括新的超时参数的COMMTIMEOUTS布局。COMMTIMEOUTS布局界说如下:
typedef struct _ COMMTIMEOUTS{
DWORD ReadIntervalTimeout;
DWORD ReadTotalTimeoutMultiplier;
DWORD ReadTotalTimeoutconstant;
DWORD WriteTotalTimeoutMultiplier;
DWORD WriteTotalTimeoutconstant;
}COMMTIMEOUTS, LPCOMMTIMEOUTS;
ReadIntervalTimeout: 以毫秒为单元指定通信线上两个字符达到之间的最大时间。在ReadFile操纵其间,收到第一个字符时开始计较时间。若任意两个字符达到之间的隔断高出这个最大值,ReadFile操纵完成,返回缓冲数据。0值暗示不消隔断限时。若该成员为MAXDWORD,且ReadTotalTimeoutconstant和ReadTotalTimeoutMultiplier成员为零,则指出读操纵要当即返回已吸收到的字符,纵然未收到字符,读操纵也要返回。
ReadTotalTimeoutMultiplier:以毫秒为单元指定一个乘数,该乘数用来计较读操纵的总限时时间。每个读操纵的总限时时间便是读操纵所需的字节数与该值的乘积。
ReadTotalTimeoutConstant:以毫秒为单元指定一个常数,用于计较读操纵的总限时时间。每个操纵的总限时时间便是ReadTotalTimeoutMultiplier成员乘以读操纵所需字节数再加上该值的和。ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant成员的值为0暗示读操纵不利用限时时间。
WriteTotalTimeoutMultiplier和WriteTotalTimeoutconstant的意义和浸染别离与ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant相似,不再反复。
(3)BuilderCommDCB
BOOL BuilderCommDCB(lpszDef,lpdcb)
这个函数按lpszDef字符串所指定的名目来设置串口的DCB。
LpszDef:指向一个以NULL竣事的字符串,该字符串指定串口的节制信息。好比,“1200,N,8,1”指定波特率为1200,无奇偶校验位,有8个数据位和1个遏制位。
lpdcb:指向被填充的DCB布局。
(4)SetCommMask
BOOL SetCommMask(hCommDev,fdwEvtMask);
fdwEvtMask指向一个32位的屏蔽码,假如指定为EV_RXCHAR | EV_CTS,暗示措施监控串口的收、发事件。
下面以简朴的例子说明串口配置的步调:
m_CommTimeouts.ReadIntervalTimeout = 1000;
m_CommTimeouts.ReadTotalTimeoutMultiplier = 1000;
m_CommTimeouts.ReadTotalTimeoutConstant = 1000;
m_CommTimeouts.WriteTotalTimeoutMultiplier = 1000;
m_CommTimeouts.WriteTotalTimeoutConstant = 1000;
if (SetCommTimeouts(m_hComm, &m_CommTimeouts))
// 串口超时参数配置
if (SetCommMask(m_hComm, dwCommEvents))
// 配置串口事件掩码
if (GetCommState(m_hComm, &m_dcb))
// 获取串口当前状态
if (BuildCommDCB(“1200,N,8,1”, &m_dcb))
// 成立串口设备节制块
if (SetCommState(m_hComm, &m_dcb));
// 配置串口参数
……
以上任何一个if语句的判定条件为假时都将挪用GetLastError函数获取错误信息,举办错误处理惩罚。
4、读写串口数据
Win32API函数ReadFile和WriteFile支持对串行口 的读写操纵。这些函数的行为还受是否利用异步I/O(Overlapped)及通信超时配置的影响。串行口读写的同步、异步方法是在打初步口的同时给dwGlagsAndAttributes参数传入适当的值而设定的。
在同步方法下,挪用ReadFile或WriteFile后,当实际读写操纵完成或产生超时时才返回挪用措施。
而异步方法函数在启动吸收或发送进程后当即返回,措施继承向下执行。措施在挪用ReadFile和WriteFile时必需提供一个Overlapped数据布局指针,该布局中包括一个手动的事件同步工具,其后的措施必需借助于该事件同步工具,完成数据的吸收和发送进程。
通信端口的超时配置对读写的处理惩罚方法也会发生影响,假如挪用读写函数时产生端口超时,则读写函数当即返回并返回已传输的数据字节数。
下面先容主要的用于串口读写的函数:
(1)ReadFile 函数的挪用要领如下:
#p#分页标题#e#
BOOL ReadFile (
HANDLE hFile, //用CreateFile 得到的文件句柄
LPVOID lpBuffer, //输入缓冲区首址
DWORD nNumberOfBytesToRead,//设定读入字节数
LPDWORD lpNumberOfByteRead, //实际读入字节数
LPOVERLAPPED lpOverlapped //重叠操纵方法数据布局地点
);
(2)WriteFile函数的挪用要领如下:
BOOL WriteFile(
HANDLE hFile, //用CreateFile 得到的文件句柄
LPCVOID lpBuffer, //输出缓冲区首址
DWORD nNumberOfBytesToWrite, //要求输出的字节数
LPDWORD lpNumberOfBytesWritten,//实际输出字节数
LPOVERLAPPED lpOverlapped): //重叠操纵方法数据布局地点
(3)GetOverlappedResult函数挪用要领如下:
BOOL GetOverlappedResult(
HANDLE hFile, //用CreateFile 得到的文件句柄
LPOVERLAPPED lpOverlapped,
//指向一个在启动重叠操纵时指定的
OVERLAPPED布局(即读写函数中指定的OverLapped布局)
LPDWORD lpNumberOfBytesTransferred,//实际传输的字节数
BOOL bWait, //是否期待悬挂的重叠操纵完成,若为
//TRUE,则此函数直到操纵完成后才返回。
);
OVERLAPPED布局界说如下:
typedef struct _OVERLAPPED {
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
HANDLE hEvent;
} OVERLAPPED;
假如回收异步方法,则在挪用ReadFile或WriteFile函数时必须指定一个Overlapped布局,挪用后措施可继承执行其它操纵,在符合的处所再挪用函数GetOverlappedResult判定异步重叠操纵是否完成(判定OVERLAPPED布局中的hEvent是否被置位)。
(4)WaitCommEvent函数的挪用法如下:
BOOL WaitCommEvent(
HANDLE hCommDev, //串口句柄
LPDWORD lpfdwEvtMask, //见SetCommEvent函数说明
LPOVERLAPPED lpo, //重叠操纵方法数据布局地点
);
当由SetCommMask函数所指定的事件发生时这个函数将返回TRUE。
下面是一个串口读写的流程图。
串口操纵流程图
5、封锁串口
假如不再利用某一端口,须将该端口封锁,以便其他措施可以利用该端口。假如不显式封锁某端口,当措施退出时打开的端口也将被自动封锁。但为了安详起见,最好是显式的封锁它。封锁串口的语句为CloseHandle(hCommDev);个中hCommDev为用CreateFile成立的串口句柄。
6、一个完整的例子
这个串口节制类是我做的一个软件中利用的,根基用WINAPI实现,在Win95/Win98/Win2000下运行精采 ,可不做修改的用在其它BCB措施中 。读数据回收事件驱动方法,用一个线程处理惩罚相应事件,收到的字符用动静发往父窗口;由于我编的软件中写串口的数据不多,所以回收直接写的方法,而没用线程。
串口节制类 COM.h COM.cpp
串口节制类中利用的读串口线程 COMThread.h COMThread.cpp
打包下载
这个类的利用要领如下:
1、成立串口类的实例,如:
SerialPort=new TSerialPort(); //实例化一个串口节制类
//初始化串口,Baud:9600,DataBits:8,StopBits:1,无校验位
SerialPort->InitPort(this,g_iSerialPort,9600);
SerialPort->StartMonitoring(); //建设串口监控线程,开始监控
2、吸收串口数据
(1)映射串口动静,在建设以上实例的窗口类的声明中插手:
#p#分页标题#e#
void __fastcall OnComRx(TMessage &Message);
//指定动静WM_COMM_RXCHAR(由串口监控线程发出的动静,暗示吸收到一个字符)的处理惩罚函数为OnComRx
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_COMM_RXCHAR, TMessage, OnComRx)
END_MESSAGE_MAP(TForm)
(2)实现函数OnComRx(),如:
//---------------------------------------------------------------------------
//串口动静(吸收到一个字符)处理惩罚函数
//动静的参数WParam就是吸收到的字符
void __fastcall TDlgProgram::OnComRx(TMessage &Message)
{
unsigned char ucRxData=(unsigned char)Message.WParam; //串口监控线程收到的字符
………………
}
//---------------------------------------------------------------------------
3、向串口发送数据
挪用函数bool WriteToPort(unsigned char ucTxChar)可向串口写一个字符。
如SerialPort->WriteToPort(0x55);