Background Logic programs are scan routine programs that constantly run in the background. They are executed one line at a time, from start to finish, repeatedly. If you know anything about PLC programming, they are very similar to the way those programs are structured and run. You can only use assignment statements in background logic programs. This means that you can not use motion instructions, branching statements (jump, label, call, run, etc...), timers, WAIT instructions or macros.
So basically you are limited to setting registers, I/O and system variables. And the only conditional statement you can use is the mixed logic IF statement: IF (...)
All of this is to keep the program simple so that it can be executed quickly. In my experience, if you're dealing with less than 16 inputs and 16 outputs, background logic is a good way to avoid using a PLC in the workcell if you don't have to. Anything more than that would probably be better suited to either the PMC option or a PLC. For me, the primary way I use it is to set up run conditions that must be met before the robot can be operated in automatic.
A "one shot" is probably the most valuable tool you can use when doing this sort of programming. Remember that this program is repeatedly scanning (looping) in the background. We use a one shot to make something happen for only one scan. For example, let's say we have a button connected to an input (DI[1]). Every time we push the button, we want to add 1 to the value of a register (R[1]).
IF (DI[1), R[1]=R[1]+1
The problem with this code though is that the program is repeating every 8ms. Unless you have very quick hands, it's likely that the button will be held down for more than one scan of the program, resulting in more than 1 being added the the register. So to fix it, we have to use a one shot:
F[1]=(OFF)
IF (DI[1] AND !F[2]),F[1]=(ON)
F[2]=(DI[1])
IF (F[1]), R[1]=R[1]+1
And here's how it works scan by scan:
First scan with DI[1] on:F[1]=(OFF)
;We always turn this flag off at the start of the cycleIF (DI[1] AND !F[2]),F[1]=(ON)
;If DI[1] is on, and F[2] is off, we want to turn F[1] onF[2]=(DI[1])
;Now we want to set F[2] to the same state as DI[1]. This means that on the next "scan", the condition in the previous line will not be met.IF (F[1]), R[1]=R[1]+1
;If we've gotten this far, and we see that F[1] is on, that means we know it's the first scan where we've seen DI[1] onSecond scan with DI[1] on:F[1]=(OFF)
;Again, this flag gets turned off to avoid repeating any of those assignments againIF (DI[1] AND !F[2]),F[1]=(ON)
;This time around F[2] is on, so this condition is not met, and F[1] stays offF[2]=(DI[1])
;This holds F[2] on until we see DI[1] turn off.IF (F[1]), R[1]=R[1]+1
;This line will not executeFirst scan with DI[1] off:F[1]=(OFF)
IF (DI[1] AND !F[2]),F[1]=(ON)
;DI[1] is off, and F[2] is still on from the last cycle. Condition still not met.F[2]=(DI[1])
;Now F[2] is turned off. The next time DI[1] comes on, the above condition will be met again.IF (F[1]), R[1]=R[1]+1
Basically it all comes down to order of execution. If you put things in the right order, you can do practically anything. Earlier I said that I prefer to use BG Logic with less than 16 inputs and outputs, but I have used it as a cell controller on a system that had 32 inputs and 48 outputs. It controlled everything from the notification lights, an exit conveyor, a couple of pneumatic cylinders, a small printer and another robot. All with background logic. I would have preferred a PLC or even just the PMC option, but the customer beat us up on price and the budget didn't allow it.
Here's a chunk of code from one of the four BG Logic programs running on that system that uses all sorts of one shots and cascading logic:
1: F[69:PrntMaint]=(!DI[26:PrntrRunPos]) ;
2: ;
3: !Cycle Printer & Shear ;
4: IF (DI[14:LblReelEmpty]),F[19:ReelLockout]=(ON) ;
5: IF (DI[15:ManualPrint] AND DI[27:PrntrMaintPos] AND !DI[14:LblReelEmpty]),F[19:ReelLockout]=(OFF) ;
6: IF (DI[12:LblShtl_Retr] AND F[9:PrntrInCycle]),F[17:ManTrig]=(OFF) ;
7: IF (DI[15:ManualPrint] AND !F[18:ManTrigPB_OS] AND DI[27:PrntrMaintPos] AND !F[19:ReelLockout]),F[17:ManTrig]=(ON) ;
8: F[18:ManTrigPB_OS]=(DI[15:ManualPrint]) ;
9: IF (F[17:ManTrig] AND F[13:LabelPresent] AND DI[27:PrntrMaintPos] AND !F[9:PrntrInCycle]),DO[16:LblSlideVac]=(OFF) ;
10: IF (F[17:ManTrig] AND F[13:LabelPresent] AND DI[27:PrntrMaintPos] AND !F[9:PrntrInCycle]),F[13:LabelPresent]=(OFF) ;
11: F[24:AutoPTrigRdy]=(F[23:AutoPTrigAllow] AND DI[26:PrntrRunPos]) ;
12: F[12:PrntAutoTrig]=(F[17:ManTrig] OR F[24:AutoPTrigRdy]) ;
13: F[10:PrntTrigOS]=(OFF) ;
14: IF (F[19:ReelLockout]),F[12:PrntAutoTrig]=(OFF) ;
15: IF (!F[10:PrntTrigOS] AND !F[9:PrntrInCycle] AND F[12:PrntAutoTrig] AND DI[12:LblShtl_Retr] AND !F[13:LabelPresent]),F[10:PrntTrigOS]=(ON) ;
16: IF (F[10:PrntTrigOS]),F[9:PrntrInCycle]=(ON) ;
17: IF (F[9:PrntrInCycle] AND F[10:PrntTrigOS]),F[14:TrigPrntr]=(ON) ;
18: IF (F[9:PrntrInCycle] AND F[14:TrigPrntr]),F[66:ShearDelay]=PULSE,0.6sec ;
19: ;
20: IF (F[9:PrntrInCycle] AND F[14:TrigPrntr] AND F[66:ShearDelay]),DO[12:TriggerPrinter]=PULSE,0.3sec ;
21: ;
22: IF (F[9:PrntrInCycle] AND F[14:TrigPrntr] AND !F[66:ShearDelay]),F[11:PrntFinished]=(ON) ;
23: IF (F[9:PrntrInCycle] AND F[14:TrigPrntr] AND !F[66:ShearDelay]),F[14:TrigPrntr]=(OFF) ;
24: ;
25: IF (F[9:PrntrInCycle] AND F[11:PrntFinished] AND DI[10:ShearOpen] AND !F[15:ShearClosed]),DO[14:CloseShear]=(ON) ;
26: IF (F[9:PrntrInCycle] AND F[11:PrntFinished] AND DI[10:ShearOpen] AND !F[15:ShearClosed]),DO[16:LblSlideVac]=(ON) ;
27: IF (F[9:PrntrInCycle] AND F[11:PrntFinished] AND DO[14:CloseShear] AND DI[11:ShearClosed] AND !F[15:ShearClosed]),F[15:ShearClosed]=(ON) ;
28: IF (F[9:PrntrInCycle] AND F[11:PrntFinished] AND DO[14:CloseShear] AND DI[11:ShearClosed] AND F[15:ShearClosed]),DO[14:CloseShear]=(OFF) ;
29: IF (F[9:PrntrInCycle] AND F[11:PrntFinished] AND DI[10:ShearOpen] AND F[15:ShearClosed] AND !F[16:ShearDone]),F[16:ShearDone]=(ON) ;
30: IF (F[9:PrntrInCycle] AND F[16:ShearDone] AND DI[12:LblShtl_Retr] AND !DO[15:LblSlideExtend]),DO[15:LblSlideExtend]=(ON) ;
31: IF (F[9:PrntrInCycle] AND F[16:ShearDone] AND DI[13:LblShtl_Ext] AND DO[15:LblSlideExtend]),F[13:LabelPresent]=(ON) ;
32: IF (F[13:LabelPresent] AND F[9:PrntrInCycle]),F[15:ShearClosed]=(OFF) ;
33: IF (F[13:LabelPresent] AND F[9:PrntrInCycle]),F[16:ShearDone]=(OFF) ;
34: IF (F[13:LabelPresent] AND F[9:PrntrInCycle]),F[11:PrntFinished]=(OFF) ;
35: IF (F[13:LabelPresent] AND F[9:PrntrInCycle]),F[9:PrntrInCycle]=(OFF) ;
36: IF (!F[13:LabelPresent] AND !F[9:PrntrInCycle] AND DI[13:LblShtl_Ext]),DO[15:LblSlideExtend]=(OFF) ;
This code basically triggers a printer to print a label, cut the label with a shear, move the cut label with a small slide so that the robot can pick it, and tells the robot that there's a label present. Once the robot picks it, it turns off the label present flag which starts the print cycle again automatically. All of this happens in under a second while the robot is off applying the label. It also has allowances for a manual push button trigger so that the printer can be set up and tested in a maintenance mode. This is the primary reason I used BG Logic instead of a macro. Because BG Logic runs all the time, whether the robot is paused, in automatic, or in teach mode. This means maintenance personnel don't need to mess with the teach pendant as much. From a support standpoint, I prefer that whenever possible.
Like I said, you can do anything with it.