CNC Services Northwest

Centroid CNC control sales, service, training and support


MPU11 PLC Program Application: Twin Turret Lathe

Devices such as tool changers, gear shifters, and pallet changers usually require step-by-step sequencing to perform their functions. This can be programmed in a variety of ways.

I find that breaking down the sequence into discrete steps, and defining a PLC program Stage for each step, provides a clear structure. Clear structure, with clearly defined entry and exit conditions and with clearly delineated responsibilities, improves readability, reliability and maintainability.

The following example is for a twin-turret lathe, with hydraulically actuated turrets. Each turret has one solenoid valve for operation. If the solenoid valve is powered, the turret lifts, then rotates continuously. When the solenoid valve is unpowered, the turret stops and locks back down. Each turret has one switch which detects whether the turret is unlocked, plus a series of switches which encode the turret position. Each turret has four positions. The front turret holds T01 - T04; the rear turret holds T05 - T08.

Implementation

To implement automatic tool changing on this lathe, I added the following code to a standard "basic" MPU11 lathe PLC program:

In Message Definitions

  INVALID_TN_ERR              IS 27906;(2+256*109)
  FRONT_TURRET_UNLOCK_TIMEOUT IS 28162;(2+256*110)
  FRONT_TURRET_ROTATE_TIMEOUT IS 28418;(2+256*111)
  FRONT_TURRET_LOCK_TIMEOUT   IS 28674;(2+256*112)
  REAR_TURRET_UNLOCK_TIMEOUT  IS 28930;(2+256*113)
  REAR_TURRET_ROTATE_TIMEOUT  IS 29186;(2+256*114)
  REAR_TURRET_LOCK_TIMEOUT    IS 29442;(2+256*115)
  ATC_DONE_TIMEOUT_ERR        IS 29698;(2+256*116)
  ATC_FINAL_POSITION_ERR      IS 29954;(2+256*117)
  INVALID_FRONT_SWITCHES_ERR  IS 30978;(2+256*121)
  INVALID_REAR_SWITCHES_ERR   IS 31234;(2+256*122)
  FRONT_NOT_LOCKED_ERR        IS 31490;(2+256*123)
  REAR_NOT_LOCKED_ERR         IS 31746;(2+256*124)

In Input Definitions

  TurretPosT01          IS INP33  ; 1 = in this position  0 = not in this position
  TurretPosT02          IS INP34  ; 1 = in this position  0 = not in this position
  TurretPosT03          IS INP35  ; 1 = in this position  0 = not in this position
  TurretPosT04          IS INP36  ; 1 = in this position  0 = not in this position
  TurretPosT05          IS INP37  ; 1 = in this position  0 = not in this position
  TurretPosT06          IS INP38  ; 1 = in this position  0 = not in this position
  TurretPosT07          IS INP39  ; 1 = in this position  0 = not in this position
  TurretPosT08          IS INP40  ; 1 = in this position  0 = not in this position
  FrontTurretUnlocked   IS INP41  ; 1 = turret unlocked   0 = turret locked
  RearTurretUnlocked    IS INP42  ; 1 = turret unlocked   0 = turret locked

In Output Definitions

  FrontTurretIndexSol   IS OUT33 ; 1 = unlock and rotate  0 = lock down
  RearTurretIndexSol    IS OUT34 ; 1 = unlock and rotate  0 = lock down

In Memory Definitions

  ATCDone_M                IS MEM20  ; cnctch.mac depends on this location
  M6InProgress_M           IS MEM21
  IndexInProgress_M        IS MEM22
  ;...
  RearTurretActive_M       IS MEM35
  TurretsShouldBeLocked_M  IS MEM36
  ;...
  Blink_M                  IS MEM88

In M Function Request Definitions

  M6       IS SV_M94_M95_6 ; Tool Change

In Word Variable Definitions (32-bit integers)

  ATCError_W               IS W8
  ATCStage_W               IS W9
  ;...
  FrontTurretPosition_W    IS W26
  RearTurretPosition_W     IS W27
  RequestedPosition_W      IS W28
  TurretPattern_W          IS W29

