Sign in to follow this  
Followers 0
CanaanP

Comments/Advice on my program?

9 posts in this topic

Hi, I posted a while back about possibly having to write a program for a project where many conveyors will be modular. The user input the order that the conveyors are arranged in, starting from furthest away working back towards the control house. There are two rows, and a bank of 9 conveyors. Each conveyor could be in either row. The PLC must start the conveyors in which ever order was specified and remember the sequence so that the shut down can occur in the proper order. Well, I'm very new at this, and I asked for advice on it. Ken Moore had showed me a sample of something he threw together real quick. At first I used it and was happy, but I realized it had a flaw... that is, if (for example) only 3 conveyors are chosen, it would still look at slot 4, 5, 6, etc... This is my final version of the program, which is pretty much a totally different animal then Ken's version. I thought perhaps all the guru's could look at it and see what they think. I don't want to seem like I'm an expert, I know that I'm not... but this is how I was able to solve this problem. Can anyone comment on it? I'm welcome to all criticism. Thanks. BTW, there are a lot rungs in this program that may seem weird, but they're like that because I'm not using real buttons, or inputs of any kind. I'm using AdvancedHMI so the buttons are just toggling bits in the PLC. TEST.pdf

Share this post


Link to post
Share on other sites
It doesn't matter so much how it "reads", the real question is does it work like you want?

Share this post


Link to post
Share on other sites
Sure, it works great... I was just hoping to get some feedback on it is all. I've learned so much about RSLogix 500 since I started this little project of mine, and now I pretty much consider it done. I picked up quite a few tips from this forum, and quite a bit from advice you've given. I consider this forum a great source of knowledge, and I'm grateful for the information I've gotten from here. Thanks again. If anyone has any comments on it though, I'd like to hear them.

Share this post


Link to post
Share on other sites
One thing I see is that you JSR to your output logic first. So, on first scan when returning to run mode, it is possible that some of the outputs could be true for one scan based on the last state of some of your internal bits, since they will not be updated until after the output addresses are updated. Once the program has made it past the first scan this is no longer an issue, but is easy enough to correct by simply moving the JSR 3 to the end of the main routine. Another detail is the use of nested branches. In situations where you have more than one parallel branch, you can extend the branch straight down rather than nesting them. (right click on the corner of the branch and select extend branch down). Your program doesn't have rungs with five or six nested branches so it is not bad, just a tiny detail that can make a program more readable and save a handful of bytes. I have seen some that look like upside down wedding cakes and are kinda hard to read! Overall, the program appears to be very organized, well commented and like you said, "it works great" and that, my friend, is the bottom line... Paul

Share this post


