User Limits


This page describes User Limits for post-20000913xx versions and releases of MPI/XMP software.

User limits are a way that a user can configure and generate custom XMP events via the mpiMotorEventConfigGet(...) / mpiMotorEventConfigSet(.) functions by modifying the MEIMotorEventConfig/MEIXmpLimitData data structure. These events will be treated like other events (such as MPIEventTypeMOTION_DONE) and can be passed to event managers and used by notify objects. A User Limit will evaluate conditional statements on memory registers in the XMP and generate an Event when those registers meet the specified conditions. Also, when the event is generated, the Limit can write an output word (or bit) to a user-defined register in the XMP memory.

Initialization of user limits will require the configuration of:

  • MEIXmpLimitData. Enables the user limit and includes the general configuration data.
  • MEIXmpLimitCondition. Includes parameters of the actual conditions that are evaluated to generate the event.
  • MEIXmpLimitOutput. In addition to generating an Event that is returned to the host, the User Limits can be configured to write an Output value to an XMP memory register.
  • MEIXmpStatus. Sets an action when a user limit becomes true.

Sample applications demonstrating the use of User Limits can be found in the (install directory)\mei\xmp\app directory or the sample application page.


MEIXmpLimitData is the main structure that holds user limit configurations.

typedef MEIXmpLimitData	MEIMotorEventConfig;
typedef struct{ 
    MEIXmpLogic		    Logic;
    /* These variables are used internally.
    They should not be changed by the Host */
    long                    State;
    long                    Count;
    MEIXmpLimitCondition    Condition[MEIXmpLimitConditions];
    MEIXmpStatus            Status;
    MEIXmpLimitOutput       Output;
} MEIXmpLimitData;

MEIXmpLimitConditions is currently defined to have the value of two. This allows the user the option of evaluating two conditions and then either logically AND-ing or logically OR-ing them together.

Count and State are for internal use only.
The MPI method, mpiMotorEventConfigSet(...) will not write these values.


MEIXmpLogic is the logic applied between the two condition block outputs, Condition[0] and Condition[1]. Note that MEIXmpLimitConditions is currently set to 2.

Value of MEIXmpLogic Evaluates Motor object notified that a limit has occurred if...
MEIXmpLogicNEVER Nothing No event is generated
MEIXmpLogicSINGLE Condition[0] Condition[0] == TRUE
MEIXmpLogicOR Condition[0], Condition[1] (Condition[0] || Condition[1]) == TRUE
MEIXmpLogicAND Condition[0], Condition[1] (Condition[0] && Condition[1]) == TRUE
Other MEIXmpLogic enums For internal use only.  


Note on MEIXmpLogic:

  • When finished with user limits, it is good practice to set MEIXmpLogic to MEIXmpLogicNEVER so that the XMP will no longer use background time to process completed events.
  • When reading the value of MEIXmpLogic on User Limits, remember to use OR MEIXmpLogicCustom on the value. See sample code section below on how to implement this.

Specifying a duration
User can specify a time duration that user limit condition(s) must be asserted before event is generated. Time duration is defaulted to 0 and has to be less or equal to 2^27 controller sample. See usrLim4.c for a sample application that sets the duration value. Below is an example how to specify a time duration in controller sample:

MEIMotorEventConfig motorEventConfig;
long samples = 1000; /* 1000 controller sample at 2000Hz = 0.5 second */

motorEventConfig.Logic = MEIXmpLogicSINGLE | (samples << MEIXmpLogicBITS);



This structure holds a condition that needs to be evaluated. When using two condition block outputs, the user can evaluate both conditions logically by using AND or OR.

typedef struct{
     MEIXmpLimitType     Type;
     void                *SourceAddress;
     MEIXmpGenericValue  LimitValue;
     long                Mask;
} MEIXmpLimitCondition;

SourceAddress is a pointer to an XMP memory location. Use the mpiMemory…() functions to obtain pointers to the structures of interest. Please see sample code section below for specific examples.

A condition will evaluate to TRUE if:

( (*SourceAddress & Mask) Type LimitValue.l )
       for long comparison Type's

( *SourceAddress Type LimitValue.f )
       for float comparison Type's

evaluates TRUE, where Type represents some comparison type such as < or > (see MEIXmpLimitType table below).

Mask ANDed to (*SourceAddress) †
MEIXmpLimitTypeFALSE Condition evaluates FALSE
MEIXmpLimitTypeTRUE Condition evaluates TRUE
MEIXmpLimitTypeGT > (long data types)
MEIXmpLimitTypeGE >= (long data types)
MEIXmpLimitTypeLT < (long data types)
MEIXmpLimitTypeLE <= (long data types)
MEIXmpLimitTypeEQ = = (long data types)
MEIXmpLimitTypeBIT_CMP = = (bit masks)
MEIXmpLimitTypeNE !=
MEIXmpLimitTypeABS_GT |*SourceAddress & mask| > (long data types)
MEIXmpLimitTypeABS_LE |*SourceAddress & mask| <= (long data types)
MEIXmpLimitTypeFGT > (float data types)
MEIXmpLimitTypeFGE >= (float data types)
MEIXmpLimitTypeFLT < (float data types)
MEIXmpLimitTypeFLE <= (float data types)
MEIXmpLimitTypeFEQ = = (float data types)
MEIXmpLimitTypeFNE != (float data types)
MEIXmpLimitTypeFABS_GT |*SourceAddress| > (float data types)
MEIXmpLimitTypeFABS_LE |*SourceAddress| <= (float data types)