In Positive Differential (One Shot) Definitions

  
  FrontTurretNewPosPD      IS PD28
  RearTurretNewPosPD       IS PD29

In Timer Definitions

  Blink_T                IS T8
  ;...
  ATCUnlock_T            IS T15
  ATCRotate_T            IS T16
  ATCPause_T             IS T17
  ATCLock_T              IS T18
  ATCCheck_T             IS T19
  ATCDone_T              IS T20
  InvalidFrontPos_T      IS T21
  InvalidRearPos_T       IS T22

In Stage Definitions

  
  ATCStartStage              IS STG30
  ATCFrontUnlockStage        IS STG31
  ATCFrontRotateStage        IS STG32
  ATCFrontPauseStage         IS STG33
  ATCFrontLockStage          IS STG34
  ATCRearUnlockStage         IS STG35
  ATCRearRotateStage         IS STG36
  ATCRearPauseStage          IS STG37
  ATCRearLockStage           IS STG38
  ATCCheckStage              IS STG39
  ATCDoneStage               IS STG40
  ATCErrorStage              IS STG41
  ATCResetStage              IS STG42

In InitialStage

(inserted along with all the other tasks which are performed just once as the PLC program starts up, in a lengthy "IF (1==1)" block)

             WTB SV_NV_W1 RearTurretActive_M 1,

In MainStage

(to be performed on every scan)


  ; Cancel M6 request if the CNC program cycle is no longer running
  IF !SV_PROGRAM_RUNNING THEN RST M6

  ; Run 1Hz blinker for Aux LEDs
  IF True THEN Blink_T = 500, SET Blink_T
  IF Blink_T ^ Blink_M THEN (Blink_M)
  IF Blink_T THEN RST Blink_T

  ; Read positions of both turrets

  ; Turret switch patterns, reading four bits into an integer:
  ; T01 or T05 = 0001 = 1
  ; T02 or T06 = 0010 = 2
  ; T03 or T07 = 0100 = 4
  ; T04 or T08 = 1000 = 8

  ; Read and validate front turret position
  IF True THEN Temp_W = 0,
               BTW TurretPattern_W TurretPosT01 4
  IF TurretPattern_W == 1 THEN Temp_W = 1
  IF TurretPattern_W == 2 THEN Temp_W = 2
  IF TurretPattern_W == 4 THEN Temp_W = 3
  IF TurretPattern_W == 8 THEN Temp_W = 4
  IF Temp_W != 0 && Temp_W != FrontTurretPosition_W
    THEN (FrontTurretNewPosPD),
         FrontTurretPosition_W = Temp_W
  ; if switch pattern is invalid for too long a time, report error
  IF Temp_W == 0 THEN InvalidFrontPos_T = 1000,
                      SET InvalidFrontPos_T
  IF Temp_W != 0 THEN RST InvalidFrontPos_T
  IF InvalidFrontPos_T && SV_PROGRAM_RUNNING
    THEN ErrorMsg_W = INVALID_FRONT_SWITCHES_ERR,
         SET ErrorFlag_M

  ; Read and validate rear turret position
  IF True THEN Temp_W = 0,
               BTW TurretPattern_W TurretPosT05 4
  IF TurretPattern_W == 1 THEN Temp_W = 5
  IF TurretPattern_W == 2 THEN Temp_W = 6
  IF TurretPattern_W == 4 THEN Temp_W = 7
  IF TurretPattern_W == 8 THEN Temp_W = 8
  IF Temp_W != 0 && Temp_W != RearTurretPosition_W
    THEN (RearTurretNewPosPD),
         RearTurretPosition_W = Temp_W
  ; if switch pattern is invalid for too long a time, report error
  IF Temp_W == 0 THEN InvalidRearPos_T = 1000,
                      SET InvalidRearPos_T
  IF Temp_W != 0 THEN RST InvalidRearPos_T
  IF InvalidRearPos_T && SV_PROGRAM_RUNNING
    THEN ErrorMsg_W = INVALID_REAR_SWITCHES_ERR,
         SET ErrorFlag_M

  IF SV_JOB_IN_PROGRESS && SpindleEnableOut && !M6InProgress_M
    THEN (TurretsShouldBeLocked_M)
  IF TurretsShouldBeLocked_M && FrontTurretUnlocked
    THEN ErrorMsg_W = FRONT_NOT_LOCKED_ERR,
         SET ErrorFlag_M
  IF TurretsShouldBeLocked_M && RearTurretUnlocked
    THEN ErrorMsg_W = REAR_NOT_LOCKED_ERR,
         SET ErrorFlag_M

  ; Report currently-active tool (turret position) to CNC11 software.
  ; CNC11 expects to receive this value in BCD format, so convert it.
  IF !RearTurretActive_M THEN Temp_W = FrontTurretPosition_W
  IF RearTurretActive_M THEN Temp_W = RearTurretPosition_W
  IF True THEN BCD Temp_W,
               SV_PLC_CAROUSEL_POSITION = Temp_W

  ; Detect incoming tool-change request and dispatch to ATC stages
  IF M6 && !M6InProgress_M THEN SET M6InProgress_M,
                                SET ATCStartStage

  ; Write the turret status bit(s) into SV_NV_W1
  ; so they can be retrieved on next power-up.
  IF True THEN BTW Temp_W RearTurretActive_M 1,
               SV_NV_W1 = Temp_W

  ; Detect Aux5 and Aux8 key presses, use to initiate manual turret indexing
  ; when no CNC program is running and no faults are present
  IF Aux5Key && !IndexInProgress_M && !SV_JOB_IN_PROGRESS && !SV_STOP
    THEN SET IndexInProgress_M,
         SET ATCRearUnlockStage

  IF Aux8Key && !IndexInProgress_M && !SV_JOB_IN_PROGRESS && !SV_STOP
    THEN SET IndexInProgress_M,
         SET ATCFrontUnlockStage

  ; Use the Aux5 and Aux8 LEDs to indicate turret operation:
  ;   LED on = turret index solenoid powered
  ;   LED blinking = solenoid not powered, but turret not locked
  IF RearTurretIndexSol || (RearTurretUnlocked && Blink_M) THEN (Aux5LED)
  IF FrontTurretIndexSol || (FrontTurretUnlocked && Blink_M) THEN (Aux8LED)

