From 5643514721f64abdc028164e867a01164a8ec93d Mon Sep 17 00:00:00 2001 From: Kurt Goetze Date: Thu, 7 Jun 2012 21:19:37 +0000 Subject: [PATCH] Add Newport SMC100 support --- motorApp/NewportSrc/Makefile | 3 + motorApp/NewportSrc/README | 16 ++ motorApp/NewportSrc/SMC100Driver.cpp | 365 ++++++++++++++++++++++++++ motorApp/NewportSrc/SMC100Driver.h | 52 ++++ motorApp/NewportSrc/SMC100Register.cc | 58 ++++ motorApp/NewportSrc/SMC100Register.h | 45 ++++ motorApp/NewportSrc/devNewport.dbd | 1 + 7 files changed, 540 insertions(+) create mode 100644 motorApp/NewportSrc/SMC100Driver.cpp create mode 100644 motorApp/NewportSrc/SMC100Driver.h create mode 100644 motorApp/NewportSrc/SMC100Register.cc create mode 100644 motorApp/NewportSrc/SMC100Register.h diff --git a/motorApp/NewportSrc/Makefile b/motorApp/NewportSrc/Makefile index ffe3b20d..8d6ab32c 100644 --- a/motorApp/NewportSrc/Makefile +++ b/motorApp/NewportSrc/Makefile @@ -31,6 +31,9 @@ Newport_SRCS += devPM500.cc drvPM500.cc # ESP300 device driver. Newport_SRCS += devESP300.cc drvESP300.cc +# SMC100 device driver +Newport_SRCS += SMC100Driver.cpp + # XPS C8 device driver Newport_SRCS += asynOctetSocket.cpp Newport_SRCS += XPS_C8_drivers.cpp diff --git a/motorApp/NewportSrc/README b/motorApp/NewportSrc/README index d6da995c..1a9f2a85 100644 --- a/motorApp/NewportSrc/README +++ b/motorApp/NewportSrc/README @@ -123,6 +123,18 @@ in the record's INIT field. LSIZE is in units of either um or arc-sec. For example, if your RDBD = 0.123 mm, then set the INIT field to "LSIZE 123.0". +Newport SMC100 +============== +The SMC100 controller has a fixed baud rate of 57,600. This means that it +cannot be used with the IP-Octal232. If you want to use it from a vxWorks ioc, +you will need to connect it via a Moxa or similar. I have tested this code on +both Linux and Cygwin soft ioc's. The stage I tested it with is a GTS30V. +Initially, the controller needs to be connected to the stage it will be running, +and then configured with Newport's software. I used Newport's PC program called +"SMC100 User Tool", which allows for the controller to set and keep all of the +settings for the stage. + + ******************************************************************************** What's what in this directory ----------------------------- @@ -151,6 +163,10 @@ XPSAxis.cpp XPSAxis.h XPSController.cpp XPSController.h +SMC100Driver.cpp +SMC100Driver.h +SMC100Register.cc +SMC100Register.h support code common to models 2 and 3 ------------------------------------- diff --git a/motorApp/NewportSrc/SMC100Driver.cpp b/motorApp/NewportSrc/SMC100Driver.cpp new file mode 100644 index 00000000..7574571e --- /dev/null +++ b/motorApp/NewportSrc/SMC100Driver.cpp @@ -0,0 +1,365 @@ +/* +FILENAME... SMC100Driver.cpp +USAGE... Motor driver support for the Newport SMC100 controller. + +Based on the ACS MCB-4B Model 3 device driver written by: +Mark Rivers +March 1, 2012 + +K. Goetze 2012-03-23 + +*/ + + +#include +#include +#include +#include + +#include +#include + +#include + +#include "SMC100Driver.h" +#include + +#define NINT(f) (int)((f)>0 ? (f)+0.5 : (f)-0.5) + +/** Creates a new SMC100Controller object. + * \param[in] portName The name of the asyn port that will be created for this driver + * \param[in] SMC100PortName The name of the drvAsynSerialPort that was created previously to connect to the SMC100 controller + * \param[in] numAxes The number of axes that this controller supports + * \param[in] movingPollPeriod The time between polls when any axis is moving + * \param[in] idlePollPeriod The time between polls when no axis is moving + */ +SMC100Controller::SMC100Controller(const char *portName, const char *SMC100PortName, int numAxes, + double movingPollPeriod, double idlePollPeriod) + : asynMotorController(portName, numAxes, NUM_SMC100_PARAMS, + 0, // No additional interfaces beyond those in base class + 0, // No additional callback interfaces beyond those in base class + ASYN_CANBLOCK | ASYN_MULTIDEVICE, + 1, // autoconnect + 0, 0) // Default priority and stack size +{ + int axis; + asynStatus status; + SMC100Axis *pAxis; + static const char *functionName = "SMC100Controller::SMC100Controller"; + + /* Connect to SMC100 controller */ + status = pasynOctetSyncIO->connect(SMC100PortName, 0, &pasynUserController_, NULL); + if (status) { + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, + "%s: cannot connect to SMC100 controller\n", + functionName); + } + for (axis=0; axis 0 then information is printed about each axis. + * After printing controller-specific information it calls asynMotorController::report() + */ +void SMC100Controller::report(FILE *fp, int level) +{ + fprintf(fp, "SMC100 motor driver %s, numAxes=%d, moving poll period=%f, idle poll period=%f\n", + this->portName, numAxes_, movingPollPeriod_, idlePollPeriod_); + + // Call the base class method + asynMotorController::report(fp, level); +} + +/** Returns a pointer to an SMC100Axis object. + * Returns NULL if the axis number encoded in pasynUser is invalid. + * \param[in] pasynUser asynUser structure that encodes the axis index number. */ +SMC100Axis* SMC100Controller::getAxis(asynUser *pasynUser) +{ + return static_cast(asynMotorController::getAxis(pasynUser)); +} + +/** Returns a pointer to an SMC100Axis object. + * Returns NULL if the axis number encoded in pasynUser is invalid. + * \param[in] No Axis index number. */ +SMC100Axis* SMC100Controller::getAxis(int axisNo) +{ + return static_cast(asynMotorController::getAxis(axisNo)); +} + + +// These are the SMC100Axis methods + +/** Creates a new SMC100Axis object. + * \param[in] pC Pointer to the SMC100Controller to which this axis belongs. + * \param[in] axisNo Index number of this axis, range 0 to pC->numAxes_-1. + * + * Initializes register numbers, etc. + */ +SMC100Axis::SMC100Axis(SMC100Controller *pC, int axisNo) + : asynMotorAxis(pC, axisNo), + pC_(pC) +{ +} + +/** Reports on status of the axis + * \param[in] fp The file pointer on which report information will be written + * \param[in] level The level of report detail desired + * + * After printing device-specific information calls asynMotorAxis::report() + */ +void SMC100Axis::report(FILE *fp, int level) +{ + if (level > 0) { + fprintf(fp, " axis %d\n", + axisNo_ + 1); + } + + // Call the base class method + asynMotorAxis::report(fp, level); +} + +asynStatus SMC100Axis::sendAccelAndVelocity(double acceleration, double velocity) +{ + asynStatus status; + // static const char *functionName = "SMC100::sendAccelAndVelocity"; + + // Send the velocity in egus + sprintf(pC_->outString_, "%1dVA%f", axisNo_ + 1, (velocity/1e5)); + status = pC_->writeController(); + + // Send the acceleration in egus/sec/sec + //printf("velocity: %f\n", velocity); + //printf("acceleration: %f", acceleration); + sprintf(pC_->outString_, "%1dAC%f", axisNo_ + 1, (acceleration/1e5)); + status = pC_->writeController(); + return status; +} + + +asynStatus SMC100Axis::move(double position, int relative, double minVelocity, double maxVelocity, double acceleration) +{ + asynStatus status; + // static const char *functionName = "SMC100Axis::move"; + + status = sendAccelAndVelocity(acceleration, maxVelocity); + + if (relative) { + sprintf(pC_->outString_, "%1dPR%f", axisNo_ + 1, (position/1e5)); + } else { + sprintf(pC_->outString_, "%1dPA%f", axisNo_ + 1, (position/1e5)); + } + status = pC_->writeController(); + return status; +} + +asynStatus SMC100Axis::home(double minVelocity, double maxVelocity, double acceleration, int forwards) +{ + asynStatus status; + // static const char *functionName = "SMC100Axis::home"; + + // set Home search velocity + //sprintf(pC_->outString_, "%1dOH%f", axisNo_ + 1, maxVelocity); + //status = pC_->writeController(); + + sprintf(pC_->outString_, "%1dOR", axisNo_ + 1); + + status = pC_->writeController(); + return status; +} + +// Jog +asynStatus SMC100Axis::moveVelocity(double minVelocity, double maxVelocity, double acceleration) +{ + double high_limit; + double low_limit; + asynStatus comStatus; + static const char *functionName = "SMC100Axis::moveVelocity"; + + asynPrint(pasynUser_, ASYN_TRACE_FLOW, + "%s: minVelocity=%f, maxVelocity=%f, acceleration=%f\n", + functionName, minVelocity, maxVelocity, acceleration); + + comStatus = sendAccelAndVelocity(acceleration, maxVelocity); + if (comStatus) goto skip; + + /* SMC100 supports the notion of jog, but only for a remote control keypad */ + // SMC100 will not allow moves outside of those set with the SL and SR commands + // first we query these limits and then make the jog a move to the limit + + // get the high limit + sprintf(pC_->outString_, "%1dSR?", axisNo_ + 1); + comStatus = pC_->writeReadController(); + if (comStatus) goto skip; + // The response string is of the form "1SR25.0" + high_limit = (atof(&pC_->inString_[3])); + + // get the low limit + sprintf(pC_->outString_, "%1dSL?", axisNo_ + 1); + comStatus = pC_->writeReadController(); + if (comStatus) goto skip; + // The response string is of the form "1SL-5.0" + low_limit = (atof(&pC_->inString_[3])); + + if (maxVelocity > 0.) { + /* This is a positive move in SMC100 coordinates (egus) */ + sprintf(pC_->outString_, "%1dPA%f", axisNo_ + 1, high_limit); + } else { + /* This is a negative move in SMC100 coordinates (egus) */ + sprintf(pC_->outString_, "%1dPA%f", axisNo_ + 1, low_limit); + } + comStatus = pC_->writeController(); + if (comStatus) goto skip; + + skip: + setIntegerParam(pC_->motorStatusProblem_, comStatus ? 1:0); + callParamCallbacks(); + return comStatus ? asynError : asynSuccess; + +} + +asynStatus SMC100Axis::stop(double acceleration ) +{ + asynStatus status; + //static const char *functionName = "SMC100Axis::stop"; + + sprintf(pC_->outString_, "%1dST", axisNo_ + 1); + status = pC_->writeController(); + return status; +} + +asynStatus SMC100Axis::setPosition(double position) +{ + asynStatus status; + //static const char *functionName = "SMC100Axis::setPosition"; + + // ? not sure yet + //sprintf(pC_->outString_, "#%02dP=%+d", axisNo_ + 1, NINT(position)); + status = pC_->writeReadController(); + return status; +} + +asynStatus SMC100Axis::setClosedLoop(bool closedLoop) +{ + asynStatus status; + //static const char *functionName = "SMC100Axis::setClosedLoop"; + + // ? not sure yet + //sprintf(pC_->outString_, "#%02dW=%d", axisNo_ + 1, closedLoop ? 1:0); + status = pC_->writeReadController(); + return status; +} + +/** Polls the axis. + * This function reads motor position, limit status, home status, and moving status + * It calls setIntegerParam() and setDoubleParam() for each item that it polls, + * and then calls callParamCallbacks() at the end. + * \param[out] moving A flag that is set indicating that the axis is moving (true) or done (false). */ +asynStatus SMC100Axis::poll(bool *moving) +{ + int done; + //int driveOn; + int limit; + double position; + asynStatus comStatus; + + // Read the current motor position + sprintf(pC_->outString_, "%1dTP", axisNo_ + 1); + comStatus = pC_->writeReadController(); + if (comStatus) goto skip; + // The response string is of the form "1TP-0.123" + position = (atof(&pC_->inString_[3]) * 1e5); + setDoubleParam(pC_->motorPosition_, position); + + // Read the moving status of this motor + sprintf(pC_->outString_, "%1dTS", axisNo_ + 1); + comStatus = pC_->writeReadController(); + if (comStatus) goto skip; + // The response string is of the form "1TS000028" + // May need to add logic for moving while homing + done = ((pC_->inString_[7] == '2') && (pC_->inString_[8] == '8')) ? 0:1; + setIntegerParam(pC_->motorStatusDone_, done); + *moving = done ? false:true; + + // Read the limit status + // The response string is of the form "1TS001328" + // + // The stage I tested this with is a GTS30V vertical jack. When the controller is initialized + // for this device using Newport's software, +25 and -5 limits get set. The controller does not + // let you set limits outside these values, so I never was able to run into a "hard" limit. + // The controller also does not allow position settings outside these limits, and does not give + // an indication. So, my recommendation is to leave the controller's travel limits set to the + // Newport defaults, and use the motor record's soft limits, set to the controller's limits or within. + // + // Should a hard limit be actually encounted, this code *should* report it to the motor record + limit = (pC_->inString_[6] == '2') ? 1:0; + setIntegerParam(pC_->motorStatusHighLimit_, limit); + limit = (pC_->inString_[6] == '1') ? 1:0; + setIntegerParam(pC_->motorStatusLowLimit_, limit); + limit = ((pC_->inString_[7] == '3') && (pC_->inString_[8] == '2')) ? 1:0; + setIntegerParam(pC_->motorStatusAtHome_, limit); + + // Read the drive power on status + //sprintf(pC_->outString_, "#%02dE", axisNo_ + 1); + //comStatus = pC_->writeReadController(); + //if (comStatus) goto skip; + //driveOn = (pC_->inString_[5] == '1') ? 1:0; + //setIntegerParam(pC_->motorStatusPowerOn_, driveOn); + //setIntegerParam(pC_->motorStatusProblem_, 0); + + skip: + setIntegerParam(pC_->motorStatusProblem_, comStatus ? 1:0); + callParamCallbacks(); + return comStatus ? asynError : asynSuccess; +} + +/** Code for iocsh registration */ +static const iocshArg SMC100CreateControllerArg0 = {"Port name", iocshArgString}; +static const iocshArg SMC100CreateControllerArg1 = {"SMC100 port name", iocshArgString}; +static const iocshArg SMC100CreateControllerArg2 = {"Number of axes", iocshArgInt}; +static const iocshArg SMC100CreateControllerArg3 = {"Moving poll period (ms)", iocshArgInt}; +static const iocshArg SMC100CreateControllerArg4 = {"Idle poll period (ms)", iocshArgInt}; +static const iocshArg * const SMC100CreateControllerArgs[] = {&SMC100CreateControllerArg0, + &SMC100CreateControllerArg1, + &SMC100CreateControllerArg2, + &SMC100CreateControllerArg3, + &SMC100CreateControllerArg4}; +static const iocshFuncDef SMC100CreateControllerDef = {"SMC100CreateController", 5, SMC100CreateControllerArgs}; +static void SMC100CreateContollerCallFunc(const iocshArgBuf *args) +{ + SMC100CreateController(args[0].sval, args[1].sval, args[2].ival, args[3].ival, args[4].ival); +} + +static void SMC100Register(void) +{ + iocshRegister(&SMC100CreateControllerDef, SMC100CreateContollerCallFunc); +} + +extern "C" { +epicsExportRegistrar(SMC100Register); +} diff --git a/motorApp/NewportSrc/SMC100Driver.h b/motorApp/NewportSrc/SMC100Driver.h new file mode 100644 index 00000000..b471d799 --- /dev/null +++ b/motorApp/NewportSrc/SMC100Driver.h @@ -0,0 +1,52 @@ +/* +FILENAME... SMC100Driver.h +USAGE... Motor driver support for the Newport SMC100 controller. + +Based on the ACS MCB-4B Model 3 device driver written by: +Mark Rivers +March 1, 2012 + +K. Goetze 2012-03-23 + +*/ + +#include "asynMotorController.h" +#include "asynMotorAxis.h" + +#define MAX_SMC100_AXES 1 + +// No controller-specific parameters yet +#define NUM_SMC100_PARAMS 0 + +class SMC100Axis : public asynMotorAxis +{ +public: + /* These are the methods we override from the base class */ + SMC100Axis(class SMC100Controller *pC, int axis); + void report(FILE *fp, int level); + asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration); + asynStatus moveVelocity(double min_velocity, double max_velocity, double acceleration); + asynStatus home(double min_velocity, double max_velocity, double acceleration, int forwards); + asynStatus stop(double acceleration); + asynStatus poll(bool *moving); + asynStatus setPosition(double position); + asynStatus setClosedLoop(bool closedLoop); + +private: + SMC100Controller *pC_; /**< Pointer to the asynMotorController to which this axis belongs. + * Abbreviated because it is used very frequently */ + asynStatus sendAccelAndVelocity(double accel, double velocity); + +friend class SMC100Controller; +}; + +class SMC100Controller : public asynMotorController { +public: + SMC100Controller(const char *portName, const char *SMC100PortName, int numAxes, double movingPollPeriod, double idlePollPeriod); + + void report(FILE *fp, int level); + SMC100Axis* getAxis(asynUser *pasynUser); + SMC100Axis* getAxis(int axisNo); + +friend class SMC100Axis; +}; diff --git a/motorApp/NewportSrc/SMC100Register.cc b/motorApp/NewportSrc/SMC100Register.cc new file mode 100644 index 00000000..3796d68e --- /dev/null +++ b/motorApp/NewportSrc/SMC100Register.cc @@ -0,0 +1,58 @@ +/* +FILENAME... SMC100Register.cc +USAGE... Register SMC100 motor device driver shell commands. + +Version: $Revision: 1.4 $ +Modified By: $Author: sluiter $ +Last Modified: $Date: 2004-07-16 19:06:58 $ +*/ + +/***************************************************************** + COPYRIGHT NOTIFICATION +***************************************************************** + +(C) COPYRIGHT 1993 UNIVERSITY OF CHICAGO + +This software was developed under a United States Government license +described on the COPYRIGHT_UniversityOfChicago file included as part +of this distribution. +**********************************************************************/ + +#include +#include "SMC100Register.h" +#include "epicsExport.h" + +extern "C" +{ + +// SMC100 Setup arguments +static const iocshArg setupArg0 = {"Max. controller count", iocshArgInt}; +static const iocshArg setupArg1 = {"Polling rate", iocshArgInt}; +// SMC100 Config arguments +static const iocshArg configArg0 = {"Card being configured", iocshArgInt}; +static const iocshArg configArg1 = {"asyn port name", iocshArgString}; + +static const iocshArg * const SMC100SetupArgs[2] = {&setupArg0, &setupArg1}; +static const iocshArg * const SMC100ConfigArgs[2] = {&configArg0, &configArg1}; + +static const iocshFuncDef setupSMC100 = {"SMC100Setup", 2, SMC100SetupArgs}; +static const iocshFuncDef configSMC100 = {"SMC100Config", 2, SMC100ConfigArgs}; + +static void setupSMC100CallFunc(const iocshArgBuf *args) +{ + SMC100Setup(args[0].ival, args[1].ival); +} +static void configSMC100CallFunc(const iocshArgBuf *args) +{ + SMC100Config(args[0].ival, args[1].sval); +} + +static void SMC100Register(void) +{ + iocshRegister(&setupSMC100, setupSMC100CallFunc); + iocshRegister(&configSMC100, configSMC100CallFunc); +} + +epicsExportRegistrar(SMC100Register); + +} // extern "C" diff --git a/motorApp/NewportSrc/SMC100Register.h b/motorApp/NewportSrc/SMC100Register.h new file mode 100644 index 00000000..9c0e506c --- /dev/null +++ b/motorApp/NewportSrc/SMC100Register.h @@ -0,0 +1,45 @@ +/* +FILENAME... SMC100Register.h +USAGE... This file contains function prototypes for SMC100 IOC shell commands. + +Version: $Revision: 1.3 $ +Modified By: $Author: sluiter $ +Last Modified: $Date: 2004-07-16 19:06:58 $ +*/ + +/* + * Original Author: Ron Sluiter + * Date: 05/19/03 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + */ + +#include "motor.h" +#include "motordrvCom.h" + +/* Function prototypes. */ +extern RTN_STATUS SMC100Setup(int, int); +extern RTN_STATUS SMC100Config(int, const char *); + diff --git a/motorApp/NewportSrc/devNewport.dbd b/motorApp/NewportSrc/devNewport.dbd index 5fc91f45..8b5b34c9 100644 --- a/motorApp/NewportSrc/devNewport.dbd +++ b/motorApp/NewportSrc/devNewport.dbd @@ -17,6 +17,7 @@ registrar(XPSInterposeRegister) registrar(drvXPSAsynAuxRegister) registrar(MM4005_trajectoryScanRegistrar) registrar(XPS_trajectoryScanRegistrar) +registrar(SMC100Register) #variable(devXPSC8Debug) #variable(drvXPSC8Debug) #variable(drvESP300debug)