motion control

plcopen

servo

PLCopen Motion Blocks: MC_MoveAbsolute and Friends

‌
PLCopen motion control function blocks MC_Power MC_Home MC_MoveAbsolute chained to a servo drive, flat vector diagram

If you have ever set up a servo axis in TwinCAT, Sysmac, CODESYS, or ctrlX Works, you have already met PLCopen motion control whether you knew it or not. The PLCopen MC function blocks are a vendor-neutral standard (Part 1 of the PLCopen Motion Control specification) that defines a common set of function blocks for single-axis motion. Siemens, Beckhoff, Omron, Bosch Rexroth and a dozen others all implement the same block names, the same input/output pins, and the same axis state machine. You learn it once and it transfers.

That said, there are real differences between vendor implementations that catch people out. This post walks through the core blocks, the axis state machine they all share, the sequencing rules that matter, and the gotchas I have run into on real commissioning jobs.

The PLCopen Axis State Machine

Every PLCopen axis lives in exactly one state at any moment. The blocks you call transition the axis between states. Understanding the state machine is more important than memorising individual block pins, because if you call the wrong block in the wrong state you will get an error code, not motion.

StateWhat it meansTypical trigger to enter
DisabledPower stage off, no control loop activeStart condition or after MC_Power.Enable = FALSE
StandstillPower on, axis stopped, ready for commandsMC_Power.Enable = TRUE and Status = TRUE
HomingReference run in progressMC_Home.Execute rising edge
Discrete MotionMoving to a target position or velocityMC_MoveAbsolute / MC_MoveRelative / MC_MoveVelocity
Continuous MotionVelocity command with no end positionMC_MoveVelocity
Synchronized MotionGearing or camming with another axisMC_GearIn / MC_CamIn (Part 2)
StoppingControlled deceleration in progressMC_Stop.Execute rising edge
ErrorStopFault detected, axis decelerating or stoppedDrive or encoder fault, limit hit
ErrorAxis halted with active errorRemains until MC_Reset clears it
PLCopen single-axis states. An axis can only accept new motion commands from Standstill or during Discrete/Continuous Motion (buffered mode).
ErrorStop is not the same as Error. In ErrorStop the axis is still decelerating. You cannot issue MC_Reset until the axis has fully stopped and entered the Error state. Calling Reset too early just returns ErrorID 0 and does nothing, which confuses a lot of people during commissioning.

The Core PLCopen Motion Control Blocks

MC_Power: Enabling the Axis

MC_Power is always the first block in your program. It connects the PLC's logical axis object to the physical drive and enables the power stage. The two key outputs are Status (TRUE when the drive is powered and ready) and Error. You gate every other motion block on Status = TRUE.

On Beckhoff TwinCAT, MC_Power also has EnablePositive and EnableNegative inputs that software-limit travel direction. They are both TRUE by default, but wiring your positive and negative hardware limit switch signals here is cleaner than using separate interlock rungs.

MC_Home: Reference the Axis Before You Move It

Absolute encoders skip this step, but most applications with incremental encoders need a homing run on every power-up. MC_Home accepts a HomingMode integer that tells the drive which homing strategy to use. The exact mode numbers are vendor-specific, which is one of the biggest portability traps in PLCopen. Mode 3 might mean 'home to negative limit then index pulse' in TwinCAT and something completely different in Sysmac. Always cross-reference the vendor's axis parameter documentation.

The Position input sets the axis coordinate value that will be assigned when the home sensor is detected. If your machine zero is 50 mm from the hard stop, set Position = 50.0 and the axis will call that point zero after homing. This saves you from applying offsets everywhere else in the program.

MC_MoveAbsolute: Go to a Specific Position

This is the workhorse for point-to-point positioning. You supply Position (in the axis engineering unit, usually mm or degrees), Velocity, Acceleration and Deceleration. The block moves the axis and sets Done TRUE when it arrives within the in-position window defined in the axis parameters.

One thing that trips people up: Execute is edge-triggered. You raise it once, the block latches the command internally, and the axis starts moving. If you hold Execute = TRUE and then change Position mid-move, the block ignores the new value until the next rising edge. This is intentional, but it means you must pulse Execute, not hold it. In ladder, an OSR (one-shot rising) instruction is your friend here.

MC_MoveRelative: Move by an Offset

Same pins as MC_MoveAbsolute but Distance replaces Position. The axis moves the specified distance from wherever it is right now. Useful for indexing applications where you always step forward 200 mm per part regardless of absolute position. Just remember that homing still matters; if you lose position on power loss, a relative move from a wrong starting point crashes into a hard stop.

MC_MoveVelocity: Run at a Constant Speed

MC_MoveVelocity puts the axis in continuous motion at a commanded velocity. It has no target position; the axis runs until you call MC_Stop or issue a new motion command. The InVelocity output goes TRUE when the axis has reached the commanded speed within a tolerance band. This block is what you use for conveyor-style applications where the servo is acting like a fancy variable-speed drive.

MC_Stop: Controlled Deceleration

