急急急在线等 modbus MSCOMM控件中oncomm函数中接受数据的len大小问题 pc机与单片机的问题

asdjy123 2012-11-05 07:50:33
求各位大大救命,我是新手。我用window7、vc6.0、SCOmm控件和modbus协议编写串口通信,就是数据的发送和接受并解析报文。问题出现在解析接收报文里,只有两种报文(16进制)解析:01 03 26 01 4E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 64 00 00 00 00 00 00 00 00 00 00 59 0F和01 03 02 01 12 11 22(红色为数据)共43字节。后面一个短报文是能正常解析的,但前面长报文只接受了部分(14字节),后面的就乱了。显示在edit框里的报文对的,就是没14字节换行了。后来发现oncomm函数中缓冲区获取接收到的字符数len(len=OleArray.GetOneDimSize();)为14字节,太小了,我要43个,能否每次响应oncomm函数使len大于43或有其他的办法。


我是在和单片机进行接受和发送报文时出现这种情况,在用虚拟串口在一台pc机子上用调试精灵当从机发报文实现没问题。
void CModbusSCommDlg::OnComm() 
{
// TODO: Add your control notification handler code here
const int Size = 50;
VARIANT Input;
CString temp;
long k;
int len=0;
COleSafeArray OleArray;//构造一个任何类型的空数组.
BYTE rxdata[50];
if (2==m_Comm.GetCommEvent())
{
Input=m_Comm.GetInput(); //读缓冲区
OleArray=Input;//VARIANT型变量转换为ColeSafeArray型变量
len=OleArray.GetOneDimSize();//获取接收到的字符数.也可使用m_com.GetInBufferCount()获取字符数.
for (k=0;k<len;k++)
{
OleArray.GetElement(&k,rxdata+k);//将OleArray数组中第K个元素赋予BYTE数组中第K个元素.
BYTE bt=*(char *)(rxdata+k);//字节数组元素转换字符型
temp.Format("%02X ",bt);//输出两位十六进制值
m_Receive+=temp;
}
//我测到len=14.能否使len大于43
char error[30];
sprintf(error,"len长度为%d",len);
AfxMessageBox(error);
show(rxdata,Size,len);
//m_Receive为界面显示报文的edit框
m_Receive+="\r\n"; //接受框内容换行

}
UpdateData(FALSE);//成员变量的值在控制中体现出来.当为TRUE时,将控件中用户输入的值赋值给成员变量

}

void CModbusSCommDlg::show(BYTE rxdata[], int S, int L)
{
BYTE bt;//中间量
BYTE lbt[38];//储存19个寄存器数据(共38字节)的数组
BYTE sbt[2];//储存遥信开关状态数据的数组
int i;
if(L==7)//处理解析接受7个报文
{
unsigned char data[9];//存放开关的9个状态,0为断开,非0为闭合
for(i=0;i<2;i++)//从7字节数据帧中得到2字节的数据
{
bt=rxdata[i+3];
sbt[i]=bt;
}
BYTE a=sbt[0];//高位数据
data[8]=a&0x1;//第九开关状态
BYTE b=sbt[1];
for(i=0;i<8;i++)//把1-8号8个开关状态数据对应的放到数组data[0]-data[7]中
{
data[i]=b&(1<<i);
}
for(i=0;i<9;i++)//把9个开关状态显示到界面中
{
if(data[i]==0x00)
{
SetSwitchStatus(i+1,FALSE);//使开关状态显示关状态
}
else
{
SetSwitchStatus(i+1,TRUE);//使开关状态显示开状态
}
}
}
else if(L==43)//解析处理43个报文m_edit1-15为15个edit变量除13、14个为float类型,其他为long型
{ ULONG ddata;//用于存放十进制数
float aa;//
for(i=0;i<38;i++)
{
bt=rxdata[i+3];
lbt[i]=bt;
}
ddata=(lbt[0]<<8)+lbt[1];
m_Edit1=ddata; //A相电流
ddata=(lbt[2]<<8)+lbt[3];
m_Edit2=ddata; //B相电流
ddata=(lbt[4]<<8)+lbt[5];
m_Edit3=ddata; //C相电流
ddata=(lbt[6]<<8)+lbt[7];
m_Edit4=ddata; //A相电压
ddata=(lbt[8]<<8)+lbt[9];
m_Edit5=ddata; //B相电压
ddata=(lbt[10]<<8)+lbt[11];
m_Edit6=ddata; //C相电压
ddata=(lbt[12]<<8)+lbt[13];
m_Edit7=ddata; //AB线电压
ddata=(lbt[14]<<8)+lbt[15];
m_Edit8=ddata; //BC线电压
ddata=(lbt[16]<<8)+lbt[17];
m_Edit9=ddata; //CA线电压
ddata=(lbt[18]<<24)+(lbt[19]<<16)+(lbt[20]<<8)+lbt[21];
m_Edit10=ddata; //三相有功功率
ddata=(lbt[22]<<24)+(lbt[23]<<16)+(lbt[24]<<8)+lbt[25];
m_Edit11=ddata; //三相无功功率
ddata=(lbt[26]<<8)+lbt[27];
aa=(float)ddata/100;
m_Edit12=aa; //三相功率因数
ddata=(lbt[28]<<8)+lbt[29];
aa=(float)ddata/100;
m_Edit13=aa; //系统频率
ddata=(lbt[30]<<24)+(lbt[31]<<16)+(lbt[32]<<8)+lbt[33];
m_Edit14=ddata; //有功电度
ddata=(lbt[34]<<24)+(lbt[35]<<16)+(lbt[36]<<8)+lbt[37];
m_Edit15=ddata;//无功电度

UpdateData(FALSE);
}
else
AfxMessageBox("接受数据错误!");
}

