Windows 下串口通信(下)

来源:互联网 发布:防范电信网络诈骗征文 编辑:程序博客网 时间:2024/05/15 19:48

作者:小马

 

三 发送数据
往串口发送数据用WriteFile, 这个函数有如下几个参数,
HANDLE hFile
之前用CreateFile打开的有效的串口句柄.
LPCVOID lpBuffer
待发送的数据缓冲区. 把要发送的数据放在这里.
DWORD nNumberOfBytesToWrite
这里指定要发送的字节数, 可以比发送缓冲区总字节数小,具体看实际应用中要发多少.
LPDWORD lpNumberOfBytesWritten
该值由函数返回, 指明函数返回时,实际发送成功的字节数. 这个值很有用, 有些时候,函数返回TRUE, 但由于一些原因(比如超时), 实际发送的少于nNumberOfBytesToWrite, 这种情况要继续发送.
LPOVERLAPPED lpOverlapped
这个参数指出当前发送数据,是用异步,还是用同步. 如果是前者, 传进一个指向OVERLAPPED变量的指针, 如果是后者,该值为NULL.

 

在同步模式下, 发送数据是比较简单的. 可以用类似下面的形式:
If(!WriteFile(hComm, buffer, strlen(buffer), &dwBytesWritten, NULL))
{
 //错误处理.
}
一般情况下,WriteFile都会返回TRUE,数据就会按指定的长度发送成功. 如果想更安全一些,也可以加入类似下面这样的判断.
If(dwBytesWritten != strlen(buffer))
{
    //错误处理
}

 

在同步模式下,WriteFile会一直等待指定的数据发送完毕,直到超时,如果未指定超超时间,数据发送完毕之前,程序会一直挂起.

 

异步模式下的处理稍复杂一些. 首先, 如果WriteFile返回FALSE,不一定表示发送失败, 可以用GetLastError获取错误码,如果错误码是ERROR_IO_PENDING,表示操作正在进行, 这种不是真正的失败. 在错误码是ERROR_IO_PENDING的情况下, 可以用WaitForSingleObject等待操作完成. 这个函数的用法不作过多说明,如果你做一个多线程应用,应该比较熟悉它. 通过串口发送数据时, 这个函数类似下面这样用:

dwRes = WaitForSingleObject(osWrite.hEvent, INFINITE);
switch(dwRes)
{
     // OVERLAPPED structure's event has been signaled. 
    case WAIT_OBJECT_0:
          //处理
     break;
            
    default:
       //错误处理
    break;
}
第一个参数osWrite.hEvent是要等待的事件, 第二个参数是超时时间. 要注意这个超时时间并不是前面讲到的读写超时时间. 它是WaitForSingleObject等待osWrite.hEvent变为有信号的时间.


举个例子,如果前面设置的写超超时间是5秒, WaitForSingleObject第二个参数用INFINITE, 如果5秒写操作还没有完成,这时出现写超时, 但WaitForSingleObject返回的是WAIT_OBJECT_0,而不是WAIT_TIMEOUT. 因为对WaitForSingleObject来说, 写操作已经完成了. 为了避免混淆,建议WaitForSingleObject的第二个参数用INFINITE.
还有一点要注意, WaitForSingleObject返回WAIT_OBJECT_0, 并不表示发送是成功的, 只说明发送数据的操作完成了. 是否成功, 还要通过另一个函数GetOverlappedResult来判断. 这个函数有必要说明一下, 它有如下几个参数:
HANDLE hFile
串口句柄
LPOVERLAPPED lpOverlapped
指向OVERLAPPED变量的指针
LPDWORD lpNumberOfBytesTransferred
这个值返回实际发送的字节数
BOOL bWait
如果该值是TRUE,这个函数会一直等待操作完成才返回, 如果是FALSE, 该函数立即返回, 如果这个时候操作还没有完成, 函数返回FALSE, 用GetLastError可以获取错误码是ERROR_IO_INCOMPLETE