MC_Stop decelerates the axis at the Deceleration rate you specify and then holds the axis in the Stopping state for as long as Execute is TRUE. This is important: the axis will not accept new motion commands while MC_Stop.Execute is held TRUE, even after the axis reaches zero speed. You must drop Execute to release the axis back to Standstill. I have seen programs where MC_Stop.Execute was tied to a latched bit that never cleared, and the machine was stuck in a loop wondering why MC_MoveAbsolute was not responding.

MC_Reset: Clear an Error

After a fault, the axis sits in the Error state. MC_Reset on a rising edge clears the error and, if the underlying drive fault is gone, transitions the axis back to Standstill. It does not re-enable the power stage by itself; MC_Power must still be active. On most drives you also need to acknowledge the drive-level fault separately before MC_Reset will succeed, either via a drive parameter write or a dedicated digital output to the drive reset input.

Buffered vs Aborting Motion: The BufferMode Input

MC_MoveAbsolute, MC_MoveRelative and MC_MoveVelocity all have a BufferMode input. The default is Aborting (value 0), which cancels any active motion and starts the new command immediately. Set it to Buffered (1) and the new command queues behind the current one and executes when the current move finishes. BlendingPrevious and BlendingNext are higher modes that blend the velocity profile between moves for smoother multi-segment trajectories.

Use Aborting carefully on high-inertia loads. Aborting a move at full speed means the axis immediately starts decelerating at the new command's deceleration rate, not the old one. If the new rate is higher than the drive or mechanics can handle, you will get a following error or a mechanical jolt. Buffered mode is safer for multi-step sequences.

A Real Sequence: Power On, Home, Move to Position

Here is how a typical startup sequence looks in Structured Text under CODESYS or TwinCAT. The same logic ports directly to Omron Sysmac Studio or Bosch ctrlX Works because they all use the same block interfaces.

AxisStartup.st
// Axis startup sequence: Power -> Home -> MoveAbsolute
// Works in CODESYS 3.5, TwinCAT 3, Sysmac, ctrlX Works

VAR
    Axis1           : AXIS_REF;
    fbPower         : MC_Power;
    fbHome          : MC_Home;
    fbMoveAbs       : MC_MoveAbsolute;
    fbStop          : MC_Stop;
    fbReset         : MC_Reset;

    bEnable         : BOOL := FALSE;   // operator enable
    bStartHome      : BOOL := FALSE;   // rising edge triggers home
    bStartMove      : BOOL := FALSE;   // rising edge triggers move
    bStopCmd        : BOOL := FALSE;
    bFaultAck       : BOOL := FALSE;

    rTargetPos      : LREAL := 250.0;  // mm
    rVelocity       : LREAL := 100.0;  // mm/s
    rAccel          : LREAL := 500.0;  // mm/s2
    rDecel          : LREAL := 500.0;  // mm/s2

    eSeqState       : INT := 0;
END_VAR

// --- MC_Power: always running ---
fbPower(
    Axis            := Axis1,
    Enable          := bEnable,
    EnablePositive  := TRUE,
    EnableNegative  := TRUE,
    Override        := 100.0,
    Status          =>,
    Error           =>,
    ErrorID         => );

// --- MC_Reset: fault acknowledgement ---
fbReset(
    Axis    := Axis1,
    Execute := bFaultAck);

// --- MC_Stop: operator stop ---
fbStop(
    Axis         := Axis1,
    Execute      := bStopCmd,
    Deceleration := rDecel);

// --- Sequencer ---
CASE eSeqState OF

    0: // Wait for power ready
        IF fbPower.Status THEN
            eSeqState := 10;
        END_IF

    10: // Wait for home command
        IF bStartHome THEN
            bStartHome := FALSE;
            eSeqState  := 20;
        END_IF

    20: // Execute home
        fbHome(
            Axis       := Axis1,
            Execute    := TRUE,
            Position   := 0.0,
            HomingMode := 3);   // vendor-specific: check your drive manual
        IF fbHome.Done THEN
            fbHome(Axis := Axis1, Execute := FALSE);  // drop Execute
            eSeqState := 30;
        ELSIF fbHome.Error THEN
            eSeqState := 90;
        END_IF

    30: // Wait for move command
        IF bStartMove THEN
            bStartMove := FALSE;
            eSeqState  := 40;
        END_IF

    40: // Execute MoveAbsolute
        fbMoveAbs(
            Axis         := Axis1,
            Execute      := TRUE,
            Position     := rTargetPos,
            Velocity     := rVelocity,
            Acceleration := rAccel,
            Deceleration := rDecel,
            BufferMode   := 0);     // Aborting
        IF fbMoveAbs.Done THEN
            fbMoveAbs(Axis := Axis1, Execute := FALSE);
            eSeqState := 30;        // back to idle, ready for next move
        ELSIF fbMoveAbs.Error THEN
            eSeqState := 90;
        END_IF

    90: // Error state
        IF bFaultAck THEN           // bFaultAck also feeds MC_Reset above
            bFaultAck  := FALSE;
            eSeqState  := 0;
        END_IF