我的初始化设置,其他默认。
     m_Comm.SetInputMode(1);//1:表示以二进制方式检取数据
m_Comm.SetRThreshold(1);//参数1表示每当串口接收缓冲区中有多余或等于1个个字符时将引发一个接收数据的OnComm事件
m_Comm.SetInputLen(0);//设置当前接收区数据长度为0
m_Comm.GetInput();//先预读缓冲区以清除残留数据
...全文
208 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
a6008202030 2014-01-17
  • 打赏
  • 举报
回复
说的很好,看完后有解决问题的思路了。
zhuxiaohangn 2013-04-19
  • 打赏
  • 举报
回复
你好,我也遇到了类似的问题,不过我是用PC做MODBUS从的,那就没法设置RThreshold的值了,因为主机发过来的报文长度是不确定的,请问有什么好方法解决吗? 使用VS(VC++)MSComm控件接收数据时不能连续接收(发给它11个字节,它接收时自动分成了两段,一段8字节,一段3字节;但是我需要连起来接收),请问如何解决? 以下是串口初始化代码: m_comm1.put_CommPort(1); m_comm1.put_PortOpen(TRUE); m_comm1.put_RThreshold(1); m_comm1.put_InputMode(1); m_comm1.put_Settings(_T("9600,n,8,1")); m_comm1.put_InBufferCount(0); m_comm1.put_OutBufferCount(0); m_comm1.put_InputLen(0); 以下是接收部分的代码: CString strtemp; VARIANT variant1; COleSafeArray safearray; long len,k; variant1=m_comm1.get_Input(); safearray=variant1; len=safearray.GetOneDimSize(); for(k=0;k<len;k++) {safearray.GetElement(&k,receBuf+k); } 楼上说的超时判断具体应该怎么做?
asdjy123 2012-11-06
  • 打赏
  • 举报
回复
谢谢,搞定了,我分别在两个发送按钮里设不同数据响应函数(m_Comm.SetRThreshold(43)和m_Comm.SetRThreshold(7)),并把初始化m_Comm.SetRThreshold(1)去掉就行,呵呵。 还有就是我的usb转232的驱动程序版本太高了,搞的我试了4台机子才发现。
asdjy123 2012-11-06
  • 打赏
  • 举报
回复
我是PC机是主机,通过两个按钮发送两种不同的报文,按下其中按钮就发一次,这样的话该怎么设置
hdg3707 2012-11-05
  • 打赏
  • 举报
回复
modbus 通信谁主谁从: 1.如果PC机是主机,就好办了:MSCOMM控件可以设收到多少个数据后再响应OnComm函数,这样,2个报文的长度肯定不一样,PC机发送一个报文前先设响应字节数后再发送.当收到下位机发来的数据后再设另一个报文的响应字节数,然后再发第二个报文. 2.如果是下位机是主,PC是从机,那么,就设成只要有一个字节就响应OnComm函数,根据协议里的长度来判断是否接收完毕(就是累加计数值),同时再设个超时判断,也就是所说的心跳报文(这里是超时的时间值),如果接收完报文就清超时的时间值为零,或者是超时间退出接收. 在定时器里判断是否接收超时,当超时就认为无效数据,把接收的数据清除. MODBUS是应答方式,一方发送数据后必须有回应,所以同步是简单的,但单片机也不能发得太快了.两个报文间隔不能太短了,不能低于10毫秒,最好大于55毫秒
asdjy123 2012-11-05
  • 打赏
  • 举报
回复
哎!还是没人答,今天伤心的走了
asdjy123 2012-11-05
  • 打赏
  • 举报
回复
怎么没高手解救俺啊,自己先顶个

16,472

社区成员

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

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

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