如果你细心的话,可能已经发现, GetOverlappedResult也要传进一个指向OVERLAPPED变量的指针, 加上对bWait参数的描述, 很明显,这个函数跟WaitForSingleObject功能基本相同, 是的,在串口应用中,它们两个的用法基本相同, 当bWait为TRUE时, 跟WaitForSingleObject的第二个参数用INFINITE效果是一样的. 但GetOverlappedResult可以判读操作是否成功, 方法就是当WaitForSingleObject返回WAIT_OBJECT_0时,调用GetOverlappedResult,如果它返回TRUE就表示操作成功完成, 否则表示操作失败. 调用过程类似下面的形式:
dwRes = WaitForSingleObject(osWrite.hEvent, INFINITE);
switch(dwRes)
{
       
   case WAIT_OBJECT_0:
        if (!GetOverlappedResult(hComm, &osWrite, &dwWritten, FALSE))
            //操作失败
        else
          //操作成功完成
  break;
            
 default:
   // An error has occurred in WaitForSingleObject.
   //错误处理

 break;
}
这样,在异步模式下的写操作, 就可以用类似下面这样的语句来实现:
if (!WriteFile(hComm, lpBuf, dwToWrite, &dwWritten, &osWrite)) {
      if (GetLastError() != ERROR_IO_PENDING)
{
         //真正的出错情况
         fRes = FALSE;
 }
else// 操作还未完成, 等待操作完成
    dwRes = WaitForSingleObject(osWrite.hEvent, INFINITE);
    switch(dwRes)
         {
               case WAIT_OBJECT_0:// 写操作完成, 但不确定是否成功?
                   if (!GetOverlappedResult(hComm, &osWrite, &dwWritten, FALSE))
                  {
                      //操作失败,错误处理
                  }
                 else
                 //操作成功
                 break;
           
             default:
                 //错误处理
                 break;
         }
      }
   }


有时候看到一些串口通信的代码,发现有些异步模式下的写操作并没有上面那样繁琐的判断. 有些只用了WaitForSingleObject,或者只用了GetOverlappedResult, 其实也是可以的, 有些简单的应用场合并没有必要做这么细致的流程设计, 上述流程是MSDN推荐的,应该算是比较完整和健壮的.

 

四 接收数据
接收数据的操作跟发送数据差别不大,在接收数据之前, 一般会先用ClearCommError读取接收缓冲区中有多少未读的数据(未用ReadFile读取), 然后读取这个长度的数据. 如果读到的数据长度小于自己想要读的长度,则继续读.

ClearCommError获取接收缓冲区未读数据原理是传出一个指向COMSTAT 的参数变量, COMSTAT中的cbInQ ue成员就是当前未被读出的接收到的数据长度.

 

这里有一个问题要讨论一下, 系统什么时候置cbInQue不为零呢, 答案是, 它跟前面讲到的读字节超时间隔有关系, 它的机制是几个byte超时时间内没有收到数据,认为这一次接收完成,然后用当前接收到的字节数置cbInQue.


举个例子, 设备返回给主机的字节一共有10个, 每隔100ms返回一个, 如果, ReadIntervalTimeout设置为10ms, 那么, 主机读到一个字节就会置cbInQue为1, 如果ReadIntervalTimeout设置大一些,比如100ms, 那么主机会读完10个字节才会置cbInQue为10.实际应用中,ReadIntervalTimeout应该设置大一些还是小一些要根据不同应用场合. 明白了原理,就可以灵活的应对各种应用
 
其实对于串口通信, 还有更复杂的高阶应用,在这些应用中会涉及到多线程, 窗体的消息传递与通知(与MFC结合)等, 如果有时间,后续我会接着写相关的文章. 但用上面讲到的那些,设计一个简单实用的串口通信类应该不成问题.

原文 http://ponymaggie.blog.sohu.com/167517052.html

原创粉丝点击