Create Timed For Loop Structured Text

1.7k views Asked by At

Im trying to create a FOR TO loop that has a time delay. But it doesn't seem to work correctly I can't figure out where the error is, but it seems that the loop runs intirely and doesnt look to the time delay.

FOR vCount := 0 TO 800 DO

fbBlink(ENABLE := TRUE, TIMELOW := T#1s, TIMEHIGH := T#1s, OUT => bSignal);

    IF bSignal THEN
        vVsample[vCount] := INT_TO_REAL(WORD_TO_INT(vVin));
    END_IF

END_FOR

So I would expect that this loop would take up 801 seconds. And therefore each second a new value is added to the array. But when I run the code after a short time (e.g. 1 sec) I have allready 801 values in the array, so the timer doesnt work.

I have tried codes with TON and now I found on this forum the FB Blink but still it doesn't work.

Could anybody help me out

2

There are 2 answers

3
Guiorgy On BEST ANSWER

You seem to have a misconception on how CODESYS runs the code. Unlike languages such as C/C++ where the code gets executed once from top to bottom, in codesys the code is executed many times a second as long as theres power to the controller (on the PLC I work, it is executed every 20 milliseconds, or in other words 50 times a second).

Because of that, you can't have functions that stall/delay execution. What timers actually do in CODESYS, is they remember the time you first called them, and then on every call they just check how much time passed (they don't stall!). Once the set time passes, they'll raise a flag on their output so you can perform some task.

So let's break down what happens when you run your code:

// first run:
FOR vCount := 0 TO 800 DO
    // on first itewration (vCount = 0) this will remember the current time,
    // however it won't stop here and it will continue to the next line.
    // on next iterations (vCount >= 1) the timer will check the elapsed time,
    // which will be 0, so it will do nothing and continue to the next line.
    fbBlink(ENABLE := TRUE, TIMELOW := T#1s, TIMEHIGH := T#1s, OUT => bSignal);
    // the timer just started so bSignal is FALSE
    IF bSignal THEN
        vVsample[vCount] := INT_TO_REAL(WORD_TO_INT(vVin));
    END_IF
END_FOR

// second run:
FOR vCount := 0 TO 800 DO
    // the timer will compare current time to the recorded first time.
    // the elapsed time is below the target, so skip to the next line.
    // this will be repeated 800 times for no good reason...
    fbBlink(ENABLE := TRUE, TIMELOW := T#1s, TIMEHIGH := T#1s, OUT => bSignal);
    // the timer hasn't reached target time so bSignal is FALSE
    IF bSignal THEN
        vVsample[vCount] := INT_TO_REAL(WORD_TO_INT(vVin));
    END_IF
END_FOR
// the above will reapeat for another 48 cycles

// 50th run:
FOR vCount := 0 TO 800 DO
    // the timer will compare current time to the recorded first time.
    // the elapsed time has reached the target 1 second (TIMELOW := T#1s),
    // so it will raise the OUT flag to TRUE.
    // the comparison will happen on every 800 iterations, and every time
    // it will be set to TRUE.
    fbBlink(ENABLE := TRUE, TIMELOW := T#1s, TIMEHIGH := T#1s, OUT => bSignal);
    // since bSignal is TRUE on every iteration, ener the if for every element.
    IF bSignal THEN
        vVsample[vCount] := INT_TO_REAL(WORD_TO_INT(vVin));
    END_IF
END_FOR

If what you want is to add a value to the array once a second, then here's how you may do it (Do note, that this won't stop/stall/delay the execution and the code after this will still be executed every run. If that's undesired, you have to handle that separately):

timer(IN := vCount <> 801, PT := T#1S); // timer is TON
IF (timer.Q) THEN
    timer(IN := FALSE); // reset the timer so it starts counting again nextr run
    vVsample[vCount] := INT_TO_REAL(WORD_TO_INT(vVin));
    vCount := vCount + 1; // increment the counter
END_IF
0
dwpessoa On

BINK and TON do not work like a "Wait" or "Delay", where the program will be stuck on it until the time is reached). When the time is up an output bit will be triggered (Q, OUT, OUTPUT or other depending on the library you use), but you should be processing it every program cycle, continuously, waiting for the output.

A better way to handle a PLC program is that it should never stop, it should always be running, and then make decisions in the flow.

So, because of this Delay, FOR would not be the best approach... A good concept for you to use is State Machine.

Since you didn't specify how your program starts, it includes a bStart bit to control when to start the loop.

VAR
    bStart : BOOL;
    vVsample : ARRAY [0 .. 800] OF REAL;
    vVin : WORD;
    fbTon : TON;
    bStartTimer : BOOL := FALSE;
    vCount : INT := 0;
    vState : INT := 0;
END_VAR


//The timer is processed continuously 
fbTon(IN := bStartTimer, PT := T#1S);

//State Machine
CASE vState OF

    0 : //Start the Timer
        
        IF bStart THEN
            bStartTimer := TRUE;    
            //Go to State 10 (Wait Timer)   
            vState := 10;
        END_IF
        
    10 : //Wait for the timer
        
        IF fbTon.Q AND vCount <= 800 THEN
            //Set value and increment counter
            vVsample[vCount] := INT_TO_REAL(WORD_TO_INT(vVin));         
            vCount := vCount + 1;           
            //Reset the timer in the next cycle
            bStartTimer := FALSE;           
            //Return to state 0
            vState := 0;
        END_IF
        
        //Checks if reached the end of the array
        IF vCount > 800 THEN
            //reset flags
            bStartTimer := FALSE;   
            bStart := FALSE;
            vCount := 0;
            //Return to state 0
            vState := 0;
        END_IF
        
END_CASE