菜鸟求救命!!用CSerialPort类,数据接收问题 modbus规约 OnCommunication(WPARAM ch, LPARAM port)

asdjy123 2012-11-13 12:50:07

我在OnCommunication(WPARAM ch, LPARAM port)里接收两种16进制的报文(两种报文长度不一),但不知一个报文什么时候接收结束,并结束能放进数组中,求各位高手指教!!!!
接收到的两种报文是用来解析用的,解析函数我已近写好。

如我要接受的报文为01 03 02 00 00 c5 d5 和01 03 10 01 02 02 20 01 02 02 02 02 02 c5 d5,怎么实现接受到一个报文保存到数组中OnCommunication(WPARAM ch, LPARAM port)改怎么写,或有其他方法。

我用的是modbus规约

//这是我写的只实现显示到文本中。
LONG CModbusSCommDlg::OnCommunication(WPARAM ch, LPARAM port)
{ CString str;
str.Format("%02X ",ch);
m_Receive+=str;//m_Receive为RichEdit的字符变量
BYTE data[50];//后面该怎么写
。。。。。。
show(data,50,len);//这是我处理解析报文的函数,len为报文长度


UpdateData(FALSE);
return 0;
}
...全文
564 27 打赏 收藏 转发到动态 举报
写回复
用AI写文章
27 条回复
切换为时间正序
请发表友善的回复…
发表回复
asdjy123 2012-11-15
  • 打赏
  • 举报
回复
我发现我的CSerialPort是使用OverLapped。 WaitSingleObject()在API没找到,只找到WaitForSingleObject(),超时返回事件为WAIT_TIMEOUT,好像与你说的有点不一样。 最终我还是找到了解决方法:在oncommunication()加了个计时器SetTimer(3,1,NULL);然后再OnTimer()的case 3:加 m_RDataCount=0; PurgeComm(mycom.m_hComm, PURGE_RXCLEAR);//清空接收缓冲区 KillTimer(3); 解决了判断一帧报文的结束与开始问题。 谢谢大侠的一路帮助,希望下次遇到能问题时能再次给帮帮小弟!!
asdjy123 2012-11-15
  • 打赏
  • 举报
回复
嗯,今天我试试,谢谢大侠
I_ask_who 2012-11-14
  • 打赏
  • 举报
回复
API没有事件的概念 如果使用阻塞方式,检查ReadFile()函数返回字节长度是否为0,为零说明超时. 如果使用OverLapped的串口,那么WaitSingleObject()会返回,检查字节长度为0说明超时.
asdjy123 2012-11-14
  • 打赏
  • 举报
回复
是用API写的,就是《[Visual.C.TurboC串口通信编程实践](第2版)》书里第五章里配套代码里考的,原来是一个外国人写的,开源的,书的作者进行改进。 SetCommTimeouts()只是设置,能得到读时间间隔超时的响应事件吗?
I_ask_who 2012-11-14
  • 打赏
  • 举报
回复
SerialPort控件是VS2005开始的自带控件,VC6是没有的. SerialPort.h,SerialPort.cpp应该是你自己写的,不知道用API还是MsComm ActiveX组件,如果是后者就无法设置了. 如果是前者SetCommTimeouts()可以设置
asdjy123 2012-11-14
  • 打赏
  • 举报
回复
你如果用.Net的SerialPort控件的话,ReadTimeout()可能是正确的选项 ".Net的SerialPort控件的话"这是什么。我是直接添加SerialPort.cpp和SerialPort.h两个文件,用VC6.0建一个对话框项目的。这样能像我提的两种方法(19楼)实现吗?
I_ask_who 2012-11-14
  • 打赏
  • 举报
回复
CommTimeouts一般参数级别都在5~30之间,具体定义看API 一般VC用API的ReadFile()直接接收,如果超时,该函数会返回,然后看收到内容长度.由于是阻塞或者异步的操作没有OnXXX的概念. 你如果用.Net的SerialPort控件的话,ReadTimeout()可能是正确的选项 如果是MSCOMM控件,好像没有地方设,10秒好像默认.
asdjy123 2012-11-14
  • 打赏
  • 举报
回复
读字符间间隔设为3ms是直接在CSerialPort类中改的,这样行吗?
asdjy123 2012-11-14
  • 打赏
  • 举报
回复
	// set the timeout values
	m_CommTimeouts.ReadIntervalTimeout = 3;
	m_CommTimeouts.ReadTotalTimeoutMultiplier = 1000;
	m_CommTimeouts.ReadTotalTimeoutConstant = 1000;
	m_CommTimeouts.WriteTotalTimeoutMultiplier = 1000;
	m_CommTimeouts.WriteTotalTimeoutConstant = 1000;
我把读字符间间隔设为3ms,但在响应函数oncommunication()我不知怎么得到超时时间,本来我打算得到超时事件算一帧的结束,这样行吗? 我有想oncommunication()加个计时器,每次计算两次响应oncommunication()的时间间隔,大于3ms算一帧报文结束,这样行吗?如型用什么计时,settimer()好像是固定时间循环的干某件事,好像不行
I_ask_who 2012-11-14
  • 打赏
  • 举报
回复
缺少一般会触发timeout事件,因为串口在干等,SDK的COMMTIMEOUTS结构,或者控件的某个超时属性都可以设置. PurgeComm()加在每次你没办法正确处理之后,就是坏帧处理流程之中. 必须假设通讯有问题,而且是常态,485有时候会发生噪声干扰,设备断线等乱七八糟的事情. 就算正好收到响应数量的帧,也会因为校验不合格而认为损坏. 在更为高级的TCP/IP通讯中这种事情还常有发生,何况串口通讯. 坏帧就重发,很简单.
asdjy123 2012-11-14
  • 打赏
  • 举报
