在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去检查一遍定时器时间到了没,不管定时器时间到了还是没到,程序都依旧按照设定扫描顺序依次扫描。
//循环开始
//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.通讯数量的问题
//来自于西门子官方文档:
//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逻辑!
//串行传输,传输效率极其低下。
posted @ 2024-05-13 10:34  不愿透露姓名的小村村  阅读(24)  评论(0编辑  收藏  举报