冯华亮 2002年4月 四川·电子科大
以往的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 (
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结构定义如下(只介绍主要的几项):
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、读写串口数据
SerialPort_DataReceived()事件是.net提供好的很完善的实时接收串口响应的方法.正常情况用它就可以了.
mSerialPort.DataReceived
+=
new
SerialDataReceivedEventHandler(SerialPort_DataReceived)
这行代亩伍哪码执行后就意味着程序已经另开一线程了.它不会影响主进程的 *** 作,也就是说不会卡死主程序.
当你不开线程直接serialPort_jmt.Read的时候如果没有串口响应来的数据那么该方法会一直等待,也就是说回卡住主程序.
这不是死循环,而是主线程等待.
如果不用SerialPort_DataReceived()事迅码件橘皮,可以自己写线程,用线程去serialPort_jmt.Read,这样就让线程一直等待,而不会卡死主程序.
有帮助请采纳,如有疑问请追问.
初始化://串行设备句柄;
HANDLE hComDev=0
//串口打开标志;
BOOL bOpen=FALSE
//线程同步事件句柄;
HANDLE hEvent=0
DCB dcb
COMMTIMEOUTS timeouts
//设备已打开
if(bOpen) return FALSE
//打开COM1
if((hComDev=CreateFile(“COM1”,GENERIC�READ|GENERIC�WRITE,0,NULL,OPEN�EXISTING,FILE�ATTRIBUTE�NORMAL,NULL))==INVALID�HANDLE�VALUE)
return FALSE
//设置超时控制
SetCommTimeouts(hComDev,&timeouts)
//设置接收缓冲区和输出缓冲区的大小
SetupComm(hComDev,1024,512)
//获取缺省的DCB结构的值
GetCommState(hComDev,&dcb)
//设定波特率为9600 bps
dcb.BaudRate=CBR�9600
//设定无奇偶校验
dcb.fParity=NOPARITY
//设定数据位为8
dcb.ByteSize=8
//设定一个停止位
dcb.StopBits=ONESTOPBIT
//监视串口的错误和接收到字符两种事件
SetCommMask(hComDev,EV�ERR|EV�RXCHAR)
//设置串行设备控制参数
SetCommState(hComDev,&dcb)
//设备已打开
bOpen=TRUE
//创建人工重设、未发信号的事件
hEvent=CreateEvent(NULL,FALSE,FALSE,
“WatchEvent”)
//创建一个事件监视线程来监视串口事件
AfxBeginThread(CommWatchProc,pParam)
}
数据发送
数据发送利用WriteFile(山歼)函数实现。对于同步I/O *** 作,它的最后一个参数可为NULL;而对异步I/O *** 作,它的最后一个参数必需是一个指向OVERLAPPED结构的指针,通过OVERLAPPED结构来获得当前的 *** 作状态。
BOOL WriteComm(LPCVOID lpSndBuffer,DWORD dwBytesToWrite)
{
//lpSndBuffer为发送数据缓冲区指针,
dwBytesToWrite为将要发送的字节长度
//设备已打开
BOOL bWriteState
//实际发送的字节数
DWORD dwBytesWritten
//设备未打开
if(!bOpen) return FALSE
bWriteState=WriteFile(hComDev,lpSndBuffer,dwBytesToWrite,&dwBytesWritten,NULL)
if(!bWriteState || dwBytesToWrite!=dwBytesWritten)
//发送失败
return FALSE
else
//发送成功
return TRUE
}
数据接收
接收数据的任务由ReadFile函数完成。该函数从串口接收缓冲区中读取数据,读取数据前,先用ClearCommError函数获得接收缓冲区中的字节数。接收数据时,同步和异步读取的差别同发送数据是一样的返做。
DWORD ReadComm(LPVOID lpInBuffer,DWORD dwBytesToRead)
{
//lpInBuffer为接收数据的缓冲区指针, dwBytesToRead为准备读取的数据长度(字节数)
//串行设备状态结构
COMSTAT ComStat
DWORD dwBytesRead,dwErrorFlags
//设备未打开
if(!bOpen) return 0
//读取串行设备的当前状态
ClearCommError(hComDev,&dwErrorFlags,&ComStat)
/漏唯衡/应该读取的数据长度
dwBytesRead=min(dwBytesToRead,ComStat.cbInQue)
if(dwBytesRead>0)
//读取数据
if(!ReadFile(hComDev,lpInBuffer,dwBytesRead,&dwBytesRead,NULL))
dwBytesRead=0
return dwBytesRead
}
事件监视线程
事件监视线程对串口事件进行监视,当监视的事件发生时,监视线程可将这个事件发送(SendMessage)或登记(PostMessage)到对事件进行处理的窗口类(由pParam指定)中。
UINT CommWatchProc(LPVOID pParam)
{
DWORD dwEventMask=0//发生的事件;
while(bOpen)
{
//等待监视的事件发生
WaitCommEvent(hComDev, &dwEventMask,NULL)
if ((dwEventMask & EV�RXCHAR)==EV�RXCHAR)
……//接收到字符事件后,可以将此消息登记到由pParam有指定的窗口类中进行处理
if(dwEventMask & EV�ERR)==EV�ERROR)
……//发生错误时的处理
}
SetEvent(hEvent)
//发信号,指示监视线程结束
return 0
}
关闭串行设备
在整个应用程序结束或不再使用串行设备时,应将串行设备关闭,包括取消事件监视,将设备打开标志bOpen置为FALSE以使事件监视线程结束,清除发送/接收缓冲区和关闭设备句柄。
void CloseSynComm()
{
if(!bOpen) return
//结束事件监视线程
bOpen=FALSE
SetCommMask(hComDev,0)
//取消事件监视,此时监视线程中的WaitCommEvent将返回
WaitForSingleObject(hEvent,INFINITE)
//等待监视线程结束
CloseHandle(hEvent)//关闭事件句柄
//停止发送和接收数据,并清除发送和接收缓冲区
PurgeComm(hComDev,PURGE�TXABORT| PURGE�RXABORT|PURGE�TXCLEAR|PURGE�RXCLEAR)
//关闭设备句柄
CloseHandle(hComDev)
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)