You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
442 lines
13 KiB
442 lines
13 KiB
FUNCTION_BLOCK BS_ModbusCom
|
|
VAR_EXTERNAL
|
|
|
|
END_VAR
|
|
|
|
VAR_INPUT
|
|
|
|
SerialCom : byte;
|
|
NetSend : array[0..64] of NetSendConfig;
|
|
NetSendDelay : time;
|
|
ComSend : array[0..64] of ComSendConfig;
|
|
ComSendDelay : time; (*系统默认t#200ms*)
|
|
ComRecvDelay : time; (*系统默认t#120ms*)
|
|
ComAnalysisDelay: time; (*系统默认t#150ms*)
|
|
DataReadLength : int;
|
|
|
|
END_VAR
|
|
|
|
VAR_OUTPUT
|
|
|
|
DataStr : array[0..128] of byte;
|
|
DataRead : array[0..128] of DataFormat;
|
|
|
|
END_VAR
|
|
|
|
VAR
|
|
|
|
SendFB : NW_SerSend;
|
|
RecvFB : NW_SerRecv;
|
|
SendP : pointer;
|
|
RecvP : pointer;
|
|
Send_act : bool;
|
|
Recv_act : bool;
|
|
i,j,k : int;
|
|
i0,i1,i2,i3 : int;
|
|
CRC16Lo1 : Byte;
|
|
CRC16Hi1 : Byte;
|
|
CRC16Lo : Byte;
|
|
CRC16Hi : Byte;
|
|
CL,CH : Byte;
|
|
SaveHi,SaveLo : Byte;
|
|
Flag : int;
|
|
ENO_Count0 : dword;
|
|
ENO_Count1 : dword;
|
|
timer1 : ton;
|
|
timer1_run : bool;
|
|
NetVarSend1 : NetVarSend;
|
|
t1 : ton;
|
|
t1_in : bool;
|
|
t1_q : bool;
|
|
t2 : ton;
|
|
t2_in : bool;
|
|
t2_q : bool;
|
|
t3 : ton;
|
|
t3_in : bool;
|
|
t3_q : bool;
|
|
Commandsend : int;
|
|
SendStr : array[0..8] of byte;
|
|
RecvStr : array[0..200] of byte;
|
|
CalcStr : array[0..200] of byte;
|
|
ErrStr : array[0..100] of int;
|
|
Recv_len : int;
|
|
Recv_lenp : int;
|
|
Recv_DataLen : int;
|
|
Recv_CRCLo : int;
|
|
Recv_CRCHi : int;
|
|
TestMode : bool;
|
|
StopMem : int;
|
|
ComTimeoutCount : int;
|
|
ComTimeoutClear : bool;
|
|
checkmark1 : bool;
|
|
checkmark2 : bool;
|
|
checkmark3 : bool;
|
|
checkmark4 : bool;
|
|
|
|
DATA1 : int;
|
|
DATA2 : int;
|
|
|
|
TempStr : array[0..128] of byte;
|
|
TempReal : real;
|
|
v2v : NW_MemCopy;
|
|
P1 : Pointer;
|
|
P2 : Pointer;
|
|
|
|
END_VAR
|
|
(*
|
|
时 间:20200915
|
|
版 本:1.3
|
|
作 者:姚立
|
|
名 称:通讯功能块
|
|
说 明:
|
|
支持Modbus协议的03/04命令的读取及解析
|
|
支持读取失败次数查询
|
|
支持读取失败数据清除或保留设置
|
|
支持Modbus协议的06命令的单个寄存器写入
|
|
进一步修正了通讯错位问题
|
|
|
|
input 输入说明
|
|
|
|
SerialCom : byte;
|
|
NetSend : array[0..64] of NetSendConfig;
|
|
NetSendDelay : time;
|
|
ComSend : array[0..64] of ComSendConfig;
|
|
ComSendDelay : time;
|
|
ComRecvDelay : time;
|
|
ComAnalysisDelay: time;
|
|
DataReadLength : int;
|
|
DataType : array[0..256] of int ;
|
|
|
|
output 输出说明
|
|
|
|
DataStr : array[0..256] of byte;
|
|
DataRead : array[0..256] of DataFormat;
|
|
|
|
config 读写设置
|
|
|
|
ComSend[1].Enable :=1; 读取启用1 禁用为0,测试为2,测试时,该码被跳过,但不中断;
|
|
ComSend[1].EquipAddr :=1; 设备站号0-255
|
|
ComSend[1].FunctionCode :=16#03; 功能码 目前支持 读取HR区16#03,读取AR区16#04 ,写入HR区,16#06
|
|
ComSend[1].StartAddr :=2504; Modscan测试地址
|
|
ComSend[1].Length :=12; 读取寄存器数量
|
|
ComSend[1].StartMem :=0; 存放入DataStr数组起始位置,一般为前一数据的起始地址+前一数据读取寄存器长度*2
|
|
ComSend[1].WriteDataH1 :=0; 写入数据的高位,读取时不需要输入
|
|
ComSend[1].WriteDataL1 :=WriteTestW; 写入数据的低位,读取时不需要输入
|
|
|
|
read 读取数据
|
|
|
|
DataType[00]:=1; 数据类型设置 1 int 2 float 3 byte 0 任意,数据多时不建议为0
|
|
|
|
DATA:=DataRead[00].TO_BYTE; 分别对应 DataType 3
|
|
DATA:=DataRead[00].TO_REAL; 分别对应 DataType 2
|
|
DATA:=DataRead[00].TO_INT; 分别对应 DataType 1
|
|
|
|
|
|
备 注:
|
|
目前为示例程序,暂未完成封装,测试程序需两台EXC5000C3版本以上CPU,本程序下载CPU配置COM口为自由口,另一CPU配置COM口为Modbus
|
|
配套示例读取程序为表具仿真程序MeterSim
|
|
依赖块:无
|
|
|
|
*)
|
|
|
|
(**********************************************通讯配置**********************************************)
|
|
(*
|
|
以下延迟时间关系为基于EXC1000COM口485读取,测试速度为9600BPS所得
|
|
若简短和延长,需同步调整,比例关系参考目前关系
|
|
延迟时间和总线波特率,被访问设备响应速度有关,需根据实际情况调整
|
|
*)
|
|
|
|
|
|
|
|
(********************************************串口通讯发送********************************************)
|
|
|
|
t1_in:=not t1.q;
|
|
t1(IN :=t1_in , PT :=ComSendDelay );
|
|
t2(IN :=t1_in , PT :=ComRecvDelay );
|
|
t3(IN :=t1_in , PT :=ComAnalysisDelay );
|
|
|
|
if t1.q=0 and t1_q=1 then
|
|
Send_act:=1;
|
|
else
|
|
Send_act:=0;
|
|
end_if;
|
|
|
|
if t2.q=1 then
|
|
Recv_act:=1;
|
|
else
|
|
Recv_act:=0;
|
|
end_if;
|
|
|
|
if ComSend[1].Enable=1 then (*序列首位有配置的话开始执行发送命令*)
|
|
if Send_act=1 then
|
|
if TestMode=0 then
|
|
Commandsend:=Commandsend + 1;
|
|
end_if;
|
|
end_if;
|
|
if ComSend[Commandsend].Enable=0 then (*执行到非启用位结束*)
|
|
Commandsend:=0;
|
|
end_if;
|
|
|
|
if ComSend[Commandsend].Enable=1 then
|
|
(*读取命令*)
|
|
if ComSend[Commandsend].FunctionCode = 16#03 then
|
|
SendStr[0]:=ComSend[Commandsend].EquipAddr;(*地址*)
|
|
SendStr[1]:=ComSend[Commandsend].FunctionCode;(*功能码*)
|
|
SendStr[2]:=int_to_byte((ComSend[Commandsend].StartAddr-1) / 256);(*起始地址高位*)
|
|
SendStr[3]:=int_to_byte((ComSend[Commandsend].StartAddr-1) mod 256);(*起始地址低位*)
|
|
SendStr[4]:=0;(*长度高位*)
|
|
SendStr[5]:=ComSend[Commandsend].Length;(*长度低位*)
|
|
end_if;
|
|
(*读取命令*)
|
|
if ComSend[Commandsend].FunctionCode = 16#04 then
|
|
SendStr[0]:=ComSend[Commandsend].EquipAddr;(*地址*)
|
|
SendStr[1]:=ComSend[Commandsend].FunctionCode;(*功能码*)
|
|
SendStr[2]:=int_to_byte((ComSend[Commandsend].StartAddr-1) / 256);(*起始地址高位*)
|
|
SendStr[3]:=int_to_byte((ComSend[Commandsend].StartAddr-1) mod 256);(*起始地址低位*)
|
|
SendStr[4]:=0;(*长度高位*)
|
|
SendStr[5]:=ComSend[Commandsend].Length;(*长度低位*)
|
|
end_if;
|
|
(*写入命令*)
|
|
if ComSend[Commandsend].FunctionCode = 16#06 then
|
|
SendStr[0]:=ComSend[Commandsend].EquipAddr;(*地址*)
|
|
SendStr[1]:=ComSend[Commandsend].FunctionCode;(*功能码*)
|
|
SendStr[2]:=int_to_byte((ComSend[Commandsend].StartAddr-1) / 256);(*起始地址高位*)
|
|
SendStr[3]:=int_to_byte((ComSend[Commandsend].StartAddr-1) mod 256);(*起始地址低位*)
|
|
SendStr[4]:=ComSend[Commandsend].WriteDataH1;
|
|
SendStr[5]:=ComSend[Commandsend].WriteDataL1;
|
|
end_if;
|
|
end_if;
|
|
|
|
if ComSend[Commandsend].Enable=2 then
|
|
SendStr[0]:= 16#00 ;
|
|
SendStr[1]:= 16#00 ;
|
|
SendStr[2]:= 16#00 ;
|
|
SendStr[3]:= 16#00 ;
|
|
SendStr[4]:= 16#00 ;
|
|
SendStr[5]:= 16#00 ;
|
|
end_if;
|
|
|
|
(*CRC校验*)
|
|
CRC16Lo := 255;
|
|
CRC16Hi := 255;
|
|
CL := 1;
|
|
CH := 160;
|
|
|
|
for i := 0 To 5 by 1 do
|
|
CRC16Lo := CRC16Lo xor SendStr[i];
|
|
for Flag := 0 to 7 by 1 do
|
|
SaveHi := CRC16Hi;
|
|
SaveLo := CRC16Lo;
|
|
CRC16Hi := shr(CRC16Hi,1);
|
|
CRC16Lo := shr(CRC16Lo,1);
|
|
|
|
if ((SaveHi and 1) = 1) then
|
|
CRC16Lo := CRC16Lo Or 128;
|
|
end_If;
|
|
If ((SaveLo and 1) = 1) then
|
|
CRC16Hi := CRC16Hi xor CH;
|
|
CRC16Lo := CRC16Lo xor CL;
|
|
end_if;
|
|
end_for;
|
|
end_for;
|
|
|
|
SendStr[6]:=CRC16Lo;
|
|
SendStr[7]:=CRC16Hi;
|
|
|
|
end_if;
|
|
|
|
|
|
if t3.q=1 then
|
|
|
|
(*接收数据处理*)
|
|
if Recv_len>=6 then(*有返回*)
|
|
Recv_lenp :=Recv_len-3;(*去掉校验位-2,0开始-1*)
|
|
|
|
CRC16Lo := 255;
|
|
CRC16Hi := 255;
|
|
CL := 1;
|
|
CH := 160;
|
|
|
|
for i := 0 To Recv_lenp by 1 do
|
|
CRC16Lo := CRC16Lo xor RecvStr[i];
|
|
for Flag := 0 to 7 by 1 do
|
|
SaveHi := CRC16Hi;
|
|
SaveLo := CRC16Lo;
|
|
CRC16Hi := shr(CRC16Hi,1);
|
|
CRC16Lo := shr(CRC16Lo,1);
|
|
|
|
if ((SaveHi and 1) = 1) then
|
|
CRC16Lo := CRC16Lo Or 128;
|
|
end_If;
|
|
If ((SaveLo and 1) = 1) then
|
|
CRC16Hi := CRC16Hi xor CH;
|
|
CRC16Lo := CRC16Lo xor CL;
|
|
end_if;
|
|
end_for;
|
|
end_for;
|
|
CRC16Hi1:=CRC16Hi;
|
|
CRC16Lo1:=CRC16Lo;
|
|
|
|
Recv_CRCLo:=Recv_lenp+1;
|
|
Recv_CRCHi:=Recv_lenp+2;
|
|
|
|
(*
|
|
funtionCode03
|
|
Tx 设备地址 03 起始地址高位 起始地址地位 寄存器数量高位 寄存器数量低位 CRCH CRCL
|
|
Rx 设备地址 03 字节数 寄存器1高位 寄存器1低位... CRCH CRCL
|
|
Recv_DataLen =(RecvStr[2])-1 是因为数据长度=“字节数”(字节数刚好是RecvStr[2])-1(i从0开始,所以-1)
|
|
j = i+3 是因为数据位是从第3位之后开始的
|
|
|
|
funtionCode06
|
|
Tx 设备地址 06 起始地址高位 起始地址地位 寄存器值高位 寄存器值低位 CRCH CRCL
|
|
Rx 设备地址 06 起始地址高位 起始地址地位 寄存器值高位 寄存器值低位 CRCH CRCL
|
|
Recv_DataLen =(RecvStr[2])-1 是因为数据长度=2(单个寄存器读取只有2位)-1(i从0开始,所以-1)
|
|
j = i+4 是因为数据位是从第4位之后开始的
|
|
|
|
*)
|
|
if ComSend[Commandsend].FunctionCode = 16#03 or ComSend[Commandsend].FunctionCode = 16#04 then
|
|
Recv_DataLen:=byte_to_int(RecvStr[2])-1;
|
|
end_if;
|
|
if ComSend[Commandsend].FunctionCode = 16#06 then
|
|
Recv_DataLen:=1;
|
|
end_if;
|
|
if Commandsend<>0 then
|
|
if RecvStr[Recv_CRCLo]=CRC16Lo and RecvStr[Recv_CRCHi]=CRC16Hi then(*校验正确*)
|
|
|
|
for i:=0 to Recv_DataLen by 1 do
|
|
if ComSend[Commandsend].FunctionCode = 16#03 or ComSend[Commandsend].FunctionCode = 16#04 then
|
|
j:=i+3;
|
|
end_if;
|
|
if ComSend[Commandsend].FunctionCode = 16#06 then
|
|
j:=i+4;
|
|
end_if;
|
|
if (SendStr[2]=int_to_byte((ComSend[Commandsend].StartAddr-1) / 256)) and (SendStr[3]=int_to_byte((ComSend[Commandsend].StartAddr-1) mod 256) )and (SendStr[1]=ComSend[Commandsend].FunctionCode) then(*功能码*)
|
|
CalcStr[i]:=RecvStr[j];
|
|
k:=ComSend[Commandsend].StartMem+i;
|
|
DataStr[k]:=CalcStr[i];
|
|
end_if;
|
|
end_for;
|
|
ErrStr[commandsend]:=0;
|
|
else
|
|
(*错误累计*)
|
|
ErrStr[Commandsend]:=ErrStr[Commandsend]+1;
|
|
if ErrStr[Commandsend]>999 then
|
|
ErrStr[Commandsend]:=999;
|
|
end_if;
|
|
end_if;
|
|
end_if;
|
|
(*清空接收*)
|
|
for i:=0 to 200 by 1 do
|
|
RecvStr[i]:=0;
|
|
CalcStr[i]:=0;
|
|
end_for;
|
|
|
|
(*错误返回累计超时,清空内容*)
|
|
if ErrStr[Commandsend]>ComTimeoutCount and ComTimeoutClear=1 then
|
|
StopMem:=ComSend[Commandsend].StartMem+byte_to_int(ComSend[Commandsend].Length);
|
|
for i:=ComSend[Commandsend].StartMem to StopMem by 1 do
|
|
DataStr[i]:=0;
|
|
end_for;
|
|
end_if;
|
|
|
|
end_if;
|
|
|
|
end_if;
|
|
|
|
|
|
|
|
|
|
SendP:=&SendStr;
|
|
RecvP:=&RecvStr;
|
|
SendFB(EN :=Send_act , COM :=SerialCom , DATA :=SendP , LENGTH :=8 , IQM :=0 );
|
|
RecvFB(EN :=Recv_act , COM :=SerialCom , DATA :=RecvP , IQM :=0 | Recv_len:= LENGTH);
|
|
|
|
t1_q:=t1.q;
|
|
t2_q:=t2.q;
|
|
t3_q:=t3.q;
|
|
|
|
(********************************************数据处理********************************************)
|
|
|
|
for i := 0 To DataReadLength by 1 do
|
|
|
|
(* Type 为0时全读*)
|
|
|
|
i0:=i;
|
|
i1:=i+1;
|
|
i2:=i+2;
|
|
i3:=i+3;
|
|
|
|
(* 1 int *)
|
|
DataRead[i].TO_INT:=byte_to_int(DataStr[i0])*256+byte_to_int(DataStr[i1]);
|
|
|
|
(* 2 Float *)
|
|
TempStr[0]:=DataStr[i1];
|
|
TempStr[1]:=DataStr[i0];
|
|
TempStr[2]:=DataStr[i3];
|
|
TempStr[3]:=DataStr[i2];
|
|
P1:=&TempStr;
|
|
P2:=&TempReal;
|
|
v2v(DEST :=P2 , SRC :=P1 , LENGTH :=4 , IQM :=0 );
|
|
DataRead[i].TO_REAL:=TempReal;
|
|
|
|
(* 3 byte *)
|
|
DataRead[i].TO_BYTE:=1;
|
|
|
|
end_for;
|
|
|
|
(* 此段解析方式上后会导致错位,有时间时测试原因
|
|
for i := 0 To DataReadLength by 1 do
|
|
|
|
if DataType[i]=1 or DataType[i]=0 then
|
|
i0:=i;
|
|
i1:=i+1;
|
|
DataRead[i].TO_INT:=byte_to_int(DataStr[i0])*256+byte_to_int(DataStr[i1]);
|
|
end_if;
|
|
|
|
if DataType[i]=2 or DataType[i]=0 then
|
|
i0:=i;
|
|
i1:=i+1;
|
|
i2:=i+2;
|
|
i3:=i+3;
|
|
TempStr[0]:=DataStr[i1];
|
|
TempStr[1]:=DataStr[i0];
|
|
TempStr[2]:=DataStr[i3];
|
|
TempStr[3]:=DataStr[i2];
|
|
P1:=&TempStr;
|
|
P2:=&TempReal;
|
|
v2v(DEST :=P2 , SRC :=P1 , LENGTH :=4 , IQM :=0 );
|
|
DataRead[i].TO_REAL:=TempReal;
|
|
end_if;
|
|
|
|
if DataType[i]=3 or DataType[i]=0 then
|
|
i0:=i;
|
|
DataRead[i].TO_BYTE:=DataStr[i];
|
|
end_if;
|
|
|
|
4 BCD
|
|
if DataType[i]=4 or DataType[i]=0 then
|
|
i0:=i;
|
|
i1:=i+1;
|
|
k1:=byte_to_int(DataStr[i0])*256+byte_to_int(DataStr[i1]);
|
|
DataRead[i].TO_BCD01:=int_to_bool( k1 Mod 2#0000000000000001 );
|
|
DataRead[i].TO_BCD02:=int_to_bool( k1 Mod 2#0000000000000010 );
|
|
DataRead[i].TO_BCD03:=int_to_bool( k1 Mod 2#0000000000000100 );
|
|
DataRead[i].TO_BCD04:=int_to_bool( k1 Mod 2#0000000000001000 );
|
|
DataRead[i].TO_BCD05:=int_to_bool( k1 Mod 2#0000000000010000 );
|
|
DataRead[i].TO_BCD06:=int_to_bool( k1 Mod 2#0000000000100000 );
|
|
DataRead[i].TO_BCD07:=int_to_bool( k1 Mod 2#0000000001000000 );
|
|
DataRead[i].TO_BCD08:=int_to_bool( k1 Mod 2#0000000010000000 );
|
|
DataRead[i].TO_BCD09:=int_to_bool( k1 Mod 2#0000000100000000 );
|
|
DataRead[i].TO_BCD10:=int_to_bool( k1 Mod 2#0000001000000000 );
|
|
DataRead[i].TO_BCD11:=int_to_bool( k1 Mod 2#0000010000000000 );
|
|
DataRead[i].TO_BCD12:=int_to_bool( k1 Mod 2#0000100000000000 );
|
|
DataRead[i].TO_BCD13:=int_to_bool( k1 Mod 2#0001000000000000 );
|
|
DataRead[i].TO_BCD14:=int_to_bool( k1 Mod 2#0010000000000000 );
|
|
DataRead[i].TO_BCD15:=int_to_bool( k1 Mod 2#0100000000000000 );
|
|
DataRead[i].TO_BCD16:=int_to_bool( k1 Mod 2#1000000000000000 );
|
|
|
|
end_if;
|
|
*)
|
|
|
|
|
|
END_FUNCTION_BLOCK
|