New stages, added after MainStage

  ;================================================================
     ATCStartStage
  ;================================================================
  ; Validate requested tool number, determine whether a turret index
  ; is required, and dispatch to the appropriate turret stages if needed
  IF True THEN ATCStage_W = 1,
               RequestedPosition_W = SV_TOOL_NUMBER

  IF RequestedPosition_W < 1 || RequestedPosition_W > 8
    THEN SET ErrorFlag_M,
         ErrorMsg_W = INVALID_TN_ERR,
         JMP ATCResetStage

  IF RequestedPosition_W == FrontTurretPosition_W
    THEN RST RearTurretActive_M,
         JMP ATCDoneStage

  IF RequestedPosition_W == RearTurretPosition_W
    THEN SET RearTurretActive_M,
         JMP ATCDoneStage

  IF RequestedPosition_W >= 1 && RequestedPosition_W <= 4
     && RequestedPosition_W != FrontTurretPosition_W
    THEN JMP ATCFrontUnlockStage

  IF RequestedPosition_W >= 5 && RequestedPosition_W <= 8
     && RequestedPosition_W != RearTurretPosition_W
    THEN JMP ATCRearUnlockStage

  ;================================================================
     ATCFrontUnlockStage
  ;================================================================
  ; Power the turret's index solenoid, wait for the turret to be unlocked
  IF True THEN ATCStage_W = 2,
               ATCUnlock_T = 1000,
               SET ATCUnlock_T,
               RST RearTurretActive_M,
               SET FrontTurretIndexSol

  IF FrontTurretUnlocked THEN JMP ATCFrontRotateStage

  IF ATCUnlock_T THEN ErrorMsg_W = FRONT_TURRET_UNLOCK_TIMEOUT,
                      SET ErrorFlag_M,
                      JMP ATCErrorStage

  IF SV_STOP || (M6InProgress_M && !M6) THEN JMP ATCResetStage

  IF !ATCFrontUnlockStage THEN RST ATCUnlock_T

  ;================================================================
     ATCFrontRotateStage
  ;================================================================
  ; Wait while the turret rotates to the target position
  IF True THEN ATCStage_W = 3
  IF M6InProgress_M THEN ATCRotate_T = 8000,
                         SET ATCRotate_T

  ; For a programmed change, rotate until we are at the requested position.
  ; For a manual index, wait until the first position arrival after the
  ; Aux key has been released.
  IF M6InProgress_M && FrontTurretPosition_W == RequestedPosition_W ||
     IndexInProgress_M && FrontTurretNewPosPD && !Aux8Key
    THEN JMP ATCFrontPauseStage

  IF ATCRotate_T THEN ErrorMsg_W = FRONT_TURRET_ROTATE_TIMEOUT,
                      SET ErrorFlag_M,
                      JMP ATCErrorStage

  IF SV_STOP || (M6InProgress_M && !M6) THEN JMP ATCResetStage

  IF !ATCFrontRotateStage THEN RST ATCRotate_T

  ;================================================================
     ATCFrontPauseStage
  ;================================================================
  ; Continue to rotate a short additional time after the turret-position
  ; switches showed us to be at the target position
  IF True THEN ATCStage_W = 4,
               ATCPause_T = 60,
               SET ATCPause_T

  IF ATCPause_T THEN JMP ATCFrontLockStage

  IF SV_STOP || (M6InProgress_M && !M6) THEN JMP ATCResetStage

  IF !ATCFrontPauseStage THEN RST ATCPause_T

  ;================================================================
     ATCFrontLockStage
  ;================================================================
  ; Turn off the turret's index solenoid and wait for it to lock back down
  IF True THEN ATCStage_W = 5,
               ATCLock_T = 2000,
               SET ATCLock_T,
               RST FrontTurretIndexSol

  IF !FrontTurretUnlocked THEN JMP ATCCheckStage

  IF ATCLock_T THEN ErrorMsg_W = FRONT_TURRET_LOCK_TIMEOUT,
                    SET ErrorFlag_M,
                    JMP ATCErrorStage

  IF SV_STOP || (M6InProgress_M && !M6) THEN JMP ATCResetStage

  IF !ATCFrontLockStage THEN RST ATCLock_T

  ;================================================================
     ATCRearUnlockStage
  ;================================================================
  IF True THEN ATCStage_W = 4,
               ATCUnlock_T = 1000,
               SET ATCUnlock_T,
               SET RearTurretActive_M,
               SET RearTurretIndexSol

  IF RearTurretUnlocked THEN JMP ATCRearRotateStage

  IF ATCUnlock_T THEN ErrorMsg_W = REAR_TURRET_UNLOCK_TIMEOUT,
                      SET ErrorFlag_M,
                      JMP ATCErrorStage

  IF SV_STOP || (M6InProgress_M && !M6) THEN JMP ATCResetStage

  IF !ATCRearUnlockStage THEN RST ATCUnlock_T

  ;================================================================
     ATCRearRotateStage
  ;================================================================
  IF True THEN ATCStage_W = 5
  IF M6InProgress_M THEN ATCRotate_T = 8000,
                         SET ATCRotate_T

  IF M6InProgress_M && RearTurretPosition_W == RequestedPosition_W ||
     IndexInProgress_M && RearTurretNewPosPD && !Aux5Key
    THEN JMP ATCRearPauseStage

  IF ATCRotate_T THEN ErrorMsg_W = REAR_TURRET_ROTATE_TIMEOUT,
                      SET ErrorFlag_M,
                      JMP ATCErrorStage

  IF SV_STOP || (M6InProgress_M && !M6) THEN JMP ATCResetStage

  IF !ATCRearRotateStage THEN RST ATCRotate_T

  ;================================================================
     ATCRearPauseStage
  ;================================================================
  IF True THEN ATCStage_W = 6,
               ATCPause_T = 60,
               SET ATCPause_T

  IF ATCPause_T THEN JMP ATCRearLockStage

  IF SV_STOP || (M6InProgress_M && !M6) THEN JMP ATCResetStage

  IF !ATCRearPauseStage THEN RST ATCPause_T

  ;================================================================
     ATCRearLockStage
  ;================================================================
  IF True THEN ATCStage_W = 7,
               ATCLock_T = 2000,
               SET ATCLock_T,
               RST RearTurretIndexSol

  IF !RearTurretUnlocked THEN JMP ATCCheckStage

  IF ATCLock_T THEN ErrorMsg_W = REAR_TURRET_LOCK_TIMEOUT,
                    SET ErrorFlag_M,
                    JMP ATCErrorStage

  IF SV_STOP || (M6InProgress_M && !M6) THEN JMP ATCResetStage

  IF !ATCRearLockStage THEN RST ATCLock_T

  ;================================================================
     ATCCheckStage
  ;================================================================
  ; Turret is clamped back down.  If this is a programmed tool change,
  ; pause a moment and verify that we did clamp down at the correct
  ; final position.
  IF True THEN ATCStage_W = 8,
               ATCCheck_T = 500,
               SET ATCCheck_T

  IF IndexInProgress_M || ATCCheck_T THEN JMP ATCDoneStage

  IF M6InProgress_M &&
     (!RearTurretActive_M && FrontTurretPosition_W != RequestedPosition_W ||
      RearTurretActive_M && RearTurretPosition_W != RequestedPosition_W)
    THEN ErrorMsg_W = ATC_FINAL_POSITION_ERR,
         SET ErrorFlag_M,
         JMP ATCErrorStage

  IF SV_STOP || (M6InProgress_M && !M6) THEN JMP ATCResetStage

  IF !ATCCheckStage THEN RST ATCCheck_T

  ;================================================================
     ATCDoneStage
  ;================================================================
  IF True THEN ATCStage_W = 9,
               SET ATCDone_M,
               ATCDone_T = 2000, SET ATCDone_T

  IF !M6 THEN JMP ATCResetStage

  IF ATCDone_T THEN ErrorMsg_W = ATC_DONE_TIMEOUT_ERR,
                    SET ErrorFlag_M,
                    JMP ATCResetStage

  IF !ATCDoneStage THEN RST ATCDone_T

  ;================================================================
     ATCErrorStage
  ;================================================================
  IF True THEN ATCError_W = 0,
               JMP ATCResetStage
  IF TurretPosT01 THEN ATCError_W = 1
  IF TurretPosT02 THEN ATCError_W = ATCError_W + 2
  IF TurretPosT03 THEN ATCError_W = ATCError_W + 4
  IF TurretPosT04 THEN ATCError_W = ATCError_W + 8
  IF TurretPosT05 THEN ATCError_W = ATCError_W + 16
  IF TurretPosT06 THEN ATCError_W = ATCError_W + 32
  IF TurretPosT07 THEN ATCError_W = ATCError_W + 64
  IF TurretPosT08 THEN ATCError_W = ATCError_W + 128
  IF FrontTurretUnlocked THEN ATCError_W = ATCError_W + 256
  IF RearTurretUnlocked THEN ATCError_W = ATCError_W + 512
  IF M6 THEN ATCError_W = ATCError_W + 1024
  IF M6InProgress_M THEN ATCError_W = ATCError_W + 2048
  IF IndexInProgress_M THEN ATCError_W = ATCError_W + 4096
  IF FrontTurretIndexSol THEN ATCError_W = ATCError_W + 8192
  IF RearTurretIndexSol THEN ATCError_W = ATCError_W + 16384

  ;================================================================
     ATCResetStage
  ;================================================================
  ; Turn off all outputs and flags related to turret indexing
  IF True THEN RST FrontTurretIndexSol,
               RST RearTurretIndexSol,
               RST M6InProgress_M,
               RST IndexInProgress_M,
               RST ATCDone_M,
               RST ATCResetStage
  