- To be safe set Mask=0xFFFFFFFF whenever a long or float comparison is desired.


Status defines what actions the XMP will take when a user limit evaluates as TRUE. Always set Status to at least MEIXmpStatusLIMIT to notify the motor object that a limit has occurred.

Value of Status †‡

Action to be taken




Axes attached to the motor will be Paused


Axes attached to the motor will be Stopped


Axes attached to the motor will be Aborted


Axes attached to the motor will be E-Stopped


Axes attached to the motor will be E-Stopped and Aborted

- If MEIXmpStatusLIMIT is not included in the value of Status, then if another action is taken (i.e. an abort event if Status = MEIXmpStatusABORT) an application will not be able to identify why the action was taken.

- Do not OR more than one MEIXmpStatus bit to MEIXmpStatusLIMIT.
For example, do not set Status = MEIXmpStatusLIMIT | MEIXmpStatusABORT | MEIXmpStatusESTOP.



MEIXmpLimitOutput allows a user to write an Output value to an XMP memory register in addition to generating an Event.

typedef struct {
     long      AndMask;
     long      OrMask;
     long      *OutputPtr;
     long      Enabled;
} MEIXmpLimitOutput;

AndMask - a bit mask that will be bit-wise AND-ed with the data pointed to by OutputPtr.

OrMask - a bit mask that will be bit-wise OR-ed with the result of (AndMask & *OutputPtr).

*OutputPtr - a pointer to an XMP memory location. Use the mpiMemory.() functions to obtain the pointers of interest.

Enabled - tells the XMP whether or not to use the MEIXmpLimitOutput structure. It takes either TRUE or FALSE values.

Effectively, if a user limit evaluates to TRUE, then the MEIXmpLimitOutput structure is used as follows:

if (Enabled) {
        *OutputPtr = OrMask | ( AndMask & (*OutputPtr) );

The following example shows what occurs for each combination of bits for *OutputPtr, AndMask, and OrMask:

The setup of the five most common output operations are outlined below:

To set bit(s), set:

Enabled   =   TRUE;
AndMask   =   0xFFFFFFFF;
OrMask    =   ( bit(s) to set );

To clear bit(s), set:

Enabled   =   TRUE;
AndMask   =   ~( bit(s) to clear );
OrMask    =   0;

To set a long value, set:

Enabled   =   TRUE;
AndMask   =   0;
OrMask    =   ( value );

To set a float value, set:

Enabled   =   TRUE;
AndMask   =   0;
OrMask    =   *(long*)( & ( float variable ) );


MEIXmpGenericValue   generic;

Enabled   =   TRUE;
AndMask   =   0;
generic.f =   ( value );
OrMask    =   generic.l;

To not set any output:

Enabled   =   FALSE;

/* To be safe, these values won't change value
of (*OutputPtr) if Enabled=TRUE */
AndMask   =   0xFFFFFFFF;
OrMask    =   0;

Unlike user limit events, the output structure is used every time the user limit evaluates to TRUE, not just when the user limit changes from a FALSE state to a TRUE state.


Sample Code

Sample Code 1

The following sample function reads and prints out the Logic Type of User Limit 0.

void ReadUserLimitLogic(MPIControl control){
     MEIXmpData          *firmware;
     MEIXmpBufferData    *buffer;
     long                UserLimitLogic;

     /* Get pointer to XMP firmware */
     returnValue = mpiControlMemory(control, (void **)&firmware, (void **)&buffer);

     UserLimitLogic = buffer->UserLimit[0].Limit[0].Logic;
         case (MEIXmpLogicCUSTOM | MEIXmpLogicNEVER):{
               printf("NEVER Triggers");
         case (MEIXmpLogicCUSTOM | MEIXmpLogicSINGLE):{
               printf("Triggers on CONDITION[0]=True");
         case (MEIXmpLogicCUSTOM | MEIXmpLogicOR):{ 
               printf("Triggers on EITHER CONDITION = TRUE");
         case (MEIXmpLogicCUSTOM | MEIXmpLogicAND):{
               printf("Triggers when BOTH CONDITIONS = TRUE"); 
               printf("** Logic Not Set **"); 
     }//End Switch 

Sample Code 2

The following sample code sets up user limit events to trigger on Actual Position compare then write output data to user buffer data 0. For example, when calling


The userLimitPositionEvent configures to

  • Get a User Limit 0 event when actual position is greater than 3000 and commanded velocity is greater than 0.
  • Write 1 to user buffer data 0 when the event happens
  • Set action to NONE when the event happens.
void userLimitPositionEvent(MPIMotor        motor,
                            long            axisNumber,
                            MEIEventType    eventType,
                            MEIXmpLimitType limitType,
                            long            position,
                            long            outputData) 
		   MPIControl           control;
		   MEIXmpData           *firmware;
		   MEIXmpBufferData     *buffer;
		   MEIMotorEventConfig  motorEventConfig;     
		   MPIEventMask         resetMask;       
		   MEIXmpLimitType      limitTypeVel;
		   long                 returnValue = MPIMessageOK;
		   mpiEventMaskSET(resetMask, eventType);
		/* Determine control handle then Get pointer to XMP firmware */     
		control = mpiMotorControl(motor);
		returnValue =
					  (void **)&firmware,
					  (void **)&buffer);
		   /* Change the limit type for velocity to be float data type
			Velocity < 0 or Velocity > 0 */
		   if(limitType == MEIXmpLimitTypeGE){
			  limitTypeVel = MEIXmpLimitTypeFGT;
			  limitTypeVel = MEIXmpLimitTypeFLT;
		   /* Get motor configuration. Note: Always do GET before SET */
		   returnValue =
		   /* Set up limit condition 0 */
		   motorEventConfig.Condition[0].Type          = limitType;
		   motorEventConfig.Condition[0].SourceAddress = &firmware->Axis[axisNumber].ActPosition;
		   motorEventConfig.Condition[0].Mask          = 0xffffffff;
		   /* AND mask */     
		   motorEventConfig.Condition[0].LimitValue.g32.l  = position;
		   /* Set up limit condition 1 */
		   motorEventConfig.Condition[1].Type             = limitTypeVel;
		   motorEventConfig.Condition[1].SourceAddress    = &firmware->Axis[axisNumber].CommandVelocity;
		   motorEventConfig.Condition[1].Mask             = 0xffffffff;     
		   motorEventConfig.Condition[1].LimitValue.g32.l = 0;
		   /* Set up limit logic */
		   motorEventConfig.Logic  = MEIXmpLogicAND;
		   /* Set up limit action to NONE */
		   motorEventConfig.Status = MEIXmpStatusLIMIT;
		   /* Set up limit output */
		   motorEventConfig.Output.OutputPtr = &buffer->UserBuffer.Data[0];
		   /* output address */
		   motorEventConfig.Output.AndMask   = 0;
		   motorEventConfig.Output.OrMask    = outputData;
		   motorEventConfig.Output.Enabled   = TRUE;
		   /* Set motor configuration */
		   returnValue =
		  /* Reset event in case conditions have already been satisfied */
		  returnValue =
		  msgCHECK(returnValue); }  


Performance Characteristics

It is important to understand that user limits:

  • are evaluated in the background cycle.

  • trigger events only when the limit changes state from FALSE to TRUE. In other words,
    a user limit must be reset by changing its state to FALSE before it can trigger another event.

The foreground cycle processes crucial information that needs to be updated every servo cycle. The background cycle processes all other information. The background cycle runs continuously on the XMP as often as it can. A foreground cycle starts when a timer interrupt on the XMP puts the background cycle on hold. After the foreground cycle is done, the background cycle continues running. The foreground cycle will be started at regular intervals at the rate of once per servo cycle. The background cycle runs as quickly as possible with whatever spare time the foreground cycle does not use.

Usually the background cycle will complete many cycles in the time it takes to complete a servo cycle. However, the time it takes to complete a single background cycle can end up spanning many servo cycles if the sample rate is raised or if the foreground cycle takes more time to process. If an application requires a high number of XMP objects and features or requires a fast sample rate, it is possible that background cycles could take multiple servo cycles to process, even dozens if the XMP is pushed to its limits.

Since user limits are evaluated in the background cycle, events might not be generated or output might not be written in the same servo cycle as when the conditions for the user limit would otherwise first be evaluated as TRUE. In the example below, a user limit is set up to evaluate TRUE when the axis position is greater than 1000. We would expect the conditions of the user limit to evaluate TRUE for sample 240. The XMP's background cycle, however, does not evaluate the user limit until sample 242. Therefore, an event is triggered two samples later than one would hope. The delay of two samples shown here is not indicative of typical XMP setups. Background cycles are commonly evaluated more quickly than foreground cycles, but as explained above, it is possible for the foreground cycle to process more frequently.

The following example shows how it is possible to even miss a user limit when the conditions that would cause the user limit to evaluate as TRUE and change too quickly back to FALSE. In this example, a user limit has been set up to evaluate TRUE when the axis position is greater than 200. The axis is performing sinusoidal motion of amplitude 220 encoder counts with an approximate period of eight samples. If the background cycle delays evaluating the user limit by even one servo cycle, it will miss triggering an event.

We now return to our first example to show the corollary to the previous example. It is equally possible to miss resetting the state of the user limit to FALSE after the position drops below 1000, so that it will not trigger an event when the conditions for the user limit to evaluate TRUE occur again.


       Legal Notice  |  Tech Email  |  Feedback
Copyright ©
2001-2021 Motion Engineering