Hide Transcript
View Transcript
Subroutines are great for building re-useable
and easy to maintain code because it let’s you use one chunk of code in multiple places. And that saves you time and effort. Let’s do an example so you can see how to
use subroutines and learn exactly what you can and – and more importantly - can’t
do with them. Suppose we have an analog input measuring
something like the fluid level of a tank. And when it gets to a certain level let’s
scale the input by some scaling factor in R10 and put that in D1. And when we reach a certain calculated result,
we want to turn on an output coil and an analog output that is a scaled version of the input. Now suppose we need to use this code for several
different machines or processes. The temptation is to copy this code, change
the input, the calibration parameters and the outputs. Then copy it and do it again and again and
again for each machine. But that’s a lot of work, the code is really
hard to read and when it comes time to change or update the code you have to do it all these
different places and hope you didn’t miss anything. The easy way to do this is to create a single
subroutine using this code which is identical every time we use it and then just tell the
subroutine which inputs, calibration data and outputs to use. To do that, you call the subroutine with a
call instruction. We haven’t created that subroutine yet,
so we’ll just do that here by giving it a new name. The yellow dot is reminding me the subroutine
doesn’t exist yet. That’s ok – we’ll fix that shortly. Our input to the routine is WX0 so internally
we’ll copy that to N100 for the subroutine to use internally and we only need one of
those. We’re multiply the input by R10 – that’s
fine. And for outputs we need a digital internal
register to map to Y0 and an internal analog register to map to WY0. You can re-arrange the rows, delete rows,
and move rows around. We’ll use this as a power flow enabled subroutine
– you can make it edge triggered if you want to - and we can add a counter that keeps
track of the number of times this routine gets used. That can be handy when you are debugging. We’ll leave it disabled for now. Hit OK and Do-more designer reminds us we
have a new subroutine name – did we want allocate memory for that? Yep. Do we want to create the Subroutine for that? Yep. The name is already filled in and since you
can’t do time slicing in subroutines all of this is greyed out. You can password protect the subroutine – but
be careful. Make sure you don’t forget that password
– if you do the only way to recover the PLC is to clear it and reset it to factory
default. Well look. Here’s our new empty subroutine. I’ll use Shift up arrow to select, and control-X
to cut. Go over to the new subroutine and I’ll use
control-Z to paste it in. This block of ladder code still has the original
references so let’s change those to the generic internal references. WX0 becomes N100 here and here. Y0 becomes C0. WY0 becomes N101. All subroutines require a RETURN instruction
at the end, but Do-more designer already put one there for us so we are good to go, though
I am going to delete these extra rungs just to clean things up a bit. So whatever gets mapped into N100 will get
compared to this and if it exceeds that, then that result will get multiplied by whatever
is in R10. If that result is greater or equal to than
this, C0 will get set and the result will be copied to N101. To use the subroutine we just use the CALL
Instruction to map the external values we want to use to the internal registers in the
CALL instruction - which we already did - so we are done. In this subroutine we are using R10 as a scaling
factor. That could be set anywhere, OR we could set
it in the CALL instruction. It’s nice to do it here in the Call instruction
because now everything you need to know is in one place. That’s all there is to setting up a subroutine. You create the subroutine, map external registers
to internal registers, and you are ready to go. But here is the real power. When we need that code to run another machine,
we just use another CALL instruction. And look at this. Do-more Designer asks if we want to use an
existing parameter list – of course we do. We want this guy’s parameter list with the
2 ins and 2 outs. Look at that. The parameter list is ready to go – we just
fill in the empty boxes. Suppose this new machine’s analog input
is coming from WX1, the calibration number we need is this, the output coil goes to Y2,
the analog output goes to WY1 for this new machine. Done. So this line of code is calling the subroutine
using these I/O can parameters. And this line is calling the exact same routine
with but with these parameters. I can now do this on as many machines as I
want and every one of them will use the exact same code in the subroutine. So if I ever need to change that code I only
have to change it in one place. And that makes maintain and debugging your
code so much easier. There are a couple things to keep in mind
when using subroutines. You CAN use loops in a subroutine, just beware
that since subroutines don’t have time slicing the routine won’t exit until that loop is
done which could have a big impact on your scan time. If an OUT instruction in a subroutine turns
on a coil inside a subroutine that coil will stay on when you exit the routine because
the subroutine is really just a continuation of the calling logic so the power flow is
always present. When you exit a program the power flow is
disrupted so the active OUT instruction turns off. So make sure you understand that difference
– it’s really important. You can’t use edge triggered inputs inside
a Subroutine because they require two scans to see one state of the signal on one scan
and then the changed state on the second scan. And there is no way to guarantee the subroutine
will be run for more than one scan. So that’s why Do-more designer won’t allow
you to use edge triggered instructions in a subroutine. Same thing for asynchronous instructions. They require more than one scan and we can’t
guarantee the subroutine will be run for more than one scan so Do-more Designer warns us
when we attempt to do that. Other than those couple special cases, for
all practical purposes subroutines are no different than your main code block. By the way, Subroutines CAN call Subroutines. You can nest those calls up t 84 calls deep. Just beware that will have an impact on your
scan time. Subroutines can also call themselves. We call that recursion. That’s real handy if you are searching for
files in directory structures. You have a routine that goes down into a folder
and looks for a file. If it finds a folder, it calls another copy
of itself to drop down into that folder and search for the file. If it finds another folder, it calls another
copy of itself to drop down into that folder and search for the file. Etc. We call that recursive programming. Will you need this very often in your day
to day PLC programming? Probably not. But when you do – it’s an awesome feature
to have in your back pocket. Just be careful – as you can image this
can have a huge impact on your scan time. Finally, subroutines don’t have a structure
associated with the like the other code blocks do. Which explains why this counter is an extra
thing off to the side. The other code block have that built into
their structures – right? We’ll that ought to be enough to get you
going with subroutines. If you have any questions, please contact
AutomationDirect’s free award winning tech support during regular business hours. They will be happy to help. And don’t forget the forums – there are
lots of automation professionals there that love to share their years of experience. Just don’t post anything directed at AutomationDirect’s
support team there, they don’t monitor the forums on a regular basis.