最近论坛关于Modbus-RTU通讯轮询的讨论很火热,连版主都出来发程序,我也凑个热闹,发个Modbus-RTU通讯轮询的程序,此程序在S7-1200+CB1241上测试成功,和西门子G120、丹佛斯FC360、ABB ACS510上做过测试,理论上也可以用于S7-1500上,我后天也要回老家过年去了,大家娱乐一下,祝大家新年合家欢乐身体健康。
FUNCTION_BLOCK "Modbus通讯"
TITLE = Modbus-RTU通讯处理
{ S7_Optimized_Access := 'FALSE' }
AUTHOR : hushuguo
VERSION : 0.1
//用于西门子S7-1200/1500和Modbus-RTU从站通讯
//
VAR_INPUT
PARITY { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : UInt := 2; // 奇偶校验 0无校验1奇校验2偶校验
PORT { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : PORT; // 通讯端口
BAUD { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : UDInt := 57600; // 波特率
OverTime { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : UInt := 100; // 从站等待超时
Replace { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool := TRUE; // 0:读错误保持接受数据不变;1:读错误则数据用零替代
END_VAR
VAR_OUTPUT
InitialError { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // 初始化错误
Error { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // 没有任务被选择
END_VAR
VAR_IN_OUT
Initial { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // 初始化
END_VAR
VAR
Task { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Array[1..#MaxTask] of Struct // 任务
Enable { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // 启用该任务
Error { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // 通讯错误
Mode { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : USInt; // 读写模式 0读取1写入
Address { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : UInt; // 从站地址
Registor { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : UDInt; // 从站寄存器地址
Length { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : UInt; // 要通讯的数据长度
Data { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Array[1..#MaxLength] of Word; // 数据区
END_STRUCT;
COMM_LOAD {InstructionName := 'Modbus_Comm_Load'; LibVersion := '3.1'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Modbus_Comm_Load;
Master {InstructionName := 'Modbus_Master'; LibVersion := '3.2'; ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Modbus_Master;
Index { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Int := 1; // 任务索引
I { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Int;
Req { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // 请求信号
InitialDone { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool; // 初始化已完成
P1 { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
P2 { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
END_VAR
VAR CONSTANT
MaxTask : Int := 8; // 定义最大任务数量
MaxLength : Int := 4; // 定义最大数据长度
END_VAR
BEGIN
(*
注意事项:
1:通讯连接要正确
2:奇偶校验、数据位、停止位、波特率等几个参数要保证在程序里、硬件组态、从站三处设置一致
3:通讯端口填写通讯模块使用的端口,见硬件组态->属性->系统常数
4:本程序里已强制为485通讯
5:请认真看接口区里的个变量的注释
使用方法:
首先将本程序导入项目并编译,由于块里使用了部分系统块,所以第一次编译会有错误,没关系,再编译一次就好,
在程序里调用本块并生成背景数据块,然后修改背景数据块里的Task结构里的数据,本程序默认提供了8个任务,
但需要使用者自己启用,并修改相关参数,具体见参数注释,任务数及数据长度不够请修改Constant里关于数量定义的变量
*)
IF NOT #InitialDone THEN
#Initial := TRUE;
END_IF;
IF #Initial THEN //通讯模块初始化
IF NOT #P1 THEN
#Req := FALSE;
#InitialDone := FALSE;
#COMM_LOAD.MODE := 4; //工作模式设为485通讯
END_IF;
IF #COMM_LOAD.DONE THEN
#Initial := FALSE;
#InitialDone := TRUE;
END_IF;
ELSE
IF #Master.ERROR AND #Task[#Index].Mode=0 AND #Replace THEN //读错误则清掉之前接受的数据
FOR #I := 1 TO #MaxLength DO
#Task[#Index].Data[#I] := 0;
END_FOR;
END_IF;
IF #Master.DONE OR #Master.ERROR THEN
#Req := FALSE;
#Task[#Index].Error := #Master.ERROR;
ELSE
#Req := TRUE;
END_IF;
IF (#Req AND NOT #P2) OR #Error THEN //开始通讯
FOR #I := 1 TO #MaxTask DO
#Index := #Index + 1;
IF #Index > #MaxTask THEN
#Index := #Index - #MaxTask;
END_IF;
IF #Task[#Index].Enable THEN
EXIT;
END_IF;
END_FOR;
#Error := #I > #MaxTask;
END_IF;
END_IF;
#P1 := #Initial;
#P2 := #Req;
#COMM_LOAD(REQ := #Initial,
"PORT" := #PORT,
BAUD := #BAUD,
PARITY := #PARITY,
RESP_TO := #OverTime,
ERROR => #InitialError,
MB_DB := #Master.MB_DB);
#Master(REQ := #Req AND NOT #Error,
MB_ADDR := #Task[#Index].Address,
MODE := #Task[#Index].Mode,
DATA_ADDR := #Task[#Index].Registor,
DATA_LEN := #Task[#Index].Length,
DATA_PTR := #Task[#Index].Data);
END_FUNCTION_BLOCK