If a sub-routine is called, unconditionally, on every scan... then, why make it a sub-routine?
Shouldn't it be part of the mainbody? What is the benefit of putting that into a sub-routine?
The primary purpose, and benefit, of using sub-routines is that, in total, you can have an awful lot of code, but only that code which is necessary for a particular scan (based on conditions) runs on that scan. The benefit being that scan-time is minimized, as much as possible, on all scans.
If you have a process where any one of a million possible situations might occur, and you have a unique sub-routine function to handle each of those possible situations, all you need to do is determine what the particular situation is, then... call only that particular sub-routine which applies to the current situation. There is no need to continuously run all of the sub-routines... it only increases scan-time!
If all sub-routines are called, all the time, unconditionally, then there is no saving in scan-time.
Now, before I gone on...
I do use Special-Function Programming for Real-Math kinda stuff... but only when it is needed.
Now, regarding sub-routines... I don't use any sub-routines... none... nada... zilch... goose-egg!
I do, however, use SKIP (to Label-X) and LABEL-X. The SKIP instruction is controlled by a set of conditions. If I have a large section of code that is used only on a now-and-then basis, I develop a conditional-combination that determines whether or not that section of code is needed on that particular scan. If conditions indicate that the code is NOT needed, then I envoke the "SKIP to Label". When envoked, the processor skips to the specified label (located at a point after the large section of code - this means that the code must be modular) and resumes processing code from that point.
Otherwise, the code in the large section is executed. This means... if I don't KNOW that the code is not needed, then the code is executed (a little bit of double-negative, reverse-logic in there...). That is, I only skip the code if I KNOW, for sure, that it is not needed at this time. Once that section of code begins running, it continues running, scan after scan, until, once again, I KNOW that the code is not needed.
Most larger processes have a lot of Stop-n-Go aspects. The larger the process, the more critical the scan-time. A well designed process (the code, that is) will have many complete, contiguous sections that are idle now and then. If they are idle at the time, they are not needed at the time. By simply skipping that section of code you reduce your over-all scan-time. This might be critical for some fast-acting/fast-clearing conditions that are not tied into some sort of Interrupt handler. As far as I know, Interrupt handling can be accomplished ONLY on those inputs at the base-station - where the processor is installed.
Essentially, my sub-routine-type functions are built into the mainbody of my code.
It takes careful planning to make this work... successfully. But then, that's what process developing is all about. Careful Planning! If you can't do this... well... maybe you're not really a process developer.
Now, in some OEM situations (I was an OEM... so I KNOW!), there is a perceived benefit in providing "canned functionality" for a particular device or function to a process.
However, you can't just simply throw a bunch of sub-routines, from some library, together to produce a process. You have to KNOW that no sub-routine is going to "STEP" on another sub-routine, or the mainbody. A "STEP" could cause disaster!
So... if an OEM wants to throw a bunch of sub-routines together... he has to be sure there is no "stepping". Also, he needs to know that the particular processor can handle the I/O and Control Relays used by the sub-routine.
If a particular sub-routine controls Output-XYZ, or Control Relay-ABC and that particular output number, or Control Relay number, doesn't exist in the current process... modifications have to be made.
What if one sub-routine controls Output-ABC on a smaller processor, and another sub-routine, controlling a different device, happens to control Output-ABC on a larger processor? Again, conflict. Modifications have to be made.
To eliminate the Output-conflict within a sub-routine, the throw-together sub-routines would have to control Control-Relays which would be used to control the actual outputs in the mainbody. But then... might there not be conflicts between those control-relays?
It could be that an OEM always uses ONE particular processor with such-n-such a number of Inputs, and such-n-such a number of Outputs, and such-n-such a number of Control Relays... in that case, he can pull it off... at least, for a while... until he decides to add more functionality... where the total of the possible functionalities exceeds the I/O and Control Relay count.
With all of that, and with over twenty-years of programming experience, I've learned that there is little value in using sub-routines to build a throw-together process.
In a field as dynamic as this, there is simply too much effort to make it work!
Sub-routines work best when they are used in the manner for which they were originally designed... that is, performing a particular function only on an as needed basis.
Otherwise... What the hell is the point?
BTW Burnerman...
If a sub-routine is not active, the counters and timers, in that sub-routine, do not increment or decrement. Outputs remain as they were.
The counter and timer must be actually be scanned to be updated.
Regarding Outputs... if the code controlling the Output is not scanned then the Output does not change state from the last time it was scanned.
Darren....
MCR is a whole different game! You simply have to KNOW the difference!