Chinaunix首页 | 论坛 | 博客
  • 博客访问: 537531
  • 博文数量: 576
  • 博客积分: 40000
  • 博客等级: 大将
  • 技术积分: 5020
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-13 14:47
文章分类

全部博文(576)

文章存档

2011年(1)

2008年(575)

我的朋友

分类:

2008-10-14 14:53:33

多线程,多接收模式串口类 LsComm 之二

作者:




描述:一个串口通讯类
应用平台:Windows
版本:v1.2

上次我做的“”,说实在的有不少的问题 。 好不容易有一段空闲的时间,把以前发现的Bugs修改了一下。

一、Bugs修正

1.ERR : 修改了98下 AutoReceiveBySignal 模式不能正常执行的bug
原因: CcomPort::m_WriteOverlapped.hEvent 没有设置事件!!!!粗心,害人阿。导致在Win98下发送数据异常,不过奇怪在
Win2K正常,而且测试的时候忽略了测试环境平台的影响。
修改: 相应的代码
    if(this->IsOverlapped())   
    {    
        this->m_hWriteEvent= ::CreateEvent(NULL,true,false,NULL);   
        if(this->m_hCloseEvent==NULL)	   return false;   
        this->m_WriteOverlapped.hEvent = this->m_hWriteEvent;   
    }
2.ERR : 修改了 ManualReceiveByQuery 模式下,发送会出现不动的情况。
原因: 在
dwWriteBytes= this->m_pPort->Write(pBuf,Count);

前,串口已经设置中断,所以需要等待中断事件发生。
修改: 去掉中断设置的代码 this->m_pPort->GetSerialPort()->SetMask(dwStoredFlags);

3.ERR : 打开一个计算机上不存在的串口的时候没有异常捕获。
原因: 没有捕获 CserialPort 抛出的 CserialException 异常
修改: 不过对于异常的处理改为不抛出异常,不知道是否妥当?

try        
{
   this->m_pPort->Open(nPort,dwBaud,spParity,DataBits,spStopbits,spFC,m_IsOverlapped);    
}   
catch(CSerialException* pE)   
{   
   //AfxMessageBox(pE->GetErrorMessage());   
   pE->Delete();   
       return false;   
} 
4.ERR : 感觉 AutoReceiveByBreak 意义不大应该去掉
原因: 全部的事件都可以用AutoReceiveBySignal方式实现
修改: 暂时保留:DWORD dwStoredFlags = EV_BREAK | EV_CTS | EV_DSR | EV_ERR | EV_RING |EV_RLSD | EV_RXCHAR | EV_RXFLAG ;
//??| EV_TXEMPTY 添加后在首次执行时总是接收不到全部数据 ,以后就正常了。现在还搞不清楚是什么原因。

5. ERR : 感觉 ManualReceiveByConst 意义不大应该去掉
原因: 实现方式不太好
修改: 暂时保留

二、遗留问题

1.ERR : 流控制模式下可能不正确,自己在这方面没有经验,没有测试。

2.ERR : 结构问题,把 this->m_ComPort.GetSerialPort() 获取 CserialPort 的指针好像不妥,如果类的使用者在程序中这样用
this->m_ComPort.GetSerialPort()->Close();
收发数据的时候就会产生异常。但是由于使用别人的CserialPort类 ,比较
稳定,但有不能破坏别人代码的完整性。

3.ERR : ManualReceiveByConst 方式受 CommTimeOUts 的影响可能不太准确。

其它问题:我的计算机上只有一个串口可用,所以是2-3口短接进行的测试,其它测试还不太完全。

三、由于前一段时间确实比较忙,有好多网友提出的问题,都没有回复,实在非常抱歉,在这里简单的回复一下

1.风也飘飘:你好,我感觉这个类做的很好,但我想实现两台电脑之间的实时数据传输(为字符型),不知道怎么用,可否指点一二?
答: 感觉可以这样写: (1)首先:定义一个接收函数:
void OnReceiveData(LPVOID pSender,void* pBuf,DWORD InBufferCount)   
{//在此处理要接收的数据   
} 
(2)然后,打开串口,监听Com2
    this->m_ComPort.Open(2,LsComm::CComPort::AutoReceiveBySignal );     
    this->m_ComPort.SetReceiveFunc((FOnReceiveData)OnReceiveData,this);
