异步方式winAPI串口通信
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
使用winAPI串口通信(二)
分类:Windows转载2009-09-23 16:51 263人阅读评论(0) 收藏举报
采用同步方式的查询方式读取串口数据时,若由于串口操作耗费较长的时间,则程序会被挂起.为解决这种问题,可以在读取数据时采用重叠I/O操作.此时,读写数据的操作在单独的线程中进行,发出读写要求的主线程可以继续运行.当读写数据成功后,读写数据线程可以通过某种方式通知主线程.两个线程协调工作 ,可以明显提高程序的效率.
为了实现重叠I/O操作,主要有如下几个编程步骤: 定义全局变量、创建串口、发出读写操作、读写线程函数的建立、关闭串口等.
1. 定义全局变量
HANDLE hCom; //串口句柄
DWORD ThreadProcWrite(LPVOID pParam); //写线程函数
DWORD ThreadProcRead(LPVOID pParam); //读线程函数
OVERLAPPED Wol = { 0 }; //写操作OVERLAPPED结构变量
OVERLAPPED Rol = { 0 }; //读操作OVERLAPPED结构变量
HANDLE hThreadWrite; //写线程句柄
HANDLE hThreadRead; //读线程句柄
HWND hWnd; //窗口句柄
2. 创建串口
hCom = CreateFile ( "COM2",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,//使用重叠方式
NULL );
if( hCom != INVALID_HANDLE_VALUE)
{
SetupComm(hCom,1024,512);
DCB myDCB;
GetCommState( hCom,&myDCB);
myDCB.BaudRate=CBR_19200;
myDCB.fBinary=TRUE;
myDCB.fParity=TRUE;
myDCB.ByteSize=8;
myDCB.Parity=ODDPARITY;
myDCB.StopBits=ONESTOPBIT;
SetCommState(hCom,&myDCB);
}
else
{
AfxMessageBox("创建串口失败!");
}
hWnd = GetSafeHwnd(); //获取当前窗口的句柄
3. 发出读写操作
(1) 读操作
在读操作中,只是启动读线程.实际的读操作在读线程函数中完成.
DWORD dwThreadID;
DWORD dwParam;
hThreadRead = CreateThread ( NULL,
0,
(LPTHREAD_START_ROUTINE)ThreadProcRead,
&dwParam,
0, //创建线程后,立即执行该线程
&dwThreadID);
if(hThreadRead==NULL)
{
AfxMessageBox("读线程创建失败!");
}
CreateThread()函数用于创建一个线程.
HANDLE CreateThread (
//线程安全属性,NULL表示该线程不能被继承
LPSECURITY_ATTRIBUTES lpThreadAttributes,
//初始化栈大小.0表示默认值或按调用栈线程配置
DWORD dwStackSize,
//执行函数名称
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter, //向新线程传递的参数
DWORD dwCreationFlags, //创建标志.0表示创建后立即执行
LPDWORD lpThreadId
);
(2) 写操作
在写操作中,同样只是启动写线程.实际的写操作在写线程函数中完成.
DWORD dwThreadID;
DWORD dwParam;
hThreadWrite = CreateThread( NULL,
0,
(LPTHREAD_START_ROUTINE)ThreadProcWrite,
&dwParam,
0,
&dwThreadID);
if(hThreadWrite==NULL)
{
AfxMessageBox("写线程创建失败!");
}
4.读写线程函数的建立
读写的实际操作在读写线程函数中执行. 这两个函数都是全局函数.
(1) 读线程函数
在读线程函数中,首先应初始化重叠结构Rol的成员hEvent,将其设置为无信号状态.当读操作完成或出现通信错误时,该变量会被自动设置为有信号状态.接下来就可以使用ReadFile()函数发出读命令.若该读函数返回TRUE,说明读操作已经完成,可以处理读取的数据.若该读函数返回FALSE,说明读操作未完成.此时使用WaitForSingleObject()函数等待读操作的结构。
根据返回结果的不同,采取相应的处理代码即可.
DWORD ThreadProcRead(LPVOID pParam)
{
BYTE myByte[20];
CString myStr;
DWORD dwRes;
DWORD dwRead;
BOOL fRes;
char myChar[10];
Rol.hEvent = CreateEvent ( NULL, //创建Rol的hEvent成员为无信号状态
TRUE;
FALSE;
NULL);
if (Rol.hEvent == NULL)
{
AfxMessageBox ("hEvent 空");
return -1;
}
if (ReadFile (hCom, //串口句柄
&myByte, //存放读取数据
3, //要读取的字节数
NULL,
&Rol) ) //指向创建hCom时的Rol的指针
{
//AfxMessageBox("成功读出!");
//在这里加入处理读取数据代码,数据存放在myByte数组中
}
else
{
dwRes = WaitForSingleObject ( Rol.Event , 5000 ); //5秒超时
switch(dwRes)
{
case WAIT_OBJECT_0:
if (!GetOverlappedResult (hCom,
&Rol,
&dwRead, //实际读出的字节数
TRUE) ) //TRUE表示直到操作完成函数才返回
{
//操作失败,可以使用GetLastError()获取错误信息 }
else
{
//操作成功完成,数据读取存入myByte中
//这里加入处理数据的代码
}
break;
case WAIT_TIMEOUT:
//读操作失败,原因是超时
break;
default:
//这里加入默认处理代码
break;
}
}
closeHandle(Rol.hEvent);
return 0;
}
CreateEvent() 函数用来创建一个事件句柄,并设置其信号状态.
HANDLE CreateEvent (
//事件安全属性,NULL表示该事件句柄不能用于继承
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset, //TRUE表示该事件需人工复位
BOOL bInitialState, //事件对象的初始状态,TRUE表示有信号,反之无信号
LPCTSTR lpName //指向事件对象的名称,NULL表示创建一个无名事件对象
);
ReadFile()函数是读缓冲区命令,若该命令执行后返回TRUE,说明已经读成功. 若该命令返回FALSE,说明该命令返回时,读操作尚未完成,读操作继续在后台中进行.此时使用waitForSingleObject()函数来获取后台读命令的操作结果.
DWORD WaitForSingleObject (
HANDLE hHandle, //指向等待对象的句柄
DWORD dwMilliseconds, //超时时间
);
GetOverlappedResult()函数其实与WaitForSingleObject()函数功能类似,都可以返回某操作的操作结果. 之所以使用GetOverlappedResult()函数,是因为它可以返回读操作实际读出的字节数量.
BOOL GetOverlappedResult (
HANDLE hFile, //指向串口句柄
LPOVERLAPPED lpOverlapped, //执行读函数中使用的OVERLAPPED结构变量
LPDWORD lpNumberOfBytesTransferred, //存放实际读出字节数量变量的地址
BOOL bWait //设为TRUE表示读操作完成该函数才返回,若为FALSE表示如果操作未完成时函数将返回FALSE.
);
(2) 写线程函数
写线程函数所作操作与读线程函数的操作相似.首先初始化Wol的hEvent事件成员,然后发出写命令WriteFile().若该写命令返回TRUE,说明写操作成功完成.若写命令返回FALSE,说明写操作没有完成.使用WaitForSingleObject()函数等待写操作的结果,根据返回结果的不同,采取相应的处理代码.
DWORD ThreadProcWrite(LPVOID pParam)
{
BYTE myByte[9];
CString myStr;
DWORD dwRes;
DWORD dwWrite;
BOOL fRes;
char myChar[10];
for (i=0;i<=9;i++)
{
myByte[i] = i; //发送数据写入myByte中
}
Wol.Internal = 0; //设置OVERLAPPED结构Wol
Wol.InternalHigh = 0;
Wol.Offset = 0;
Wol.OffsetHigh = 0;
Wol.hEvent = CreateEvent ( NULL, //创建Wol的hEvent成员为无信号状态 TRUE;
FALSE;
NULL);
if (Wol.hEvent == NULL)
{
AfxMessageBox ("hEvent 空");
return -1;
}
if (WriteFile (hCom, //串口句柄
&myByte, //存放待发送数据
3, //欲发送的字节数
NULL,
&Wol) ) //指向创建hCom时的Wol的指针
{
//AfxMessageBox("发送成功!");
//在这里加入处理读取数据代码,数据存放在myByte数组中
}
else
{
dwRes = WaitForSingleObject ( Wol.Event , 500 ); //5ms超时
switch(dwRes)
{
case WAIT_OBJECT_0:
if (!GetOverlappedResult (hCom,
&Wol,
&dwWrite,
TRUE) ) //TRUE表示直到操作完成函数才返回
{
//操作失败,可以使用GetLastError()获取错误信息 }
else
{
//发送数据成功
//这里加入发送成功的处理代码
}
break;
case WAIT_TIMEOUT:
//读操作失败,原因是超时
break;
default:
//这里加入默认处理代码
break;
}
}
closeHandle(Wol.hEvent);
return 0;
}
5. 关闭串口
使用CloseHandle()函数关闭串口句柄.
CloseHandle(hCom);。