User Limits
Introduction
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
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
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 */
mpiMotorEventConfigGet(...)
...
motorEventConfig.Logic = MEIXmpLogicSINGLE | (samples << MEIXmpLogicBITS);
...
mpiMotorEventConfigSet(...)
|
MEIXmpLimitCondition
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).
MEIXmpLimitType |
Meaning |
Mask ANDed to (*SourceAddress) † |
MEIXmpLimitTypeFALSE |
Condition evaluates
FALSE |
No |
MEIXmpLimitTypeTRUE |
Condition evaluates TRUE |
No |
MEIXmpLimitTypeGT |
> (long data types) |
Yes |
MEIXmpLimitTypeGE |
>= (long data types) |
Yes |
MEIXmpLimitTypeLT |
< (long data types) |
Yes |
MEIXmpLimitTypeLE |
<= (long data types) |
Yes |
MEIXmpLimitTypeEQ |
= = (long data types) |
Yes |
MEIXmpLimitTypeBIT_CMP |
= = (bit masks) |
Yes |
MEIXmpLimitTypeNE |
!= |
Yes |
MEIXmpLimitTypeABS_GT |
|*SourceAddress & mask| > (long
data types) |
Yes |
MEIXmpLimitTypeABS_LE |
|*SourceAddress & mask| <= (long
data types) |
Yes |
MEIXmpLimitTypeFGT |
> (float data types) |
No |
MEIXmpLimitTypeFGE |
>= (float data types) |
No |
MEIXmpLimitTypeFLT |
< (float data types) |
No |
MEIXmpLimitTypeFLE |
<= (float data types) |
No |
MEIXmpLimitTypeFEQ |
= = (float data types) |
Yes |
MEIXmpLimitTypeFNE |
!= (float data types) |
Yes |
MEIXmpLimitTypeFABS_GT |
|*SourceAddress| > (float data
types) |
No |
MEIXmpLimitTypeFABS_LE |
|*SourceAddress| <= (float data
types) |
No |
† - To be safe set Mask=0xFFFFFFFF whenever
a long or float comparison is desired.
MEIXmpStatus
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 |
MEIXmpStatusLIMIT |
None |
MEIXmpStatusLIMIT | MEIXmpStatusPAUSE |
Axes attached to the motor will be Paused |
MEIXmpStatusLIMIT | MEIXmpStatusSTOP |
Axes attached to the motor will be Stopped |
MEIXmpStatusLIMIT | MEIXmpStatusABORT |
Axes attached to the motor will be Aborted |
MEIXmpStatusLIMIT | MEIXmpStatusESTOP |
Axes attached to the motor will be E-Stopped |
MEIXmpStatusLIMIT | MEIXmpStatusESTOP_ABORT |
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
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
) ); |
or |
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);
msgCHECK(returnValue);
UserLimitLogic = buffer->UserLimit[0].Limit[0].Logic;
switch(UserLimitLogic)
{
case (MEIXmpLogicCUSTOM | MEIXmpLogicNEVER):{
printf("NEVER Triggers");
break;
}
case (MEIXmpLogicCUSTOM | MEIXmpLogicSINGLE):{
printf("Triggers on CONDITION[0]=True");
break;
}
case (MEIXmpLogicCUSTOM | MEIXmpLogicOR):{
printf("Triggers on EITHER CONDITION = TRUE");
break;
}
case (MEIXmpLogicCUSTOM | MEIXmpLogicAND):{
printf("Triggers when BOTH CONDITIONS = TRUE");
break;
}
default:{
printf("** Logic Not Set **");
break;
}
}//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
userLimitPositionEvent(motor,
axisNumber,
MEIEventTypeLIMIT_USER0,
MEIXmpLimitTypeGE,
3000,
1);
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;
mpiEventMaskCLEAR(resetMask);
mpiEventMaskSET(resetMask, eventType);
/* Determine control handle then Get pointer to XMP firmware */
control = mpiMotorControl(motor);
msgCHECK(mpiControlValidate(control));
returnValue =
mpiControlMemory(control,
(void **)&firmware,
(void **)&buffer);
msgCHECK(returnValue);
/* Change the limit type for velocity to be float data type
Velocity < 0 or Velocity > 0 */
if(limitType == MEIXmpLimitTypeGE){
limitTypeVel = MEIXmpLimitTypeFGT;
}else{
limitTypeVel = MEIXmpLimitTypeFLT;
}
/* Get motor configuration. Note: Always do GET before SET */
returnValue =
mpiMotorEventConfigGet(motor,
eventType,
NULL,
&motorEventConfig);
msgCHECK(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 =
mpiMotorEventConfigSet(motor,
eventType,
NULL,
&motorEventConfig);
msgCHECK(returnValue);
/* Reset event in case conditions have already been satisfied */
returnValue =
mpiMotorEventReset(motor,
resetMask);
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.
|