(3)发送:
    char a[10000];//字符数组     
    BYTE b[10000];//字节数组     
    memset(a,''''a'''',sizeof(a));     
    memset(b,0x0b,sizeof(b));     
    this->m_ComPort.Output(a,sizeof(a)); //发送字符数组     
    this->m_ComPort.Output(b,sizeof(b)); //发送字节数组    
感觉在C里面是不分 char 和字节的,像 char c=’a’;和char c=0x61;是一样的,只不过 char 只能取字符类型范围,超过就被截短。

2. zkf00:中断接收函数OnComBreak怎么用? 答:试验了一下
void OnComBreak(LPVOID pSender,DWORD dwMask,COMSTAT stat)    
{    
	//deal with the break of com here    
    switch(dwMask)    
    {    
	case  EV_BREAK:    
	{    
		break;    
	}    
	case EV_CTS: //在这里处理CTS信号    
	{    
		break;    
	}
    } 
}	  
3. greatim:DataWaiting 是有什么用的??在例子程序里没有引用到。而且在 Open 的函数里,CreateEvent 被屏蔽了,是否代表 DataWaiting 这函数不能使用呢? DataWaiting 和 Attech 有什么关系?
答:因为 DataWaiting 是 PJ Naughter 写的,请仔细看他的源码:
    BOOL CSerialPort::DataWaiting(DWORD dwTimeout)    
    {    
       ASSERT(IsOpen());    
       ASSERT(m_hEvent);    
       //Setup to wait for incoming data    
       DWORD dwOldMask;    
       GetMask(dwOldMask);    
       SetMask(EV_RXCHAR);//1.设置接收中断事件    
   
       //Setup the overlapped structure    
       OVERLAPPED o;    
       o.hEvent = m_hEvent;    

       //Assume the worst;    
       BOOL bSuccess = FALSE;    

       DWORD dwEvent;    
       bSuccess = WaitEvent(dwEvent, o);//2. 设置监听    
       if (!bSuccess)    
       {//3.dwTimeOut为所等待的时间,有数据收到,返回发现数据    
           if (WaitForSingleObject(o.hEvent, dwTimeout) == WAIT_OBJECT_0)    
           {    
              DWORD dwBytesTransferred;    
              GetOverlappedResult(o, dwBytesTransferred, FALSE);    
              bSuccess = TRUE;    
           }    
       }    

       //Reset the event mask    
       SetMask(dwOldMask);    

       return bSuccess;    
       }
这好像与 Attach 没什么关系吧?

4. Sander:在win2000下能用no overlapped吗?
答:查看了一下 MSDN 中 CreateFile 的说明,没有在 Win2K 下的使用限制,应该是可以的。

5. Sander:当用 ExecuteByAutoSignalRecvMode,
BOOL bSuccess = ReadFile(m_hComm, lpBuf, dwCount, &dwBytesRead, &overlapped); 
dwSignaledHandle=::WaitForMultipleObjects(3,WaitHandles,false,INFINITE); 

this->m_pPort->GetSerialPort()->GetOverlappedResult(overlapped,this->m_InBufferCount,false) 

这个两个函数

WaitForMultipleObject 

GetOverlappedResult)

它会以 COMMTIMEOUTS 中设置的 timeout 来返回吗?也就是说 Overlapped 中的 Event 是在什么时候激活的?

答:这个问题我以前没有仔细考虑,真是不好意思。好像 COMMTIMEOUTS的TimeOut 仅对 ReadFile,WriteFile 起作用,具体可以看 MSDN 中 COMMTIMEOUTS 的描述 。

WaitForSingleObject(m_ReadOverlapped.hEvent,dwMilliseconds)==WAIT_OBJECT_0)

会等待 m_ReadOverlapped.hEvent 事件置信号标志的时候返回。那么读取 时间什么时候返回呢,找了下面的一段话:

When reading from a communications device, the behavior of ReadFile is governed by the current 
communication time-outs as set and retrieved using the SetCommTimeouts and GetCommTimeouts 
functions. Unpredictable results can occur if you fail to set the time-out values. For more 
information about communication time-outs, see COMMTIMEOUTS.

可见 SetCommTimeouts 对这 ReadFile 起作用,也就是对 m_ReadOverlapped.hEvent 起作用。因此定时间接收模式在 COMMTIMEOUTS 时也会置读事件的 hEvent,所以等待 时间的限制就有可能不太准确。

6. Hi_nihaoma:为什么我使用重叠方式打开串口,根据示例:

for(int j= 0; j< 10; j++)    
{    
	if (!port2.Write(pBuf, 10000, overlapped))    
	{    
		DWORD dwBytesWritten;    
		WaitForSingleObject(event, INFINITE);    
		port2.GetOverlappedResult(overlapped, dwBytesWritten, TRUE);    
	}    
	if (!port2.Read(pBuf, 10, overlapped))    
	{    
		DWORD dwBytesRead;    
		if (WaitForSingleObject(event,1000) == WAIT_OBJECT_0)    
		{    
			TRACE(_T("Data was read from the serial port\n"));    
			port2.GetOverlappedResult(overlapped,dwBytesRead,FALSE);    
		}    
		else    
			TRACE(_T("No data was read from the serial port\n"));    
	}    
	
	port2.SetMask(EV_TXEMPTY);     
	port2.WaitEvent(dwMask,overlapped);       
}    
port2.GetOverlappedResult(overlapped,dwBytesRead,FALSE);    
dwByteRead= 4啊???急急急!!!    
答:感觉可能是接收的问题,不能所有容纳发送的全部数据,而引发异常吧。看你这行port2.Write(pBuf, 10000, overlapped);一次发送这么大的数据量 ,是不是这的问题, 我的计算机上只有一个串口,把2,3口连接后收发数据测试了一下。一次发送超过100个就会发生读地址错误,但是低于这个速率就没问题。可能是接收的速度跟不上吧 。具体问题正在找。

结束语:
  
本来早就想要改一下,可实在是没有时间,不是开玩笑,大概3个月没有时间上网看看新闻了。写程序,就是要认真仔细的面对自己写的每一行代码 ,不放过自己的每一个Bug。谁都希望自己的程序不出一个Bug,可这实在是说起来容易做起来难。希望大家看到Bugs要贴在下面,有时间我会努力改的。上面的一些问题自己的水平实在是太有限了 ,搞得还不是太清楚,希望如果谁知道,就告诉偶。有些问题就得摆出来,然后再一点点搞清楚才会有提高。如果只是默许的认为简单,容易,不求甚解,相反会害了自己。

--------------------next---------------------

在98下退出程序 就会出现死机,那个例子也是一样 在98下退出程序就会死机, 不只是怎么回事?清高手指点!小弟再此谢了! ( jnsd 发表于 2006-10-11 15:12:00)
 
就是关闭的时候,有点问题,不应该是Sleep(1000),而应该使用WaitForSingleObject(readThread.Handle)来等待读线程结束。还是感谢作者的Nice Job. ( dyj057 发表于 2006-6-29 15:30:00)
 
请问如何设置串口发送校验方式,串口设备句柄老是死掉,希望高手指点!!!

m_SerialPort.Open(1);
//改变校验方式为标志1
DCB dcb;
dcb.DCBlength = sizeof(DCB);
m_SerialPort.GetState(dcb);
dcb.Parity = MARKPARITY:
m_SerialPort.SetState(dcb);
//传送设备地址符(DeviceAddress为一设备地址位)
m_SerialPort.TransmitChar(DeviceAddress);
dcb.Parity = SPACEPARITY;
m_SerialPort.SetState(dcb);
//传送设备状态
CString strDeviceState(_T(""));
for(int nItemIndex=0;nItemIndex<10;nItemIndex++)
{
    strDeviceState.AppendChar(nItemIndex);//灯索引
    strDeviceState.AppendChar(TRUE);      //灯状态
}
strDeviceState.AppendChar(FinshTag); //完成标志 ( LYKNet 发表于 2004-7-31 9:14:00)
 
.......................................................

--------------------next---------------------

阅读(223) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~