在PLC的扫描特性下,FOR循环里面套IF或者定时器会发生什么?
1. 为什么会提出这个题目
- 在PLC指令的执行过程中,FOR循环会在每个扫描周期执行完一个循环,然后程序才会接着往下扫描
- 在PLC指令的执行过程中,IF判断会在每次扫描中判断一次条件,满足则进入执行体,不满足则跳出IF
- 把IF放在FOR循环里面,会导致循环体执行时间过长,或者其他问题吗?(答案是不会)毕竟每个循环周期for循环的变量都要从起始值到最大值循环一遍。
- 在FOR循环里面嵌入定时器,会导致PLC扫描时间超看门狗时间吗?(答案是不会)
- 如果把FOR循环换成
REPEAT..UNTIL
,程序会容易超过看门狗时间吗?(答案是会,repeat会一直循环执行体中内容,直到满足break条件;换句话说,FOR和REPEAT的工作原理不一样)
2. 从应用讲起
- 做一个跑马灯,让PLC DQ输出从小到大依次点亮,再达到最大时再依次减少;设计思路用FOR循环去做DQ点灯的遍历,同时依靠IF去判断当前实际应该点亮的灯是哪个,IF里面再放入定时器来做点亮时间控制。
- 虽然每一遍FOR循环都会把循环数完全跑一遍,但是仅仅只有当满足IF条件的时候才能真正进得去循环体;相当于每个扫描周期内IF被判断了FOR的循环次数那么多次。
- 如果FOR循环的次数都结束了还没有进入IF执行体,PLC程序会卡死在这儿吗?答案是不会。FOR循环结束后程序会继续往下
扫描
,不会在意是否进入了IF的判断体 - 在IF或者直接在FOR里面嵌套了长时间的定时器,会拉长FOR循环的循环时间吗?答案是不会。因为每经过一次扫描周期,PLC去检查一遍定时器时间到了没,不管定时器时间到了还是没到,程序都依旧按照设定扫描顺序依次扫描。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67//循环开始
//variant "QB0_QB1" means %QW0,as a word.
IF "QB0_QB1" = 0 AND NOT #subflag_bool THEN
#addflag_bool := TRUE;
END_IF;
IF #addflag_bool THEN
#BOOL_ARRAY[0] := true;
#TIME_add_ARRAY[0](IN := #BOOL_ARRAY[0],
PT := T#2S);
FOR #FOR_I := 0 TO 8 DO
IF #TIME_add_ARRAY[#FOR_I].Q THEN
#BOOL_ARRAY[#FOR_I + 1] := TRUE;
#TIME_add_ARRAY[#FOR_I + 1](IN := #BOOL_ARRAY[#FOR_I + 1],
PT := T#2S);
END_IF;
END_FOR;
END_IF;
//正循环结束
IF #TIME_add_ARRAY[9].Q THEN
#TIME_sub_ARRAY[9](IN := #BOOL_ARRAY[9],
PT := t#2s);
IF #TIME_sub_ARRAY[9].Q THEN
#addflag_bool := FALSE;
#subflag_bool := true;
END_IF;
END_IF;
//负循环开始
IF #subflag_bool THEN
FOR #FOR_J := 9 TO 1 BY -1 DO
IF #TIME_sub_ARRAY[#FOR_J].Q THEN
#BOOL_ARRAY[#FOR_J] := false;
#TIME_sub_ARRAY[#FOR_J - 1](IN := #BOOL_ARRAY[#FOR_J - 1],
PT := T#2S);
END_IF;
END_FOR;
END_IF;
//全周期结束
IF #TIME_sub_ARRAY[0].Q OR "Switch" THEN
#subflag_bool := false;
#addflag_bool := FALSE;
FOR #FOR_K := 0 TO 9 DO
RESET_TIMER(#TIME_add_ARRAY[#FOR_K]);
RESET_TIMER(#TIME_sub_ARRAY[#FOR_K]);
#BOOL_ARRAY[#FOR_K] := FALSE;
END_FOR;
END_IF;
//赋值
"Q0.0" := #BOOL_ARRAY[0];
"Q0.1" := #BOOL_ARRAY[1];
"Q0.2" := #BOOL_ARRAY[2];
"Q0.3" := #BOOL_ARRAY[3];
"Q0.4" := #BOOL_ARRAY[4];
"Q0.5" := #BOOL_ARRAY[5];
"Q0.6" := #BOOL_ARRAY[6];
"Q0.7" := #BOOL_ARRAY[7];
"Q1.0" := #BOOL_ARRAY[8];
"Q1.1" := #BOOL_ARRAY[9];
3. 这种嵌套模式的应用设想
- 在做MODBUS轮询的时候,有些时候我们需要调用很多次功能块,那么能不能利用上述方法,然调用块只被写一次,从而提高效率呢?
- 下例中,主要注意点都写在注释中了
- 下例中,ModbusMaster块没有以
ARRAY..OF iDB
的方式去做多个iDB是因为轮询的方式其实在同一时刻只会调用一次MobusMaster,不必浪费内存去重复做存储区。共用存储区也是可以的。(当然做成iDB数组动态调用也是可以的) - 下例中,代码没有被调试过,只是用来做一种ModBus轮询思路的探讨
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41//1.通讯数量的问题
//来自于西门子官方文档:
//Modbus 寻址支持最多 247 个从站(从站编号 1 到 247)。每个 Modbus 网段最多可以有 32 个设备,具体取决于 RS485 接口的负载和驱动能力。当达到 32 个设备的限制时,必须使用中继器来扩展到下一个网段。需要七个中继器才能将 247 个从站连接到同一个主站的 RS485 接口。
//Siemens 中继器仅支持 PROFIBUS;其功能为监视 PROFIBUS 令牌传递。Siemens 中继器不支持其它协议。因此,需要第三方 Modbus 中继器。
//Modbus 超时默认较长;使用多个中继器不会产生延时问题。Modbus 主站不关心从站是否响应慢或者多个中继器是否延迟了响应。
//2.思路:若要想要只调用一次Modbus_master指令。我想到的方法是用smart200的子程序编程思路去实现(摒弃背景DB的方法来实现只把逻辑写一遍)。
//程序如下:
//********************************************************START****************************************************//
//用for执行程序,做轮询!切记同一时间只能访问一个从站,其他从站需要排队!
FOR #SlaveNumber := 0 TO 40 DO
//主执行逻辑
//req逻辑依据实际情况在外部写逻辑
IF "ModbusRTUDB".modbusMasterArray[#SlaveNumber].req AND NOT "ModbusRTUDB".modbusMasterArray[#SlaveNumber].done THEN
//调用一次初始化块,MB_DB接口用array间接寻址
#Modbus_Comm_Load_Instance(REQ := "FirstScan",
"PORT" := 261,
BAUD := 9600,
PARITY := 0,
MB_DB := #MB_MASTER[#SlaveNumber].MB_DB);
//调用一次Master块,背景DB用ARRAY序列化,接口参数用UDT封装后用globleDB序列化
#commMB_MASTER(REQ := "ModbusRTUDB".modbusMasterArray[#SlaveNumber].req,
MB_ADDR := "ModbusRTUDB".modbusMasterArray[#SlaveNumber].mb_addr,
MODE := "ModbusRTUDB".modbusMasterArray[#SlaveNumber].mode,
DATA_ADDR := "ModbusRTUDB".modbusMasterArray[#SlaveNumber].data_addr,
DATA_LEN := "ModbusRTUDB".modbusMasterArray[#SlaveNumber].data_len,
DONE => "ModbusRTUDB".modbusMasterArray[#SlaveNumber].done,
BUSY => "ModbusRTUDB".modbusMasterArray[#SlaveNumber].busy,
ERROR => "ModbusRTUDB".modbusMasterArray[#SlaveNumber].error,
STATUS => "ModbusRTUDB".modbusMasterArray[#SlaveNumber].statrus,
DATA_PTR := "ModbusRTUDB".modbusMasterArray[#SlaveNumber].data_ptr);
END_IF;
END_FOR;
//********************************************************END****************************************************//
//3.总结:毕竟PLC是结构化编程而不是面向对象编程,所以要想做到继承父类就能直接调用所有父类方法的思想不容易,
//其实西门子官方也不建议向上面这么玩,因为数据安全和数据稳定问题。为每个对象均分配一个专有的背景DB才是西门子的思想所在。
//这种玩法参考了没有背景DB思想的SMART200程序,SMART200里面子程序是common的,所有重复调用必须满足异步调用,这很重要,否则数据要出错。
//4.缺点:这个方法属于轮询方法,同一时间只会有一个从站设备在通信!数据传输可能会出现排队现象,必须处理好req逻辑!
//串行传输,传输效率极其低下。
在PLC的扫描特性下,FOR循环里面套IF或者定时器会发生什么?
http://example.com/2024/07/21/在PLC的扫描特性下,FOR循环里面套IF或者定时器会发生什么?/