motGate2.c -- Load and trigger motion profiles using a control Gate.
/* motGate2.c */
/* Copyright(c) 1991-2006 by Motion Engineering, Inc. All rights reserved.
*
* This software contains proprietary and confidential information of
* Motion Engineering Inc., and its suppliers. Except as may be set forth
* in the license agreement under which this software is supplied, use,
* disclosure, or reproduction is prohibited without the prior express
* written consent of Motion Engineering, Inc.
*/
/*
:Load and trigger motion profiles using a control Gate.
This sample program demonstrates how to use the motion HOLD attribute and
a control Gate, to pre-load a single axis motion, and to synchronize the
start of two independent motion profiles.
Here are the steps:
1) Move the X axis.
2) Set a control Gate.
3) Load a Y axis motion profile, using the HOLD attribute.
4) Clear the control Gate, causing the Y axis motion to execute.
5) Set a control Gate.
6) Load X and Y axis motion profiles, using the HOLD attribute.
7) Clear the control Gate, causing the X and Y axis motions to execute
simultaneously.
The XMP-Series controller supports a motion HOLD attribute, which is useful
for pre-loading and triggering motion profiles. One or more motion supervisors
can be started from the same HOLD conditions. When multiple motion supervisors
are triggered by the same HOLD conditions, the individual motion profiles will
start in the same DSP sample period. The HOLD can be set/cleared with a host
function call, an XMP-Series controller internal variable or a Motor I/O
state change.
The MEIMotionAttrHold{...} structure is used to configure the HOLD conditions:
typedef struct MEIMotionAttrHold {
MEIMotionAttrHoldType type;
MEIMotionAttrHoldSource source;
float timeout;
} MEIMotionAttrHold;
The HOLD "type" can be one of the following:
MEIMotionHoldTypeGATE - host software controlled.
MEIMotionHoldTypeINPUT - XMP controller internal variable.
MEIMotionHoldTypeMOTOR - XMP controller motor I/O state change.
For each HOLD "type" the "source" union, MEIMotionAttrHoldSource{...},
configures the HOLD conditions:
MEIMotionHoldTypeGATE:
long gate - number between 0 to 31.
The motion is held until the "gate" is cleared. The function
meiControlGateSet(...) is used to set/clear a gate. When the closed
parameter is TRUE, the gate is set, and motion is held. When the
closed parameter is FALSE, the gate is cleared, and motion starts.
MEIMotionHoldTypeINPUT
long *input - address of XMP memory location.
long mask - bit mask, bitwise ANDed with the input value.
long pattern - HOLD is released when masked value matches pattern.
The motion is held until the value of the internal XMP memory location
(pointed to by *input) bitwise ANDed with the mask matches the pattern.
MEIMotionHoldTypeMOTOR
long number - motor "n", XMP's dedicated inputs (Motor[n].IO.DedicaedIN.IO)
long mask - bit mask, bitwise ANDed with the dedicated inputs.
long pattern - HOLD is released when masked inputs matches pattern.
The motion is held until the value of the dedicated input word
(Motor[n].IO.DedicaedIN.IO) bitwise ANDed with the mask matches the
pattern.
The HOLD "timeout" will cause the motion to start after the specified period
(in seconds) even if the hold criteria have not been met. To disable the
timeout feature, set the timeout value to zero.
The MPI expects an array of hold attributes specifying separate attributes
form each axis of a motion supervisor. All axes holding with the same hold
attributes (same gate, same input, mask, and pattern) will start motion in
the same sample even if the moves are specified using different motion
supervisors.
Warning! This is a sample program to assist in the integration of an
MEI motion controller with your application. It may not contain all
of the logic and safety features that your application requires.
The msgCHECK(...) macros used in the following sample code are intended
to convey our strong belief that ALL error return codes should be checked.
Actual application code should use specific error handling techniques (other
than msgCHECKs) best suited to your internal error recovery methods.
*/
#include <stdlib.h>
#include <stdio.h>
#include "stdmpi.h"
#include "stdmei.h"
#include "apputil.h"
#define MOTION_NUMBER1 (0)
#define MOTION_NUMBER2 (1)
#define AXIS_NUMBER1 (0)
#define AXIS_NUMBER2 (1)
#define GATE_NUMBER (0)
#define TIMEOUT (0) /* Wait forever */
#define GOAL_POSITION1 ( 10000.0)
#define GOAL_POSITION2 ( 0.0)
#define VELOCITY ( 10000.0)
#define ACCELERATION (100000.0)
#define DECELERATION (100000.0)
#define JERK_PERCENT ( 100.0)
/* Perform basic command line parsing. (-control -server -port -trace) */
void basicParsing(int argc,
char *argv[],
MPIControlType *controlType,
MPIControlAddress *controlAddress)
{
long argIndex;
/* Parse command line for Control type and address */
argIndex = argControl(argc, argv, controlType, controlAddress);
/* Check for unknown/invalid command line arguments */
if (argIndex < argc) {
fprintf(stderr,"usage: %s %s\n", argv[0], ArgUSAGE);
exit(MPIMessageARG_INVALID);
}
}
/* Create and initialize MPI objects */
void programInit(MPIControl *control,
MPIControlType controlType,
MPIControlAddress *controlAddress,
MPIMotion *motionX,
MPIMotion *motionY,
long *motionNumber,
MPIAxis *axisX,
MPIAxis *axisY,
long *axisNumber)
{
long returnValue;
/* Create control object */
*control =
mpiControlCreate(controlType,
controlAddress);
msgCHECK(mpiControlValidate(*control));
/* Initialize motion controller */
returnValue = mpiControlInit(*control);
msgCHECK(returnValue);
/* Create X axis object using AXIS_X number on controller */
*axisX =
mpiAxisCreate(*control,
axisNumber[0]); /* axis Number */
msgCHECK(mpiAxisValidate(*axisX));
/* Create Y axis object using AXIS_Y number on controller */
*axisY =
mpiAxisCreate(*control,
axisNumber[1]); /* axis Number */
msgCHECK(mpiAxisValidate(*axisY));
/* Create Motion object for axisX */
*motionX =
mpiMotionCreate(*control,
motionNumber[0], /* motion supervisor number */
*axisX);
msgCHECK(mpiMotionValidate(*motionX));
/* Create Motion object for axisY */
*motionY =
mpiMotionCreate(*control,
motionNumber[1], /* motion supervisor number */
*axisY);
msgCHECK(mpiMotionValidate(*motionY));
}
/* Delete MPI objects */
void programCleanup(MPIControl *control,
MPIMotion *motionX,
MPIMotion *motionY,
MPIAxis *axisX,
MPIAxis *axisY)
{
long returnValue;
/* Delete motion supervisor Y */
returnValue =
mpiMotionDelete(*motionY);
msgCHECK(returnValue);
*motionY = MPIHandleVOID;
/* Delete motion supervisor X */
returnValue =
mpiMotionDelete(*motionX);
msgCHECK(returnValue);
*motionX = MPIHandleVOID;
/* Delete axis Y */
returnValue =
mpiAxisDelete(*axisY);
msgCHECK(returnValue);
*axisY = MPIHandleVOID;
/* Delete axis X */
returnValue =
mpiAxisDelete(*axisX);
msgCHECK(returnValue);
*axisX = MPIHandleVOID;
/* Delete control object */
returnValue =
mpiControlDelete(*control);
msgCHECK(returnValue);
*control = MPIHandleVOID;
}
/*
waitForMotionDone() polls motion to see if the axis is in an idle or error
state every 10ms. When it gets an MPIStateIdle, MPIStateERROR, or
MPIStateSTOPPING_ERROR then the waitForMotionDone() exits.
*/
void waitForMotionDone(MPIMotion motion)
{
MPIStatus status;
long motionDone;
long returnValue = 0;
/* Poll status until motion done */
motionDone = FALSE;
while (motionDone == FALSE) {
/* Get the motion supervisor status */
returnValue =
mpiMotionStatus(motion,
&status,
NULL);
msgCHECK(returnValue);
switch (status.state) {
case MPIStateSTOPPING:
case MPIStateMOVING: {
/* Sleep for 10ms and give up control to other threads */
meiPlatformSleep(10);
break;
}
case MPIStateIDLE:
case MPIStateSTOPPED:
case MPIStateERROR:
case MPIStateSTOPPING_ERROR: {
/* Motion is done */
motionDone = TRUE;
break;
}
default: {
/* Unknown State */
fprintf(stderr, "Unknown state from mpiMotionStatus.\n");
msgCHECK(MPIMessageFATAL_ERROR);
break;
}
}
}
}
/* Function demonstrating how gates can control motion */
void gateControlledMotion(MPIControl control,
MPIMotion motion1,
MPIMotion motion2,
MPIMotionParams *motionParams,
MPIMotionType motionType,
long gateNumber,
long hold)
{
long returnValue;
/* If hold = TRUE, turn on the HOLD bit of MPIMotionType */
if (hold==TRUE) {
motionType = (MPIMotionType) (motionType | MEIMotionAttrMaskHOLD);
}
printf("Turning control gate ON.\r");
/* Set a control gate to prevent the motion profile from executing */
returnValue =
meiControlGateSet(control,
gateNumber,
TRUE); /* set gate */
msgCHECK(returnValue);
printf("Turned control gate ON. \n");
printf("Press any key to call mpiMotionStart(...) %s HOLD attribute.\r",
(hold) ? "with" : "without");
/* Wait for a key form the user */
meiPlatformKey(MPIWaitFOREVER);
/* Load motion profile */
returnValue = mpiMotionStart(motion1,
motionType,
motionParams);
msgCHECK(returnValue);
/* If user specified a second motion object */
if (motion2!=MPIHandleVOID) {
/* Load motion profile */
returnValue = mpiMotionStart(motion2,
motionType,
motionParams);
msgCHECK(returnValue);
}
printf("Called mpiMotionStart(...) %s HOLD attribute. \n",
(hold) ? "with" : "without");
printf("Press any key to turn control gate OFF\r");
/* Wait for a key form the user */
meiPlatformKey(MPIWaitFOREVER);
/* Clear a control gate, executing the preloaded motion */
returnValue =
meiControlGateSet(control,
gateNumber,
FALSE); /* clear the gate */
msgCHECK(returnValue);
printf("Turned control gate OFF. \n");
}
int main(int argc,
char *argv[])
{
MPIControl control; /* motion controller handle */
MPIAxis axisX; /* X axis */
MPIAxis axisY; /* Y axis */
MPIMotion motionX; /* motion object for axisX */
MPIMotion motionY; /* motion object for axisY */
MPIControlType controlType;
MPIControlAddress controlAddress;
MPIMotionParams motionParams; /* motion parameters */
MPITrajectory trajectory; /* trajectory information */
MEIMotionAttributes attributes; /* motion attributes */
MEIMotionAttrHold hold; /* hold attribute configuration */
long axisNumber[2] = { AXIS_NUMBER1, AXIS_NUMBER2, };
long motionNumber[2] = { MOTION_NUMBER1, MOTION_NUMBER2, };
long gateNumber = GATE_NUMBER;
float gateTimeout = (float)TIMEOUT;
double position;
/* Set up motion parameters */
trajectory.velocity = VELOCITY; /* counts per sec */
trajectory.acceleration = ACCELERATION; /* counts per sec * sec */
trajectory.deceleration = DECELERATION; /* counts per sec * sec */
trajectory.jerkPercent = JERK_PERCENT;
motionParams.sCurve.trajectory = &trajectory;
motionParams.sCurve.position = &position;
/* Perform basic command line parsing. (-control -server -port -trace) */
basicParsing(argc, argv, &controlType, &controlAddress);
/* Create and initialize MPI objects */
programInit(&control,
controlType,
&controlAddress,
&motionX,
&motionY,
motionNumber,
&axisX,
&axisY,
axisNumber);
/* Configure Motion Attributes */
hold.type = MEIMotionAttrHoldTypeGATE; /* software motion Gate control */
hold.source.gate = gateNumber;
hold.timeout = gateTimeout;
attributes.hold = &hold;
motionParams.external = &attributes;
/* Tell axis to go to GOAL_POSITION1 */
position = GOAL_POSITION1; /* counts */
printf("\n*** X AXIS ***\n");
/* Execute X-axis motion without HOLD attribute */
gateControlledMotion(control,
motionX,
MPIHandleVOID,
&motionParams,
MPIMotionTypeS_CURVE,
gateNumber,
FALSE);
printf("Wating for motion to complete...\n");
/* Wait for motion to complete */
waitForMotionDone(motionX);
printf("\n*** Y AXIS ***\n");
/* Execute Y-axis motion with HOLD attribute */
gateControlledMotion(control,
motionY,
MPIHandleVOID,
&motionParams,
MPIMotionTypeS_CURVE,
gateNumber,
TRUE);
printf("Wating for motion to complete...\n");
/* Wait for motion to complete */
waitForMotionDone(motionY);
/* Tell axis to go to GOAL_POSITION2 */
position = GOAL_POSITION2; /* counts */
printf("\n*** X & Y AXES ***\n");
/* Execute X,Y-axes motion with HOLD attribute */
gateControlledMotion(control,
motionX,
motionY,
&motionParams,
MPIMotionTypeS_CURVE,
gateNumber,
TRUE);
/* Delete MPI objects */
programCleanup(&control,
&motionX,
&motionY,
&axisX,
&axisY);
return MPIMessageOK;
}
|