Link to post
Share on other sites
Everything is in all-caps everywhere. This is popular in the maintenance world but actually reduces readability. Research has shown that the human brain reads not only the actual lettering but also reads more or less by "shape". Putting things in all caps destroys that visual cue. Just because the programming software tries to make everything all caps doesn't mean it's a good idea. There are no comments (rung comments or rung titles) ANYWHERE. Which makes it tough for someone, especially say an electrician on night shift, to try to follow your code. You're moving lots of data around into various registers but your commenting is nonexistant so as you said, it would be really confusing to someone that doesn't study your code for a long time. For instance, just what the heck is an "output integer". You use the term a lot but you never explain it. It would be utterly confusing to an electrician that came upon it. I made lots of comments on the FIRST instance below. There are additional instances. I just pointed out specific things to give you the idea. You have to go through and find them all yourself. For instance my comment on ladder 5, rungs 12-13 also applies to ladder 5, rungs 16-17. Also, I made my comments without realizing at first that you have a Micrologix. I know those things are fairly limited in terms of their instruction set so some of my suggestions to use more advanced instructions may not apply because those are not available depending on which Micrologix you are using. All of my comments are made for the most part in reference to making your code smaller in terms of total rungs, memory footprint, and speed. It's nothing major...just some little optimization tricks that help polish the code. Keep in mind that all the tricks I'm suggesting can frequently increase code speed by 25-50% with corresponding decreases in code size in my experience. It's probably tilting at windmills for such a small program but when you start doing programs with thousands of lines of code, those good practices (in terms of writing optimized code) add up very quickly in terms of cramming more into a processor and going from semi-sluggish 50-100 ms execution times down to a much more comfortable 20-40 ms execution time. For instance, the simple strength reduction operation of going from MOV 0 xxx to CLR xxx reduces the memory footprint by one word (about 10-25% depending on how complicated the other operand is) and reduces execution time from 3 units to 2 since it eliminates the extra step of loading the constant. It's not a lot to do one or two of these sporadically but if you do it uniformly over the entire program, you will see those kinds of code size/speed improvements on your entire program. Study the guides on memory and execution time data that AB provides carefully because you will quickly pick up all kinds of tricks from those on what's the best way to do certain things. Overall: You have a bunch of relatively short ladders called by a bunch of JSR's. Your code all refers to essentially doing one or perhaps two functions. The JSR's are fairly quick but I usually like to keep similar code grouped together more. If you turn on the "integrated diagnostics" feature in Logix 5/500, and you use rung titles, then you get an additional level to the "tree" on the left hand side of your screen. This lets you organize your code with an extra layer of freedom. I'd put all the code for "conveyor 1" in one ladder, except for perhaps the HMI, and all the code for "conveyor 2" into a second ladder. You'd be down to 2-3 JSR's which is somewhat quicker. JSR's are not very fast on non-Logix 5000 processors so fewer and larger ladders are preferred so long as it doesn't compromise readability. Ladder 4, rung 22: you have a classic example of a "birthday cake". The other version of this is a "tunnel". What happens when the PLC encounters a branch is that it extends the "stack space" memory, which is only allowed to be 8 branches deep by the way. Then it scans through the branches, jumping down to the next branch in order as they fail. Then it continues through the rest of the rung. If you use branch extensions, it only scans until it hits the first TRUE rung. Then the stack space is cleared immediately. In your case, it has to treat both branches as separate scans, eating up additional memory (1 extra word of memory for each additional branch compared to a branch extension), and some extra execution time. In the worst case birthday cake (8 layers), this would force 8 branches worth of overhead plus it would have to scan all 8 branches individually. With a branch extension, there is only 1 branch worth of overhead and scanning time progrsses after the first true branch rung, ignoring all further false conditions. And you can have dozens of branches (an unlimited amount), not just 8. Birthday cakes (additional branches without conditions on either side) and tunnels (branches inside branches without addition conditions) are not technically "incorrect" coding but they are bad programming practices. I see them a lot more with RS-Logix because people are getitng so point-and-click happy with the "instruction toolbar" that they are even doing branches this way instead of accessing the branch menu. This was never an issue with AI or 6200, but I'm now dating myself back almost 15 years. Get in the habit of doing a right-click and adding branches this way and you probably won't create any more tunnels or birthday cakes. Instead of adding a branch as you did, use a branch extension. Right click on the lower left hand corner of the branch and select "extend branch down". Or since you already wrote it, grab the lower left hand corner of the second branch and drag it out onto the red dot that will show up in space below the branch so that it gets turned into an extended branch. Ladder 5, rung 1: Strength reduction should be applied. Strength reduction means turning CPT blocks into MOV, DIV, ADD, SUB, or MUL when you are only doing one or two math operations. Turning MOV into CLR whenever you are moving a zero into a location. Takes less memory, runs faster, and is more intuitive to the reader in many cases. Ladder 5, rungs 1-10: Strength reduction. In this case, use FAL to simply add together all the consecutive locations. Or you could do a CPT block and add together all the locations. I just can't remember if you have CPT with MLX. Since a negative timer is BAD, add a single rung after the FAL to simply zero it if negative (avoid faulting the processor) or add a check in front of the timer (GEQ T4:1.PRE 0) to prevent it from executing with illegal values. Should also program the HMI to disallow negative values in the first place. Since right now you manipulate it in the PLC (with protection from negative values), the additional GEQ checks are extraneous anyways. Advantage of the single rung doing a check is that since everything is wrapped inside a one shot, it executes only once. For clarity, either way, you could then move everything together into a single rung and get rid of the extra internal one shot state bit (merge into rung 0). Ladder 5, rungs 12-13: Delete rung 13. Change rung 12 to read "GEQ" instead of GRT. Does the same thing, saves memory, runs faster. Ladder 5, rung 18: The enable check on B3/2 is extraneous. Any time you are using TON's and you daisy chain them, the starting conditions for the FIRST TON also apply to all subsequent TON instructions. Think about it...during scan, the first TON (T4:2) would be disabled if B3/2 goes to 0 (and the DN bit is cleared). This in turn disables T4:3 via DN bit alone (no need to recheck B3/2). Ladder 5, rungs 44-52: Here we have a possible opportunity for indexed/indirect addressing. If it is possible to move all your outputs together in a single block, say B3:1/0,1,2,3,4..., then you could simply do something like this: GRT N7:100 0 CLR B3:1; MOV B3/2 B3:1.[N7:100] (put the output operations on two separate output branches). That way you do the entire sequence of comparisons and output momentaries in a single ladder rung. Again, faster execution. Ladder 10, and lots of other rungs: I know you're clearing the push buttons on an HMI and that's fine. You could also just en masse do a CLR of an entire word or file at the end if they are all grouped together. That takes all the little "XIC ___, OTU ___" rungs out and consolidates it into a single line or two of code at the end of the scan. Alternatively, the way you are using them, you could also add branched outputs and clear them on the same rung that you are using them. Execution speed is about the same but you'd gain a little in the readability category. Ladder 11, rungs 0 & 2: Clock is 600 ms PLUS ONE SCAN. Not quite sure what you are using this for but you may want to think about that. Your units are kind of odd so I'm not sure what the intent on this code really is. If you want a truer "600 ms" timer, set up your timer to count to say 100. Then change the next two lines to subtract off 60 from the .ACC value of the timer whenever it exceeds 60 counts. This trick gives you a pulse every 600 ms ON AVERAGE. Also, it appears that the intent is a 1 second timer. So you should be going to 100 and not 60 if that's your goal. Ladder 11, rungs 3 & 4: I usually merge these into a single rung with a branched output for readability reasons. Ladder 11, rung 5: Strength reduction. CLR instead of MOV. Ladder 11, rungs 6-8: Same comment as before about free running timers. 0.01 second timers take exactly the same amount of execution time and are more accurate...+/-0.01 seconds instead of +/-1 second timing accuracy. If you are timing something smaller than a few minutes, use the 0.01 second time base. In addition, you are making N7:22 count down from 100 to 1 and then resetting back to 100. Would it screw things up not to simply count UP from 0 to 100 in the timer itself and use T4:10.ACC? Again, shorter code and eliminates some overhead. Edited by paulengr