END_CASE
Notice that every call to MC_Home and MC_MoveAbsolute drops Execute back to FALSE after Done is seen. This is required. If you leave Execute TRUE and the same block is called again next scan, some implementations re-trigger the move on the next rising edge of a control bit, causing unexpected repeat moves. Always clean up Execute.

PLCopen Motion Control Across Vendors: What Changes

The block names and pin names are standardised. The axis engineering units, the internal axis object type, and homing mode numbers are not. Here is a quick comparison of what actually differs between common platforms.

PlatformAxis object typeHoming mode sourceTypical cycle timeNotes
Beckhoff TwinCAT 3AXIS_REFTwinCAT MC2 documentation, modes 1-35250 µs to 1 ms EtherCATMost complete Part 1 implementation; MC_TouchProbe supported
CODESYS 3.5 (generic)AXIS_REFDepends on drive plugin1 to 4 ms typicalBlock library version matters; check SM3_Basic vs SM3_CNC
Omron Sysmac (NJ/NX)AXIS_REFOmron EtherCAT slave docs500 µs EtherCATMC_Home mode linked to Sysmac axis parameters, not block input
Bosch Rexroth ctrlXAXIS_REFctrlX MOTION parameter set1 msUses CODESYS runtime underneath
Siemens S7-1500TTO_Axis (technology object)TIA Portal TO config wizard1 to 4 ms PN/EtherCATUses S_MC blocks, not standard PLCopen names exactly
PLCopen motion control implementation differences by platform. Block names match the standard; axis types and homing modes do not.

Siemens deserves a special mention. The S7-1500T uses technology objects (TOs) and instruction names like MC_Power, MC_Home and MC_MoveAbsolute that look identical to PLCopen, but the axis data type is a Siemens-specific TO_Axis structure, and some pin names differ slightly. It is PLCopen-inspired, not a strict Part 1 implementation. If you are migrating code from CODESYS to TIA Portal, plan for a non-trivial adaptation pass. If you want a deeper look at TIA Portal data structures, the post on TIA Portal Data Blocks is worth reading alongside this one.

Common Commissioning Mistakes

  • Calling MC_MoveAbsolute before homing. The axis state machine allows it if the axis is in Standstill, but your position reference is garbage. Always interlock on a Homed status bit.
  • Not handling the Error output. Both MC_MoveAbsolute.Error and Axis1.Status.Error exist and they are not always the same signal. Log both, and log the ErrorID number, not just a boolean.
  • Forgetting that velocity and acceleration are in axis units per second, not per minute. If your axis unit is mm and you set Velocity = 100, that is 100 mm/s, not 100 mm/min. I have seen this set up wrong more times than I can count.
  • Using the same FB instance for two axes. Each axis needs its own MC_Power, MC_Home, MC_MoveAbsolute instance. Sharing instances corrupts internal state.
  • Setting Deceleration = 0. Some implementations treat zero as 'use drive default', others throw an error immediately. Always supply a real non-zero value.
  • Ignoring the following error. If the axis cannot keep up with the commanded profile (inertia too high, drive limits too tight, cycle time too slow), the drive will eventually trip on a following error. Check your drive's position error window setting early in commissioning.

Choosing Between a PLC Motion Solution and a Standalone Motion Controller

If you have ever wondered whether you need a dedicated motion controller or whether a PLCopen-capable PLC is enough, the honest answer is: for most single-axis and simple multi-axis applications, a modern PLCopen PLC handles it fine. Where you actually need a standalone controller is when you need sub-250 µs cycle times for high-dynamic applications, complex multi-axis interpolation (5-axis CNC paths), or cam profiles with thousands of points that would crush a standard PLC task. For a conveyor index, a pick-and-place robot with 2 to 4 axes, or a winder with tension control, a PLCopen PLC on EtherCAT is the right tool and costs less to program and maintain. You might also find the comparison in Stepper vs Servo Motor useful when deciding what actuator to pair with your PLCopen axis.

PLCopen motion control axis state machine diagram showing transitions between Disabled Standstill Homing Discrete Motion Stopping ErrorStop and Error states
The PLCopen single-axis state machine. Every motion block either enters or exits one of these states on each scan.

Quick Reference: PLCopen Block Inputs and Outputs

BlockKey inputsKey outputsTrigger type
MC_PowerEnable, EnablePositive, EnableNegativeStatus, Error, ErrorIDLevel (not edge)
MC_HomeExecute, Position, HomingModeDone, Busy, Error, ErrorIDRising edge
MC_MoveAbsoluteExecute, Position, Velocity, Acceleration, Deceleration, BufferModeDone, Busy, Active, Error, ErrorIDRising edge
MC_MoveRelativeExecute, Distance, Velocity, Acceleration, Deceleration, BufferModeDone, Busy, Active, Error, ErrorIDRising edge
MC_MoveVelocityExecute, Velocity, Acceleration, Deceleration, DirectionInVelocity, Busy, Active, Error, ErrorIDRising edge
MC_StopExecute, DecelerationDone, Busy, Error, ErrorIDRising edge; hold to stay in Stopping
MC_ResetExecuteDone, Busy, ErrorRising edge
Core PLCopen motion block interface summary. All blocks also take Axis as the first input.

Related Blogs