There is, of course, a lot going on here. However, it can be broken down into some fairly clear tasks:

Note: In the PLC program I name the incoming tool-change request from the CNC program as "M6", even though on a lathe, there is no actual M6 code. The request comes from the "cnctch.mac" CNC macro, which is called whenever the CNC program calls for a T number different from the active one. "M6", in this case, is just a convenient name.

I use "BTW" to read all four turret-position switches into an integer word variable, then test the result against the valid combinations. This is a little more complex than just testing the four switches, but protects us against the possibility that one or more switches are faulty.

Each Stage in the sequence handles one operation: typically turning an output on or off, then waiting for inputs to change to show that the expected action took place. If the action completes successfully within the allowable time limit, then that stage jumps to the next stage in the sequence.

Each stage typically has an error timer, which begins running as soon as we enter the stage. If the expected action does not complete successfully in the allowed time, then the timer expires and an Error or Fault is triggered.

The error timer, and any other timers that are started within a stage, are reset by the stage whenever it exits.

Each stage also monitors to see if a Fault has occurred, or if the CNC program cycle which may have started the sequence has been cancelled. In such a situation, the stage cancels itself (typically by jumping to a "Reset" stage to turn off any related outputs and flags).

Let us look again at one of the turret operation stages to see how these actions are coded:

  ;================================================================
     ATCRearUnlockStage
  ;================================================================