回复
嗯。对多出来的用清空一下缓存.PurgeComm()能行,但缺少的呢? 我每次只接受43后7字节大小数据就行了,假设我接收43字节,但只发送了42字节,少了一字节(也可能少了两三字节)这一帧就结束了,但我计算字节个数变量m_RDataCount=42(实际不知已经接受43个了),下一帧发来时就把第一字节的数当做第43字节数,再接下来就全乱了。我就想知道什么什么时候应该把变量m_RDataCount=0,缓存.PurgeComm()应该加哪?(最好指出具体的代码中)大侠能指点下? 50只是我设定的BYTE型数组大小
I_ask_who 2012-11-14
  • 打赏
  • 举报
回复
这个就是串口缓存的作用,你第一帧多出一个字节,这个字节被存放在缓存中,由于第一帧只要求50字节,第51字节还留在硬件中,第二次读取报头的时候先将这个字节读出来,所以就乱了. 乱了之后只有清空缓存,让发送方重发. 如果嫌麻烦,每一次正确读取后,清空一下缓存.PurgeComm()
asdjy123 2012-11-14
  • 打赏
  • 举报
回复
每帧报文多长,我是可以通过第三个字节(m_RData[2])来判断的,我做的是上位机程序,就是有两个发送按钮(每一个发送固定的报文),通过按下按钮(发一帧报文),就接受一帧下位机的报文并解析报文。现在我用调试精灵试验,发一次错误一帧报文中(如比正常一帧多一个字节),接受也显示正确,但调试精灵接下去正确一帧报文再发过来(发现m_RDataCount不等于0,就这个原因),我就不能处理了,以后也就都错误了。 我的CRC校验是同过调用show()才进行的,我觉得好像是5楼的接受函数不完善,但有不知怎么改,我都想了今天一白天了,都没改成,最终还是原封不动的。 大侠帮忙改改或提提意见,万分感谢!!!
I_ask_who 2012-11-14
  • 打赏
  • 举报
回复
报文什么时候结束要看报文头,如果出错你查看校验位就知道了.出错就清空缓存,让/等发送方重发,也没有其他办法. 计算机接收报文不要去管3.5的事情,这是报文间隔不是字节间隔,这是发送方的责任,一个毫秒都不到你也没办法控制. 1.5可能你指的是停止位,更加是硬件的事情,在建立串口的时候就确定了. 串口打开后,缓存会自动处理接收,用原始串口API只要告诉它你要收几个字节就可以了,它会将缓存里的东西读一点出来给你,甚至对方发了1个报文,你隔1分钟去串口取也取得到,只不过实效性已经不存在了.
asdjy123 2012-11-14
  • 打赏
  • 举报
回复
总之就是要判断出一帧报文结束,在此接收前或后加m_RDataCount=0,就行了。这判断有什么方法吗,网上找了都没找到。
asdjy123 2012-11-14
  • 打赏
  • 举报
回复
你给的代码只能看到懂大概,对我来说有难度,不过可以用来参考,x谢谢大侠指点,你这怎么判断一帧报文的结束?我是用vc开发的能用吗? 还有今天我发现了我写的接受响应函数(就是5楼的代码)有bug了,我只能接受正确报文(我用调试精灵测试的),如果报文少了一字节或多了一字节之后,再发正确的报文就没用了(m_RDataCount已不是从0开始了),接下来都报错误(在RichEdit也只显示在一行了),发现是在要接受正确报文时m_RDataCount没清零,考虑到只要再接收时能判断这个数据是这个报文的最后一个时加m_RDataCount=0;就行了。 CserialPort能判断接受的是最后一个报文了吗?modbus是每帧报文的间隔至少在3.5个字符时间,每帧(我的是每帧至少7个字节报文)里的每两个字节之间的报文时间隔小于1.5字符时间。 求大侠帮帮忙,解决这问题和提意见,再次先谢过了
I_ask_who 2012-11-13
  • 打赏
  • 举报
回复
如果你会用Virtual Factory模式,效率可能更高,不用去扫描vector<>
I_ask_who 2012-11-13
  • 打赏
  • 举报
回复
上述代码中string只是打个比方,modbus是纯正的非ASCII码,要用数组处理
xzhdy 2012-11-13
  • 打赏
  • 举报
回复
好深奥啊
I_ask_who 2012-11-13
  • 打赏
  • 举报
回复

//定义一个虚拟基类作为各种报文处理的接口
class BaseProcess{
 virtual bool IsMyType(string strHead)=0;
 virtual int GetLen(string strHead)=0;
 virtual void Process(string strMsg)=0;
}
//AI类报文
class ProcessAI:public BaseProcess{
...
}
//DI类报文
class ProcessDI:public BaseProcess{
...
}

//你的接收流程
void RecvMsg(){
 vector<BaseProcess*> vec;
 BuildVector(vec);//将各种报文处理类存入
 string strHead=Recv(3);//modbus报文头总是3 bytes长
 for(vector<>::iterator it=m_vec.begin();it!=m_vec.end();++it){
  BaseProcess* pProc=*it;
  if(pProc->IsMyType(strHead)){//扫描何种处理类有能力搞定
   string strMsg=strHead+Recv(pProc->GetLen(strHead));//接收剩余报文
   pProc->Process(strMsg);//处理
   break;
  }
 }
}
加载更多回复(7)

16,472

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