遵循Modbus协议通过Usb(Ch375)通信的上位机传输问题
http://www.delphi2007.net/DelphiNetwork/html/delphi_20061129164355231.html
Usb芯片:Ch375
协议:Modbus
上位机开发软件:delphi
传输格式(RTU):
帧定义:
头|命令|长度|数据(高) ---- 数据(低)|CRC
C0C1C2 S0S1S2 T10T11 T20T21 P0P1
01 02 0C 000000 000000 0000 0000 0000 XX
第一次做这种软件,还没有在下位机上测试,先贴出部分源码,请大家帮忙改正。
还有对于从下位机取出来的数据(已从16进制转换成10进制)有几个疑问:
1。是不是就代表了真实的值,是否还需要再转换;
2。如何处理浮点型数据。
下面是部分源码:
//读数据
function ReadData(iIndex:Integer;var oBuffer:PChar;var ioLength:Integer):Boolean;
stdcall; external DllFile name 'CH375ReadData';
function TPublicFun.GetData:Integer;
var
Buff_HAdd : Byte;//帧:头地址
Buff_Cmd : Byte;//帧:命令
Buff_Len : Byte;//帧:长度
Buff_DatLC : array[1..3] of Byte;//帧:LC数据
Buff_DatLS : array[1..3] of Byte;//帧:LS数据
Buff_DatT1 : array[1..2] of Byte;//帧:T1数据
Buff_DatT2 : array[1..2] of Byte;//帧:T2数据
Buff_DatP : array[1..2] of Byte;//帧:P数据
Buff_Send : array of Byte;//用于存放各个域合成的总的串
I,BuffLen,TempLen : Integer;//传输的实际数据长度
TempStr,CSort : string;//用于存放string类型的测试传输帧
Rtl : Boolean;
PChar_Send : PChar;
begin
Result := -1;
//测试用,假设Usb设备已打开
Usb_Opened := True;
if Usb_Opened then
begin
//设置头地址
Buff_HAdd := HeadCode;
//设置命令
Buff_Cmd := StatusCode;
//设置长度
Buff_Len := strtohex1(IntToHex(17,2),2);
TempLen := 17;
//初始化LC,LS
for I:=1 to 3 do
begin
Buff_DatLC[I] := $00;
Buff_DatLS[I] := $00;
end;
//初始化T,P
for I:=1 to 2 do
begin
Buff_DatT1[I] := $00;
Buff_DatT2[I] := $00;
Buff_DatP[I] := $00;
end;
SetLength(Buff_Send,TempLen);
//设置总串
Buff_Send[1] := Buff_HAdd;
Buff_Send[2] := Buff_Cmd;
Buff_Send[3] := Buff_Len;
Buff_Send[4] := Buff_DatLC[1];
Buff_Send[5] := Buff_DatLC[2];
Buff_Send[6] := Buff_DatLC[3];
Buff_Send[7] := Buff_DatLS[1];
Buff_Send[8] := Buff_DatLS[2];
Buff_Send[9] := Buff_DatLS[3];
Buff_Send[10] := Buff_DatT1[1];
Buff_Send[11] := Buff_DatT1[2];
Buff_Send[12] := Buff_DatT2[1];
Buff_Send[13] := Buff_DatT2[2];
Buff_Send[14] := Buff_DatP[1];
Buff_Send[15] := Buff_DatP[2];
//CRC校验:即校验域前的几个域的集合
I := CRC(Buff_Send[1],15);
Buff_Send[16] := I div 256;
Buff_Send[17] := I mod 256;
//获得string类型的测试传输帧
TempStr := '';
for I:= 1 to TempLen do
TempStr := TempStr + inttohex(Buff_Send[i],2);
BuffLen := TempLen * 2;
PChar_Send := PChar(TempStr);
//发送命令
//测试屏蔽
Rtl := ReadData(Usb_Index,PChar_Send,BuffLen);
if Rtl then
begin
//读取返回串
for I:= 0 to TempLen-1 do
Buff_rev[I+1] := strtohex1(copy(PChar_Send,I*2+1,2),2);
//CRC校验
if CRC_Check=1 then
begin
TempStr := '';
//校验成功,读出数据
//TempStr := TempStr + inttohex(Buff_rev[i+4],2);
for I:= 0 to 2 do
begin
Buff_DatLC[I+1] := Buff_rev[I+4];
Buff_DatLS[I+1] := Buff_rev[I+7];
end;
for I:= 0 to 1 do
begin
Buff_DatT1[I+1] := Buff_rev[I+10];
Buff_DatT2[I+1] := Buff_rev[I+12];
Buff_DatP[I+1] := Buff_rev[I+14];
end;
//数据处理
Result := 1;
//初始化接受串
for I:=1 to Maxlen do Buff_rev[I]:=0;
end;
end
else
begin
ShowMessage('数据传输出错,无法得到正确的值!','提示');
end;
end
else
begin
ShowMessage('没有发现可用的Usb设备!','提示');
end;
end;
对于传输帧,我现在有点疑惑:
我传向下位机请求和从下位机得到数据都是以上面我和下位机开发人员定义的格式传输并不象下面Modbus中的例子,不知可行否?
因为我定义的帧中要获取5个参数的值:LC由C0C1C2构成(高-低)、LS由S0S1S2构成、T1由T10T11构成、T2由T20T21构成、P由P0P1构成。
下面是Modbus RTU中一个例子:
主机请求:
地址 功能码 第一个寄存器的高位地址 第一个寄存器的低位地址 寄存器的数量的高位 寄存器的数量的低位 错误校验
01 03 00 38 00 01 XX
从机应答:
地址 功能码 字节数 数据高字节 数据低字节 错误校验
01 03 2 41 24 XX
数据:16进制=4124,转换成十进制=16676
//发送命令
//测试屏蔽
Rtl := ReadData(Usb_Index,PChar_Send,BuffLen);
ReadData应该是读吧?
你的例子:
请求:
0103 00380001XX
01=>hdr
03=>cmd
00=>len
38=>crc
即无数据,后面的数据就错了
按协议,好像是这样写的
PRTUData = ^TRTUData;
TRTUData = packed record
hdr: Byte;
cmd: Byte;
len: Byte;
data: array [0..0] of Byte;
crc: Byte;
end;
data取决于len的长度
var
P: PByte;
rtuLen: Byte;
rtu: PRTUData;
begin
rtuLen := SizeOf(TRTUData) + DataLen - 1;
rtu := allocMem(rtuLen);
rtu.hdr := $01;
rtu.cmd := $02;
rtu.len := DataLen;
P := @rtu.data[0];
for I := 0 to DataLen - 1 do
beg
P^ := $XX;
inc(P);
end;
P^ := crc(xxx); // P ==> crc
WriteData(..., rtu, rtuLen)
end;
先谢谢ERR0RC0DE()
我知道了,你是先发送命令,WriteData(..., rtu, rtuLen),然后在通过ReadData(Usb_Index,PChar_Send,BuffLen);读取数据,是吧?
看不懂,^_^
顶下!
有兴趣的朋友欢迎继续讨论阿