wince串口通信编程遇到的问题

1.CreateFile()参数的差异
    首先说明一下WinCE和WinXP打开串口时参数的差异.以打开串口COM1为例子,WinCE下的名字为"COM1:",而WinXP为"COM1",两者的唯一区别仅仅在于WinCE下多个分号.
    例如:
    HANDLE hd = CreateFile(TEXT("COM1:"),GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL); //WinCE
    HANDLE hd = CreateFile(TEXT("COM1"),GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL); //WinXP
    在这稍微多说一下,在默认的环境下,TEXT宏在WinCE下会编译为双字节,而WinXP为单字节.换句话说,TEXT("COM1")在WinCE下相当于L"COM1",而WinXP则为"COM1".
    
    
2.单线程比较
   还是用代码做例子来说明比较形象.这是一段单线程的代码,先对串口进行写操作,然后再读.对于WinXP来说,这是同步模式.(与主题无关的代码省略)   
   int WINAPI WinMain( HINSTANCE hInstance,
     HINSTANCE hPrevInstance,
     LPTSTR    lpCmdLine,
     int       nCmdShow)
   {
     ...      
      HANDLE hCom = CreateFile(TEXT("COM1:"),GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL); //WinCE
      //HANDLE Com = CreateFile(TEXT("COM1"),GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL); //WinXP
      
      ...
      DWORD dwBytes;
      if(WriteFile(hCom,TEXT("COM1:"),5,&dwBytes,NULL) == FALSE) //WinCE
      //if(WriteFile(hCom,TEXT("COM1"),5,&dwBytes,NULL) == FALSE) //WinXP
      {
        return 0x05;
      }
      
      ...
      DWORD dwRead;
      char szBuf[MAX_READ_BUFFER];
      if(ReadFile(hCom,szBuf,MAX_READ_BUFFER,&dwRead,NULL) == FALSE)
      {
        return 0x10;
      }
      
      ...
   }
    经过实验,可以发现这段代码在WinCE和WinXP下都能正常工作,并且其表现也相同,都是在WriteFile()函数返回后才执行ReadFile().
    由于异步模式在单线程中也能正常运作,唯一的不同只是在执行WriteFile()时可能也会执行ReadFile()(依WriteFile()函数执行的时间而定),所在此不表.
   
3.多线程比较
    单线程两者表现相同,那多线程呢?下面这段代码采用多线程,先是创建一个读的线程,用来监控串口是否有新数据到达,然后在主线程中对串口写出数据.
    这里假设是这么一个情况,有两台设备,分别为A和B,下面的代码运行于设备A,设备B仅仅只是应答而已.换句话说,只有A向B发送数据,B才会返回应答信号.
    //主线程
    int WINAPI WinMain( HINSTANCE hInstance,
     HINSTANCE hPrevInstance,
     LPTSTR    lpCmdLine,
     int       nCmdShow)
    {
      ...
      CreateThread(NULL,0,ReadThread,0,0,&dwThrdID); //创建一个读的线程.
    
      ...      
      HANDLE hCom = CreateFile(TEXT("COM1:"),GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL); //WinCE
      //HANDLE Com = CreateFile(TEXT("COM1"),GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL); //WinXP
      
      ...
      DWORD dwBytes;
      if(WriteFile(hCom,"AT/r/n",4,&dwBytes,NULL) == FALSE) //WinCE
      //if(WriteFile(hCom,"AT/r/n",5,&dwBytes,NULL) == FALSE) //WinXP
      {
        return 0x05;
      }
      
      ...
    
    }
    
    //读线程
    DWORD WINAPI ReadThread()
    {
      ...
      SetCommMask(hCom),EV_RXCHAR);
      DWORD dwCommStatus = 0;
    if(WaitCommEvent(hCom),&dwCommStatus,NULL) == FALSE)
    {
     //Clear the error flag
     DWORD dwErrors;
     COMSTAT comStat;
     memset(&comStat,0,sizeof(comStat));
     ClearCommError(hCom,&dwErrors,&comStat);
     return 0x15;
    }
    
    ...
    char szBuf[MAX_READ_BUFFER]={0};
    DWORD dwRead;
    if(ReadFile(hCom),szBuf,MAX_READ_BUFFER,&dwRead,NULL) == FALSE || dwRead == 0)
   {
    return 0x20;
   }
   
   ...
    }
    
    这段代码在WinCE下运行完全正常,读线程在监听收到的数据的同时,主线程顺利地往外发数据.
    然而同样的代码,在WinXP下则根本无法完成工作.运行此代码,我们将发现CPU的占用率高达99%.通过单步调试,发现两个线程分别卡在WaitCommEvent()和WriteFile()函数中.因为根据同步模式的定义,当前对设备的操作必须要等待上一个操作完毕方可执行.在以上代码中,因为设备B没接到设备A的命令而不会向设备A发送应答,故WaitCommEvent()函数因为没有检测到接受数据而一直在占用串口;而WaitCommEvent()一直占据串口使得WriteFile()没有得到串口资源而处于阻塞状态,这就造成了死锁.
    而这种情况没有在WinCE上出现,只要WaitCommEvent()和WriteFile()不在同一个线程,就可以正常工作.这应该和系统的调度方式有关.
    
    如果要在PC上同时进行WaitCommEvent()和WriteFile()操作,需要把串口的模式改写为异步模式.
    更改后的代码如下:
    
    //主线程
    int WINAPI WinMain( HINSTANCE hInstance,
     HINSTANCE hPrevInstance,
     LPTSTR    lpCmdLine,
     int       nCmdShow)
    {
      ...
      CreateThread(NULL,0,ReadThread,0,0,&dwThrdID); //创建一个读的线程.
    
      ...      
      HANDLE Com = CreateFile(TEXT("COM1"),GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,FILE_FLAG_OVERLAPPED);
      
      ...
      OVERLAPPED olWrite;
      memset(&olWrite,0,sizeof(m_olWrite));
     olWrite.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
 
      DWORD dwBytes;  
     if(WriteFile(hCom,"AT/r/n",4,&dwBytes,&olWrite) == FALSE)
     {
 if(GetLastError() != ERROR_IO_PENDING)
 {
  return 0x20;
 }
}
if(GetOverlappedResult(hCom,&olWrite,&dwBytes,TRUE) == FALSE)
{
 return 0x25;
}
      ...
    
    }
    
    //读线程
    DWORD WINAPI ReadThread()
    {
      ...     
memset(&olWaite,0,sizeof(olWaite));  
olWaite.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);//人工重置事件,初始为      
 SetCommMask(hCom),EV_RXCHAR);
 DWORD dwCommStatus = 0;
