在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或者定时器会发生什么?/
作者
xiao cuncun
发布于
2024年7月21日
许可协议