1 IF True THEN ATCStage_W = 4,
2              ATCUnlock_T = 1000,
               SET ATCUnlock_T,
               SET RearTurretActive_M,
               SET RearTurretIndexSol

3 IF RearTurretUnlocked THEN JMP ATCRearRotateStage

4 IF ATCUnlock_T THEN ErrorMsg_W = REAR_TURRET_UNLOCK_TIMEOUT,
                      SET ErrorFlag_M,
                      JMP ATCErrorStage

5 IF SV_STOP || (M6InProgress_M && !M6) THEN JMP ATCResetStage

6 IF !ATCRearUnlockStage THEN RST ATCUnlock_T
  
  1. The ATCStage_W variable is not essential. It serves as a troubleshooting aid: if the tool-change sequence hangs up or exits prematurely, the value in that variable tells what step we were last at.
  2. On entry to the stage we set the error timer to a suitable limit and start it running; set a flag that shows we are now using the rear turret; and turn on the turret's index solenoid.
  3. We then wait for the turret-unlocked switch to close, and if/when it does, we jump to the next stage. The jump will deactivate this stage for future scans, but -- and this is important -- it does not "jump out" of the stage in this scan. Any remaining logic in this stage will still be evaluated on this scan of the PLC program.
  4. If the error timer reaches its limit and we have not yet gone on to the next step, then an error condition is asserted and we jump out via the error-encoding stage so that diagnostic information will be available in the ATCError_W variable.
  5. If any kind of fault has occurred, or if we are doing a programmed tool change (initiated with the "M6" request from cnctch.mac) but the M6 flag has been removed (likely due to CNC program cancellation), then we jump out directly to the Reset stage.
  6. Finally, if we see that our own stage-active bit is no longer set (because we executed one of the three jumps to some other stage), then we reset the error timer. Remember that this line will still be scanned and executed on the PLC scan in which the JMP was executed, even though the JMP instruction was on a previous line. This structure ensures that the timer is always cancelled on the way out of the stage, no matter how or why the stage ended.

When using this type of Stage structure, it is critical that no other logic, elsewhere in the program, use a RST command to turn off any of these stages. If that were to happen, the stage would not be evaluated in the scan it was turned off in, and the error timer would not get reset. The next time we called on this service and again entered the stage, the timer would long since have reached its preset, and the "Timeout" error would be reported immediately upon entry.

Many Centroid factory PLC programs use a different strategy and structure: somewhere in MainStage or ATCMainStage there may be a lengthy block of RST commands to turn off all ATC-related stages and timers in case of Fault, E-Stop or cancellation. The trouble with that approach is that the RST block needs to be updated every time a new stage or timer is introduced to the sequence. If an item is overlooked, then subtle bugs can result.


CNC Services Northwest Home

Copyright © 2015 Marc Leonard
Last updated 05-Dec-2015 MBL