WaitCommEvent(hCom,&dwCommStatus,olWaite);
DWORD dwByte; //norains:It is only suitable for the GetOverlappedResult(),not undefined here.
if(GetOverlappedResult(hCom,olWaite,&dwByte,TRUE) == FALSE)
{
if(GetLastError() != ERROR_IO_PENDING)
{
 return 0x30;
}
//Clear the error flag
DWORD dwErrors;
COMSTAT comStat;
memset(&comStat,0,sizeof(comStat));
ClearCommError(hCom,&dwErrors,&comStat);
return 0x35;
}

...
memset(&olRead,0,sizeof(olRead));
olRead.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
char szBuf[MAX_READ_BUFFER]={0};
DWORD dwRead;
if(ReadFile(hCom,szBuf,MAX_READ_BUFFER,&dwRead,olRead) ==FALSE)
{
if(GetLastError() != ERROR_IO_PENDING)
{
 return 0x40;
}
if(GetOverlappedResult(hCom,olRead,&dwRead,TRUE) == FALSE)
{
 return 0x45;
}
if(dwRead == 0)
{
 return 0x50;
}
}
   
   ...
    }
    
    测试经过更改后的代码,可以发现在WinXP下终于可以同时调用WaitCommEvent()和WriteFile()而不造成死锁.
    在这里可以发现WinCE和WinXP的串口调度的差异性:单线程中,WinCE的串口工作方式和WinXP串口的同步工作模式相符;
而多线程中,WinCE串口工作方式却又和WinXP的异步方式吻合.虽然无法确切比较WinCE的单一串口模式是否比WinXP的双模式更为优越,
但可以确认的是,WinCE的这种串口调用方式给程序员带来了极大的便利.
    
    
4.WinXP异步模式两种判断操作是否成功的方法
    因为在WinXP的异步模式中,WriteFile(),ReadFile()和WaitCommEvent()大部分情况下都是未操作完毕就返回,
所以不能简单地判断返回值是否为TRUE或FALSE来判断.
    以ReadFile()函数做例子.
    一种是上文所用的方法:
    if(ReadFile(hCom,szBuf,MAX_READ_BUFFER,&dwRead,olRead) ==FALSE)
    {
if(GetLastError() != ERROR_IO_PENDING)
{
 return 0x40;
}
if(GetOverlappedResult(hCom,olRead,&dwRead,TRUE) == FALSE)
{
 return 0x45;
}
if(dwRead == 0)
{
 return 0x50;
}
    }
    如果ReadFile()返回为TRUE,则表明读文件已经完成.但这种情况几乎不会出现,因为对外设的读写相对于内存的读写来说非常慢,
所以一般在ReadFile()函数还没执行完毕,程序已经执行到下一个语句.
    当ReadFile()返回为FALSE时,需要采用GetLastError()函数判断读操作是否在后台进行.
