demandModeSwitch.c -- Use multiple filters and user limits for on-the-fly demand mode switching
/* demandModeSwitch.c */
/* Copyright(c) 1991-2007 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.
*/
/*
:Use multiple filters and user limits for on-the-fly demand mode switching.
This sample application demonstrates how to use multiple filters on a single
motor for multiple demand modes. One filter will be used for Torque Mode and
the other for Velocity Mode. This application also demonstrates how to use
User Limits to do on-the-fly demand mode switching.
This program remaps the input pointers used by the demandChannels for the
specified MOTOR to data in the specified VELOCITY_FILTER. This program is
not responsible for determining which Filters are used and which Motors are
used, or how many motors or filters are active in the firmware.
In this example, motor MOTOR_NUMBER has its demand channels remapped to get
velocity and torque information from Filter VELOCITY_FILTER_NUMBER. This
example does not check to see if filter VELOCITY_FILTER_NUMBER is being
processed. The definition of MOTOR_NUMBER and VELOCITY_FILTER_NUMBER is up to
the user. It is also up to the user to make sure that the chosen MOTOR_NUMBER
and VELOCITY_FILTER_NUMBER represent objects that are being processed by the
firmware.
This code assumes that motion supervisor 0 is mapped to axis 0, filter 0, and
motor 0, and that filter 1 is unused and unmapped. To change which objects
are used, change the defines below.
This application commands moves to three points: a high starting position,
zero, and the starting position again. From the starting position, the motor
is commanded in Velocity Mode. When it passes below a position threshold
(see defines below), it switches to Torque Mode. When it comes back above
a second threshold, it switches back to Velocity Mode. When these positions
are passed, a user-defined location in the User Buffer is written to,
allowing visualization of the mode change with Motion Scope. The code also
configures the Motion Done events to include Move IDs, allow the application
to determine when the final move has completed.
This code was original developed for and tested on a Kollmorgan S200 drive.
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"
#if defined(ARG_MAIN_RENAME)
#define main modeSwitch
argMainRENAME(main, modeSwitch)
#endif
/* Motion used to command motion */
#define MOTION_NUMBER (0)
/* Axis used for feedback */
#define AXIS_NUMBER (0)
/* Motor whose demand structure will be modified */
#define MOTOR_NUMBER (0)
/* Filter that will be used for Velocity Mode */
#define VELOCITY_FILTER_NUMBER (1)
/* Filter that motor will be switched back to */
#define ORIGINAL_FILTER_NUMBER (0)
/* USER_LIMIT0 will trigger below this position */
#define POSITION_LIMIT0 (2000000)
/* USER_LIMIT1 will trigger above this position */
#define POSITION_LIMIT1 (1000000)
/* This location in the User Buffer will be written when user events trigger */
#define USER_BUFFER_INDEX (0)
/* Positions for motion */
#define POS0 (1E7)
#define VEL0 (1E8)
#define ACC0 (1E8)
#define DEC0 (1E8)
#define JERK0 (66)
#define DELAY0 (1)
#define POS1 (0)
#define VEL1 (VEL0/2)
#define ACC1 (1E8)
#define DEC1 (1E8)
#define JERK1 (66)
#define DELAY1 (1)
#define POS2 (1E7)
#define VEL2 (VEL0)
#define ACC2 (ACC1)
#define DEC2 (ACC1)
#define JERK2 (66)
#define DELAY2 (2)
typedef struct MotionProfile{
double position;
MPITrajectory trajectory;
double delay;
} MotionProfile;
MotionProfile Profile[] = {
{ POS0, {VEL0, ACC0, DEC0, JERK0}, DELAY0 },
{ POS1, {VEL1, ACC1, DEC1, JERK1}, DELAY1 },
{ POS2, {VEL2, ACC2, DEC2, JERK2}, DELAY2 },
};
#define NUMBER_OF_PROFILES (sizeof(Profile)/sizeof(MotionProfile))
/* Parse command line */
long parseCommandLine(int argc,
char *argv[],
MPIControlType *controlType,
MPIControlAddress *controlAddress,
long *axisNumber,
long *motionNumber)
{
long argIndex;
/* Application-specific command line arguments */
Arg argList[] =
{
{ "-axis", ArgTypeLONG, &(*axisNumber), },
{ "-motion", ArgTypeLONG, &(*motionNumber), },
{ NULL, ArgTypeINVALID, NULL, }
};
/* Parse command line for Control type and address */
argIndex =
argControl(argc,
argv,
controlType,
controlAddress);
/* Parse command line for application-specific arguments */
while (argIndex < argc)
{
long argIndexNew;
argIndexNew = argSet(argList, argIndex, argc, argv);
if (argIndexNew <= argIndex)
{
argIndex = argIndexNew;
break;
}
else
{
argIndex = argIndexNew;
}
}
/* Check for unknown/invalid command line arguments */
if (argIndex < argc)
{
meiPlatformConsole("usage: %s %s\n"
"\t [-axis # (0 .. %d)]\n"
"\t [-motion # (0 .. %d)]\n",
argv[0],
ArgUSAGE,
MEIXmpMAX_Axes,
MEIXmpMAX_MSs);
exit(MPIMessageARG_INVALID);
}
return 0;
}
/* Create and initialize MPI objects */
void programInit(MPIControl *control,
MPIControlType controlType,
MPIControlAddress *controlAddress,
MPIMotor *motor,
long motorNumber,
MPIFilter *velocityFilter,
long velFilterNumber,
MPIFilter *originalFilter,
long origFilterNumber,
MPIAxis *axis,
long axisNumber,
MPIMotion *motion,
long motionNumber,
MPIEventMgr *eventMgr,
MPINotify *notify,
Service *service)
{
MPIEventMask eventMask;
long returnValue;
/* Create motion controller object */
*control =
mpiControlCreate(controlType, controlAddress);
msgCHECK(mpiControlValidate(*control));
/* Initialize motion controller */
returnValue =
mpiControlInit(*control);
msgCHECK(returnValue);
/* Create motor object */
*motor =
mpiMotorCreate(*control, motorNumber);
msgCHECK(mpiMotorValidate(*motor));
/* Create filter objects */
*velocityFilter =
mpiFilterCreate(*control, velFilterNumber);
msgCHECK(mpiFilterValidate(*velocityFilter));
*originalFilter =
mpiFilterCreate(*control, origFilterNumber);
msgCHECK(mpiFilterValidate(*originalFilter));
/* Create axis object */
*axis =
mpiAxisCreate(*control, axisNumber);
msgCHECK(mpiAxisValidate(*axis));
/* Create motion supervisor object with axis */
*motion =
mpiMotionCreate(*control, motionNumber, *axis);
msgCHECK(mpiMotionValidate(*motion));
/* Map axis to motion supervisor on XMP */
returnValue =
mpiMotionAction(*motion, MEIActionMAP);
msgCHECK(returnValue);
/* Create event manager object */
*eventMgr =
mpiEventMgrCreate(*control);
msgCHECK(mpiEventMgrValidate(*eventMgr));
/* Request notification of ALL events from motion */
mpiEventMaskCLEAR(eventMask);
mpiEventMaskALL(eventMask);
meiEventMaskALL(eventMask);
/* Calling mpiMotionEventNotifySet(...) so that events will be reported */
returnValue =
mpiMotionEventNotifySet(*motion,
eventMask,
NULL);
msgCHECK(returnValue);
/* Create event notification object for NULL (all objects) */
*notify =
mpiNotifyCreate(eventMask, NULL);
msgCHECK(mpiNotifyValidate(*notify));
/* Append notify to event manager */
returnValue =
mpiEventMgrNotifyAppend(*eventMgr, *notify);
msgCHECK(returnValue);
/* Create service thread */
*service =
serviceCreate(*eventMgr,
-1, /* default (max) priority */
-1); /* -1 => enable interrupts */
if (*service == NULL)
{
msgCHECK(MPIMessageHANDLE_INVALID);
}
}
/* Perform certain cleanup actions and delete MPI objects */
void programCleanup(MPIControl *control,
MPIMotor *motor,
MPIFilter *velocityFilter,
MPIFilter *originalFilter,
MPIAxis *axis,
MPIMotion *motion,
MPIEventMgr *eventMgr,
MPINotify *notify,
Service *service)
{
long returnValue;
/* Delete service object */
returnValue =
serviceDelete(*service);
msgCHECK(returnValue);
/* Delete event manager object */
returnValue =
mpiEventMgrDelete(*eventMgr);
msgCHECK(returnValue);
*eventMgr = MPIHandleVOID;
/* Delete event notification object */
returnValue =
mpiNotifyDelete(*notify);
msgCHECK(returnValue);
*notify = MPIHandleVOID;
/* Delete motion supervisor object */
returnValue =
mpiMotionDelete(*motion);
msgCHECK(returnValue);
*motion = MPIHandleVOID;
/* Delete axis object */
returnValue =
mpiAxisDelete(*axis);
msgCHECK(returnValue);
*axis = MPIHandleVOID;
/* Delete filter handles */
returnValue =
mpiFilterDelete(*velocityFilter);
msgCHECK(returnValue);
*velocityFilter = MPIHandleVOID;
returnValue =
mpiFilterDelete(*originalFilter);
msgCHECK(returnValue);
*originalFilter = MPIHandleVOID;
/* Delete motor handle */
returnValue =
mpiMotorDelete(*motor);
msgCHECK(returnValue);
*motor = MPIHandleVOID;
/* Delete motion controller object */
returnValue =
mpiControlDelete(*control);
msgCHECK(returnValue);
*control = MPIHandleVOID;
}
/* Wait for and display motion done and user limit events,
and switch demand modes when the appropriate user limits
are hit.
*/
void displayEventsUntilMotionDone(MPINotify notify,
MPIMotor motor,
long lastMotionID)
{
MPIEventStatus eventStatus;
long returnValue;
MEIEventStatusInfo *info;
info = (MEIEventStatusInfo *)eventStatus.info;
while (TRUE)
{
/* Wait for events */
returnValue =
mpiNotifyEventWait(notify,
&eventStatus,
MPIWaitFOREVER);
msgCHECK(returnValue);
if (eventStatus.type == MEIEventTypeLIMIT_USER0)
{
meiPlatformConsole("User Limit 0 event received: "
"switching to torque mode\n");
/* Switch to torque mode here */
msgCHECK(meiMotorDemandModeSet(motor, MEIMotorDemandModeTORQUE));
}
if (eventStatus.type == MEIEventTypeLIMIT_USER1)
{
meiPlatformConsole("User Limit 1 event received: "
"switching to velocity mode\n");
/* Switch to velocity mode here */
msgCHECK(meiMotorDemandModeSet(motor, MEIMotorDemandModeVELOCITY));
}
if (eventStatus.type == MPIEventTypeMOTION_DONE)
{
meiPlatformConsole("Motion Done event received from motion %d\n",
info->data.word[1]);
if (info->data.word[1] == lastMotionID) {
break;
}
}
}
}
/* Configure user limits for actual position comparison
and writing to the user buffer.
*/
long userLimitPositionEvent(MPIMotor motor,
long axisNumber,
MEIEventType eventType,
MEIXmpLimitType limitType,
long position,
long userBufferIndex,
long outputData)
{
MPIControl control;
MEIPlatform platform;
MEIXmpData *firmware;
MEIXmpBufferData *buffer;
MEIMotorEventConfig motorEventConfig;
MPIEventMask resetMask;
MEIXmpLimitType limitTypeVel;
long *actPositionPtr;
long *outputAddr;
long returnValue = MPIMessageOK;
mpiEventMaskCLEAR(resetMask);
mpiEventMaskSET(resetMask, eventType);
/* Determine control handle, then get pointer to XMP firmware */
control = mpiMotorControl(motor);
msgCHECK(mpiControlValidate(control));
platform = meiControlPlatform(control);
returnValue =
mpiControlMemory(control,
(void **)&firmware,
(void **)&buffer);
msgCHECK(returnValue);
returnValue =
mpiMotorEventConfigGet(motor,
eventType,
NULL,
&motorEventConfig);
msgCHECK(returnValue);
/* Velocity < 0 or Velocity > 0 */
if(limitType == MEIXmpLimitTypeGE){
limitTypeVel = MEIXmpLimitTypeFGE;
}else{
limitTypeVel = MEIXmpLimitTypeFLT;
}
actPositionPtr = (long*) &firmware->Axis[axisNumber].ActPosition;
returnValue = meiPlatformHostAddress64To32(platform,
&actPositionPtr,
1); /* low word */
msgCHECK(returnValue);
motorEventConfig.Condition[0].Type = limitType;
motorEventConfig.Condition[0].SourceAddress = actPositionPtr;
motorEventConfig.Condition[0].Mask = 0xffffffff; /* AND mask */
motorEventConfig.Condition[0].LimitValue.g32.l = position;
motorEventConfig.Condition[1].Type = limitTypeVel;
motorEventConfig.Condition[1].SourceAddress =
&firmware->Axis[axisNumber].CommandVelocity;
motorEventConfig.Condition[1].Mask = 0xffffffff;
motorEventConfig.Condition[1].LimitValue.g32.f = 0;
motorEventConfig.Status = MEIXmpStatusLIMIT;
/* Determine logic result from Condition[0] and Condition[1] */
motorEventConfig.Logic = MEIXmpLogicAND;
/* Write output data to User Buffer */
outputAddr = &buffer->UserBuffer.Data[userBufferIndex];
motorEventConfig.Output.OutputPtr = outputAddr;
motorEventConfig.Output.AndMask = 0;
motorEventConfig.Output.OrMask = outputData;
motorEventConfig.Output.Enabled = TRUE;
returnValue =
mpiMotorEventConfigSet(motor,
eventType,
NULL,
&motorEventConfig);
msgCHECK(returnValue);
/* Reset event in case conditions have already been satisfied */
returnValue =
mpiMotorEventReset(motor,
resetMask);
msgCHECK(returnValue);
return returnValue;
}
/* Setup second filter for velocity mode moves with the specified motor and axis*/
long remapVelocityDemandInput(MPIMotor motor,
MPIFilter filter,
MPIAxis axis)
{
long returnValue;
MPIControl control;
MEIPlatform platform;
MEIXmpMotor *motorPtr;
MEIXmpMotorControl motorControl;
MEIXmpFilter *filterPtr;
MEIXmpAxis *axisPtr;
control = mpiMotorControl(motor);
returnValue = mpiControlValidate(control);
if (returnValue == MPIMessageOK) {
platform = meiControlPlatform(control);
returnValue = meiPlatformValidate(platform);
}
if (returnValue == MPIMessageOK) {
returnValue = mpiMotorMemory(motor,
&motorPtr);
}
if (returnValue == MPIMessageOK) {
returnValue = mpiFilterMemory(filter,
&filterPtr);
}
if (returnValue == MPIMessageOK) {
returnValue = mpiAxisMemory(axis,
&axisPtr);
}
/* Get motorControl struct */
if (returnValue == MPIMessageOK) {
returnValue = mpiMotorMemoryGet(motor,
&motorControl,
&motorPtr->IO.Control,
sizeof(motorControl));
}
/* Verify that we're in Velocity Mode */
if (returnValue == MPIMessageOK) {
if (motorControl.ModeControlData[MEIXmpMotorModeSelectVELOCITY].Mode ==
MEIXmpMotorControlModeVELOCITY) {
/* Demand Channels 2 and 3 are used for Velocity Mode.
Channel 2 is used for the torque demand which comes
from the filter's feedforward term.
*/
long *inputPtrTorque, *inputPtrVelocity;
MEIXmpMotorControlModeData velData =
motorControl.ModeControlData[MEIXmpMotorModeSelectVELOCITY];
inputPtrTorque = (long*)&filterPtr->Data.FFOutput;
if (returnValue == MPIMessageOK) {
returnValue = meiPlatformMemoryToFirmware(platform,
inputPtrTorque,
&inputPtrTorque);
if (returnValue == MPIMessageOK) {
returnValue =
mpiMotorMemorySet(motor,
&motorPtr->IO.DemandChannel[2].Input,
&inputPtrTorque,
sizeof(inputPtrTorque));
}
}
/* Channel 3 is used for the velocity demand which comes from the
filter's PID, so we'll use the post filter output.
*/
if (returnValue == MPIMessageOK) {
inputPtrVelocity = (long*)&filterPtr->Data.PostFilterOutput;
returnValue = meiPlatformMemoryToFirmware(platform,
inputPtrVelocity,
&inputPtrVelocity);
if (returnValue == MPIMessageOK) {
returnValue =
mpiMotorMemorySet(motor,
&motorPtr->IO.DemandChannel[3].Input,
&inputPtrVelocity,
sizeof(inputPtrVelocity));
}
}
/* Grab the filter's gain index and integrator disable settings
and put them into the motor's demand mode configuration for
velocity mode.
*/
if (returnValue == MPIMessageOK) {
long *gainIndex = &filterPtr->GainIndex;
long *nodeIntegratorDisable =
&filterPtr->NodeIntegratorDisable;
returnValue = meiPlatformMemoryToFirmware(platform,
gainIndex,
&gainIndex);
if (returnValue == MPIMessageOK) {
returnValue =
meiPlatformMemoryToFirmware(platform,
nodeIntegratorDisable,
&nodeIntegratorDisable);
}
if (returnValue == MPIMessageOK) {
MEIXmpMotorControlModeData *modePtr =
motorPtr->IO.Control.ModeControlData;
modePtr = &modePtr[MEIXmpMotorModeSelectVELOCITY];
velData.GainIndex = gainIndex;
velData.NodeIntegratorDisable =
nodeIntegratorDisable;
returnValue =
mpiMotorMemorySet(motor,
&modePtr->GainIndex,
&velData.GainIndex,
sizeof(gainIndex));
if (returnValue == MPIMessageOK) {
returnValue =
mpiMotorMemorySet(motor,
&modePtr->NodeIntegratorDisable,
&velData.NodeIntegratorDisable,
sizeof(nodeIntegratorDisable));
}
}
/* Map the axis to the filter's input so we get feedback */
if (returnValue == MPIMessageOK) {
long* inputPtrAxis = (long*) axisPtr;
returnValue = meiPlatformMemoryToFirmware(platform,
inputPtrAxis,
&inputPtrAxis);
if (returnValue == MPIMessageOK) {
returnValue =
mpiFilterMemorySet(filter,
&filterPtr->Axis[0].Ptr,
&inputPtrAxis,
sizeof(inputPtrAxis));
}
if (returnValue == MPIMessageOK) {
returnValue =
mpiFilterMemorySet(filter,
&filterPtr->Axis[1].Ptr,
&inputPtrAxis,
sizeof(inputPtrAxis));
}
}
}
}
else {
returnValue = MPIMessageARG_INVALID;
}
}
return (returnValue);
}
/* Configure motion done events to record the move ID so we know when
the final move in the profile is finished.
*/
void configMotionDoneNotification(MPIMotor motor,
MPIAxis axis,
MPIMotion motion)
{
long returnValue;
MPIEventMask eventMask;
MEIEventNotifyData motionData;
MEIXmpAxis *xmpAxis;
meiPlatformConsole("Configuring Motion Done event notification\n");
/* Set up event notification to record move ID */
returnValue = mpiAxisMemory(axis, &xmpAxis);
msgCHECK(returnValue);
returnValue =
mpiMotorEventNotifyGet(motor,
&eventMask,
NULL);
msgCHECK(returnValue);
returnValue =
mpiMotorEventNotifySet(motor,
eventMask,
NULL);
returnValue =
mpiAxisEventNotifyGet(axis,
&eventMask,
&motionData);
msgCHECK(returnValue);
motionData.address[1] = (void *)(&xmpAxis->MoveID);
returnValue =
mpiAxisEventNotifySet(axis,
eventMask,
&motionData);
msgCHECK(returnValue);
returnValue =
mpiMotionEventNotifyGet(motion,
&eventMask,
&motionData);
msgCHECK(returnValue);
motionData.address[1] = (void *)(&xmpAxis->MoveID);
returnValue =
mpiMotionEventNotifySet(motion,
eventMask,
&motionData);
msgCHECK(returnValue);
}
/* Set user limit events */
void configUserLimitNotification(MPIMotor motor,
MPIAxis axis,
long positionLimit0,
long positionLimit1,
long userBufferIndex)
{
long axisNumber;
long returnValue;
meiPlatformConsole("Configuring User Limit event notification\n");
returnValue =
mpiAxisNumber(axis,
&axisNumber);
msgCHECK(returnValue);
userLimitPositionEvent(motor,
axisNumber,
MEIEventTypeLIMIT_USER0,
MEIXmpLimitTypeLT,
positionLimit0,
userBufferIndex,
1);
userLimitPositionEvent(motor,
axisNumber,
MEIEventTypeLIMIT_USER1,
MEIXmpLimitTypeGE,
positionLimit1,
userBufferIndex,
0);
}
/* Remap filters for velocity mode, start move, wait for events, and restore */
void commandMoves(MPIControl control,
MPIMotor motor,
MPIFilter velocityFilter,
MPIFilter originalFilter,
MPIAxis axis,
MPIMotion motion,
MotionProfile *profile,
long numberOfProfiles,
MPINotify notify)
{
MPIMotionParams params;
long index;
long returnValue;
/* Add secondary filter for Velocity Mode */
meiPlatformConsole("Switching to Velocity Mode\n");
returnValue = meiMotorDemandModeSet(motor, MEIMotorDemandModeVELOCITY);
msgCHECK(returnValue);
meiPlatformConsole("Remapping filter for Velocity Mode\n");
returnValue = remapVelocityDemandInput(motor,velocityFilter,axis);
msgCHECK(returnValue);
/* Start motion */
meiPlatformConsole("Starting motion\n");
for(index = 0; index < numberOfProfiles; index++) {
params.sCurve.position = &profile[index].position;
params.sCurve.trajectory = &profile[index].trajectory;
params.attributes.delay = &profile[index].delay;
params.attributes.id = index;
returnValue =
mpiMotionStart(motion,
MPIMotionTypeS_CURVE | MPIMotionAttrMaskAPPEND |
MPIMotionAttrMaskDELAY | MPIMotionAttrMaskID,
¶ms);
meiPlatformConsole("mpiMotionStart #%d returns 0x%x: %s\n",
index,
returnValue,
mpiMessage(returnValue, NULL));
}
/* Wait for the final motion done event */
meiPlatformConsole("Waiting for events\n");
displayEventsUntilMotionDone(notify, motor, numberOfProfiles - 1);
meiPlatformConsole("Finished waiting for events\n");
/* Remap Velocity Mode demand back to original filter */
meiPlatformConsole("Remapping original filter for Velocity Mode\n");
returnValue = remapVelocityDemandInput(motor,originalFilter,axis);
msgCHECK(returnValue);
/* Switch back to Torque Mode */
meiPlatformConsole("Switching back to Torque Mode\n");
returnValue = meiMotorDemandModeSet(motor, MEIMotorDemandModeTORQUE);
msgCHECK(returnValue);
}
/* Main routine */
int main(int argc,
char *argv[])
{
MPIControl control;
MPIControlType controlType;
MPIControlAddress controlAddress;
MPIMotor motor;
MPIFilter velocityFilter;
MPIFilter originalFilter;
MPIAxis axis;
MPIMotion motion;
MPIEventMgr eventMgr;
MPINotify notify;
Service service = MPIHandleVOID;
long motorNumber = MOTOR_NUMBER;
long velFilterNumber = VELOCITY_FILTER_NUMBER;
long origFilterNumber = ORIGINAL_FILTER_NUMBER;
long axisNumber = AXIS_NUMBER;
long motionNumber = MOTION_NUMBER;
/* Parse command line */
parseCommandLine(argc,
argv,
&controlType,
&controlAddress,
&axisNumber,
&motionNumber);
/* Create and initialize MPI objects */
programInit(&control,
controlType,
&controlAddress,
&motor,
motorNumber,
&velocityFilter,
velFilterNumber,
&originalFilter,
origFilterNumber,
&axis,
axisNumber,
&motion,
motionNumber,
&eventMgr,
¬ify,
&service);
/* Configure motion done event */
configMotionDoneNotification(motor,
axis,
motion);
/* Configure user limit events */
configUserLimitNotification(motor,
axis,
POSITION_LIMIT0,
POSITION_LIMIT1,
USER_BUFFER_INDEX);
/* Remap filters for velocity mode, start move,
wait for events, and restore
*/
commandMoves(control,
motor,
velocityFilter,
originalFilter,
axis,
motion,
Profile,
NUMBER_OF_PROFILES,
notify);
/* Perform certain cleanup actions and delete MPI objects */
programCleanup(&control,
&motor,
&velocityFilter,
&originalFilter,
&axis,
&motion,
&eventMgr,
¬ify,
&service);
return MPIMessageOK;
}
|