Share this post


Link to post
Share on other sites
The Pauls {ENGR and OKIE} have done a good job reviewing your program. I only have one comment and it is a positive to add. Thanks so much for posting your code as a PDF rather than an RSS. I don't have ab software at home on the weekends usually and the pdf let me check out your program. A real positive.

Share this post


Link to post
Share on other sites
Wow, thanks for all that info... it's gonna take me a while to digest all of this, but I can tell you right now some of the things you mention, paulengr, although may be good advice, don't really apply to this example. I am fixing all the birthday cakes, and have replaced my mov 0 with clr in all instances. The stuff on ladder 11 is just some crap I threw together to show off some controls in AdvancedHMI, they have no real functionality, it's just create changing values for different types of displays in the application. As far as putting the unlatching rungs into branches of the original rung, I never thought of that, and wasn't even sure if it would work because I figured it would just cancel itself out before it could execute and it would never pull in. But I tested it and it worked, so thanks for that :) I don't have FAL in my tool pallet for whatever reason, and the CPT is always greyed out, so I can't use those features. I've still got some adjustments to make on here, I'm gonna make all the possible changes you suggested and rerun it and compare scan times and things like that. One thing though, I understand you guys mostly work with factory environments and stuff like that, and ms are probably critical at times. But for us, we are an OEM for rock crushers, quarries, asphalt plants, soil remediation plants, that type of stuff... we use PLC's for the interlocking abilities and stuff like that. A lot of the advice you gave was for shrinking the program and reducing scan time, which I can totally appreciate... but I think maybe it's not quite so critical for time accuracy on that level as it may be for your type of work. I do appreciate all the advice though, and I like a clean program, so I'm making those changes. For what it's worth, I have learned a thing or two from this thread alone so I'm very happy. Thanks again guys!

Share this post


Link to post
Share on other sites
I don't know if anyone is still watching this thread... but an interesting thing happened. I made some changes based on the advice given, not all things though. I fixed all the "birthday cakes" by extending the branches down, I also replaced all the MOV 0 with clear. Finally I took the rungs where I was unlatching my start buttons and made them into branches off the main rung. The stop buttons could not go there also though because they are normally closed contacts which would make the rung not true. Finally I moved the JSR to the output ladder to the end of list. The funny thing is that after making those changes, the maximum scan time increased from 24ms to 25ms! How could this be?

Share this post


Link to post
Share on other sites
During the editing, monitoring, and even possibly even the download process, the status register can "spike" due to increased comm loads. Monitor the last and average scan time registers, and clear the maximum. It is safe to zero out that address and reset it especially after making any online edits. Also, have the minimum number of visible items on the screen to more accurately determine the scan times of a machine that is not being monitored. Then test the HMI comms, by loading the HMI channel (if one exists). You need to know that app well enough to cause that effect. I have a very heavily loaded pair of SLCs that hit 993ms at startup initialing 30 slots plus multiple scanner cards, but it settles down to a nice 22-35ms range after the first scan. Online editing can have an exponential impact on scan times based on the number of edit zones. I know of several stories (one is my own ) about hitting the watchdog limit just by installing too many scattered online edits. Conditional JSRs, indirect addressing, and conditional math intensive instructions as well as messaging instructions can have a big impact. I bet you could get <20ms avg by eliminating all of the JSRs. The CLR is provable to be faster than a MOV. I once replaced over 100 MOV zeroes with CLRs and 'FiLLs zero' in a complicated tire building machine and dropped 2ms with no other changes... Anyway, that is the long story about SLC scan time in my experiences... Paul Edited by OkiePC

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0