Search code examples
plccodesysstructured-text

Create Timed For Loop Structured Text


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


Solution

  • 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