如果在后台进行,则调用GetOverlappedResult()函数获取ReadFile()函数的结果.在这里要注意的是,
GetOverlappedResult()函数的最后一个参数必须设置为TRUE,表明要等ReadFile()函数在后台运行完毕才返回.
如果最后一个参数设置为FALSE,则即使ReadFile()还在后台执行,GetOverlappedResult()函数也会立刻返回,从而造成判断错误.
    
    另一种是调用WaitForSingleObject函数达到此目的:
    if(ReadFile(hCom,szBuf,MAX_READ_BUFFER,&dwRead,olRead) ==FALSE)
    {
if(GetLastError() != ERROR_IO_PENDING)
{
 return 0x40;
}
if(WaitForSingleObject(olRead.hEvent,INFINITE) != WAIT_OBJECT_0)
{
return 0x55;

if(GetOverlappedResult(hCom,olRead,&dwRead,FALSE) == FALSE)
{
 return 0x45;
}
if(dwRead == 0)
{
 return 0x50;
}
    }
    因为ReadFile()在后台执行完毕以后,会发送一个event,所以在这里可以调用WaitForSingleObject()等待ReadFile()执行完毕,
然后再调用GetOverlappedResult()获取ReadFile()的最终结果.在这里需要注意的是,
GetOverlappedResult()的最后一个参数一定要设置为FALSE,因为WaitForSingleObject()已经捕获了ReadFile()发出的event,
再也没有event传递到GetOverlappedResult()函数.如果此时GetOverlappedResult()最后一个参数设置为TRUE,
则线程会一直停留在GetOverlappedResult()函数而不往下执行.
--------------------------------------------------------------------------------
eVC中串口编程思路和VC大致相同,但是有几点要注意:
1) Windows CE是Unicode编码,读取字符时候,要注意字节数的确定。
2) eVC不支持重叠I/O,所有的函数中与OVERLAPPED结构有关的参数都必须置为 NULL。
3) eVC不支持BuildCommDCB(),GetOverlappedResult()。
4) eVC中串口的写法和一般VC中的写法不同,如串口1,要写为“COM1:”而不能写为“COM1”。




GetLastError函数可以截获错误码ERROR_IO_PENDING(#define ERROR_IO_PENDING 997),表示操作转到后台运行。在WaitCommEvent函数返回之前,
系统将OVERLAPPED结构中的hEven句柄设置为无信号状态;当WaitCommEvent函数所等待的任何一个Event发生后,系统将OVERLAPPED结构中的hEven句柄
设置为有信号状态,同时将所发生事件赋给lpEvtMask。
父进程可以根据lpEvtMask来做出相应的事件处理,然后也可以调用GetOverlappedResult函数来判断WaitCommEvent的操作是否成功。


overlapped变量的hEvent事件在ReadFile时被置为无信号状态,在重叠操作完成之后,将被置为有信号状态,若操作没有完成而且你的
GetOverlappedResult函数最后一个参数为TRUE时,将会一直阻塞在这里直到读完成或发生错误返回。假如为FALSE那么到了你设定的超时之后
hEvent成员会变成有信号的,那么这时等待将会结束。建议读前判断下缓冲区数据量,和你要读取数据量取最小值读取,并且设置
overlapped.Offset = 0,overlapped.OffsetHigh= 0.





  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
wince串口调试工具是一种用于调试Windows CE操作系统中串口通信功能的工具。它主要用于检测和排除串口通信中的问题,如数据接收发送异常、波特率设置错误等。 wince串口调试工具具有以下主要功能和特点: 1. 连接和配置串口:该工具可以自动扫描并显示可用的串口列表,用户可以选择要调试的串口,并设置波特率、数据位、校验位、停止位等串口参数。 2. 监控和显示串口数据:该工具可以实时读取和显示串口接收的数据,用户可以查看串口接收的数据流,并通过该工具对接收到的数据进行分析和处理。 3. 发送和模拟数据:该工具可以发送用户定义的数据到串口,用户可以通过输入数据并发送到串口进行测试和调试。此外,还可以使用该工具模拟发送数据流,以检查接收端的处理能力。 4. 监控串口状态:该工具可以实时显示并监控串口的状态,包括波特率、数据位、校验位、停止位等参数的实际设置情况,以及串口的接收和发送状态等。 5. 错误检测和排查:该工具可以检测串口通信中的错误,并提供相应的错误提示和解决方案,帮助用户更快地排查和解决串口通信问题。 总之,wince串口调试工具是一种方便实用的工具,可以帮助开发人员和测试人员对Windows CE操作系统中的串口通信进行调试和排查问题,提高开发和测试效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值