forked from epics_driver_modules/motorBase
Removed ImsSrc; Added motorIms submodule
This commit is contained in:
@@ -58,3 +58,6 @@
|
||||
[submodule "modules/motorNewFocus"]
|
||||
path = modules/motorNewFocus
|
||||
url = https://github.com/epics-motor/motorNewFocus.git
|
||||
[submodule "modules/motorIms"]
|
||||
path = modules/motorIms
|
||||
url = https://github.com/epics-motor/motorIms.git
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
# ImsMDrivePlus asyn model 3 substitutions file
|
||||
|
||||
# SREV (not shown) is the number of steps per revolution is 200 by
|
||||
# default
|
||||
#
|
||||
# There are three pivot points to which the six motors are attached.
|
||||
# Three provide vertical motion: M0Y, M1Y, and M2Y. One provides
|
||||
# horizontal motion in the Z direction (along beamline): M2Z. Two
|
||||
# provide horizontal motion in the X direction (perpendicular to
|
||||
# beamline): M0X, M2X.
|
||||
#
|
||||
|
||||
#
|
||||
|
||||
# For SREV:
|
||||
# 2048 = internal encoder (EQ)
|
||||
# 250 = external encoder, NUMERIK-JENA
|
||||
# 200 = external encoder, Renishaw
|
||||
# 51200 = no encoder
|
||||
|
||||
# CXI DG4 Support Motion Stand (DG4) Substitution file
|
||||
|
||||
|
||||
file "$(MOTOR)/db/basic_asyn_motor.db"
|
||||
{
|
||||
pattern
|
||||
{P, N, M, DTYP, PORT, DESC, ADDR, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, DHLM, DLLM, MRES, PREC, INIT}
|
||||
{IMS:, 1, 1, "asynMotor", IMS1, "MDrive23", 0, mm, Pos, 3.0, 0.017, 2.0, 0, 0.017, 2.0, 100, -100, 0.000083, 4, ""}
|
||||
}
|
||||
|
||||
file "$(MOTOR)/db/IMS_extra.db"
|
||||
{
|
||||
pattern
|
||||
{DEV, AREA, LOC, PORT, ADDR, TIMEOUT}
|
||||
{IMS, IOC, 1, IMS1, 0, 1}
|
||||
}
|
||||
# field(DESC,"Y DG4 DS North Motor") # same
|
||||
# field(DTYP,"ImsMDrivePlus") # same
|
||||
# field(OUT, "#C0 S0 @") # same
|
||||
# field(DIR, "Pos") # same
|
||||
# field(SREV,"2048") # SREV [steps/rev], MRES = UREV/SREV
|
||||
# field(UREV,"0.017") # UREV [egu/rev], MRES = UREV/SREV
|
||||
# field(UEIP,"No")
|
||||
# field(S, "10") # no equivalent
|
||||
# field(SMAX,"20") # SMAX[rev/s], VELO = SMAX * UREV
|
||||
# field(SBAS,"1") # VBAS = SBAS * UREV
|
||||
# field(ACCL,"2.0") # same
|
||||
# field(BDST,"0") # same
|
||||
# field(RDBD,"0.0005")
|
||||
# field(RTRY,"1")
|
||||
# field(SBAK,"1") # BVEL = SBAK * UREV
|
||||
# field(BACC,"2.0") # same
|
||||
# field(DLY, "0")
|
||||
# field(PREC,"4") # same
|
||||
# field(EGU, "mm") # same
|
||||
# field(DHLM,"200.0") # same
|
||||
# field(DLLM,"-200.0") # same
|
||||
# field(INIT,"") # same
|
||||
# field(TWV, "1") # same
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# The is the "ASYN" example for communication to an
|
||||
# IMS483 controller. The examples must be configured by including or omitting
|
||||
# The is the "ASYN" example for communication to a controller
|
||||
# The examples must be configured by including or omitting
|
||||
# comment characters (i.e., #'s) from this file.
|
||||
|
||||
# "#!" marks lines that can be uncommented.
|
||||
@@ -28,33 +28,6 @@ dbLoadRecords("$(MOTOR)/db/motorUtil.db", "P=IOC:")
|
||||
# Configure the ASYN server code. This MUST be configured too!
|
||||
< st_asynserver.cmd.Vx
|
||||
|
||||
# IMS IM483 driver setup parameters:
|
||||
# (1) maximum number of controllers in system
|
||||
# (2) motor task polling rate (min=1Hz,max=60Hz)
|
||||
# SM - single mode PL - party mode
|
||||
#!IM483SMSetup(1, 10)
|
||||
#!IM483PLSetup(1, 10)
|
||||
|
||||
# IMS IM483 configuration parameters:
|
||||
# (1) controller# (chain#) being configured,
|
||||
# (2) ASYN port name
|
||||
# SM - single mode PL - party mode
|
||||
#!IM483SMConfig(0, "a-Serial[0]")
|
||||
#!drvIM483SMdebug=4
|
||||
#!IM483PLConfig(0, "a-Serial[0]")
|
||||
#!drvIM483PLdebug=4
|
||||
|
||||
|
||||
# IMS MDrive driver setup parameters:
|
||||
# (1) maximum number of controllers in system
|
||||
# (2) motor task polling rate (min=1Hz,max=60Hz)
|
||||
#!MDriveSetup(1, 10)
|
||||
|
||||
# IMS MDrive driver configuration parameters:
|
||||
# (1) controller# being configured
|
||||
#!MDriveConfig(0, "a-Serial[0]")
|
||||
#!drvMDrivedebug = 4
|
||||
|
||||
|
||||
# Aerotech Ensemble digital servo controller Setup
|
||||
# (1) maximum number of controllers in system
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
< envPaths
|
||||
|
||||
# Tell EPICS all about the record types, device-support modules, drivers,
|
||||
# etc. in this build from CARS
|
||||
dbLoadDatabase("../../dbd/WithAsyn.dbd")
|
||||
WithAsyn_registerRecordDeviceDriver(pdbbase)
|
||||
|
||||
### Motors
|
||||
# Motors substitutions, customize this for your motor
|
||||
dbLoadTemplate "motor.substitutions.ims"
|
||||
|
||||
# Configure asyn communication port, first
|
||||
# drvAsynIPPortConfigure(IOPortName, port, priority, disable auto-connect, no process EOS)
|
||||
drvAsynIPPortConfigure("M06", "ts-b34-nw09:2101", 0, 0, 0 )
|
||||
|
||||
# Configure one controller per motor, each controller just has 1 axis
|
||||
# motorPortName, portName, deviceName, movingPollPeriod, idlePollPeriod
|
||||
ImsMDrivePlusCreateController("IMS1", "M06", "1", 200, 5000)
|
||||
|
||||
# Optional: Enable tracing
|
||||
#asynSetTraceIOMask("IMS1",0,0)
|
||||
asynSetTraceMask("IMS1",0,9)
|
||||
|
||||
# Initialize the IOC and start processing records
|
||||
iocInit()
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -21,33 +21,6 @@ asynSetOption("L0", -1, "stop", "1")
|
||||
asynSetOption("L0", -1, "clocal", "Y")
|
||||
asynSetOption("L0", -1, "crtscts", "N")
|
||||
|
||||
# IMS IM483 driver setup parameters:
|
||||
# (1) maximum number of controllers in system
|
||||
# (2) motor task polling rate (min=1Hz,max=60Hz)
|
||||
# SM - single mode PL - party mode
|
||||
#!IM483SMSetup(1, 10)
|
||||
#!IM483PLSetup(1, 10)
|
||||
|
||||
# IMS IM483 configuration parameters:
|
||||
# (1) controller# (chain#) being configured,
|
||||
# (2) ASYN port name
|
||||
# SM - single mode PL - party mode
|
||||
#!IM483SMConfig(0, "L0")
|
||||
#!var drvIM483SMdebug 4
|
||||
#!IM483PLConfig(0, "L0")
|
||||
#!var drvIM483PLdebug 4
|
||||
|
||||
# IMS MDrive driver setup parameters:
|
||||
# (1) maximum number of controllers in system
|
||||
# (2) motor task polling rate (min=1Hz,max=60Hz)
|
||||
#!MDriveSetup(1, 10)
|
||||
|
||||
# IMS MDrive driver configuration parameters:
|
||||
# (1) controller# being configured,
|
||||
# (2) ASYN port name
|
||||
#!MDriveConfig(0, "L0")
|
||||
#!var drvMDrivedebug 4
|
||||
|
||||
|
||||
# Aerotech Ensemble digital servo controller Setup
|
||||
# (1) maximum number of controllers in system
|
||||
|
||||
@@ -10,33 +10,6 @@ dbLoadRecords("$(MOTOR)/db/motorUtil.db", "P=IOC:")
|
||||
|
||||
# This is for WIN32, local serial ports not supported. Must use IP terminal server.
|
||||
|
||||
# IMS IM483 driver setup parameters:
|
||||
# (1) maximum number of controllers in system
|
||||
# (2) motor task polling rate (min=1Hz,max=60Hz)
|
||||
# SM - single mode PL - party mode
|
||||
#!IM483SMSetup(1, 10)
|
||||
#!IM483PLSetup(1, 10)
|
||||
|
||||
# IMS IM483 configuration parameters:
|
||||
# (1) controller# (chain#) being configured,
|
||||
# (2) ASYN port name
|
||||
# SM - single mode PL - party mode
|
||||
#!IM483SMConfig(0, "L0")
|
||||
#!var drvIM483SMdebug 4
|
||||
#!IM483PLConfig(0, "L0")
|
||||
#!var drvIM483PLdebug 4
|
||||
|
||||
# IMS MDrive driver setup parameters:
|
||||
# (1) maximum number of controllers in system
|
||||
# (2) motor task polling rate (min=1Hz,max=60Hz)
|
||||
#!MDriveSetup(1, 10)
|
||||
|
||||
# IMS MDrive driver configuration parameters:
|
||||
# (1) controller# being configured,
|
||||
# (2) ASYN port name
|
||||
#!MDriveConfig(0, "L0")
|
||||
#!var drvMDrivedebug 4
|
||||
|
||||
# Aerotech Ensemble digital servo controller Setup
|
||||
# (1) maximum number of controllers in system
|
||||
# (2) motor task polling rate (min=1Hz,max=60Hz)
|
||||
|
||||
@@ -13,6 +13,7 @@ SUBMODULES += motorFaulhaber
|
||||
ifdef IPAC
|
||||
SUBMODULES += motorHytec
|
||||
endif
|
||||
SUBMODULES += motorIms
|
||||
SUBMODULES += motorKohzu
|
||||
SUBMODULES += motorMclennan
|
||||
SUBMODULES += motorMicos
|
||||
|
||||
Submodule
+1
Submodule modules/motorIms added at 47aefe2c61
@@ -1,13 +0,0 @@
|
||||
# Database for IMS
|
||||
|
||||
# send IMS S command to save parameters to NVM
|
||||
record(bo,"$(DEV):$(AREA):$(LOC):IMS_S") {
|
||||
field(DESC, "Ims Save to NVM")
|
||||
field(PINI, "NO")
|
||||
field(DTYP, "asynInt32")
|
||||
field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))IMS_SAVETONVM")
|
||||
field(VAL, "0")
|
||||
field(ZNAM, "No")
|
||||
field(ONAM, "Yes")
|
||||
}
|
||||
|
||||
@@ -1,596 +0,0 @@
|
||||
//! @File : ImsMDrivePlusMotorAxis.cpp
|
||||
//! Motor record driver level support for Intelligent Motion Systems, Inc.
|
||||
//! MDrivePlus series; M17, M23, M34.
|
||||
//! Use "model 3" asyn motor, asynMotorController and asynMotorAxis classes.
|
||||
//!
|
||||
//! Original Author : Nia Fong
|
||||
//! Date : 11-21-2011
|
||||
//! Current Author : Mitch D'Ewart (SLAC)
|
||||
//
|
||||
// Revision History
|
||||
// ----------------
|
||||
// 11-21-2011 NF Initial version
|
||||
// 12-15-2014 RLS Bug fix from Thierry Zamofing (PSI); acceleration was set to the same value as the speed.
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <exception>
|
||||
#include <epicsThread.h>
|
||||
#include <iocsh.h>
|
||||
#include <asynOctetSyncIO.h>
|
||||
|
||||
#include "asynMotorController.h"
|
||||
#include "asynMotorAxis.h"
|
||||
|
||||
#include <epicsExport.h>
|
||||
#include "ImsMDrivePlusMotorController.h"
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
//! ImsMDrivePlusMotorAxis()
|
||||
// Constructor
|
||||
//
|
||||
//! @param[in] pC pointer to ImsMDrivePlusMotorController
|
||||
//! @param[in] axisNum axis number
|
||||
////////////////////////////////////////////////////////
|
||||
ImsMDrivePlusMotorAxis::ImsMDrivePlusMotorAxis(ImsMDrivePlusMotorController *pC, int axisNum)
|
||||
: asynMotorAxis(pC, axisNum), pController(pC)
|
||||
{
|
||||
static const char *functionName = "ImsMDrivePlusMotorAxis()";
|
||||
asynPrint(pC->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: Create Axis %d\n", DRIVER_NAME, functionName, axisNum);
|
||||
|
||||
// run setup/initialize routines here
|
||||
// check communication, set moving status
|
||||
if (configAxis() == asynError) {
|
||||
asynPrint(pC->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: controller config failed for motor port=%s\n", DRIVER_NAME, functionName, pController->motorName);
|
||||
// TODO throw exception
|
||||
}
|
||||
callParamCallbacks();
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
//! configAxis()
|
||||
//! Used smarACTMCMotorDriver.cpp as reference
|
||||
//
|
||||
//! check communication by checking version returns a good string
|
||||
//! set moving status (PR MV)
|
||||
////////////////////////////////////////
|
||||
asynStatus ImsMDrivePlusMotorAxis::configAxis()
|
||||
{
|
||||
asynStatus status = asynError;
|
||||
char cmd[MAX_CMD_LEN];
|
||||
char resp[MAX_BUFF_LEN];
|
||||
size_t nread;
|
||||
int maxRetries=3;
|
||||
static const char *functionName = "configAxis()";
|
||||
// figure out what needs to be done to initialize controller
|
||||
|
||||
// try getting firmware version to make sure communication works
|
||||
sprintf(cmd, "PR VR");
|
||||
for (int i=0; i<maxRetries; i++) {
|
||||
status = pController->writeReadController(cmd, resp, sizeof(resp), &nread, IMS_TIMEOUT);
|
||||
asynPrint(pController->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: Version retry.\n", DRIVER_NAME, functionName);
|
||||
if (status == asynError) {
|
||||
asynPrint(pController->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: Version inquiry FAILED.\n", DRIVER_NAME, functionName);
|
||||
} else { // ok to check firmware level/format or just strlen? v3.009
|
||||
if (strlen(resp) < 2) {
|
||||
asynPrint(pController->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: Version inquiry FAILED version=%s.\n", DRIVER_NAME, functionName, resp);
|
||||
setIntegerParam(pController->motorStatusProblem_, 1);
|
||||
setIntegerParam(pController->motorStatusCommsError_, 1);
|
||||
status = asynError; return(status);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// set encoder flags
|
||||
sprintf(cmd, "PR EE");
|
||||
status = pController->writeReadController(cmd, resp, sizeof(resp), &nread, IMS_TIMEOUT);
|
||||
if (status == asynSuccess) {
|
||||
int val = atoi(resp);
|
||||
setIntegerParam(pController->motorStatusHasEncoder_, val ? 1:0);
|
||||
setIntegerParam(pController->motorStatusGainSupport_, val ? 1:0);
|
||||
asynPrint(pController->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: set motorStatusHasEncoder_=%d, motorStatusGainSupport_=%d.\n", DRIVER_NAME, functionName, val, val);
|
||||
}
|
||||
|
||||
// start idle timer
|
||||
//idleTimeStart = epicsTime::getCurrent();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
//! setAxisMoveParameters()
|
||||
//! set base velocity, moving velocity, and acceleration
|
||||
//
|
||||
//! @param[in] minVelocity
|
||||
//! @param[in] maxVelocity
|
||||
//! @param[in] acceleration
|
||||
////////////////////////////////////////////////////////
|
||||
asynStatus ImsMDrivePlusMotorAxis::setAxisMoveParameters(double minVelocity, double maxVelocity, double acceleration)
|
||||
{
|
||||
asynStatus status = asynError;
|
||||
char cmd[MAX_CMD_LEN];
|
||||
static const char *functionName = "setAxisMoveParameters()";
|
||||
|
||||
// check if using base velocity (VI)
|
||||
// for MDrivePlus initial velocity must be below max_velocity
|
||||
if (minVelocity > 0) { // base velocity set
|
||||
if (minVelocity > maxVelocity) { // illegal state
|
||||
asynPrint(pController->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: base velocity=%f cannot be greater than max velocity=%f\n", DRIVER_NAME, functionName, minVelocity, maxVelocity);
|
||||
goto bail;
|
||||
}
|
||||
// set base velocity
|
||||
sprintf(cmd, "VI=%ld", (long)minVelocity);
|
||||
status = pController->writeController(cmd, IMS_TIMEOUT);
|
||||
if (status) goto bail;
|
||||
}
|
||||
|
||||
|
||||
// set velocity
|
||||
sprintf(cmd, "VM=%ld", (long)maxVelocity);
|
||||
status = pController->writeController(cmd, IMS_TIMEOUT);
|
||||
if (status) goto bail;
|
||||
|
||||
// set accceleration
|
||||
if (acceleration != 0) {
|
||||
sprintf(cmd, "A=%ld", (long)acceleration);
|
||||
status = pController->writeController(cmd, IMS_TIMEOUT);
|
||||
if (status) goto bail;
|
||||
}
|
||||
|
||||
bail:
|
||||
if (status) {
|
||||
char buff[LOCAL_LINE_LEN];
|
||||
sprintf(buff, "%s:%s: ERROR setting motor velocity and acceleration", DRIVER_NAME, functionName);
|
||||
handleAxisError(buff);
|
||||
}
|
||||
|
||||
callParamCallbacks();
|
||||
return status;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
//! move()
|
||||
//! Override asynMotorAxis class implementation
|
||||
// Based on smarActMCSMotorDriver.cpp
|
||||
//
|
||||
//! @param[in] position
|
||||
//! @param[in] relative making absolute or relative move
|
||||
//! @param[in] minVelocity
|
||||
//! @param[in] maxVelocity
|
||||
//! @param[in] acceleration
|
||||
////////////////////////////////////////////////////////
|
||||
asynStatus ImsMDrivePlusMotorAxis::move(double position, int relative, double minVelocity, double maxVelocity, double acceleration)
|
||||
{
|
||||
asynStatus status = asynError;
|
||||
char cmd[MAX_CMD_LEN];
|
||||
static const char *functionName = "move()";
|
||||
|
||||
// sent commands to motor to set velocities and acceleration
|
||||
status = setAxisMoveParameters(minVelocity, maxVelocity, acceleration);
|
||||
if (status) goto bail;
|
||||
|
||||
// move
|
||||
asynPrint(pController->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: VBASE=%f, VELO=%f, ACCL=%f, position=%f, relative=%d\n", DRIVER_NAME, functionName, minVelocity, maxVelocity, acceleration, position, relative);
|
||||
if (relative) { // relative move MR
|
||||
sprintf(cmd, "MR %ld", (long)position);
|
||||
} else { // absolute move MA
|
||||
sprintf(cmd, "MA %ld", (long)position);
|
||||
}
|
||||
status = pController->writeController(cmd, IMS_TIMEOUT);
|
||||
if (status) goto bail;
|
||||
|
||||
bail:
|
||||
if (status) {
|
||||
char buff[LOCAL_LINE_LEN];
|
||||
sprintf(buff, "%s:%s: ERROR moving motor", DRIVER_NAME, functionName);
|
||||
handleAxisError(buff);
|
||||
}
|
||||
|
||||
callParamCallbacks();
|
||||
return status;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
//! moveVelocity()
|
||||
//! Override asynMotorAxis class implementation
|
||||
// Based on smarActMCSMotorDriver.cpp
|
||||
//! move at a fixed velocity until stop() called
|
||||
//
|
||||
//! @param[in] minVelocity
|
||||
//! @param[in] maxVelocity
|
||||
//! @param[in] acceleration
|
||||
////////////////////////////////////////////////////////
|
||||
asynStatus ImsMDrivePlusMotorAxis::moveVelocity(double minVelocity, double maxVelocity, double acceleration)
|
||||
{
|
||||
asynStatus status = asynError;
|
||||
char cmd[MAX_CMD_LEN];
|
||||
static const char *functionName = "moveVelocity()";
|
||||
|
||||
// sent commands to motor to set velocities and acceleration
|
||||
status = setAxisMoveParameters(minVelocity, maxVelocity, acceleration);
|
||||
if (status) goto bail;
|
||||
|
||||
// move
|
||||
asynPrint(pController->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: VBASE=%f, VELO=%f, ACCL=%f\n", DRIVER_NAME, functionName, minVelocity, maxVelocity, acceleration);
|
||||
sprintf(cmd, "SL %ld", (long)maxVelocity);
|
||||
status = pController->writeController(cmd, IMS_TIMEOUT);
|
||||
if (status) goto bail;
|
||||
|
||||
bail:
|
||||
if (status) {
|
||||
char buff[LOCAL_LINE_LEN];
|
||||
sprintf(buff, "%s:%s: ERROR jogging motor", DRIVER_NAME, functionName);
|
||||
handleAxisError(buff);
|
||||
}
|
||||
|
||||
callParamCallbacks();
|
||||
return status;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
//! stop()
|
||||
//! Override asynMotorAxis class implementation
|
||||
// Based on smarActMCSMotorDriver.cpp
|
||||
//
|
||||
//! @param[in] acceleration
|
||||
////////////////////////////////////////////////////////
|
||||
asynStatus ImsMDrivePlusMotorAxis::stop(double acceleration)
|
||||
{
|
||||
asynStatus status = asynError;
|
||||
char cmd[MAX_CMD_LEN];
|
||||
static const char *functionName = "stop()";
|
||||
|
||||
// set accceleration
|
||||
if (acceleration != 0) {
|
||||
sprintf(cmd, "A=%ld", (long)acceleration);
|
||||
status = pController->writeController(cmd, IMS_TIMEOUT);
|
||||
if (status) goto bail;
|
||||
}
|
||||
|
||||
// move
|
||||
sprintf(cmd, "SL 0");
|
||||
status = pController->writeController(cmd, IMS_TIMEOUT);
|
||||
if (status) goto bail;
|
||||
|
||||
bail:
|
||||
if (status) {
|
||||
char buff[LOCAL_LINE_LEN];
|
||||
sprintf(buff, "%s:%s: ERROR stopping motor", DRIVER_NAME, functionName);
|
||||
handleAxisError(buff);
|
||||
}
|
||||
|
||||
callParamCallbacks();
|
||||
return status;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
//! home()
|
||||
//! Override asynMotorAxis class implementation
|
||||
// Based on smarActMCSMotorDriver.cpp
|
||||
//
|
||||
//! direction=1: slew at maxVelocity in the minus direction (until switch activates) then creep at vi in the plus direction (until switch becomes inactive again)
|
||||
//! direction=3: slew at maxVelocity in the plus direction (until switch activates) then creep at vi in the minus direction (until switch becomes inactive again)
|
||||
//
|
||||
//! @param[in] minVelocity
|
||||
//! @param[in] maxVelocity
|
||||
//! @param[in] acceleration
|
||||
//! @param[in] forwards direction to home, 0=minus direction, 1=plus direction
|
||||
////////////////////////////////////////////////////////
|
||||
asynStatus ImsMDrivePlusMotorAxis::home(double minVelocity, double maxVelocity, double acceleration, int forwards)
|
||||
{
|
||||
asynStatus status = asynError;
|
||||
char cmd[MAX_CMD_LEN];
|
||||
char resp[MAX_BUFF_LEN];
|
||||
size_t nread;
|
||||
int direction = 1; // direction to home, initialize homing in minus direction
|
||||
double baseVelocity=0;
|
||||
static const char *functionName = "home()";
|
||||
|
||||
// check if using base velocity (VI)
|
||||
// for MDrivePlus initial velocity must be below max_velocity
|
||||
if (minVelocity > 0) { // base velocity being configured
|
||||
if (minVelocity > maxVelocity) { // illegal state
|
||||
asynPrint(pController->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: base velocity=%f cannot be greater than max velocity=%f\n", DRIVER_NAME, functionName, minVelocity, maxVelocity);
|
||||
goto bail;
|
||||
}
|
||||
} else { // base velocity needs to be set because creeping back to home switch at base velocity, so make sure it's nonzero
|
||||
sprintf(cmd, "PR VI"); // get base velocity setting
|
||||
status = pController->writeReadController(cmd, resp, sizeof(resp), &nread, IMS_TIMEOUT);
|
||||
if (status) goto bail;
|
||||
baseVelocity = atof(resp);
|
||||
if (baseVelocity == 0) { // set to factory default of 1000
|
||||
baseVelocity=1000;
|
||||
}
|
||||
}
|
||||
|
||||
// sent commands to motor to set velocities and acceleration
|
||||
if (status = setAxisMoveParameters(minVelocity, maxVelocity, acceleration)) goto bail;
|
||||
|
||||
// home
|
||||
if (forwards == 1) { // homing in forward direction
|
||||
direction = 3;
|
||||
}
|
||||
asynPrint(pController->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: VBASE=%f, VELO=%f, ACCL=%f, forwards=%d\n", DRIVER_NAME, functionName, minVelocity, maxVelocity, acceleration, forwards);
|
||||
sprintf(cmd, "HM %d", direction);
|
||||
status = pController->writeController(cmd, IMS_TIMEOUT);
|
||||
if (status) goto bail;
|
||||
|
||||
bail:
|
||||
if (status) {
|
||||
char buff[LOCAL_LINE_LEN];
|
||||
sprintf(buff, "%s:%s: ERROR homing motor", DRIVER_NAME, functionName);
|
||||
handleAxisError(buff);
|
||||
}
|
||||
|
||||
callParamCallbacks();
|
||||
return status;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
//! setPosition()
|
||||
//! Override asynMotorAxis class implementation
|
||||
// Based on smarActMCSMotorDriver.cpp
|
||||
//
|
||||
//! @param[in] position value to set motor's internal position
|
||||
////////////////////////////////////////////////////////
|
||||
asynStatus ImsMDrivePlusMotorAxis::setPosition(double position)
|
||||
{
|
||||
asynStatus status = asynError;
|
||||
char cmd[MAX_CMD_LEN];
|
||||
static const char *functionName = "setPosition()";
|
||||
|
||||
// set position
|
||||
asynPrint(pController->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: position=%f\n", DRIVER_NAME, functionName, position);
|
||||
sprintf(cmd, "P=%ld", (long)position);
|
||||
status = pController->writeController(cmd, IMS_TIMEOUT);
|
||||
if (status) goto bail;
|
||||
|
||||
bail:
|
||||
if (status) {
|
||||
char buff[LOCAL_LINE_LEN];
|
||||
sprintf(buff, "%s:%s: ERROR setting motor position", DRIVER_NAME, functionName);
|
||||
handleAxisError(buff);
|
||||
}
|
||||
|
||||
callParamCallbacks();
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
//! poll()
|
||||
//! Override asynMotorAxis class implementation
|
||||
// Based on smarActMCSMotorDriver.cpp
|
||||
//
|
||||
//! Set position and moving flag
|
||||
//
|
||||
//! @param[in] moving pointer to moving flag
|
||||
////////////////////////////////////////////////////////
|
||||
asynStatus ImsMDrivePlusMotorAxis::poll(bool *moving)
|
||||
{
|
||||
asynStatus status = asynError;
|
||||
char cmd[MAX_CMD_LEN];
|
||||
char resp[MAX_BUFF_LEN];
|
||||
size_t nread;
|
||||
int val=0;
|
||||
double position;
|
||||
*moving = false;
|
||||
static const char *functionName = "poll()";
|
||||
//epicsTime currentTime;
|
||||
|
||||
// get position
|
||||
sprintf(cmd, "PR P");
|
||||
status = pController->writeReadController(cmd, resp, sizeof(resp), &nread, IMS_TIMEOUT);
|
||||
if (status) goto bail;
|
||||
position = atof(resp);
|
||||
// update motor record position values, just update encoder's even if not using one
|
||||
setDoubleParam(pController->motorEncoderPosition_, position);
|
||||
setDoubleParam(pController->motorPosition_, position);
|
||||
|
||||
// get moving flag
|
||||
sprintf(cmd, "PR MV");
|
||||
status = pController->writeReadController(cmd, resp, sizeof(resp), &nread, IMS_TIMEOUT);
|
||||
if (status) goto bail;
|
||||
val = atoi(resp);
|
||||
if (val == 1) *moving = true; // updating moving flag
|
||||
/* else { // not moving
|
||||
if (prevMovingState == 1) {// state changed, moving before, start idle timer
|
||||
idleTimeStart = epicsTime::getCurrent();
|
||||
}
|
||||
}
|
||||
prevMovingState = val;
|
||||
*/
|
||||
// update motor record status done with moving status
|
||||
setIntegerParam(pController->motorStatusDone_, ! *moving );
|
||||
|
||||
/*
|
||||
// if position has changed and moving stopped for over 30sec, update NVM with current position so the motor will remember
|
||||
// its location just in case its power dies
|
||||
currentTime = epicsTime::getCurrent();
|
||||
idleTime = currentTime - idleTimeStart;
|
||||
//printf("prevPos=%f, pos=%f, moving=%d, idleTime=%f\n", prevPosition, position, *moving, idleTime);
|
||||
if (prevPosition != position && *moving == false && idleTime > 30) {
|
||||
sprintf(cmd, "S");
|
||||
if (status = pController->writeController(cmd, IMS_TIMEOUT)) {
|
||||
asynPrint(pController->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:poll(): ERROR saving position to NVM\n", DRIVER_NAME);
|
||||
goto bail;
|
||||
}
|
||||
asynPrint(pController->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:poll(): saving parameters to NVM\n", DRIVER_NAME);
|
||||
idleTimeStart = epicsTime::getCurrent();
|
||||
prevPosition = position;
|
||||
}
|
||||
*/
|
||||
|
||||
// get home switch value
|
||||
if (pController->homeSwitchInput != -1) {
|
||||
sprintf(cmd, "PR I%d", pController->homeSwitchInput);
|
||||
status = pController->writeReadController(cmd, resp, sizeof(resp), &nread, IMS_TIMEOUT);
|
||||
if (status) goto bail;
|
||||
val = atoi(resp);
|
||||
setIntegerParam(pController->motorStatusHome_, val);
|
||||
}
|
||||
|
||||
// get positive limit switch value
|
||||
if (pController->posLimitSwitchInput != -1) {
|
||||
sprintf(cmd, "PR I%d", pController->posLimitSwitchInput);
|
||||
status = pController->writeReadController(cmd, resp, sizeof(resp), &nread, IMS_TIMEOUT);
|
||||
if (status) goto bail;
|
||||
val = atoi(resp);
|
||||
setIntegerParam(pController->motorStatusHighLimit_, val);
|
||||
}
|
||||
|
||||
// get negative limit switch value
|
||||
if (pController->negLimitSwitchInput != -1) {
|
||||
sprintf(cmd, "PR I%d", pController->negLimitSwitchInput);
|
||||
status = pController->writeReadController(cmd, resp, sizeof(resp), &nread, IMS_TIMEOUT);
|
||||
if (status) goto bail;
|
||||
val = atoi(resp);
|
||||
setIntegerParam(pController->motorStatusLowLimit_, val);
|
||||
}
|
||||
|
||||
// error polling
|
||||
bail:
|
||||
if (status) {
|
||||
char buff[LOCAL_LINE_LEN];
|
||||
sprintf(buff, "%s:%s: ERROR polling motor", DRIVER_NAME, functionName);
|
||||
handleAxisError(buff);
|
||||
}
|
||||
|
||||
// update motor status if polling was successful
|
||||
if (status == 0) {
|
||||
setIntegerParam(pController->motorStatusCommsError_, 0); // reset COMM error
|
||||
setIntegerParam(pController->motorStatusProblem_, 0); // reset problem error, bit 10
|
||||
}
|
||||
|
||||
// update motor record
|
||||
callParamCallbacks();
|
||||
|
||||
int mstat;
|
||||
pController->getIntegerParam(pController->motorStatus_, &mstat);
|
||||
asynPrint(pController->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: POS=%f, MSTAT=%d\n", DRIVER_NAME, functionName, position, mstat);
|
||||
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
//! saveToNVM()
|
||||
//! Save user variables and flags to non-volatile RAM in case of power loss
|
||||
//
|
||||
////////////////////////////////////////////////////////
|
||||
asynStatus ImsMDrivePlusMotorAxis::saveToNVM()
|
||||
{
|
||||
asynStatus status = asynError;
|
||||
char cmd[MAX_CMD_LEN];
|
||||
static const char *functionName = "saveToNVM()";
|
||||
|
||||
// send save command
|
||||
sprintf(cmd, "S");
|
||||
status = pController->writeController(cmd, IMS_TIMEOUT);
|
||||
if (status) goto bail;
|
||||
asynPrint(pController->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: Saved to NVM\n", DRIVER_NAME, functionName);
|
||||
|
||||
bail:
|
||||
if (status) {
|
||||
char buff[LOCAL_LINE_LEN];
|
||||
sprintf(buff, "%s:%s: ERROR saving to NVM", DRIVER_NAME, functionName);
|
||||
handleAxisError(buff);
|
||||
}
|
||||
|
||||
callParamCallbacks(); //FIXME is this necessary??
|
||||
return status;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
//! handleAxisError()
|
||||
//! Set motorStatusProblem_
|
||||
//! Then try to get and print error code
|
||||
//
|
||||
////! @param[in] errMsg pointer to error message from calling function
|
||||
////////////////////////////////////////////////////////
|
||||
void ImsMDrivePlusMotorAxis::handleAxisError(char *errMsg)
|
||||
{
|
||||
char cmd[MAX_CMD_LEN];
|
||||
char resp[MAX_BUFF_LEN];
|
||||
size_t nread=0;
|
||||
int errCode=0;
|
||||
char errCodeString[MAX_BUFF_LEN];
|
||||
static const char *functionName = "handleAxisError()";
|
||||
|
||||
// set motorStatusProblem_ bit
|
||||
setIntegerParam(pController->motorStatusProblem_, 1);
|
||||
|
||||
// read error code
|
||||
sprintf(cmd, "PR ER");
|
||||
pController->writeReadController(cmd, resp, sizeof(resp), &nread, IMS_TIMEOUT);
|
||||
errCode = atoi(resp);
|
||||
|
||||
switch (errCode) {
|
||||
case 0: strcpy(errCodeString, "No Error"); break;
|
||||
case 6: strcpy(errCodeString, "An I/O is already set to this type. Applies to non-General Purpose I/O"); break;
|
||||
case 8: strcpy(errCodeString, "Tried to set an I/O to an incorrect I/O type"); break;
|
||||
case 9: strcpy(errCodeString, "Tried to write to I/O set as Input or is 'TYPED'"); break;
|
||||
case 10: strcpy(errCodeString, "Illegal I/O number"); break;
|
||||
case 11: strcpy(errCodeString, "Incorrect CLOCK type"); break;
|
||||
case 12: strcpy(errCodeString, "Illegal Trip/Capture"); break;
|
||||
case 20: strcpy(errCodeString, "Tried to set unknown variable or flag"); break;
|
||||
case 21: strcpy(errCodeString, "Tried to set an incorrect value"); break;
|
||||
case 22: strcpy(errCodeString, "VI is set greater than or equal to VM"); break;
|
||||
case 23: strcpy(errCodeString, "VM is set less than or equal to VI"); break;
|
||||
case 24: strcpy(errCodeString, "Illegal data entered"); break;
|
||||
case 25: strcpy(errCodeString, "Variable or flag is read only"); break;
|
||||
case 26: strcpy(errCodeString, "Variable or flag is not allowed to be incremented or decremented"); break;
|
||||
case 27: strcpy(errCodeString, "Trip not defined"); break;
|
||||
case 28: strcpy(errCodeString, "Trying to redefine a program label or variable"); break;
|
||||
case 29: strcpy(errCodeString, "Trying to redefine a build in command, variable, or flag"); break;
|
||||
case 30: strcpy(errCodeString, "Unknown label or user variable"); break;
|
||||
case 31: strcpy(errCodeString, "Program label or user variable table is full"); break;
|
||||
case 32: strcpy(errCodeString, "Trying to set a label"); break;
|
||||
case 33: strcpy(errCodeString, "Trying to set and instruction"); break;
|
||||
case 34: strcpy(errCodeString, "Trying to execute a variable or flag"); break;
|
||||
case 35: strcpy(errCodeString, "Trying to print illegal variable or flag"); break;
|
||||
case 36: strcpy(errCodeString, "Illegal motor count to encoder count ratio"); break;
|
||||
case 37: strcpy(errCodeString, "Command, variable, or flag not available in drive"); break;
|
||||
case 38: strcpy(errCodeString, "Missing parameter separator"); break;
|
||||
case 39: strcpy(errCodeString, "Trip on position and trip on relative distance not allowed together"); break;
|
||||
case 40: strcpy(errCodeString, "Program not running"); break;
|
||||
case 41: strcpy(errCodeString, "Stack overflow"); break;
|
||||
case 42: strcpy(errCodeString, "Illegal program address"); break;
|
||||
case 43: strcpy(errCodeString, "Tried to overflow program stack"); break;
|
||||
case 44: strcpy(errCodeString, "Program locked"); break;
|
||||
case 45: strcpy(errCodeString, "Trying to overflow program space"); break;
|
||||
case 46: strcpy(errCodeString, "Not in program mode"); break;
|
||||
case 47: strcpy(errCodeString, "Tried to write in illegal flash address"); break;
|
||||
case 48: strcpy(errCodeString, "Program execution stopped by I/O set as stop"); break;
|
||||
case 61: strcpy(errCodeString, "Trying to set illegal baud rate"); break;
|
||||
case 62: strcpy(errCodeString, "IV already pending or IF flag already true"); break;
|
||||
case 63: strcpy(errCodeString, "Character over-run"); break;
|
||||
case 64: strcpy(errCodeString, "Startup calibration failed"); break;
|
||||
case 70: strcpy(errCodeString, "Flash check sum failed"); break;
|
||||
case 71: strcpy(errCodeString, "Internal temperature warning, 10 C to shutdown"); break;
|
||||
case 72: strcpy(errCodeString, "Internal over temp fault, disabling drive"); break;
|
||||
case 73: strcpy(errCodeString, "Tried to save while moving"); break;
|
||||
case 74: strcpy(errCodeString, "Tried to initialize parameters or clear program while moving"); break;
|
||||
case 75: strcpy(errCodeString, "Linear over temperature error"); break;
|
||||
case 80: strcpy(errCodeString, "Home switch not defined"); break;
|
||||
case 81: strcpy(errCodeString, "Home type not defined"); break;
|
||||
case 82: strcpy(errCodeString, "Went to both limits and did not find home"); break;
|
||||
case 83: strcpy(errCodeString, "Reached plus limit switch"); break;
|
||||
case 84: strcpy(errCodeString, "Reached minus limit switch"); break;
|
||||
case 85: strcpy(errCodeString, "MA or MR isn't allowed during home and home isn't allowed while moving"); break;
|
||||
case 86: strcpy(errCodeString, "Stall detected"); break;
|
||||
case 87: strcpy(errCodeString, "In clock mode"); break;
|
||||
case 88: strcpy(errCodeString, "Following error"); break;
|
||||
case 90: strcpy(errCodeString, "Motion variables are too low switching to EE=1"); break;
|
||||
case 91: strcpy(errCodeString, "Motion stopped by I/O set as stop"); break;
|
||||
case 92: strcpy(errCodeString, "Position error in closed loop"); break;
|
||||
case 93: strcpy(errCodeString, "MR or MA not allowed while correcting position"); break;
|
||||
}
|
||||
|
||||
asynPrint(pController->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: %s, %d:'%s'\n", DRIVER_NAME, functionName, errMsg, errCode, errCodeString);
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
// Description : This is the "model 3" asyn motor driver for IMS MDrivePlus.
|
||||
// Based on HytecMotorDriver.h.
|
||||
|
||||
#ifndef ImsMDrivePlusMotorAxis_H
|
||||
#define ImsMDrivePlusMotorAxis_H
|
||||
|
||||
#include <epicsTime.h>
|
||||
|
||||
#include "asynMotorController.h"
|
||||
#include "asynMotorAxis.h"
|
||||
|
||||
#define DRIVER_NAME "ImsMDrivePlusMotorDriver"
|
||||
|
||||
#define NUM_AXES 1
|
||||
#define DEFAULT_NUM_CARDS 32
|
||||
#define MAX_MESSAGES 100
|
||||
#define IMS_TIMEOUT 2
|
||||
#define MAX_BUFF_LEN 80
|
||||
#define MAX_CMD_LEN MAX_BUFF_LEN-10 // leave room for line feeds surrounding command
|
||||
#define MAX_NAME_LEN 10
|
||||
#define LOCAL_LINE_LEN 256
|
||||
|
||||
class epicsShareClass ImsMDrivePlusMotorController;
|
||||
|
||||
////////////////////////////////////
|
||||
// ImsMDrivePlusMotorAxis class
|
||||
// derived from asynMotorAxis class
|
||||
////////////////////////////////////
|
||||
class ImsMDrivePlusMotorAxis : public asynMotorAxis
|
||||
{
|
||||
public:
|
||||
///////////////////////////////////
|
||||
// Override asynMotorAxis functions
|
||||
///////////////////////////////////
|
||||
ImsMDrivePlusMotorAxis(ImsMDrivePlusMotorController *pC, int axis);
|
||||
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);
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// IMS MDrivePlus specific functions
|
||||
////////////////////////////////////////////////////
|
||||
asynStatus saveToNVM();
|
||||
protected:
|
||||
|
||||
|
||||
private:
|
||||
ImsMDrivePlusMotorController *pController;
|
||||
|
||||
//int useEncoder; //! using encoder flag
|
||||
// FIXME handle lost position in driver or ioc??
|
||||
// epicsTime idleTimeStart; //! timer used to track idle time for saving to NVM
|
||||
// double prevPosition; //! previous position used to see if motor has moved and need to update position in NVM in case of power failure
|
||||
// int prevMovingState; //! saves previous moving value
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// IMS MDrivePlus specific functions
|
||||
////////////////////////////////////////////////////
|
||||
asynStatus configAxis();
|
||||
asynStatus setAxisMoveParameters(double min_velocity, double max_velocity, double acceleration);
|
||||
void handleAxisError(char *errMsg);
|
||||
|
||||
friend class ImsMDrivePlusMotorController;
|
||||
};
|
||||
|
||||
#endif // ImsMDrivePlusMotorAxis_H
|
||||
|
||||
|
||||
@@ -1,361 +0,0 @@
|
||||
//! @File : ImsMDrivePlusController.cpp
|
||||
//! Motor record driver level support for Intelligent Motion Systems, Inc.
|
||||
//! MDrivePlus series; M17, M23, M34.
|
||||
//! Simple implementation using "model 3" asynMotorController and asynMotorAxis base classes (derived from asynPortDriver)
|
||||
//!
|
||||
//! Original Author : Nia Fong
|
||||
//! Date : 11-21-2011
|
||||
//! Current Author : Mitch D'Ewart (SLAC)
|
||||
//!
|
||||
//! Assumptions :
|
||||
//! 1) Like all controllers, the MDrivePlus must be powered-on when EPICS is first booted up.
|
||||
//! 2) Assume single controller-card-axis relationship; 1 controller = 1 axis.
|
||||
//! 3) Append Line Feed (ctrl-J) to end of command string for party mode support
|
||||
//! 4) If not using device name to address device, config ImsMDrivePlusCreateController() with empty string "" for deviceName
|
||||
//
|
||||
// Revision History
|
||||
// ----------------
|
||||
// 11-21-2011 NF Initial version
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <exception>
|
||||
#include <epicsThread.h>
|
||||
#include <iocsh.h>
|
||||
#include <asynOctetSyncIO.h>
|
||||
|
||||
#include "asynMotorController.h"
|
||||
#include "asynMotorAxis.h"
|
||||
#include "ImsMDrivePlusMotorAxis.h"
|
||||
|
||||
#include <epicsExport.h>
|
||||
#include "ImsMDrivePlusMotorController.h"
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
//! @ImsMDrivePlusMotorController()
|
||||
//! Constructor
|
||||
//! Driver assumes only 1 axis configured per controller for now...
|
||||
//!
|
||||
//! @param[in] motorPortName Name assigned to the port created to communicate with the motor
|
||||
//! @param[in] IOPortName Name assigned to the asyn IO port, name that was assigned in drvAsynIPPortConfigure()
|
||||
//! @param[in] devName Name of device (DN) assigned to motor axis in MCode, the device name is prepended to the MCode command to support Party Mode (PY) multidrop communication setup
|
||||
//! set to empty string "" if no device name needed/not using Party Mode
|
||||
//! @param[in] movingPollPeriod Moving polling period in milliseconds
|
||||
//! @param[in] idlePollPeriod Idle polling period in milliseconds
|
||||
////////////////////////////////////////////////////////
|
||||
ImsMDrivePlusMotorController::ImsMDrivePlusMotorController(const char *motorPortName, const char *IOPortName, const char *devName, double movingPollPeriod, double idlePollPeriod)
|
||||
: asynMotorController(motorPortName, NUM_AXES, NUM_IMS_PARAMS,
|
||||
asynInt32Mask | asynFloat64Mask | asynUInt32DigitalMask,
|
||||
asynInt32Mask | asynFloat64Mask | asynUInt32DigitalMask,
|
||||
ASYN_CANBLOCK | ASYN_MULTIDEVICE,
|
||||
1, // autoconnect
|
||||
0, 0), // Default priority and stack size
|
||||
pAsynUserIMS(0)
|
||||
{
|
||||
static const char *functionName = "ImsMDrivePlusMotorController()";
|
||||
asynStatus status;
|
||||
ImsMDrivePlusMotorAxis *pAxis;
|
||||
// asynMotorController constructor calloc's memory for array of axis pointers
|
||||
pAxes_ = (ImsMDrivePlusMotorAxis **)(asynMotorController::pAxes_);
|
||||
|
||||
// copy names
|
||||
strncpy(motorName, motorPortName, (MAX_NAME_LEN - 1));
|
||||
|
||||
// setup communication
|
||||
status = pasynOctetSyncIO->connect(IOPortName, 0, &pAsynUserIMS, NULL);
|
||||
if (status != asynSuccess) {
|
||||
printf("\n\n%s:%s: ERROR connecting to Controller's IO port=%s\n\n", DRIVER_NAME, functionName, IOPortName);
|
||||
// TODO would be good to implement exceptions
|
||||
// TODO THROW_(SmarActMCSException(MCSConnectionError, "SmarActMCSController: unable to connect serial channel"));
|
||||
}
|
||||
|
||||
// write version, cannot use asynPrint() in constructor since controller (motorPortName) hasn't been created yet
|
||||
printf("%s:%s: motorPortName=%s, IOPortName=%s, devName=%s \n", DRIVER_NAME, functionName, motorPortName, IOPortName, devName);
|
||||
|
||||
|
||||
// init
|
||||
pasynOctetSyncIO->setInputEos(pAsynUserIMS, "\n", 1);
|
||||
pasynOctetSyncIO->setOutputEos(pAsynUserIMS, "\r\n", 2);
|
||||
|
||||
// Create controller-specific parameters
|
||||
createParam(ImsMDrivePlusSaveToNVMControlString, asynParamInt32, &ImsMDrivePlusSaveToNVM_);
|
||||
createParam(ImsMDrivePlusLoadMCodeControlString, asynParamOctet, &this->ImsMDrivePlusLoadMCode_);
|
||||
createParam(ImsMDrivePlusClearMCodeControlString, asynParamOctet, &this->ImsMDrivePlusClearMCode_);
|
||||
|
||||
// Check the validity of the arguments and init controller object
|
||||
initController(devName, movingPollPeriod, idlePollPeriod);
|
||||
|
||||
// Create axis
|
||||
// Assuming single axis per controller the way drvAsynIPPortConfigure( "M06", "ts-b34-nw08:2101", 0, 0 0 ) is called in st.cmd script
|
||||
pAxis = new ImsMDrivePlusMotorAxis(this, 0);
|
||||
pAxis = NULL; // asynMotorController constructor tracking array of axis pointers
|
||||
|
||||
// read home and limit config from S1-S4
|
||||
readHomeAndLimitConfig();
|
||||
|
||||
startPoller(movingPollPeriod, idlePollPeriod, 2);
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
//! initController()
|
||||
//! config controller variables - axis, etc.
|
||||
//! only support single axis per controller
|
||||
//
|
||||
//! @param[in] devName Name of device (DN) used to identify axis within controller for Party Mode
|
||||
//! @param[in] movingPollPeriod Moving polling period in milliseconds
|
||||
//! @param[in] idlePollPeriod Idle polling period in milliseconds
|
||||
////////////////////////////////////////
|
||||
void ImsMDrivePlusMotorController::initController(const char *devName, double movingPollPeriod, double idlePollPeriod)
|
||||
{
|
||||
strncpy(this->deviceName, devName, (MAX_NAME_LEN - 1));
|
||||
|
||||
// initialize asynMotorController variables
|
||||
this->numAxes_ = NUM_AXES; // only support single axis
|
||||
this->movingPollPeriod_ = movingPollPeriod;
|
||||
this->idlePollPeriod_ = idlePollPeriod;
|
||||
|
||||
// initialize switch inputs
|
||||
this->homeSwitchInput=-1;
|
||||
this->posLimitSwitchInput=-1;
|
||||
this->negLimitSwitchInput=-1;
|
||||
|
||||
// flush io buffer
|
||||
pasynOctetSyncIO->flush(pAsynUserIMS);
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
//! readHomeAndLimitConfig
|
||||
//! read home, positive limit, and neg limit switch configuration from MCode S1-S4 settings
|
||||
//! S1-S4 must be set up beforehand
|
||||
//! I1-I4 are used to read the status of S1-S4
|
||||
// Use logic from existing drvMDrive.cc
|
||||
////////////////////////////////////////
|
||||
int ImsMDrivePlusMotorController::readHomeAndLimitConfig()
|
||||
{
|
||||
asynStatus status = asynError;
|
||||
char cmd[MAX_CMD_LEN];
|
||||
char resp[MAX_BUFF_LEN];
|
||||
size_t nread;
|
||||
static const char *functionName = "readHomeAndLimitConfig()";
|
||||
int type;
|
||||
|
||||
// iterate through S1-S4 and parse each configuration to see if home, pos, and neg limits are set
|
||||
for (int i=1; i<=4; i++) {
|
||||
sprintf(cmd, "PR S%d", i); // query S1-S4 setting
|
||||
status = this->writeReadController(cmd, resp, sizeof(resp), &nread, IMS_TIMEOUT);
|
||||
sscanf(resp, "%d", &type);
|
||||
//asynPrint(pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: S%d: type=%d\n", DRIVER_NAME, functionName, i, type);
|
||||
if (type != 0)
|
||||
//printf("%s:%s: S%d: type=%d\n", DRIVER_NAME, functionName, i, type);
|
||||
switch (type) {
|
||||
case 0: break; // general purpose input
|
||||
case 1: // home switch input
|
||||
homeSwitchInput = i; break;
|
||||
case 2: // positive limit switch input
|
||||
posLimitSwitchInput = i; break;
|
||||
case 3: // negative limit switch input
|
||||
negLimitSwitchInput = i; break;
|
||||
default:
|
||||
printf("%s:%s: ERROR invalid data type for S%d=%d\n", DRIVER_NAME, functionName, i, type);
|
||||
}
|
||||
}
|
||||
|
||||
printf("homeSwitchInput=%d, posLimitSwitchInput=%d, negLimitSwitchInput=%d\n", homeSwitchInput, posLimitSwitchInput, negLimitSwitchInput);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
//! getAxis()
|
||||
//! Override asynMotorController function to return pointer to IMS axis object
|
||||
//
|
||||
//! Returns a pointer to an ImsMDrivePlusAxis object.
|
||||
//! Returns NULL if the axis number encoded in pasynUser is invalid
|
||||
//
|
||||
//! param[in] pasynUser asynUser structure that encodes the axis index number
|
||||
////////////////////////////////////////
|
||||
ImsMDrivePlusMotorAxis* ImsMDrivePlusMotorController::getAxis(asynUser *pasynUser)
|
||||
{
|
||||
int axisNo;
|
||||
|
||||
getAddress(pasynUser, &axisNo);
|
||||
return getAxis(axisNo);
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
//! getAxis()
|
||||
//! Override asynMotorController function to return pointer to IMS axis object
|
||||
//
|
||||
//! Returns a pointer to an ImsMDrivePlusAxis object.
|
||||
//! Returns NULL if the axis number is invalid.
|
||||
//
|
||||
//! param[in] axisNo Axis index number
|
||||
////////////////////////////////////////
|
||||
ImsMDrivePlusMotorAxis* ImsMDrivePlusMotorController::getAxis(int axisNo)
|
||||
{
|
||||
if ((axisNo < 0) || (axisNo >= numAxes_)) return NULL;
|
||||
return pAxes_[axisNo];
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
//! writeInt32()
|
||||
//! Override asynMotorController function to add hooks to IMS records
|
||||
// Based on XPSController.cpp
|
||||
//
|
||||
//! param[in] pointer to asynUser object
|
||||
//! param[in] value to pass to function
|
||||
////////////////////////////////////////
|
||||
asynStatus ImsMDrivePlusMotorController::writeInt32(asynUser *pasynUser, epicsInt32 value)
|
||||
{
|
||||
int reason = pasynUser->reason;
|
||||
int status = asynSuccess;
|
||||
ImsMDrivePlusMotorAxis *pAxis;
|
||||
static const char *functionName = "writeInt32";
|
||||
|
||||
pAxis = this->getAxis(pasynUser);
|
||||
if (!pAxis) return asynError;
|
||||
|
||||
asynPrint(pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: function=%s, val=%d\n", DRIVER_NAME, functionName, value);
|
||||
|
||||
// Set the parameter and readback in the parameter library. This may be overwritten when we read back the
|
||||
// status at the end, but that's OK
|
||||
status = pAxis->setIntegerParam(reason, value);
|
||||
|
||||
if (reason == ImsMDrivePlusSaveToNVM_) {
|
||||
if (value == 1) { // save current user parameters to NVM
|
||||
status = pAxis->saveToNVM();
|
||||
if (status) asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: ERROR saving to NVM\n", DRIVER_NAME, functionName);
|
||||
else asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: Successfully saved to NVM\n", DRIVER_NAME, functionName);
|
||||
} else {
|
||||
asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: ERROR, value of 1 to save to NVM\n", DRIVER_NAME, functionName);
|
||||
}
|
||||
} else { // call base class method to continue handling
|
||||
status = asynMotorController::writeInt32(pasynUser, value);
|
||||
}
|
||||
|
||||
callParamCallbacks(pAxis->axisNo_);
|
||||
return (asynStatus)status;
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
//! writeController()
|
||||
//! reference ACRMotorDriver
|
||||
//
|
||||
//! Writes a string to the IMS controller.
|
||||
//! Prepends deviceName to command string, if party mode not enabled, set device name to ""
|
||||
//! @param[in] output the string to be written.
|
||||
//! @param[in] timeout Timeout before returning an error.
|
||||
////////////////////////////////////////
|
||||
asynStatus ImsMDrivePlusMotorController::writeController(const char *output, double timeout)
|
||||
{
|
||||
size_t nwrite;
|
||||
asynStatus status;
|
||||
char outbuff[MAX_BUFF_LEN];
|
||||
static const char *functionName = "writeController()";
|
||||
|
||||
// in party-mode Line Feed must follow command string
|
||||
sprintf(outbuff, "%s%s", deviceName, output);
|
||||
asynPrint(pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: deviceName=%s, command=%s\n", DRIVER_NAME, functionName, deviceName, outbuff);
|
||||
status = pasynOctetSyncIO->write(pAsynUserIMS, outbuff, strlen(outbuff), timeout, &nwrite);
|
||||
if (status) { // update comm flag
|
||||
setIntegerParam(this->motorStatusCommsError_, 1);
|
||||
}
|
||||
return status ;
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
//! writeReadController()
|
||||
//! reference ACRMotorDriver
|
||||
//
|
||||
//! Writes a string to the IMS controller and reads a response.
|
||||
//! Prepends deviceName to command string, if party mode not enabled, set device name to ""
|
||||
//! param[in] output Pointer to the output string.
|
||||
//! param[out] input Pointer to the input string location.
|
||||
//! param[in] maxChars Size of the input buffer.
|
||||
//! param[out] nread Number of characters read.
|
||||
//! param[out] timeout Timeout before returning an error.*/
|
||||
////////////////////////////////////////
|
||||
asynStatus ImsMDrivePlusMotorController::writeReadController(const char *output, char *input, size_t maxChars, size_t *nread, double timeout)
|
||||
{
|
||||
size_t nwrite;
|
||||
asynStatus status;
|
||||
int eomReason;
|
||||
char outbuff[MAX_BUFF_LEN];
|
||||
static const char *functionName = "writeReadController()";
|
||||
|
||||
// in party-mode Line Feed must follow command string
|
||||
sprintf(outbuff, "%s%s", deviceName, output);
|
||||
status = pasynOctetSyncIO->writeRead(pAsynUserIMS, outbuff, strlen(outbuff), input, maxChars, timeout, &nwrite, nread, &eomReason);
|
||||
if (status) { // update comm flag
|
||||
setIntegerParam(this->motorStatusCommsError_, 1);
|
||||
}
|
||||
asynPrint(pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: deviceName=%s, command=%s, response=%s\n", DRIVER_NAME, functionName, deviceName, outbuff, input);
|
||||
return status;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// Start code for iocsh Registration :
|
||||
// Available Functions :
|
||||
// ImsMDrivePlusCreateController()
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
//! ImsMDrivePlusCreateController()
|
||||
//! IOCSH function
|
||||
//! Creates a new IMSMDrivePlusController object
|
||||
//
|
||||
//! Configuration command, called directly or from iocsh
|
||||
//! @param[in] motorPortName User-specific name of motor port
|
||||
//! @param[in] IOPortName User-specific name of port that was configured by drvAsynIPPortConfigure()
|
||||
//! @param[in] deviceName Name of device, used to address motor by MCODE in party mode
|
||||
// If not using party mode, config ImsMDrivePlusCreateController() with empty string "" for deviceName
|
||||
//! @param[in] movingPollPeriod time in ms between polls when any axis is moving
|
||||
//! @param[in] idlePollPeriod time in ms between polls when no axis is moving
|
||||
////////////////////////////////////////////////////////
|
||||
extern "C" int ImsMDrivePlusCreateController(const char *motorPortName, const char *IOPortName, char *devName, double movingPollPeriod, double idlePollPeriod)
|
||||
{
|
||||
ImsMDrivePlusMotorController *pImsController;
|
||||
pImsController = new ImsMDrivePlusMotorController(motorPortName, IOPortName, devName, movingPollPeriod/1000., idlePollPeriod/1000.);
|
||||
pImsController = NULL;
|
||||
return(asynSuccess);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// ImsMDrivePlus IOCSH Registration
|
||||
// Copied from ACRMotorDriver.cpp
|
||||
//
|
||||
// Motor port name : user-specified name of port
|
||||
// IO port name : user-specific name of port that was initialized with drvAsynIPPortConfigure()
|
||||
// Device name : name of device, used to address motor by MCODE in party mode
|
||||
// : if not using party mode, config ImsMDrivePlusCreateController() with empty string "" for deviceName
|
||||
// Moving poll period : time in ms between polls when any axis is moving
|
||||
// Idle poll period : time in ms between polls when no axis is moving
|
||||
////////////////////////////////////////////////////////
|
||||
static const iocshArg ImsMDrivePlusCreateControllerArg0 = {"Motor port name", iocshArgString};
|
||||
static const iocshArg ImsMDrivePlusCreateControllerArg1 = {"IO port name", iocshArgString};
|
||||
static const iocshArg ImsMDrivePlusCreateControllerArg2 = {"Device name", iocshArgString};
|
||||
static const iocshArg ImsMDrivePlusCreateControllerArg3 = {"Moving poll period (ms)", iocshArgDouble};
|
||||
static const iocshArg ImsMDrivePlusCreateControllerArg4 = {"Idle poll period (ms)", iocshArgDouble};
|
||||
static const iocshArg * const ImsMDrivePlusCreateControllerArgs[] = {&ImsMDrivePlusCreateControllerArg0,
|
||||
&ImsMDrivePlusCreateControllerArg1,
|
||||
&ImsMDrivePlusCreateControllerArg2,
|
||||
&ImsMDrivePlusCreateControllerArg3,
|
||||
&ImsMDrivePlusCreateControllerArg4};
|
||||
static const iocshFuncDef ImsMDrivePlusCreateControllerDef = {"ImsMDrivePlusCreateController", 5, ImsMDrivePlusCreateControllerArgs};
|
||||
static void ImsMDrivePlusCreateControllerCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
ImsMDrivePlusCreateController(args[0].sval, args[1].sval, args[2].sval, args[3].dval, args[4].dval);
|
||||
}
|
||||
|
||||
static void ImsMDrivePlusMotorRegister(void)
|
||||
{
|
||||
iocshRegister(&ImsMDrivePlusCreateControllerDef, ImsMDrivePlusCreateControllerCallFunc);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
epicsExportRegistrar(ImsMDrivePlusMotorRegister);
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
//! Description : This is the "model 3" asyn motor driver for IMS MDrivePlus.
|
||||
//! Based on HytecMotorDriver.h.
|
||||
//! @ImsMDrivePlus.h
|
||||
|
||||
#ifndef ImsMDrivePlusMotorController_H
|
||||
#define ImsMDrivePlusMotorController_H
|
||||
|
||||
#include "asynMotorController.h"
|
||||
#include "asynMotorAxis.h"
|
||||
#include "ImsMDrivePlusMotorAxis.h"
|
||||
|
||||
////////////////////////////////////
|
||||
// ImsMDrivePlusMotorController class
|
||||
//! derived from asynMotorController class
|
||||
////////////////////////////////////
|
||||
class epicsShareClass ImsMDrivePlusMotorController : public asynMotorController
|
||||
{
|
||||
public:
|
||||
/////////////////////////////////////////
|
||||
// Override asynMotorController functions
|
||||
/////////////////////////////////////////
|
||||
ImsMDrivePlusMotorController(const char *motorPortName, const char *IOPortName, const char *deviceName, double movingPollPeriod, double idlePollPeriod);
|
||||
ImsMDrivePlusMotorAxis* getAxis(asynUser *pasynUser);
|
||||
ImsMDrivePlusMotorAxis* getAxis(int axisNo);
|
||||
//void report(FILE *fp, int level);
|
||||
asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
|
||||
|
||||
/////////////////////////////////////////
|
||||
// IMS specific functions
|
||||
/////////////////////////////////////////
|
||||
asynStatus writeReadController(const char *output, char *input, size_t maxChars, size_t *nread, double timeout);
|
||||
asynStatus writeController(const char *output, double timeout);
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
ImsMDrivePlusMotorAxis **pAxes_; // Array of pointers to axis objects
|
||||
|
||||
///////////////////////////////////////////
|
||||
// IMS MDrivePlus controller function codes
|
||||
///////////////////////////////////////////
|
||||
|
||||
//! extra parameters that the ImsMDrivePlus controller supports
|
||||
int ImsMDrivePlusLoadMCode_; //! Load MCode string, NOT SUPPORTED YET
|
||||
int ImsMDrivePlusClearMCode_; //! Clear program buffer, NOT SUPPORTED YET
|
||||
int ImsMDrivePlusSaveToNVM_; //! Store current user variables and flags to nonvolatile ram
|
||||
#define FIRST_IMS_PARAM ImsMDrivePlusLoadMCode_
|
||||
#define LAST_IMS_PARAM ImsMDrivePlusSaveToNVM_
|
||||
#define NUM_IMS_PARAMS (&LAST_IMS_PARAM - &FIRST_IMS_PARAM + 1)
|
||||
|
||||
private:
|
||||
// drvInfo strings for extra parameters that the ImsMDrivePlus controller supports
|
||||
#define ImsMDrivePlusLoadMCodeControlString "IMS_LOADMCODE" // NOT SUPPORTED YET
|
||||
#define ImsMDrivePlusClearMCodeControlString "IMS_CLEARMCODE" // NOT SUPPORTED YET
|
||||
#define ImsMDrivePlusSaveToNVMControlString "IMS_SAVETONVM"
|
||||
|
||||
asynUser *pAsynUserIMS;
|
||||
char motorName[MAX_NAME_LEN];
|
||||
char deviceName[MAX_NAME_LEN];
|
||||
int homeSwitchInput;
|
||||
int posLimitSwitchInput;
|
||||
int negLimitSwitchInput;
|
||||
|
||||
void initController(const char *devName, double movingPollPeriod, double idlePollPeriod);
|
||||
int readHomeAndLimitConfig(); // read home, positive limit, and neg limit switch configuration from controller (S1-S4 settings)
|
||||
|
||||
friend class ImsMDrivePlusMotorAxis;
|
||||
};
|
||||
//! iocsh function to create controller object
|
||||
//! NOTE: drvAsynIPPortConfigure() must be called first
|
||||
//extern "C" int ImsMDrivePlusCreateController(const char *motorPortName, const char *IOPortName, char *devName, double movingPollPeriod, double idlePollPeriod);
|
||||
|
||||
#endif // ImsMDrivePlusMotorController_H
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
FILENAME... ImsRegister.cc
|
||||
USAGE... Register IMS motor device driver shell commands.
|
||||
|
||||
*/
|
||||
|
||||
/*****************************************************************
|
||||
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 <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <iocsh.h>
|
||||
#include "motor.h"
|
||||
#include "drvIM483.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
// Ims Setup arguments
|
||||
static const iocshArg setupArg0 = {"Max. controller count", iocshArgInt};
|
||||
static const iocshArg setupArg1 = {"Polling rate", iocshArgInt};
|
||||
// Ims Config arguments
|
||||
static const iocshArg configArg0 = {"Card being configured", iocshArgInt};
|
||||
static const iocshArg configArg1 = {"asyn port name", iocshArgString};
|
||||
|
||||
|
||||
static const iocshArg * const IM483SetupArgs[2] = {&setupArg0, &setupArg1};
|
||||
static const iocshArg * const IM483ConfigArgs[2] = {&configArg0, &configArg1};
|
||||
|
||||
static const iocshFuncDef setupIM483SM = {"IM483SMSetup", 2, IM483SetupArgs};
|
||||
static const iocshFuncDef setupIM483PL = {"IM483PLSetup", 2, IM483SetupArgs};
|
||||
static const iocshFuncDef setupMDrive = {"MDriveSetup", 2, IM483SetupArgs};
|
||||
|
||||
static const iocshFuncDef configIM483SM = {"IM483SMConfig", 2, IM483ConfigArgs};
|
||||
static const iocshFuncDef configIM483PL = {"IM483PLConfig", 2, IM483ConfigArgs};
|
||||
static const iocshFuncDef configMDrive = {"MDriveConfig", 2, IM483ConfigArgs};
|
||||
|
||||
static void setupSMCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
IM483SMSetup(args[0].ival, args[1].ival);
|
||||
}
|
||||
static void setupPLCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
IM483PLSetup(args[0].ival, args[1].ival);
|
||||
}
|
||||
static void setupMDriveCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
MDriveSetup(args[0].ival, args[1].ival);
|
||||
}
|
||||
|
||||
|
||||
static void configSMCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
IM483SMConfig(args[0].ival, args[1].sval);
|
||||
}
|
||||
static void configPLCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
IM483PLConfig(args[0].ival, args[1].sval);
|
||||
}
|
||||
static void configMDriveCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
MDriveConfig(args[0].ival, args[1].sval);
|
||||
}
|
||||
|
||||
|
||||
static void IMSmotorRegister(void)
|
||||
{
|
||||
iocshRegister(&setupIM483SM, setupSMCallFunc);
|
||||
iocshRegister(&setupIM483PL, setupPLCallFunc);
|
||||
iocshRegister(&setupMDrive, setupMDriveCallFunc);
|
||||
|
||||
iocshRegister(&configIM483SM, configSMCallFunc);
|
||||
iocshRegister(&configIM483PL, configPLCallFunc);
|
||||
iocshRegister(&configMDrive, configMDriveCallFunc);
|
||||
}
|
||||
|
||||
epicsExportRegistrar(IMSmotorRegister);
|
||||
|
||||
} // extern "C"
|
||||
@@ -1,24 +0,0 @@
|
||||
# Makefile
|
||||
TOP = ../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
# The following are used for debugging messages.
|
||||
#USR_CXXFLAGS += -DDEBUG
|
||||
|
||||
DBD += devImsMotor.dbd
|
||||
|
||||
LIBRARY_IOC = Ims
|
||||
|
||||
# Intelligent Motion Systems driver support.
|
||||
SRCS += ImsRegister.cc
|
||||
SRCS += devIM483SM.cc drvIM483SM.cc
|
||||
SRCS += devIM483PL.cc drvIM483PL.cc
|
||||
SRCS += devMDrive.cc drvMDrive.cc
|
||||
# model3 driver
|
||||
SRCS += ImsMDrivePlusMotorController.cpp ImsMDrivePlusMotorAxis.cpp
|
||||
|
||||
Ims_LIBS += motor asyn
|
||||
Ims_LIBS += $(EPICS_BASE_IOC_LIBS)
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
|
||||
MDrive 17,23,34
|
||||
==============================================================================
|
||||
|
||||
Configuring MDrive for party mode.
|
||||
----------------------------------
|
||||
|
||||
The following is a modified version of the procedure defined in Section 2.1,
|
||||
"Multiple MDrive Motion Control System (Party Mode)", page #27, of the
|
||||
"MDrive Motion Control" manual; Revision 01.24.2005.
|
||||
|
||||
1. Connect the first MDrive Motion Control to the Host PC configured for Single
|
||||
Mode Operation. Since the MDrive uses RS-485, a RS-232 to RS-485
|
||||
converter is required for a Host PC to MDrive communication connection.
|
||||
Set the PC Host RS-232 port characteristics to 9600 Baud, 8 Data bits,
|
||||
1 Stop bit, No parity, No flow control.
|
||||
|
||||
2. Establish communications.
|
||||
If you can see the sign-on message "Copyright 2001-2003 by Intelligent
|
||||
Motion Systems, Inc.", then you are up and running. If the sign-on
|
||||
message does not appear, try using a software reset. Hold down the
|
||||
"Ctrl" key and press "C" (^C). If the sign-on message still does not
|
||||
appear then there may be a problem with either the connections,
|
||||
hardware or software configuration of the MDrive Motion Control or
|
||||
Host.
|
||||
|
||||
3. Using the command DN, name the MDrive Motion Control. This must be a number
|
||||
1-9. (DN="1"{enter}). Label the motor with the device name.
|
||||
|
||||
4. Define the plus, minus and home limit switches, if available, using the
|
||||
S<1-4> command.
|
||||
|
||||
5. Set Echo Mode EM=2{enter}.
|
||||
|
||||
6. Set the party flag PY=1{enter}.
|
||||
|
||||
7. Type CTRL+J to activate the Party Mode.
|
||||
|
||||
8. Type {DN}S CTRL+J where {DN} is the device name given in step #3 (Save
|
||||
device name, Echo mode, I/O Setup and Party Mode).
|
||||
|
||||
9. Remove power.
|
||||
|
||||
10. Repeat steps 1 through 8 for each additional MDrive in the system using
|
||||
successive device names "1" through "9".
|
||||
|
||||
|
||||
IMPORTANT NOTES
|
||||
---------------
|
||||
|
||||
1. The EPICS motor record does NOT support the optional encoder mode. The
|
||||
Encoder Enable Flag (EE) must be 0.
|
||||
|
||||
|
||||
MDrive Facts
|
||||
------------
|
||||
|
||||
* Default step resolution - 51,200 steps / 1 motor revolution (MS=256).
|
||||
* Encoder resolution - 2048 ticks / 1 motor revolution.
|
||||
|
||||
|
||||
IM483
|
||||
==============================================================================
|
||||
|
||||
|
||||
Configuring IM483's for Party Mode communication.
|
||||
------------------------------------------------
|
||||
|
||||
The following is a modified version of the procedure defined in the
|
||||
"Communication" section, page #9, of the "Software Reference Manual"; Revision
|
||||
051794.
|
||||
|
||||
1) Power down the IM483's. Configure each IM483 module for stand alone (single
|
||||
mode) communications. For the APS Diviero chassis, jumper J4 for Stand Alone
|
||||
(1-2) and J7 for Master (1-2).
|
||||
|
||||
2) Connect an RS-232 serial connection (9600,none,8,1) from a host terminal to
|
||||
the first ("A") controller's front panel connector.
|
||||
|
||||
3) Power up the IM483.
|
||||
|
||||
4) Assign the axis name. At the host terminal, enter the letter "A" (0x41)
|
||||
followed by a space charter (0x20).
|
||||
|
||||
5) The IM483 controller should respond by echoing the "A" character and the
|
||||
sign-on message at the host terminal, i.e.,
|
||||
|
||||
A 1342 4038 ADVANCED MICRO SYSTEMS, INC
|
||||
MAX-2000 v1.15i
|
||||
|
||||
6) Change the "Limit Polarity" to active high. At the host terminal, enter
|
||||
"l1<enter>". The IM483 should echo your input at the host terminal.
|
||||
|
||||
7) Store parameters. At the host terminal, enter "S<enter>".
|
||||
|
||||
8) Move RS-232 connection to next controller. At the host terminal, enter
|
||||
control c (^c). Repeat steps 4 through 7 while incrementing the axis name
|
||||
(i.e, A, B, C, etc.).
|
||||
|
||||
9) Set all the IM483's for Party Mode communication. For the APS Diviero
|
||||
chassis, configure all of the IM483's for Party Mode by setting J4 to (2-3).
|
||||
Connect the RS-232 port to the Master IM483 and set all the remaining IM483's
|
||||
to Slave mode by setting J7 to (2-3).
|
||||
|
||||
|
||||
SSCAN THROUGH-PUT NOTES
|
||||
-----------------------
|
||||
|
||||
The following results where done using the following;
|
||||
|
||||
- SSCAN support module R2.5.1, with scan.db configured as follows;
|
||||
- Read(R1PV) set to "time".
|
||||
- Drive(P1PV) set to motor record VAL field.
|
||||
- #PTS(NPTS) set to 1000.
|
||||
- Step Size(P1SI) set to MRES of the motor record.
|
||||
- Motor record R5.7; configured as follows;
|
||||
- Set slew velocity (VELO) 5.0 = (500 * MRES).
|
||||
- Set base velocity (VBAS) 4.9 = (499 * MRES).
|
||||
- Acceleration time (ACCL) does not matter.
|
||||
- EPICS base R3.14.7
|
||||
- WRS Tornado 2.2.1
|
||||
- MVME5100
|
||||
- IM483SM with firmware version; 1342 4038 AMS, INC MAX-2000 v1.15i
|
||||
|
||||
|
||||
Controller | sysClkRate | polling rate | ms/scan pt.
|
||||
-----------|------------|--------------|------------
|
||||
IMS483SM | 60 | 1HZ | 121.0
|
||||
-----------|------------|--------------|------------
|
||||
IMS483SM | 60 | 1HZ | 834.2 with VBAS/VELO removed
|
||||
-----------|------------|--------------|------------
|
||||
IMS483SM | 60 | 10HZ | 121.0
|
||||
-----------|------------|--------------|------------
|
||||
IMS483SM | 60 | 10HZ | 154.4 with VBAS/VELO removed
|
||||
-----------|------------|--------------|------------
|
||||
IMS483SM | 60 | 60HZ | 121.0
|
||||
-----------|------------|--------------|------------
|
||||
IMS483SM | 60 | 60HZ | 102.9 with VBAS/VELO removed
|
||||
-----------|------------|--------------|------------
|
||||
IMS483SM | 100 | 1HZ | 121.0
|
||||
-----------|------------|--------------|------------
|
||||
IMS483SM | 1000 | 1000HZ | 121.0
|
||||
-----------|------------|--------------|------------
|
||||
IMS483SM | 1000 | 1000HZ | 96.0 with VBAS/VELO removed
|
||||
|
||||
|
||||
KNOWN PROBLEMS
|
||||
==============
|
||||
|
||||
- The MDrive overshoots the target position on small moves when the velocity
|
||||
base is large.
|
||||
@@ -1,120 +0,0 @@
|
||||
=======================================
|
||||
ImsMDrivePlusMotor AsynPortDriver Notes
|
||||
|
||||
Author: Nia Fong, SLAC National Laboratory
|
||||
|
||||
First Initial: 10/28/2011
|
||||
|
||||
This driver is based on "model 3" motor asyn port driver defined by Mark Rivers.
|
||||
The driver assumes:
|
||||
1) each controller is configured for a single axis
|
||||
2) the motor is set up for Party Mode (PY=1) and Echo Mode 2 (EM=2)
|
||||
=======================================
|
||||
|
||||
=======================
|
||||
Building an Application
|
||||
=======================
|
||||
Building the driver into an application requires
|
||||
|
||||
1) The 'asyn', 'motor' and 'ImsMDrivePlusMotor' (this driver) packages/modules to be built and
|
||||
installed. The application's RELEASE file must point to these packages so that the build process
|
||||
locates headers and libraries etc.
|
||||
|
||||
2) The application's '.dbd' file must contain
|
||||
- motorSupport.dbd
|
||||
- drvAsynIPPort.dbd (or equivalent 'asyn' port driver package)
|
||||
- devImsMotor.dbd (this include support for all Ims drivers)
|
||||
|
||||
These '.dbd' files are best listed in the application src/Makefile:
|
||||
<app>_DBD += motorSupport.dbd
|
||||
<app>_DBD += devImsMotor.dbd
|
||||
<app>_DBD += drvAsynIPPort.dbd
|
||||
|
||||
3) The application must be linked against the 'Ims', 'motor' and 'asyn'
|
||||
libraries, e.g.,
|
||||
|
||||
<app>_LIBS += Ims motor asyn
|
||||
|
||||
===============
|
||||
Custom Database
|
||||
===============
|
||||
There is an IMS specific IMS_extra.db database that contains IMS-specific records.
|
||||
|
||||
IMS_S: saves user variables and flags to controller's NVM
|
||||
|
||||
record(bo,"$(DEV):$(AREA):$(LOC):IMS_S") {
|
||||
field(DESC, "Ims Save to NVM")
|
||||
field(PINI, "NO")
|
||||
field(DTYP, "asynInt32")
|
||||
field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))IMS_SAVETONVM")
|
||||
field(VAL, "0")
|
||||
field(ZNAM, "No")
|
||||
field(ONAM, "Yes")
|
||||
}
|
||||
|
||||
=============================
|
||||
Driver Run-Time Configuration
|
||||
=============================
|
||||
To configure the motor from the iocsh or a C/C++ application
|
||||
|
||||
1) Create and configure an asyn IP port
|
||||
|
||||
drvAsynIPPortConfigure(IOPortName, port, priority, disable auto-connect, no process EOS)
|
||||
motorPortName: name string assigned to the asyn IP port being created
|
||||
port: host:port [protocol]
|
||||
|
||||
2) Create and configure an ImsMDrivePlus controller
|
||||
|
||||
ImsMDrivePlusCreateController(motorPortName, IOPortName, devName, movingPollPeriod, idlePollPeriod)
|
||||
motorPortName: name string assigned to the controller
|
||||
IOPortName: name of asyn IO port that was created by drvAsynIPPortConfigure()
|
||||
devName: unique device name used to address the motor in MCode
|
||||
NOTE: the device name is prepended to all MCode commands to communicate in Party Mode.
|
||||
See README file for more information.
|
||||
movingPollPeriod: time in milliseconds between polls when axis is moving
|
||||
idlePollPeriod: time in milliseconds between polls when axis is not moving
|
||||
|
||||
=========================
|
||||
Example iocsh st.cmd file
|
||||
=========================
|
||||
#!../../bin/linux-x86/ImsMDrivePlus
|
||||
|
||||
# Register all support components
|
||||
dbLoadDatabase("dbd/ImsMDrivePlus.dbd")
|
||||
ImsMDrivePlus_registerRecordDeviceDriver pdbbase
|
||||
|
||||
# Motors substitutions, customize this for your motor
|
||||
dbLoadTemplate "db/motor.mdriveplus.substitutions"
|
||||
|
||||
# Configure asyn communication port, first
|
||||
drvAsynIPPortConfigure("M06", "ts-b34-nw08:2101", 0, 0, 0 )
|
||||
|
||||
# Configure one controller per motor, each controller just has 1 axis
|
||||
ImsMDrivePlusCreateController("IMS1", "M06", "1", 200, 5000)
|
||||
|
||||
# Optional: Enable tracing
|
||||
asynSetTraceIOMask("IMS1",0,2)
|
||||
asynSetTraceMask("IMS1",0,2)
|
||||
|
||||
=============================================
|
||||
Example application database substitions file
|
||||
=============================================
|
||||
Db/motor.mdrive.substitutions file:
|
||||
|
||||
file "$(MOTOR)/db/basic_asyn_motor.db"
|
||||
{
|
||||
pattern
|
||||
{P, N, M, DTYP, PORT, DESC, ADDR, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, DHLM, DLLM, MRES, PREC, INIT}
|
||||
{IMS:, 1, 1, "asynMotor", IMS1, "MDrive23", 0, mm, Pos, 3.0, 0.017, 2.0, 0, 0.017, 2.0, 100, -100, 0.000083, 4, ""}
|
||||
}
|
||||
|
||||
file "$(MOTOR)/db/IMS_extra.db"
|
||||
{
|
||||
pattern
|
||||
{DEV, AREA, LOC, PORT, ADDR, TIMEOUT}
|
||||
{ims, IOC, 1, IMS1, 0, 1}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,315 +0,0 @@
|
||||
/*
|
||||
FILENAME... devIM483PL.cc
|
||||
USAGE... Motor record device level support for Intelligent Motion
|
||||
Systems, Inc. IM483(I/IE).
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* Original Author: Ron Sluiter
|
||||
* Date: 07/10/2000
|
||||
*
|
||||
* 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:
|
||||
* -----------------
|
||||
* .01 07/10/00 rls copied from devIM483SM.c
|
||||
* .02 05/16/01 rls Added support for changing jog velocity while jogging.
|
||||
* .03 03/01/02 rls eliminated "ASCII record separator (IS2) = /x1E".
|
||||
* .04 04/15/02 rls Must support PRIMITIVE in build_trans() for INIT field to
|
||||
* work, and add axis name place holder (?) to message.
|
||||
* .05 03/07/03 rls R3.14 conversion.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errlog.h>
|
||||
#include "motorRecord.h"
|
||||
#include "motor.h"
|
||||
#include "motordevCom.h"
|
||||
#include "drvIM483.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
#define STATIC static
|
||||
|
||||
extern struct driver_table IM483PL_access;
|
||||
|
||||
/* ----------------Create the dsets for devIM483PL----------------- */
|
||||
STATIC struct driver_table *drvtabptr;
|
||||
STATIC long IM483PL_init(int);
|
||||
STATIC long IM483PL_init_record(void *);
|
||||
STATIC long IM483PL_start_trans(struct motorRecord *);
|
||||
STATIC RTN_STATUS IM483PL_build_trans(motor_cmnd, double *, struct motorRecord *);
|
||||
STATIC RTN_STATUS IM483PL_end_trans(struct motorRecord *);
|
||||
|
||||
struct motor_dset devIM483PL =
|
||||
{
|
||||
{8, NULL, (DEVSUPFUN) IM483PL_init, (DEVSUPFUN) IM483PL_init_record, NULL},
|
||||
motor_update_values,
|
||||
IM483PL_start_trans,
|
||||
IM483PL_build_trans,
|
||||
IM483PL_end_trans
|
||||
};
|
||||
|
||||
extern "C" {epicsExportAddress(dset,devIM483PL);}
|
||||
|
||||
/* --------------------------- program data --------------------- */
|
||||
|
||||
/* This table is used to define the command types */
|
||||
/* WARNING! this must match "motor_cmnd" in motor.h */
|
||||
|
||||
static msg_types IM483PL_table[] = {
|
||||
MOTION, /* MOVE_ABS */
|
||||
MOTION, /* MOVE_REL */
|
||||
MOTION, /* HOME_FOR */
|
||||
MOTION, /* HOME_REV */
|
||||
IMMEDIATE, /* LOAD_POS */
|
||||
IMMEDIATE, /* SET_VEL_BASE */
|
||||
IMMEDIATE, /* SET_VELOCITY */
|
||||
IMMEDIATE, /* SET_ACCEL */
|
||||
IMMEDIATE, /* GO */
|
||||
IMMEDIATE, /* SET_ENC_RATIO */
|
||||
INFO, /* GET_INFO */
|
||||
MOVE_TERM, /* STOP_AXIS */
|
||||
VELOCITY, /* JOG */
|
||||
IMMEDIATE, /* SET_PGAIN */
|
||||
IMMEDIATE, /* SET_IGAIN */
|
||||
IMMEDIATE, /* SET_DGAIN */
|
||||
IMMEDIATE, /* ENABLE_TORQUE */
|
||||
IMMEDIATE, /* DISABL_TORQUE */
|
||||
IMMEDIATE, /* PRIMITIVE */
|
||||
IMMEDIATE, /* SET_HIGH_LIMIT */
|
||||
IMMEDIATE, /* SET_LOW_LIMIT */
|
||||
VELOCITY /* JOG_VELOCITY */
|
||||
};
|
||||
|
||||
|
||||
static struct board_stat **IM483PL_cards;
|
||||
|
||||
/* --------------------------- program data --------------------- */
|
||||
|
||||
|
||||
/* initialize device support for IM483PL stepper motor */
|
||||
STATIC long IM483PL_init(int after)
|
||||
{
|
||||
long rtnval;
|
||||
|
||||
if (!after)
|
||||
{
|
||||
drvtabptr = &IM483PL_access;
|
||||
(drvtabptr->init)();
|
||||
}
|
||||
|
||||
rtnval = motor_init_com(after, *drvtabptr->cardcnt_ptr, drvtabptr, &IM483PL_cards);
|
||||
return(rtnval);
|
||||
}
|
||||
|
||||
|
||||
/* initialize a record instance */
|
||||
STATIC long IM483PL_init_record(void *arg)
|
||||
{
|
||||
struct motorRecord *mr = (struct motorRecord *) arg;
|
||||
return(motor_init_record_com(mr, *drvtabptr->cardcnt_ptr, drvtabptr, IM483PL_cards));
|
||||
}
|
||||
|
||||
|
||||
/* start building a transaction */
|
||||
STATIC long IM483PL_start_trans(struct motorRecord *mr)
|
||||
{
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
/* end building a transaction */
|
||||
STATIC RTN_STATUS IM483PL_end_trans(struct motorRecord *mr)
|
||||
{
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
/* add a part to the transaction */
|
||||
STATIC RTN_STATUS IM483PL_build_trans(motor_cmnd command, double *parms, struct motorRecord *mr)
|
||||
{
|
||||
struct motor_trans *trans = (struct motor_trans *) mr->dpvt;
|
||||
struct mess_node *motor_call;
|
||||
struct controller *brdptr;
|
||||
struct IM483controller *cntrl;
|
||||
char buff[110];
|
||||
int axis, card, maxdigits;
|
||||
unsigned int size;
|
||||
double dval, cntrl_units;
|
||||
RTN_STATUS rtnval;
|
||||
bool send;
|
||||
|
||||
send = true; /* Default to send motor command. */
|
||||
rtnval = OK;
|
||||
buff[0] = '\0';
|
||||
|
||||
/* Protect against NULL pointer with WRTITE_MSG(GO/STOP_AXIS/GET_INFO, NULL). */
|
||||
dval = (parms == NULL) ? 0.0 : *parms;
|
||||
|
||||
motor_start_trans_com(mr, IM483PL_cards);
|
||||
|
||||
motor_call = &(trans->motor_call);
|
||||
card = motor_call->card;
|
||||
axis = motor_call->signal + 1;
|
||||
brdptr = (*trans->tabptr->card_array)[card];
|
||||
if (brdptr == NULL)
|
||||
return(rtnval = ERROR);
|
||||
|
||||
cntrl = (struct IM483controller *) brdptr->DevicePrivate;
|
||||
cntrl_units = dval;
|
||||
maxdigits = 2;
|
||||
|
||||
if (IM483PL_table[command] > motor_call->type)
|
||||
motor_call->type = IM483PL_table[command];
|
||||
|
||||
if (trans->state != BUILD_STATE)
|
||||
return(rtnval = ERROR);
|
||||
|
||||
if (command == PRIMITIVE && mr->init != NULL && strlen(mr->init) != 0)
|
||||
{
|
||||
strcat(motor_call->message, "? ");
|
||||
strcat(motor_call->message, mr->init);
|
||||
}
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case MOVE_ABS:
|
||||
case MOVE_REL:
|
||||
case HOME_FOR:
|
||||
case HOME_REV:
|
||||
case JOG:
|
||||
if (strlen(mr->prem) != 0)
|
||||
{
|
||||
strcat(motor_call->message, mr->prem);
|
||||
strcat(motor_call->message, ";");
|
||||
}
|
||||
if (strlen(mr->post) != 0)
|
||||
motor_call->postmsgptr = (char *) &mr->post;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case MOVE_ABS:
|
||||
sprintf(buff, " R%.*f", maxdigits, cntrl_units);
|
||||
break;
|
||||
|
||||
case MOVE_REL:
|
||||
sprintf(buff, " %+.*f", maxdigits, cntrl_units);
|
||||
break;
|
||||
|
||||
case HOME_FOR:
|
||||
sprintf(buff, " F1000 0");
|
||||
break;
|
||||
|
||||
case HOME_REV:
|
||||
sprintf(buff, " F1000 1");
|
||||
break;
|
||||
|
||||
case LOAD_POS:
|
||||
if (cntrl_units == 0.0)
|
||||
sprintf(buff, " O");
|
||||
else
|
||||
{
|
||||
send = false;
|
||||
rtnval = ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case SET_VEL_BASE:
|
||||
sprintf(buff, " I%.*f;", maxdigits, cntrl_units);
|
||||
break;
|
||||
|
||||
case SET_VELOCITY:
|
||||
sprintf(buff, " V%.*f;", maxdigits, cntrl_units);
|
||||
break;
|
||||
|
||||
case SET_ACCEL:
|
||||
/* ????? MAKE SENSE OF ACCELERATION PARAMETER ??????*/
|
||||
send = false;
|
||||
break;
|
||||
|
||||
case GO:
|
||||
/* The IM483PL starts moving immediately on move commands, GO command
|
||||
* does nothing. */
|
||||
send = false;
|
||||
break;
|
||||
|
||||
case PRIMITIVE:
|
||||
case GET_INFO:
|
||||
/* These commands are not actually done by sending a message, but
|
||||
rather they will indirectly cause the driver to read the status
|
||||
of all motors */
|
||||
break;
|
||||
|
||||
case STOP_AXIS:
|
||||
sprintf(buff, " @ 0");
|
||||
break;
|
||||
|
||||
case JOG_VELOCITY:
|
||||
case JOG:
|
||||
sprintf(buff, " M%.*f;", maxdigits, cntrl_units);
|
||||
break;
|
||||
|
||||
case SET_PGAIN:
|
||||
case SET_IGAIN:
|
||||
case SET_DGAIN:
|
||||
send = false;
|
||||
break;
|
||||
|
||||
case ENABLE_TORQUE:
|
||||
sprintf(buff, " MO;");
|
||||
break;
|
||||
|
||||
case DISABL_TORQUE:
|
||||
sprintf(buff, " MF;");
|
||||
break;
|
||||
|
||||
case SET_HIGH_LIMIT:
|
||||
case SET_LOW_LIMIT:
|
||||
case SET_ENC_RATIO:
|
||||
trans->state = IDLE_STATE; /* No command sent to the controller. */
|
||||
send = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
send = false;
|
||||
rtnval = ERROR;
|
||||
}
|
||||
|
||||
size = strlen(buff);
|
||||
if (send == false)
|
||||
return(rtnval);
|
||||
else if (size > sizeof(buff) || (strlen(motor_call->message) + size) > MAX_MSG_SIZE)
|
||||
errlogMessage("IM483PL_build_trans(): buffer overflow.\n");
|
||||
else
|
||||
{
|
||||
strcat(motor_call->message, buff);
|
||||
motor_end_trans_com(mr, drvtabptr);
|
||||
}
|
||||
return(rtnval);
|
||||
}
|
||||
@@ -1,312 +0,0 @@
|
||||
/*
|
||||
FILENAME... devIM483SM.cc
|
||||
USAGE... Motor record device level support for Intelligent Motion
|
||||
Systems, Inc. IM483(I/IE).
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* Original Author: Ron Sluiter
|
||||
* Date: 02/10/2000
|
||||
*
|
||||
* 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:
|
||||
* -----------------
|
||||
* .01 02/10/00 rls copied from devMM4000.c
|
||||
* .02 05/16/01 rls Added support for changing jog velocity while jogging.
|
||||
* .03 03/01/02 rls eliminated "ASCII record separator (IS2) = /x1E".
|
||||
* .04 04/15/02 rls Must support PRIMITIVE in build_trans() for INIT field to
|
||||
* work.
|
||||
* .05 03/07/03 rls R3.14 conversion.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errlog.h>
|
||||
#include "motorRecord.h"
|
||||
#include "motor.h"
|
||||
#include "motordevCom.h"
|
||||
#include "drvIM483.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
#define STATIC static
|
||||
|
||||
extern struct driver_table IM483SM_access;
|
||||
|
||||
/* ----------------Create the dsets for devIM483SM----------------- */
|
||||
STATIC struct driver_table *drvtabptr;
|
||||
STATIC long IM483SM_init(int);
|
||||
STATIC long IM483SM_init_record(void *);
|
||||
STATIC long IM483SM_start_trans(struct motorRecord *);
|
||||
STATIC RTN_STATUS IM483SM_build_trans(motor_cmnd, double *, struct motorRecord *);
|
||||
STATIC RTN_STATUS IM483SM_end_trans(struct motorRecord *);
|
||||
|
||||
struct motor_dset devIM483SM =
|
||||
{
|
||||
{8, NULL, (DEVSUPFUN) IM483SM_init, (DEVSUPFUN) IM483SM_init_record, NULL},
|
||||
motor_update_values,
|
||||
IM483SM_start_trans,
|
||||
IM483SM_build_trans,
|
||||
IM483SM_end_trans
|
||||
};
|
||||
|
||||
extern "C" {epicsExportAddress(dset,devIM483SM);}
|
||||
|
||||
/* --------------------------- program data --------------------- */
|
||||
|
||||
/* This table is used to define the command types */
|
||||
/* WARNING! this must match "motor_cmnd" in motor.h */
|
||||
|
||||
static msg_types IM483SM_table[] = {
|
||||
MOTION, /* MOVE_ABS */
|
||||
MOTION, /* MOVE_REL */
|
||||
MOTION, /* HOME_FOR */
|
||||
MOTION, /* HOME_REV */
|
||||
IMMEDIATE, /* LOAD_POS */
|
||||
IMMEDIATE, /* SET_VEL_BASE */
|
||||
IMMEDIATE, /* SET_VELOCITY */
|
||||
IMMEDIATE, /* SET_ACCEL */
|
||||
IMMEDIATE, /* GO */
|
||||
IMMEDIATE, /* SET_ENC_RATIO */
|
||||
INFO, /* GET_INFO */
|
||||
MOVE_TERM, /* STOP_AXIS */
|
||||
VELOCITY, /* JOG */
|
||||
IMMEDIATE, /* SET_PGAIN */
|
||||
IMMEDIATE, /* SET_IGAIN */
|
||||
IMMEDIATE, /* SET_DGAIN */
|
||||
IMMEDIATE, /* ENABLE_TORQUE */
|
||||
IMMEDIATE, /* DISABL_TORQUE */
|
||||
IMMEDIATE, /* PRIMITIVE */
|
||||
IMMEDIATE, /* SET_HIGH_LIMIT */
|
||||
IMMEDIATE, /* SET_LOW_LIMIT */
|
||||
VELOCITY /* JOG_VELOCITY */
|
||||
};
|
||||
|
||||
|
||||
static struct board_stat **IM483SM_cards;
|
||||
|
||||
/* --------------------------- program data --------------------- */
|
||||
|
||||
|
||||
/* initialize device support for IM483SM stepper motor */
|
||||
STATIC long IM483SM_init(int after)
|
||||
{
|
||||
long rtnval;
|
||||
|
||||
if (!after)
|
||||
{
|
||||
drvtabptr = &IM483SM_access;
|
||||
(drvtabptr->init)();
|
||||
}
|
||||
|
||||
rtnval = motor_init_com(after, *drvtabptr->cardcnt_ptr, drvtabptr, &IM483SM_cards);
|
||||
return(rtnval);
|
||||
}
|
||||
|
||||
|
||||
/* initialize a record instance */
|
||||
STATIC long IM483SM_init_record(void *arg)
|
||||
{
|
||||
struct motorRecord *mr = (struct motorRecord *) arg;
|
||||
return(motor_init_record_com(mr, *drvtabptr->cardcnt_ptr, drvtabptr, IM483SM_cards));
|
||||
}
|
||||
|
||||
|
||||
/* start building a transaction */
|
||||
STATIC long IM483SM_start_trans(struct motorRecord *mr)
|
||||
{
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
/* end building a transaction */
|
||||
STATIC RTN_STATUS IM483SM_end_trans(struct motorRecord *mr)
|
||||
{
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
/* add a part to the transaction */
|
||||
STATIC RTN_STATUS IM483SM_build_trans(motor_cmnd command, double *parms, struct motorRecord *mr)
|
||||
{
|
||||
struct motor_trans *trans = (struct motor_trans *) mr->dpvt;
|
||||
struct mess_node *motor_call;
|
||||
struct controller *brdptr;
|
||||
struct IM483controller *cntrl;
|
||||
char buff[110];
|
||||
int axis, card, maxdigits;
|
||||
unsigned int size;
|
||||
double dval, cntrl_units;
|
||||
RTN_STATUS rtnval;
|
||||
bool send;
|
||||
|
||||
send = true; /* Default to send motor command. */
|
||||
rtnval = OK;
|
||||
buff[0] = '\0';
|
||||
|
||||
/* Protect against NULL pointer with WRTITE_MSG(GO/STOP_AXIS/GET_INFO, NULL). */
|
||||
dval = (parms == NULL) ? 0.0 : *parms;
|
||||
|
||||
motor_start_trans_com(mr, IM483SM_cards);
|
||||
|
||||
motor_call = &(trans->motor_call);
|
||||
card = motor_call->card;
|
||||
axis = motor_call->signal + 1;
|
||||
brdptr = (*trans->tabptr->card_array)[card];
|
||||
if (brdptr == NULL)
|
||||
return(rtnval = ERROR);
|
||||
|
||||
cntrl = (struct IM483controller *) brdptr->DevicePrivate;
|
||||
cntrl_units = dval;
|
||||
maxdigits = 2;
|
||||
|
||||
if (IM483SM_table[command] > motor_call->type)
|
||||
motor_call->type = IM483SM_table[command];
|
||||
|
||||
if (trans->state != BUILD_STATE)
|
||||
return(rtnval = ERROR);
|
||||
|
||||
if (command == PRIMITIVE && mr->init != NULL && strlen(mr->init) != 0)
|
||||
strcat(motor_call->message, mr->init);
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case MOVE_ABS:
|
||||
case MOVE_REL:
|
||||
case HOME_FOR:
|
||||
case HOME_REV:
|
||||
case JOG:
|
||||
if (strlen(mr->prem) != 0)
|
||||
{
|
||||
strcat(motor_call->message, mr->prem);
|
||||
strcat(motor_call->message, ";");
|
||||
}
|
||||
if (strlen(mr->post) != 0)
|
||||
motor_call->postmsgptr = (char *) &mr->post;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case MOVE_ABS:
|
||||
sprintf(buff, "R%.*f", maxdigits, cntrl_units);
|
||||
break;
|
||||
|
||||
case MOVE_REL:
|
||||
sprintf(buff, "%+.*f", maxdigits, cntrl_units);
|
||||
break;
|
||||
|
||||
case HOME_FOR:
|
||||
sprintf(buff, "F1000 0");
|
||||
break;
|
||||
|
||||
case HOME_REV:
|
||||
sprintf(buff, "F1000 1");
|
||||
break;
|
||||
|
||||
case LOAD_POS:
|
||||
if (cntrl_units == 0.0)
|
||||
sprintf(buff, "O");
|
||||
else
|
||||
{
|
||||
send = false;
|
||||
rtnval = ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case SET_VEL_BASE:
|
||||
sprintf(buff, "I%.*f;", maxdigits, cntrl_units);
|
||||
break;
|
||||
|
||||
case SET_VELOCITY:
|
||||
sprintf(buff, "V%.*f;", maxdigits, cntrl_units);
|
||||
break;
|
||||
|
||||
case SET_ACCEL:
|
||||
/* ????? MAKE SENSE OF ACCELERATION PARAMETER ??????*/
|
||||
send = false;
|
||||
break;
|
||||
|
||||
case GO:
|
||||
/* The IM483 starts moving immediately on move commands, GO command
|
||||
* does nothing. */
|
||||
send = false;
|
||||
break;
|
||||
|
||||
case PRIMITIVE:
|
||||
case GET_INFO:
|
||||
/* These commands are not actually done by sending a message, but
|
||||
rather they will indirectly cause the driver to read the status
|
||||
of all motors */
|
||||
break;
|
||||
|
||||
case STOP_AXIS:
|
||||
sprintf(buff, "@ 0");
|
||||
break;
|
||||
|
||||
case JOG_VELOCITY:
|
||||
case JOG:
|
||||
sprintf(buff, "M%.*f;", maxdigits, cntrl_units);
|
||||
break;
|
||||
|
||||
case SET_PGAIN:
|
||||
case SET_IGAIN:
|
||||
case SET_DGAIN:
|
||||
send = false;
|
||||
break;
|
||||
|
||||
case ENABLE_TORQUE:
|
||||
sprintf(buff, "MO;");
|
||||
break;
|
||||
|
||||
case DISABL_TORQUE:
|
||||
sprintf(buff, "MF;");
|
||||
break;
|
||||
|
||||
case SET_HIGH_LIMIT:
|
||||
case SET_LOW_LIMIT:
|
||||
case SET_ENC_RATIO:
|
||||
trans->state = IDLE_STATE; /* No command sent to the controller. */
|
||||
send = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
send = false;
|
||||
rtnval = ERROR;
|
||||
}
|
||||
|
||||
size = strlen(buff);
|
||||
if (send == false)
|
||||
return(rtnval);
|
||||
else if (size > sizeof(buff) || (strlen(motor_call->message) + size) > MAX_MSG_SIZE)
|
||||
errlogMessage("IM483SM_build_trans(): buffer overflow.\n");
|
||||
else
|
||||
{
|
||||
strcat(motor_call->message, buff);
|
||||
motor_end_trans_com(mr, drvtabptr);
|
||||
}
|
||||
return(rtnval);
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
# Intelligent Motion Systems driver support.
|
||||
device(motor,VME_IO,devIM483SM,"IM483SM")
|
||||
device(motor,VME_IO,devIM483PL,"IM483PL")
|
||||
device(motor,VME_IO,devMDrive, "MDrive")
|
||||
driver(drvIM483SM)
|
||||
driver(drvIM483PL)
|
||||
driver(drvMDrive)
|
||||
registrar(IMSmotorRegister)
|
||||
registrar(ImsMDrivePlusMotorRegister)
|
||||
variable(drvIM483SMdebug)
|
||||
variable(drvIM483PLdebug)
|
||||
variable(drvMDrivedebug)
|
||||
@@ -1,327 +0,0 @@
|
||||
/*
|
||||
FILENAME... devMDrive.cc
|
||||
USAGE... Motor record device level support for Intelligent Motion
|
||||
Systems, Inc. MDrive series of controllers.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* Original Author: Ron Sluiter
|
||||
* Date: 03/21/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:
|
||||
* -----------------
|
||||
* .01 03/21/03 rls copied from devIM483PL.c
|
||||
* .02 05/15/03 rls R3.14 compatible.
|
||||
* .03 03/15/04 rls bug fix for LOAD_POS command with encoder.
|
||||
* .04 03/16/04 rls Protect against NULL "parms" argument in
|
||||
* MDrive_build_trans().
|
||||
* .05 09/20/04 rls remove '?' command string padding.
|
||||
* .06 04/06/05 rls Bug fix for not setting accel = decel.
|
||||
* .07 01/28/11 jps MDrive Release 3.x requires an '=' on velocity commands.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errlog.h>
|
||||
|
||||
#include "motorRecord.h"
|
||||
#include "motor.h"
|
||||
#include "motordevCom.h"
|
||||
#include "drvIM483.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
#define STATIC static
|
||||
|
||||
extern struct driver_table MDrive_access;
|
||||
|
||||
/* ----------------Create the dsets for devMDrive----------------- */
|
||||
STATIC struct driver_table *drvtabptr;
|
||||
STATIC long MDrive_init(int);
|
||||
STATIC long MDrive_init_record(void *);
|
||||
STATIC long MDrive_start_trans(struct motorRecord *);
|
||||
STATIC RTN_STATUS MDrive_build_trans(motor_cmnd, double *, struct motorRecord *);
|
||||
STATIC RTN_STATUS MDrive_end_trans(struct motorRecord *);
|
||||
|
||||
struct motor_dset devMDrive =
|
||||
{
|
||||
{8, NULL, (DEVSUPFUN) MDrive_init, (DEVSUPFUN) MDrive_init_record, NULL},
|
||||
motor_update_values,
|
||||
MDrive_start_trans,
|
||||
MDrive_build_trans,
|
||||
MDrive_end_trans
|
||||
};
|
||||
|
||||
extern "C" {epicsExportAddress(dset,devMDrive);}
|
||||
|
||||
/* --------------------------- program data --------------------- */
|
||||
|
||||
/* This table is used to define the command types */
|
||||
/* WARNING! this must match "motor_cmnd" in motor.h */
|
||||
|
||||
static msg_types MDrive_table[] = {
|
||||
MOTION, /* MOVE_ABS */
|
||||
MOTION, /* MOVE_REL */
|
||||
MOTION, /* HOME_FOR */
|
||||
MOTION, /* HOME_REV */
|
||||
IMMEDIATE, /* LOAD_POS */
|
||||
IMMEDIATE, /* SET_VEL_BASE */
|
||||
IMMEDIATE, /* SET_VELOCITY */
|
||||
IMMEDIATE, /* SET_ACCEL */
|
||||
IMMEDIATE, /* GO */
|
||||
IMMEDIATE, /* SET_ENC_RATIO */
|
||||
INFO, /* GET_INFO */
|
||||
MOVE_TERM, /* STOP_AXIS */
|
||||
VELOCITY, /* JOG */
|
||||
IMMEDIATE, /* SET_PGAIN */
|
||||
IMMEDIATE, /* SET_IGAIN */
|
||||
IMMEDIATE, /* SET_DGAIN */
|
||||
IMMEDIATE, /* ENABLE_TORQUE */
|
||||
IMMEDIATE, /* DISABL_TORQUE */
|
||||
IMMEDIATE, /* PRIMITIVE */
|
||||
IMMEDIATE, /* SET_HIGH_LIMIT */
|
||||
IMMEDIATE, /* SET_LOW_LIMIT */
|
||||
VELOCITY /* JOG_VELOCITY */
|
||||
};
|
||||
|
||||
|
||||
static struct board_stat **MDrive_cards;
|
||||
|
||||
/* --------------------------- program data --------------------- */
|
||||
|
||||
|
||||
/* initialize device support for MDrive stepper motor */
|
||||
STATIC long MDrive_init(int after)
|
||||
{
|
||||
long rtnval;
|
||||
|
||||
if (!after)
|
||||
{
|
||||
drvtabptr = &MDrive_access;
|
||||
(drvtabptr->init)();
|
||||
}
|
||||
|
||||
rtnval = motor_init_com(after, *drvtabptr->cardcnt_ptr, drvtabptr, &MDrive_cards);
|
||||
return(rtnval);
|
||||
}
|
||||
|
||||
|
||||
/* initialize a record instance */
|
||||
STATIC long MDrive_init_record(void *arg)
|
||||
{
|
||||
struct motorRecord *mr = (struct motorRecord *) arg;
|
||||
return(motor_init_record_com(mr, *drvtabptr->cardcnt_ptr, drvtabptr, MDrive_cards));
|
||||
}
|
||||
|
||||
|
||||
/* start building a transaction */
|
||||
STATIC long MDrive_start_trans(struct motorRecord *mr)
|
||||
{
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
/* end building a transaction */
|
||||
STATIC RTN_STATUS MDrive_end_trans(struct motorRecord *mr)
|
||||
{
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
/* add a part to the transaction */
|
||||
STATIC RTN_STATUS MDrive_build_trans(motor_cmnd command, double *parms, struct motorRecord *mr)
|
||||
{
|
||||
struct motor_trans *trans = (struct motor_trans *) mr->dpvt;
|
||||
struct mess_node *motor_call;
|
||||
struct controller *brdptr;
|
||||
struct IM483controller *cntrl;
|
||||
char buff[110];
|
||||
int axis, card, intval;
|
||||
unsigned int size;
|
||||
RTN_STATUS rtnval;
|
||||
bool send;
|
||||
msta_field msta;
|
||||
|
||||
send = true; /* Default to send motor command. */
|
||||
rtnval = OK;
|
||||
buff[0] = '\0';
|
||||
|
||||
/* Protect against NULL pointer with WRTITE_MSG(GO/STOP_AXIS/GET_INFO, NULL). */
|
||||
intval = (parms == NULL) ? 0 : NINT(parms[0]);
|
||||
|
||||
msta.All = mr->msta;
|
||||
|
||||
motor_start_trans_com(mr, MDrive_cards);
|
||||
|
||||
motor_call = &(trans->motor_call);
|
||||
card = motor_call->card;
|
||||
axis = motor_call->signal + 1;
|
||||
brdptr = (*trans->tabptr->card_array)[card];
|
||||
if (brdptr == NULL)
|
||||
return(rtnval = ERROR);
|
||||
|
||||
cntrl = (struct IM483controller *) brdptr->DevicePrivate;
|
||||
|
||||
if (MDrive_table[command] > motor_call->type)
|
||||
motor_call->type = MDrive_table[command];
|
||||
|
||||
if (trans->state != BUILD_STATE)
|
||||
return(rtnval = ERROR);
|
||||
|
||||
if (command == PRIMITIVE && mr->init != NULL && strlen(mr->init) != 0)
|
||||
{
|
||||
strcat(motor_call->message, " ");
|
||||
strcat(motor_call->message, mr->init);
|
||||
}
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case MOVE_ABS:
|
||||
case MOVE_REL:
|
||||
case HOME_FOR:
|
||||
case HOME_REV:
|
||||
case JOG:
|
||||
if (strlen(mr->prem) != 0)
|
||||
{
|
||||
strcat(motor_call->message, mr->prem);
|
||||
strcat(motor_call->message, " ");
|
||||
}
|
||||
if (strlen(mr->post) != 0)
|
||||
motor_call->postmsgptr = (char *) &mr->post;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case MOVE_ABS:
|
||||
sprintf(buff, "MA %d", intval);
|
||||
break;
|
||||
|
||||
case MOVE_REL:
|
||||
sprintf(buff, "MR %d", intval);
|
||||
break;
|
||||
|
||||
case HOME_FOR:
|
||||
sprintf(buff, " F1000 0");
|
||||
break;
|
||||
|
||||
case HOME_REV:
|
||||
sprintf(buff, " F1000 1");
|
||||
break;
|
||||
|
||||
case LOAD_POS:
|
||||
sprintf(buff, "P=%d", intval);
|
||||
if (msta.Bits.EA_PRESENT == 1)
|
||||
{
|
||||
/* Finish 1st message; MDrive can only handle one msg. */
|
||||
strcpy(motor_call->message, buff);
|
||||
rtnval = motor_end_trans_com(mr, drvtabptr);
|
||||
rtnval = (RTN_STATUS) motor_start_trans_com(mr, MDrive_cards);
|
||||
|
||||
intval = NINT(mr->dval / mr->eres);
|
||||
sprintf(buff, "C2=%d", intval);
|
||||
motor_call->type = MDrive_table[command];
|
||||
}
|
||||
break;
|
||||
|
||||
case SET_VEL_BASE:
|
||||
sprintf(buff, "VI=%d", intval);
|
||||
break;
|
||||
|
||||
case SET_VELOCITY:
|
||||
sprintf(buff, "VM=%d", intval);
|
||||
break;
|
||||
|
||||
case SET_ACCEL:
|
||||
sprintf(buff, "A=%d", intval);
|
||||
strcpy(motor_call->message, buff);
|
||||
rtnval = motor_end_trans_com(mr, drvtabptr);
|
||||
rtnval = (RTN_STATUS) motor_start_trans_com(mr, MDrive_cards);
|
||||
sprintf(buff, "D=A");
|
||||
motor_call->type = MDrive_table[command];
|
||||
break;
|
||||
|
||||
case GO:
|
||||
/* The MDrive starts moving immediately on move commands, GO command
|
||||
* does nothing. */
|
||||
send = false;
|
||||
break;
|
||||
|
||||
case PRIMITIVE:
|
||||
case GET_INFO:
|
||||
/* These commands are not actually done by sending a message, but
|
||||
rather they will indirectly cause the driver to read the status
|
||||
of all motors */
|
||||
break;
|
||||
|
||||
case STOP_AXIS:
|
||||
sprintf(buff, "SL 0");
|
||||
break;
|
||||
|
||||
case JOG_VELOCITY:
|
||||
case JOG:
|
||||
sprintf(buff, "SL=%d", intval);
|
||||
break;
|
||||
|
||||
case SET_PGAIN:
|
||||
case SET_IGAIN:
|
||||
case SET_DGAIN:
|
||||
send = false;
|
||||
break;
|
||||
|
||||
case ENABLE_TORQUE:
|
||||
sprintf(buff, "DE=1");
|
||||
break;
|
||||
|
||||
case DISABL_TORQUE:
|
||||
sprintf(buff, "DE=0");
|
||||
break;
|
||||
|
||||
case SET_HIGH_LIMIT:
|
||||
case SET_LOW_LIMIT:
|
||||
case SET_ENC_RATIO:
|
||||
trans->state = IDLE_STATE; /* No command sent to the controller. */
|
||||
send = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
send = false;
|
||||
rtnval = ERROR;
|
||||
}
|
||||
|
||||
size = strlen(buff);
|
||||
if (send == false)
|
||||
return(rtnval);
|
||||
else if (size > sizeof(buff) || (strlen(motor_call->message) + size) > MAX_MSG_SIZE)
|
||||
errlogMessage("MDrive_build_trans(): buffer overflow.\n");
|
||||
else
|
||||
{
|
||||
strcat(motor_call->message, buff);
|
||||
motor_end_trans_com(mr, drvtabptr);
|
||||
}
|
||||
return(rtnval);
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
FILENAME... drvIM483.h
|
||||
USAGE... This file contains driver "include" information that is specific to
|
||||
Intelligent Motion Systems, Inc. IM483(I/IE) and MDrive controllers.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* Original Author: Ron Sluiter
|
||||
* Date: 02/10/2000
|
||||
*
|
||||
* 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:
|
||||
* -----------------
|
||||
* .01 02/10/2000 rls copied from drvMM4000.h
|
||||
* .02 07/01/2004 rls Converted from MPF to asyn.
|
||||
* .03 03/18/2005 rls Added MDrive input configuration structure.
|
||||
*/
|
||||
|
||||
#ifndef INCdrvIM483h
|
||||
#define INCdrvIM483h 1
|
||||
|
||||
#include "motordrvCom.h"
|
||||
#include "asynDriver.h"
|
||||
#include "asynOctetSyncIO.h"
|
||||
|
||||
#define COMM_TIMEOUT 2 /* Timeout in seconds */
|
||||
|
||||
/* MDrive input configuration - 0 indicates unassigned. */
|
||||
typedef struct inputc
|
||||
{
|
||||
epicsUInt8 plusLS; /* Input # of + limit switch. */
|
||||
epicsUInt8 minusLS; /* Input # of - limit switch. */
|
||||
epicsUInt8 homeLS; /* Input # of home switch. */
|
||||
} input_config;
|
||||
|
||||
|
||||
/* IMS specific data is stored in this structure. */
|
||||
struct IM483controller
|
||||
{
|
||||
asynUser *pasynUser; /* For RS-232 */
|
||||
char asyn_port[80]; /* asyn port name */
|
||||
CommStatus status; /* Controller communication status. */
|
||||
input_config *inconfig; /* Discrete input configuration - MDrive only. */
|
||||
};
|
||||
|
||||
/* Function prototypes. */
|
||||
extern RTN_STATUS IM483SMSetup(int, int);
|
||||
extern RTN_STATUS IM483PLSetup(int, int);
|
||||
extern RTN_STATUS MDriveSetup(int, int);
|
||||
extern RTN_STATUS IM483SMConfig(int, const char *);
|
||||
extern RTN_STATUS IM483PLConfig(int, const char *);
|
||||
extern RTN_STATUS MDriveConfig(int, const char *);
|
||||
|
||||
#endif /* INCdrvIM483h */
|
||||
|
||||
@@ -1,622 +0,0 @@
|
||||
/*
|
||||
FILENAME... drvIM483PL.cc
|
||||
USAGE... Motor record driver level support for Intelligent Motion
|
||||
Systems, Inc. IM483(I/IE).
|
||||
|
||||
*/
|
||||
|
||||
/*****************************************************************
|
||||
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.
|
||||
**********************************************************************/
|
||||
|
||||
/*
|
||||
* Original Author: Ron Sluiter
|
||||
* Date: 07/10/2000
|
||||
*
|
||||
* Modification Log:
|
||||
* -----------------
|
||||
* .01 07/10/00 rls copied from drvIM483SM.c
|
||||
* .02 10/02/01 rls allow one retry after a communication error.
|
||||
* .03 04/15/02 rls Bug fix for limit switches. Set RA_DIRECTION in
|
||||
* set_status() based on (new - old) commanded position.
|
||||
* Removed support for "ASCII record separator (IS2) = /x1E"
|
||||
* from send_mess().
|
||||
* .04 03/07/03 rls R3.14 conversion.
|
||||
* .05 02/03/04 rls Eliminate erroneous "Motor motion timeout ERROR".
|
||||
* .06 07/01/04 rls Converted from MPF to asyn.
|
||||
* .07 09/20/04 rls - increase BUFF_SIZE; response was exceeding 13 characters.
|
||||
* - support for 32axes/controller.
|
||||
* - remove '?' command line padding.
|
||||
* .08 12/14/04 rls - asyn R4.0 support.
|
||||
* - make debug variables always available.
|
||||
* - MS Visual C compatibility; make all epicsExportAddress
|
||||
* extern "C" linkage.
|
||||
* - retry on initial communication.
|
||||
*/
|
||||
|
||||
/*
|
||||
DESIGN LIMITATIONS...
|
||||
1 - Like all controllers, the IM483 must be powered-on when EPICS is first
|
||||
booted up.
|
||||
2 - The IM483 cannot be power cycled while EPICS is up and running. The
|
||||
consequences are permanent communication lose with the IM483 until
|
||||
EPICS is rebooted.
|
||||
3 - Like the Newport MM3000, the IM483's position can only be set to zero.
|
||||
4 - The IM483 uses an internal look-up table for acceleration/deceleration.
|
||||
Translation between the IM483 and the ACCL/BACC fields is not obvious.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <epicsThread.h>
|
||||
#include <drvSup.h>
|
||||
#include <stdlib.h>
|
||||
#include <errlog.h>
|
||||
#include "motor.h"
|
||||
#include "drvIM483.h"
|
||||
#include "asynOctetSyncIO.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
/* Read Limit Status response values. */
|
||||
#define L_ALIMIT 1
|
||||
#define L_BLIMIT 2
|
||||
#define L_BOTH_LIMITS 3
|
||||
|
||||
|
||||
#define IM483PL_NUM_CARDS 8
|
||||
#define MAX_AXES 8
|
||||
#define BUFF_SIZE 50 /* Maximum length of string to/from IM483PL */
|
||||
|
||||
/*----------------debugging-----------------*/
|
||||
volatile int drvIM483PLdebug = 0;
|
||||
extern "C" {epicsExportAddress(int, drvIM483PLdebug);}
|
||||
|
||||
static inline void Debug(int level, const char *format, ...) {
|
||||
#ifdef DEBUG
|
||||
if (level < drvIM483PLdebug) {
|
||||
va_list pVar;
|
||||
va_start(pVar, format);
|
||||
vprintf(format, pVar);
|
||||
va_end(pVar);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* --- Local data. --- */
|
||||
int IM483PL_num_cards = 0;
|
||||
static char *IM483PL_axis[] = {"A", "B", "C", "D", "E", "F", "G", "H"};
|
||||
|
||||
/* Local data required for every driver; see "motordrvComCode.h" */
|
||||
#include "motordrvComCode.h"
|
||||
|
||||
/*----------------functions-----------------*/
|
||||
static int recv_mess(int, char *, int);
|
||||
static RTN_STATUS send_mess(int, char const *, char *);
|
||||
static int set_status(int, int);
|
||||
static long report(int);
|
||||
static long init();
|
||||
static int motor_init();
|
||||
static void query_done(int, int, struct mess_node *);
|
||||
|
||||
/*----------------functions-----------------*/
|
||||
|
||||
struct driver_table IM483PL_access =
|
||||
{
|
||||
motor_init,
|
||||
motor_send,
|
||||
motor_free,
|
||||
motor_card_info,
|
||||
motor_axis_info,
|
||||
&mess_queue,
|
||||
&queue_lock,
|
||||
&free_list,
|
||||
&freelist_lock,
|
||||
&motor_sem,
|
||||
&motor_state,
|
||||
&total_cards,
|
||||
&any_motor_in_motion,
|
||||
send_mess,
|
||||
recv_mess,
|
||||
set_status,
|
||||
query_done,
|
||||
NULL,
|
||||
&initialized,
|
||||
IM483PL_axis
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
long number;
|
||||
long (*report) (int);
|
||||
long (*init) (void);
|
||||
} drvIM483PL = {2, report, init};
|
||||
|
||||
extern "C" {epicsExportAddress(drvet, drvIM483PL);}
|
||||
|
||||
static struct thread_args targs = {SCAN_RATE, &IM483PL_access, 0.0};
|
||||
|
||||
/*********************************************************
|
||||
* Print out driver status report
|
||||
*********************************************************/
|
||||
static long report(int level)
|
||||
{
|
||||
int card;
|
||||
|
||||
if (IM483PL_num_cards <=0)
|
||||
printf(" No IM483PL controllers configured.\n");
|
||||
else
|
||||
{
|
||||
for (card = 0; card < IM483PL_num_cards; card++)
|
||||
{
|
||||
struct controller *brdptr = motor_state[card];
|
||||
|
||||
if (brdptr == NULL)
|
||||
printf(" IM483PL controller %d connection failed.\n", card);
|
||||
else
|
||||
{
|
||||
struct IM483controller *cntrl;
|
||||
|
||||
cntrl = (struct IM483controller *) brdptr->DevicePrivate;
|
||||
printf(" IM483PL controller #%d, port=%s, id: %s \n", card,
|
||||
cntrl->asyn_port, brdptr->ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
static long init()
|
||||
{
|
||||
/*
|
||||
* We cannot call motor_init() here, because that function can do GPIB I/O,
|
||||
* and hence requires that the drvGPIB have already been initialized.
|
||||
* That cannot be guaranteed, so we need to call motor_init from device
|
||||
* support
|
||||
*/
|
||||
/* Check for setup */
|
||||
if (IM483PL_num_cards <= 0)
|
||||
{
|
||||
Debug(1, "init(): IM483PL driver disabled. IM483PLSetup() missing from startup script.\n");
|
||||
}
|
||||
return((long) 0);
|
||||
}
|
||||
|
||||
|
||||
static void query_done(int card, int axis, struct mess_node *nodeptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************
|
||||
* *
|
||||
* FUNCTION NAME: set_status *
|
||||
* *
|
||||
* LOGIC: *
|
||||
* Initialize. *
|
||||
* Send "Moving Status" query. *
|
||||
* Read response. *
|
||||
* IF normal response to query. *
|
||||
* Set communication status to NORMAL. *
|
||||
* ELSE *
|
||||
* IF communication status is NORMAL. *
|
||||
* Set communication status to RETRY. *
|
||||
* NORMAL EXIT. *
|
||||
* ELSE *
|
||||
* Set communication status error. *
|
||||
* ERROR EXIT. *
|
||||
* ENDIF *
|
||||
* ENDIF *
|
||||
* *
|
||||
* IF "Moving Status" indicates any motion (i.e. status != 0). *
|
||||
* Clear "Done Moving" status bit. *
|
||||
* ELSE *
|
||||
* Set "Done Moving" status bit. *
|
||||
* ENDIF *
|
||||
* *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
static int set_status(int card, int signal)
|
||||
{
|
||||
struct IM483controller *cntrl;
|
||||
struct mess_node *nodeptr;
|
||||
register struct mess_info *motor_info;
|
||||
/* Message parsing variables */
|
||||
char buff[BUFF_SIZE];
|
||||
int rtnval, rtn_state;
|
||||
double motorData;
|
||||
bool plusdir, ls_active = false;
|
||||
msta_field status;
|
||||
|
||||
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
|
||||
motor_info = &(motor_state[card]->motor_info[signal]);
|
||||
nodeptr = motor_info->motor_motion;
|
||||
status.All = motor_info->status.All;
|
||||
|
||||
send_mess(card, " ^", IM483PL_axis[signal]);
|
||||
rtn_state = recv_mess(card, buff, 1);
|
||||
if (rtn_state > 0)
|
||||
{
|
||||
cntrl->status = NORMAL;
|
||||
status.Bits.CNTRL_COMM_ERR = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cntrl->status == NORMAL)
|
||||
{
|
||||
cntrl->status = RETRY;
|
||||
rtn_state = 0;
|
||||
goto exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
cntrl->status = COMM_ERR;
|
||||
status.Bits.CNTRL_COMM_ERR = 1;
|
||||
status.Bits.RA_PROBLEM = 1;
|
||||
rtn_state = 1;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
rtnval = atoi(&buff[4]);
|
||||
|
||||
status.Bits.RA_DONE = (rtnval != 0) ? 0 : 1;
|
||||
|
||||
/*
|
||||
* Parse motor position
|
||||
* Position string format: 1TP5.012,2TP1.123,3TP-100.567,...
|
||||
* Skip to substring for this motor, convert to double
|
||||
*/
|
||||
|
||||
send_mess(card, " Z 0", IM483PL_axis[signal]);
|
||||
recv_mess(card, buff, 1);
|
||||
|
||||
motorData = atof(&buff[5]);
|
||||
|
||||
if (motorData == motor_info->position)
|
||||
{
|
||||
if (nodeptr != 0) /* Increment counter only if motor is moving. */
|
||||
motor_info->no_motion_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
epicsInt32 newposition;
|
||||
|
||||
newposition = NINT(motorData);
|
||||
status.Bits.RA_DIRECTION = (newposition >= motor_info->position) ? 1 : 0;
|
||||
motor_info->position = newposition;
|
||||
motor_info->no_motion_count = 0;
|
||||
}
|
||||
|
||||
plusdir = (status.Bits.RA_DIRECTION) ? true : false;
|
||||
|
||||
send_mess(card, " ] 0", IM483PL_axis[signal]);
|
||||
recv_mess(card, buff, 1);
|
||||
rtnval = atoi(&buff[5]);
|
||||
|
||||
/* Set limit switch error indicators. */
|
||||
if (rtnval & 1)
|
||||
{
|
||||
status.Bits.RA_PLUS_LS = 1;
|
||||
if (plusdir == true)
|
||||
ls_active = true;
|
||||
}
|
||||
else
|
||||
status.Bits.RA_PLUS_LS = 0;
|
||||
|
||||
if (rtnval & 2)
|
||||
{
|
||||
status.Bits.RA_MINUS_LS = 1;
|
||||
if (plusdir == false)
|
||||
ls_active = true;
|
||||
}
|
||||
else
|
||||
status.Bits.RA_MINUS_LS = 0;
|
||||
|
||||
send_mess(card, " ] 1", IM483PL_axis[signal]);
|
||||
recv_mess(card, buff, 1);
|
||||
rtnval = buff[5];
|
||||
|
||||
status.Bits.RA_HOME = (rtnval & 0x01) ? 1 : 0;
|
||||
|
||||
/* !!! Assume no closed-looped control!!!*/
|
||||
status.Bits.EA_POSITION = 0;
|
||||
|
||||
/* encoder status */
|
||||
status.Bits.EA_SLIP = 0;
|
||||
status.Bits.EA_SLIP_STALL = 0;
|
||||
status.Bits.EA_HOME = 0;
|
||||
|
||||
if (motor_state[card]->motor_info[signal].encoder_present == NO)
|
||||
motor_info->encoder_position = 0;
|
||||
else
|
||||
{
|
||||
send_mess(card, " z 0", IM483PL_axis[signal]);
|
||||
recv_mess(card, buff, 1);
|
||||
motorData = atof(&buff[5]);
|
||||
motor_info->encoder_position = (epicsInt32) motorData;
|
||||
}
|
||||
|
||||
status.Bits.RA_PROBLEM = 0;
|
||||
|
||||
/* Parse motor velocity? */
|
||||
/* NEEDS WORK */
|
||||
|
||||
motor_info->velocity = 0;
|
||||
|
||||
if (!status.Bits.RA_DIRECTION)
|
||||
motor_info->velocity *= -1;
|
||||
|
||||
rtn_state = (!motor_info->no_motion_count || ls_active == true ||
|
||||
status.Bits.RA_DONE | status.Bits.RA_PROBLEM) ? 1 : 0;
|
||||
|
||||
/* Test for post-move string. */
|
||||
if ((status.Bits.RA_DONE || ls_active == true) && nodeptr != 0 && nodeptr->postmsgptr != 0)
|
||||
{
|
||||
strcpy(buff, nodeptr->postmsgptr);
|
||||
send_mess(card, buff, IM483PL_axis[signal]);
|
||||
nodeptr->postmsgptr = NULL;
|
||||
}
|
||||
|
||||
exit:
|
||||
motor_info->status.All = status.All;
|
||||
return(rtn_state);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************/
|
||||
/* send a message to the IM483PL board */
|
||||
/* send_mess() */
|
||||
/*****************************************************/
|
||||
static RTN_STATUS send_mess(int card, char const *com, char *name)
|
||||
{
|
||||
char local_buff[MAX_MSG_SIZE];
|
||||
struct IM483controller *cntrl;
|
||||
int comsize, namesize;
|
||||
size_t nwrite;
|
||||
|
||||
comsize = (com == NULL) ? 0 : strlen(com);
|
||||
namesize = (name == NULL) ? 0 : strlen(name);
|
||||
|
||||
if ((comsize + namesize) > MAX_MSG_SIZE)
|
||||
{
|
||||
errlogMessage("drvIM483PL.c:send_mess(); message size violation.\n");
|
||||
return(ERROR);
|
||||
}
|
||||
else if (comsize == 0) /* Normal exit on empty input message. */
|
||||
return(OK);
|
||||
|
||||
if (!motor_state[card])
|
||||
{
|
||||
errlogPrintf("drvIM483PL.c:send_mess() - invalid card #%d\n", card);
|
||||
return(ERROR);
|
||||
}
|
||||
|
||||
/* Make a local copy of the string and add the command line terminator. */
|
||||
if (namesize != 0)
|
||||
{
|
||||
strcpy(local_buff, name); /* put in axis */
|
||||
strcat(local_buff, com);
|
||||
}
|
||||
else
|
||||
strcpy(local_buff, com);
|
||||
|
||||
Debug(2, "send_mess(): message = %s\n", local_buff);
|
||||
|
||||
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
|
||||
pasynOctetSyncIO->write(cntrl->pasynUser, local_buff, strlen(local_buff), COMM_TIMEOUT, &nwrite);
|
||||
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************/
|
||||
/* receive a message from the IM483 board */
|
||||
/* recv_mess() */
|
||||
/*****************************************************/
|
||||
static int recv_mess(int card, char *com, int flag)
|
||||
{
|
||||
struct IM483controller *cntrl;
|
||||
size_t nread = 0;
|
||||
asynStatus status = asynError;
|
||||
int eomReason;
|
||||
|
||||
/* Check that card exists */
|
||||
if (!motor_state[card])
|
||||
return(ERROR);
|
||||
|
||||
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
|
||||
|
||||
if (flag == FLUSH)
|
||||
pasynOctetSyncIO->flush(cntrl->pasynUser);
|
||||
else
|
||||
status = pasynOctetSyncIO->read(cntrl->pasynUser, com, BUFF_SIZE, COMM_TIMEOUT, &nread, &eomReason);
|
||||
|
||||
if ((status != asynSuccess) || (nread <= 0))
|
||||
{
|
||||
com[0] = '\0';
|
||||
nread = 0;
|
||||
}
|
||||
|
||||
Debug(2, "recv_mess(): message = \"%s\"\n", com);
|
||||
return(nread);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************/
|
||||
/* Setup system configuration */
|
||||
/* IM483PLSetup() */
|
||||
/*****************************************************/
|
||||
RTN_STATUS
|
||||
IM483PLSetup(int num_cards, /* maximum number of controllers in system. */
|
||||
int scan_rate) /* polling rate - 1/60 sec units. */
|
||||
{
|
||||
int itera;
|
||||
|
||||
if (num_cards < 1 || num_cards > IM483PL_NUM_CARDS)
|
||||
IM483PL_num_cards = IM483PL_NUM_CARDS;
|
||||
else
|
||||
IM483PL_num_cards = num_cards;
|
||||
|
||||
/* Set motor polling task rate */
|
||||
if (scan_rate >= 1 && scan_rate <= 60)
|
||||
targs.motor_scan_rate = scan_rate;
|
||||
else
|
||||
targs.motor_scan_rate = SCAN_RATE;
|
||||
|
||||
/*
|
||||
* Allocate space for motor_state structures. Note this must be done
|
||||
* before IM483Config is called, so it cannot be done in motor_init()
|
||||
* This means that we must allocate space for a card without knowing
|
||||
* if it really exists, which is not a serious problem
|
||||
*/
|
||||
motor_state = (struct controller **) malloc(IM483PL_num_cards * sizeof(struct controller *));
|
||||
|
||||
for (itera = 0; itera < IM483PL_num_cards; itera++)
|
||||
motor_state[itera] = (struct controller *) NULL;
|
||||
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************/
|
||||
/* Configure a controller */
|
||||
/* IM483PLConfig() */
|
||||
/*****************************************************/
|
||||
RTN_STATUS
|
||||
IM483PLConfig(int card, /* card being configured */
|
||||
const char *name) /* asyn server task name */
|
||||
{
|
||||
struct IM483controller *cntrl;
|
||||
|
||||
if (card < 0 || card >= IM483PL_num_cards)
|
||||
return(ERROR);
|
||||
|
||||
motor_state[card] = (struct controller *) malloc(sizeof(struct controller));
|
||||
motor_state[card]->DevicePrivate = malloc(sizeof(struct IM483controller));
|
||||
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
|
||||
|
||||
strcpy(cntrl->asyn_port, name);
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************/
|
||||
/* initialize all software and hardware */
|
||||
/* This is called from the initialization routine in */
|
||||
/* device support. */
|
||||
/* motor_init() */
|
||||
/*****************************************************/
|
||||
static int motor_init()
|
||||
{
|
||||
struct controller *brdptr;
|
||||
struct IM483controller *cntrl;
|
||||
int card_index, motor_index;
|
||||
char buff[BUFF_SIZE];
|
||||
int total_axis = 0;
|
||||
int status;
|
||||
asynStatus success_rtn;
|
||||
static const char output_terminator[] = "\n";
|
||||
static const char input_terminator[] = "\n";
|
||||
|
||||
initialized = true; /* Indicate that driver is initialized. */
|
||||
|
||||
/* Check for setup */
|
||||
if (IM483PL_num_cards <= 0)
|
||||
return(ERROR);
|
||||
|
||||
for (card_index = 0; card_index < IM483PL_num_cards; card_index++)
|
||||
{
|
||||
if (!motor_state[card_index])
|
||||
continue;
|
||||
|
||||
brdptr = motor_state[card_index];
|
||||
brdptr->ident[0] = (char) NULL; /* No controller identification message. */
|
||||
brdptr->cmnd_response = true;
|
||||
total_cards = card_index + 1;
|
||||
cntrl = (struct IM483controller *) brdptr->DevicePrivate;
|
||||
|
||||
/* Initialize communications channel */
|
||||
success_rtn = pasynOctetSyncIO->connect(cntrl->asyn_port, 0, &cntrl->pasynUser, NULL);
|
||||
|
||||
if (success_rtn == asynSuccess)
|
||||
{
|
||||
pasynOctetSyncIO->setOutputEos(cntrl->pasynUser, output_terminator, strlen(output_terminator));
|
||||
pasynOctetSyncIO->setInputEos(cntrl->pasynUser, input_terminator, strlen(input_terminator));
|
||||
/* Send a message to the board, see if it exists */
|
||||
/* flush any junk at input port - should not be any data available */
|
||||
pasynOctetSyncIO->flush(cntrl->pasynUser);
|
||||
|
||||
for (total_axis = 0; total_axis < MAX_AXES; total_axis++)
|
||||
{
|
||||
int retry = 0;
|
||||
|
||||
do
|
||||
{
|
||||
send_mess(card_index, " Z 0", IM483PL_axis[total_axis]);
|
||||
status = recv_mess(card_index, buff, 1);
|
||||
retry++;
|
||||
} while (status <= 0 && retry < 3);
|
||||
|
||||
if (status <= 0)
|
||||
break;
|
||||
}
|
||||
brdptr->total_axis = total_axis;
|
||||
}
|
||||
|
||||
if (success_rtn == asynSuccess && total_axis > 0)
|
||||
{
|
||||
brdptr->localaddr = (char *) NULL;
|
||||
brdptr->motor_in_motion = 0;
|
||||
|
||||
for (motor_index = 0; motor_index < total_axis; motor_index++)
|
||||
{
|
||||
struct mess_info *motor_info = &brdptr->motor_info[motor_index];
|
||||
int loop_state;
|
||||
|
||||
motor_info->status.All = 0;
|
||||
motor_info->no_motion_count = 0;
|
||||
motor_info->encoder_position = 0;
|
||||
motor_info->position = 0;
|
||||
brdptr->motor_info[motor_index].motor_motion = NULL;
|
||||
/* Assume encoder support, i.e., IM483IE. */
|
||||
motor_info->encoder_present = YES;
|
||||
motor_info->status.Bits.EA_PRESENT = 1;
|
||||
|
||||
/* Determine if encoder present based on open/closed loop mode. */
|
||||
loop_state = 0;
|
||||
if (loop_state != 0)
|
||||
{
|
||||
motor_info->pid_present = YES;
|
||||
motor_info->status.Bits.GAIN_SUPPORT = 1;
|
||||
}
|
||||
|
||||
set_status(card_index, motor_index); /* Read status of each motor */
|
||||
}
|
||||
}
|
||||
else
|
||||
motor_state[card_index] = (struct controller *) NULL;
|
||||
}
|
||||
|
||||
any_motor_in_motion = 0;
|
||||
|
||||
mess_queue.head = (struct mess_node *) NULL;
|
||||
mess_queue.tail = (struct mess_node *) NULL;
|
||||
|
||||
free_list.head = (struct mess_node *) NULL;
|
||||
free_list.tail = (struct mess_node *) NULL;
|
||||
|
||||
epicsThreadCreate((char *) "IM483PL_motor", epicsThreadPriorityMedium,
|
||||
epicsThreadGetStackSize(epicsThreadStackMedium),
|
||||
(EPICSTHREADFUNC) motor_task, (void *) &targs);
|
||||
|
||||
return(OK);
|
||||
}
|
||||
|
||||
@@ -1,654 +0,0 @@
|
||||
/*
|
||||
FILENAME... drvIM483SM.cc
|
||||
USAGE... Motor record driver level support for Intelligent Motion
|
||||
Systems, Inc. IM483(I/IE).
|
||||
|
||||
*/
|
||||
|
||||
/*****************************************************************
|
||||
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.
|
||||
**********************************************************************/
|
||||
|
||||
/*
|
||||
* Original Author: Ron Sluiter
|
||||
* Date: 02/10/2000
|
||||
*
|
||||
* Modification Log:
|
||||
* -----------------
|
||||
* .01 02/10/00 rls copied from drvMM4000.c
|
||||
* .02 10/02/01 rls allow one retry after a communication error.
|
||||
* .03 04/15/02 rls Bug fix for limit switches. Set RA_DIRECTION in
|
||||
* set_status() based on (new - old) commanded position.
|
||||
* Removed support for "ASCII record separator (IS2) = /x1E"
|
||||
* from send_mess().
|
||||
* .04 03/07/03 rls R3.14 conversion.
|
||||
* .05 02/03/04 rls Eliminate erroneous "Motor motion timeout ERROR".
|
||||
* .06 07/01/04 rls Converted from MPF to asyn.
|
||||
* .07 09/20/04 rls support for 32axes/controller.
|
||||
* .08 12/14/04 rls - asyn R4.0 support.
|
||||
* - make debug variables always available.
|
||||
* - MS Visual C compatibility; make all epicsExportAddress
|
||||
* extern "C" linkage.
|
||||
* - retry on initial communication.
|
||||
*/
|
||||
|
||||
/*
|
||||
DESIGN LIMITATIONS...
|
||||
1 - Like all controllers, the IM483 must be powered-on when EPICS is first
|
||||
booted up.
|
||||
2 - The IM483 cannot be power cycled while EPICS is up and running. The
|
||||
consequences are permanent communication lose with the IM483 until
|
||||
EPICS is rebooted.
|
||||
3 - Like the Newport MM3000, the IM483's position can only be set to zero.
|
||||
4 - The IM483 uses an internal look-up table for acceleration/deceleration.
|
||||
Translation between the IM483 and the ACCL/BACC fields is not obvious.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <epicsThread.h>
|
||||
#include <drvSup.h>
|
||||
#include <stdlib.h>
|
||||
#include <errlog.h>
|
||||
#include "motor.h"
|
||||
#include "drvIM483.h"
|
||||
#include "asynOctetSyncIO.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
/* Read Limit Status response values. */
|
||||
#define L_ALIMIT 1
|
||||
#define L_BLIMIT 2
|
||||
#define L_BOTH_LIMITS 3
|
||||
|
||||
|
||||
#define IM483SM_NUM_CARDS 8
|
||||
#define BUFF_SIZE 50 /* Maximum length of string to/from IM483 */
|
||||
|
||||
/*----------------debugging-----------------*/
|
||||
volatile int drvIM483SMdebug = 0;
|
||||
extern "C" {epicsExportAddress(int, drvIM483SMdebug);}
|
||||
|
||||
static inline void Debug(int level, const char *format, ...) {
|
||||
#ifdef DEBUG
|
||||
if (level < drvIM483SMdebug) {
|
||||
va_list pVar;
|
||||
va_start(pVar, format);
|
||||
vprintf(format, pVar);
|
||||
va_end(pVar);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* --- Local data. --- */
|
||||
int IM483SM_num_cards = 0;
|
||||
|
||||
/* Local data required for every driver; see "motordrvComCode.h" */
|
||||
#include "motordrvComCode.h"
|
||||
|
||||
/*----------------functions-----------------*/
|
||||
static int recv_mess(int, char *, int);
|
||||
static RTN_STATUS send_mess(int, char const *, char *);
|
||||
static int set_status(int, int);
|
||||
static long report(int);
|
||||
static long init();
|
||||
static int motor_init();
|
||||
static void query_done(int, int, struct mess_node *);
|
||||
|
||||
/*----------------functions-----------------*/
|
||||
|
||||
struct driver_table IM483SM_access =
|
||||
{
|
||||
motor_init,
|
||||
motor_send,
|
||||
motor_free,
|
||||
motor_card_info,
|
||||
motor_axis_info,
|
||||
&mess_queue,
|
||||
&queue_lock,
|
||||
&free_list,
|
||||
&freelist_lock,
|
||||
&motor_sem,
|
||||
&motor_state,
|
||||
&total_cards,
|
||||
&any_motor_in_motion,
|
||||
send_mess,
|
||||
recv_mess,
|
||||
set_status,
|
||||
query_done,
|
||||
NULL,
|
||||
&initialized,
|
||||
NULL
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
long number;
|
||||
long (*report) (int);
|
||||
long (*init) (void);
|
||||
} drvIM483SM = {2, report, init};
|
||||
|
||||
extern "C" {epicsExportAddress(drvet, drvIM483SM);}
|
||||
|
||||
static struct thread_args targs = {SCAN_RATE, &IM483SM_access, 0.0};
|
||||
|
||||
|
||||
/*********************************************************
|
||||
* Print out driver status report
|
||||
*********************************************************/
|
||||
static long report(int level)
|
||||
{
|
||||
int card;
|
||||
|
||||
if (IM483SM_num_cards <=0)
|
||||
printf(" No IM483SM controllers configured.\n");
|
||||
else
|
||||
{
|
||||
for (card = 0; card < IM483SM_num_cards; card++)
|
||||
{
|
||||
struct controller *brdptr = motor_state[card];
|
||||
|
||||
if (brdptr == NULL)
|
||||
printf(" IM483SM controller %d connection failed.\n", card);
|
||||
else
|
||||
{
|
||||
struct IM483controller *cntrl;
|
||||
|
||||
cntrl = (struct IM483controller *) brdptr->DevicePrivate;
|
||||
printf(" IM483SM controller #%d, port=%s, id: %s \n", card,
|
||||
cntrl->asyn_port, brdptr->ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
static long init()
|
||||
{
|
||||
/*
|
||||
* We cannot call motor_init() here, because that function can do GPIB I/O,
|
||||
* and hence requires that the drvGPIB have already been initialized.
|
||||
* That cannot be guaranteed, so we need to call motor_init from device
|
||||
* support
|
||||
*/
|
||||
/* Check for setup */
|
||||
if (IM483SM_num_cards <= 0)
|
||||
{
|
||||
Debug(1, "init(): IM483SM driver disabled. IM483SMSetup() missing from startup script.\n");
|
||||
}
|
||||
return((long) 0);
|
||||
}
|
||||
|
||||
|
||||
static void query_done(int card, int axis, struct mess_node *nodeptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************
|
||||
* *
|
||||
* FUNCTION NAME: set_status *
|
||||
* *
|
||||
* LOGIC: *
|
||||
* Initialize. *
|
||||
* Send "Moving Status" query. *
|
||||
* Read response. *
|
||||
* IF normal response to query. *
|
||||
* Set communication status to NORMAL. *
|
||||
* ELSE *
|
||||
* IF communication status is NORMAL. *
|
||||
* Set communication status to RETRY. *
|
||||
* NORMAL EXIT. *
|
||||
* ELSE *
|
||||
* Set communication status error. *
|
||||
* ERROR EXIT. *
|
||||
* ENDIF *
|
||||
* ENDIF *
|
||||
* *
|
||||
* IF "Moving Status" indicates any motion (i.e. status != 0). *
|
||||
* Clear "Done Moving" status bit. *
|
||||
* ELSE *
|
||||
* Set "Done Moving" status bit. *
|
||||
* ENDIF *
|
||||
* *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
static int set_status(int card, int signal)
|
||||
{
|
||||
struct IM483controller *cntrl;
|
||||
struct mess_node *nodeptr;
|
||||
register struct mess_info *motor_info;
|
||||
/* Message parsing variables */
|
||||
char buff[BUFF_SIZE];
|
||||
int rtnval, rtn_state;
|
||||
double motorData;
|
||||
bool plusdir, ls_active = false;
|
||||
msta_field status;
|
||||
|
||||
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
|
||||
motor_info = &(motor_state[card]->motor_info[signal]);
|
||||
nodeptr = motor_info->motor_motion;
|
||||
status.All = motor_info->status.All;
|
||||
|
||||
send_mess(card, "^", (char*) NULL);
|
||||
rtn_state = recv_mess(card, buff, 1);
|
||||
if (rtn_state > 0)
|
||||
{
|
||||
cntrl->status = NORMAL;
|
||||
status.Bits.CNTRL_COMM_ERR = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cntrl->status == NORMAL)
|
||||
{
|
||||
cntrl->status = RETRY;
|
||||
rtn_state = 0;
|
||||
goto exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
cntrl->status = COMM_ERR;
|
||||
status.Bits.CNTRL_COMM_ERR = 1;
|
||||
status.Bits.RA_PROBLEM = 1;
|
||||
rtn_state = 1;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
rtnval = atoi(&buff[4]);
|
||||
|
||||
status.Bits.RA_DONE = (rtnval != 0) ? 0 : 1;
|
||||
|
||||
/*
|
||||
* Parse motor position
|
||||
* Position string format: 1TP5.012,2TP1.123,3TP-100.567,...
|
||||
* Skip to substring for this motor, convert to double
|
||||
*/
|
||||
|
||||
send_mess(card, "Z 0", (char*) NULL);
|
||||
recv_mess(card, buff, 1);
|
||||
|
||||
motorData = atof(&buff[5]);
|
||||
|
||||
if (motorData == motor_info->position)
|
||||
{
|
||||
if (nodeptr != 0) /* Increment counter only if motor is moving. */
|
||||
motor_info->no_motion_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
epicsInt32 newposition;
|
||||
|
||||
newposition = NINT(motorData);
|
||||
status.Bits.RA_DIRECTION = (newposition >= motor_info->position) ? 1 : 0;
|
||||
motor_info->position = newposition;
|
||||
motor_info->no_motion_count = 0;
|
||||
}
|
||||
|
||||
plusdir = (status.Bits.RA_DIRECTION) ? true : false;
|
||||
|
||||
send_mess(card, "] 0", (char*) NULL);
|
||||
recv_mess(card, buff, 1);
|
||||
rtnval = atoi(&buff[5]);
|
||||
|
||||
/* Set limit switch error indicators. */
|
||||
if (rtnval & 1)
|
||||
{
|
||||
status.Bits.RA_PLUS_LS = 1;
|
||||
if (plusdir == true)
|
||||
ls_active = true;
|
||||
}
|
||||
else
|
||||
status.Bits.RA_PLUS_LS = 0;
|
||||
|
||||
if (rtnval & 2)
|
||||
{
|
||||
status.Bits.RA_MINUS_LS = 1;
|
||||
if (plusdir == false)
|
||||
ls_active = true;
|
||||
}
|
||||
else
|
||||
status.Bits.RA_MINUS_LS = 0;
|
||||
|
||||
send_mess(card, "] 1", (char*) NULL);
|
||||
recv_mess(card, buff, 1);
|
||||
rtnval = buff[5];
|
||||
|
||||
status.Bits.RA_HOME = (rtnval & 0x01) ? 1 : 0;
|
||||
|
||||
/* !!! Assume no closed-looped control!!!*/
|
||||
status.Bits.EA_POSITION = 0;
|
||||
|
||||
/* encoder status */
|
||||
status.Bits.EA_SLIP = 0;
|
||||
status.Bits.EA_SLIP_STALL = 0;
|
||||
status.Bits.EA_HOME = 0;
|
||||
|
||||
if (motor_state[card]->motor_info[signal].encoder_present == NO)
|
||||
motor_info->encoder_position = 0;
|
||||
else
|
||||
{
|
||||
send_mess(card, "z 0", (char*) NULL);
|
||||
recv_mess(card, buff, 1);
|
||||
motorData = atof(&buff[5]);
|
||||
motor_info->encoder_position = (epicsInt32) motorData;
|
||||
}
|
||||
|
||||
status.Bits.RA_PROBLEM = 0;
|
||||
|
||||
/* Parse motor velocity? */
|
||||
/* NEEDS WORK */
|
||||
|
||||
motor_info->velocity = 0;
|
||||
|
||||
if (!status.Bits.RA_DIRECTION)
|
||||
motor_info->velocity *= -1;
|
||||
|
||||
rtn_state = (!motor_info->no_motion_count || ls_active == true || status.Bits.RA_DONE | status.Bits.RA_PROBLEM) ? 1 : 0;
|
||||
|
||||
/* Test for post-move string. */
|
||||
if ((status.Bits.RA_DONE || ls_active == true) && nodeptr != 0 &&
|
||||
nodeptr->postmsgptr != 0)
|
||||
{
|
||||
strcpy(buff, nodeptr->postmsgptr);
|
||||
send_mess(card, buff, (char*) NULL);
|
||||
nodeptr->postmsgptr = NULL;
|
||||
}
|
||||
|
||||
exit:
|
||||
motor_info->status.All = status.All;
|
||||
return(rtn_state);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************/
|
||||
/* send a message to the IM483SM board */
|
||||
/* send_mess() */
|
||||
/*****************************************************/
|
||||
static RTN_STATUS send_mess(int card, char const *com, char *name)
|
||||
{
|
||||
struct IM483controller *cntrl;
|
||||
int size;
|
||||
size_t nwrite;
|
||||
|
||||
size = strlen(com);
|
||||
|
||||
if (size > MAX_MSG_SIZE)
|
||||
{
|
||||
errlogMessage("drvIM483SM.c:send_mess(); message size violation.\n");
|
||||
return(ERROR);
|
||||
}
|
||||
else if (size == 0) /* Normal exit on empty input message. */
|
||||
return(OK);
|
||||
|
||||
if (!motor_state[card])
|
||||
{
|
||||
errlogPrintf("drvIM483SM.c:send_mess() - invalid card #%d\n", card);
|
||||
return(ERROR);
|
||||
}
|
||||
|
||||
if (name != NULL)
|
||||
{
|
||||
errlogPrintf("drvIM483SM.c:send_mess() - invalid argument = %s\n", name);
|
||||
return(ERROR);
|
||||
}
|
||||
|
||||
Debug(2, "send_mess(): message = %s\n", com);
|
||||
|
||||
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
|
||||
pasynOctetSyncIO->write(cntrl->pasynUser, com, size, COMM_TIMEOUT, &nwrite);
|
||||
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************/
|
||||
/* receive a message from the IM483 board */
|
||||
/* recv_mess() */
|
||||
/*****************************************************/
|
||||
static int recv_mess(int card, char *com, int flag)
|
||||
{
|
||||
struct IM483controller *cntrl;
|
||||
size_t nread = 0;
|
||||
asynStatus status = asynError;
|
||||
int eomReason;
|
||||
|
||||
/* Check that card exists */
|
||||
if (!motor_state[card])
|
||||
return(ERROR);
|
||||
|
||||
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
|
||||
|
||||
if (flag == FLUSH)
|
||||
pasynOctetSyncIO->flush(cntrl->pasynUser);
|
||||
else
|
||||
status = pasynOctetSyncIO->read(cntrl->pasynUser, com, BUFF_SIZE, COMM_TIMEOUT, &nread, &eomReason);
|
||||
|
||||
if ((status != asynSuccess) || (nread <= 0))
|
||||
{
|
||||
com[0] = '\0';
|
||||
nread = 0;
|
||||
}
|
||||
|
||||
Debug(2, "recv_mess(): message = \"%s\"\n", com);
|
||||
return(nread);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************/
|
||||
/* Setup system configuration */
|
||||
/* IM483SMSetup() */
|
||||
/*****************************************************/
|
||||
RTN_STATUS
|
||||
IM483SMSetup(int num_cards, /* maximum number of controllers in system. */
|
||||
int scan_rate) /* polling rate - 1/60 sec units. */
|
||||
{
|
||||
int itera;
|
||||
|
||||
if (num_cards < 1 || num_cards > IM483SM_NUM_CARDS)
|
||||
IM483SM_num_cards = IM483SM_NUM_CARDS;
|
||||
else
|
||||
IM483SM_num_cards = num_cards;
|
||||
|
||||
/* Set motor polling task rate */
|
||||
if (scan_rate >= 1 && scan_rate <= 60)
|
||||
targs.motor_scan_rate = scan_rate;
|
||||
else
|
||||
targs.motor_scan_rate = SCAN_RATE;
|
||||
|
||||
/*
|
||||
* Allocate space for motor_state structures. Note this must be done
|
||||
* before IM483SMConfig is called, so it cannot be done in motor_init()
|
||||
* This means that we must allocate space for a card without knowing
|
||||
* if it really exists, which is not a serious problem
|
||||
*/
|
||||
motor_state = (struct controller **) malloc(IM483SM_num_cards * sizeof(struct controller *));
|
||||
|
||||
for (itera = 0; itera < IM483SM_num_cards; itera++)
|
||||
motor_state[itera] = (struct controller *) NULL;
|
||||
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************/
|
||||
/* Configure a controller */
|
||||
/* IM483SMConfig() */
|
||||
/*****************************************************/
|
||||
RTN_STATUS
|
||||
IM483SMConfig(int card, /* card being configured */
|
||||
const char *name) /* asyn server task name */
|
||||
{
|
||||
struct IM483controller *cntrl;
|
||||
|
||||
if (card < 0 || card >= IM483SM_num_cards)
|
||||
return(ERROR);
|
||||
|
||||
motor_state[card] = (struct controller *) malloc(sizeof(struct controller));
|
||||
motor_state[card]->DevicePrivate = malloc(sizeof(struct IM483controller));
|
||||
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
|
||||
|
||||
strcpy(cntrl->asyn_port, name);
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************/
|
||||
/* initialize all software and hardware */
|
||||
/* This is called from the initialization routine in */
|
||||
/* device support. */
|
||||
/* motor_init() */
|
||||
/*****************************************************/
|
||||
static int motor_init()
|
||||
{
|
||||
struct controller *brdptr;
|
||||
struct IM483controller *cntrl;
|
||||
int card_index, motor_index;
|
||||
char buff[BUFF_SIZE];
|
||||
int total_axis = 0;
|
||||
int status = 0;
|
||||
asynStatus success_rtn;
|
||||
static const char output_terminator[] = "\r";
|
||||
static const char input_terminator[] = "\r\n";
|
||||
|
||||
initialized = true; /* Indicate that driver is initialized. */
|
||||
|
||||
/* Check for setup */
|
||||
if (IM483SM_num_cards <= 0)
|
||||
return(ERROR);
|
||||
|
||||
for (card_index = 0; card_index < IM483SM_num_cards; card_index++)
|
||||
{
|
||||
if (!motor_state[card_index])
|
||||
continue;
|
||||
|
||||
brdptr = motor_state[card_index];
|
||||
brdptr->cmnd_response = true;
|
||||
total_cards = card_index + 1;
|
||||
cntrl = (struct IM483controller *) brdptr->DevicePrivate;
|
||||
|
||||
/* Initialize communications channel */
|
||||
success_rtn = pasynOctetSyncIO->connect(cntrl->asyn_port, 0, &cntrl->pasynUser, NULL);
|
||||
|
||||
if (success_rtn == asynSuccess)
|
||||
{
|
||||
int itera, retry = 0;
|
||||
char *src, *dest;
|
||||
|
||||
pasynOctetSyncIO->setOutputEos(cntrl->pasynUser, output_terminator, strlen(output_terminator));
|
||||
pasynOctetSyncIO->setInputEos(cntrl->pasynUser, input_terminator, strlen(input_terminator));
|
||||
do
|
||||
{
|
||||
/* Send a message to the board, see if it exists */
|
||||
/* flush any junk at input port - should not be any data available */
|
||||
pasynOctetSyncIO->flush(cntrl->pasynUser);
|
||||
|
||||
send_mess(card_index, "\003", (char*) NULL); /* Reset device. */
|
||||
epicsThreadSleep(1.0);
|
||||
send_mess(card_index, " ", (char*) NULL);
|
||||
|
||||
/* Save controller identification message. */
|
||||
src = buff;
|
||||
dest = brdptr->ident;
|
||||
*src = (char) NULL;
|
||||
|
||||
for (itera = 0; itera < 50; itera++)
|
||||
{
|
||||
if (*src == (char) NULL)
|
||||
{
|
||||
status = recv_mess(card_index, buff, 1);
|
||||
if (status <= 0)
|
||||
{
|
||||
if (itera != 0)
|
||||
{
|
||||
*dest = (char) NULL;
|
||||
status = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
src = buff;
|
||||
while (isspace(*src++));
|
||||
--src;
|
||||
if (itera != 0)
|
||||
{
|
||||
*dest++ = ' ';
|
||||
itera++;
|
||||
}
|
||||
}
|
||||
else if (isspace(*src))
|
||||
{
|
||||
while (isspace(*src++));
|
||||
src -= 2;
|
||||
}
|
||||
else if (strncmp(src, "AD", 2) == 0)
|
||||
{
|
||||
strcpy(dest, "AMS");
|
||||
src += 22;
|
||||
dest += 3;
|
||||
itera += 3;
|
||||
}
|
||||
*dest++ = *src++;
|
||||
}
|
||||
retry++;
|
||||
/* Return value is length of response string */
|
||||
} while (status == 0 && retry < 3);
|
||||
}
|
||||
|
||||
if (success_rtn == asynSuccess && status > 0)
|
||||
{
|
||||
brdptr->localaddr = (char *) NULL;
|
||||
brdptr->motor_in_motion = 0;
|
||||
|
||||
brdptr->total_axis = total_axis = 1;
|
||||
|
||||
for (motor_index = 0; motor_index < total_axis; motor_index++)
|
||||
{
|
||||
struct mess_info *motor_info = &brdptr->motor_info[motor_index];
|
||||
int loop_state;
|
||||
|
||||
motor_info->status.All = 0;
|
||||
motor_info->no_motion_count = 0;
|
||||
motor_info->encoder_position = 0;
|
||||
motor_info->position = 0;
|
||||
brdptr->motor_info[motor_index].motor_motion = NULL;
|
||||
|
||||
/* Determine if encoder present based on open/closed loop mode. */
|
||||
loop_state = 0;
|
||||
if (loop_state != 0)
|
||||
{
|
||||
motor_info->encoder_present = YES;
|
||||
motor_info->status.Bits.EA_PRESENT = 1;
|
||||
motor_info->pid_present = YES;
|
||||
motor_info->status.Bits.GAIN_SUPPORT = 1;
|
||||
}
|
||||
|
||||
set_status(card_index, motor_index); /* Read status of each motor */
|
||||
}
|
||||
}
|
||||
else
|
||||
motor_state[card_index] = (struct controller *) NULL;
|
||||
}
|
||||
|
||||
any_motor_in_motion = 0;
|
||||
|
||||
mess_queue.head = (struct mess_node *) NULL;
|
||||
mess_queue.tail = (struct mess_node *) NULL;
|
||||
|
||||
free_list.head = (struct mess_node *) NULL;
|
||||
free_list.tail = (struct mess_node *) NULL;
|
||||
|
||||
epicsThreadCreate((char *) "IM483SM_motor", epicsThreadPriorityMedium,
|
||||
epicsThreadGetStackSize(epicsThreadStackMedium),
|
||||
(EPICSTHREADFUNC) motor_task, (void *) &targs);
|
||||
|
||||
return(OK);
|
||||
}
|
||||
|
||||
@@ -1,745 +0,0 @@
|
||||
/*
|
||||
FILENAME... drvMDrive.cc
|
||||
USAGE... Motor record driver level support for Intelligent Motion
|
||||
Systems, Inc. MDrive series; M17, M23, M34.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* Original Author: Ron Sluiter
|
||||
* Date: 03/21/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
|
||||
*
|
||||
* NOTES
|
||||
* -----
|
||||
* Verified with firmware:
|
||||
*
|
||||
* - 1.071 E
|
||||
* - 3.003 (MDI1FRD17B4-EQ)
|
||||
* - 3.010
|
||||
*
|
||||
* Modification Log:
|
||||
* -----------------
|
||||
* .01 03/21/03 rls copied from drvIM483PL.c
|
||||
* .02 02/03/04 rls Eliminate erroneous "Motor motion timeout ERROR".
|
||||
* .03 03/15/04 rls Previous driver releases not working. Fixed by adding
|
||||
* Kevin Peterson's eat_garbage() function. Added support
|
||||
* for encoder detection via "ident".
|
||||
* .04 07/01/04 rls Converted from MPF to asyn.
|
||||
* .05 09/20/04 rls - support for 32axes/controller.
|
||||
* - remove '?' command string padding.
|
||||
* .06 12/16/04 rls - asyn R4.0 support.
|
||||
* - make debug variables always available.
|
||||
* - MS Visual C compatibility; make all epicsExportAddress
|
||||
* extern "C" linkage.
|
||||
* .07 03/18/05 rls - Flexible MDrive I/O configuration.
|
||||
* - Change Echo mode to 2; eliminate eat_garbage().
|
||||
* .08 12/17/10 rls - Test to determine if encoder is present changed to
|
||||
* accommodate new firmware.
|
||||
* - support actual velocity status update.
|
||||
* .09 02/06/11 rls - "PR PN" response overflows input buffer; increased
|
||||
* BUFF_SIZE from 13 to 80 bytes.
|
||||
* - Slow "PR PN" response; increased timeout from 1 to 2 sec.
|
||||
* - Extra "\r\n" from "PR PN" response; buffer flush added.
|
||||
* - Eliminate compiler warnings on MDrive_axis[].
|
||||
*/
|
||||
|
||||
/*
|
||||
DESIGN LIMITATIONS...
|
||||
1 - Like all controllers, the MDrive must be powered-on when EPICS is first
|
||||
booted up.
|
||||
2 - The MDrive cannot be power cycled while EPICS is up and running. The
|
||||
consequences are permanent communication lose with the MDrive until
|
||||
EPICS is rebooted.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <epicsThread.h>
|
||||
#include <drvSup.h>
|
||||
#include <stdlib.h>
|
||||
#include <errlog.h>
|
||||
#include "motor.h"
|
||||
#include "drvIM483.h"
|
||||
#include "asynOctetSyncIO.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
#define MDrive_NUM_CARDS 8
|
||||
#define MAX_AXES 8
|
||||
#define BUFF_SIZE 80 /* Maximum length of string to/from MDrive */
|
||||
|
||||
/*----------------debugging-----------------*/
|
||||
volatile int drvMDrivedebug = 0;
|
||||
extern "C" {epicsExportAddress(int, drvMDrivedebug);}
|
||||
|
||||
static inline void Debug(int level, const char *format, ...) {
|
||||
#ifdef DEBUG
|
||||
if (level < drvMDrivedebug) {
|
||||
va_list pVar;
|
||||
va_start(pVar, format);
|
||||
vprintf(format, pVar);
|
||||
va_end(pVar);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* --- Local data. --- */
|
||||
int MDrive_num_cards = 0;
|
||||
static const char* const MDrive_axis[] = {"1", "2", "3", "4", "5", "6", "7", "8"};
|
||||
|
||||
/* Local data required for every driver; see "motordrvComCode.h" */
|
||||
#include "motordrvComCode.h"
|
||||
|
||||
/*----------------functions-----------------*/
|
||||
static int recv_mess(int, char *, int);
|
||||
static RTN_STATUS send_mess(int, char const *, const char *const);
|
||||
static int set_status(int, int);
|
||||
static long report(int);
|
||||
static long init();
|
||||
static int motor_init();
|
||||
static void query_done(int, int, struct mess_node *);
|
||||
|
||||
/*----------------functions-----------------*/
|
||||
|
||||
struct driver_table MDrive_access =
|
||||
{
|
||||
motor_init,
|
||||
motor_send,
|
||||
motor_free,
|
||||
motor_card_info,
|
||||
motor_axis_info,
|
||||
&mess_queue,
|
||||
&queue_lock,
|
||||
&free_list,
|
||||
&freelist_lock,
|
||||
&motor_sem,
|
||||
&motor_state,
|
||||
&total_cards,
|
||||
&any_motor_in_motion,
|
||||
(RTN_STATUS (*)(int, const char*, char*)) send_mess,
|
||||
recv_mess,
|
||||
set_status,
|
||||
query_done,
|
||||
NULL,
|
||||
&initialized,
|
||||
(char **) MDrive_axis
|
||||
};
|
||||
|
||||
struct drvMDrive_drvet
|
||||
{
|
||||
long number;
|
||||
long (*report) (int);
|
||||
long (*init) (void);
|
||||
} drvMDrive = {2, report, init};
|
||||
|
||||
extern "C" {epicsExportAddress(drvet, drvMDrive);}
|
||||
|
||||
static struct thread_args targs = {SCAN_RATE, &MDrive_access, 0.0};
|
||||
|
||||
|
||||
/*********************************************************
|
||||
* Print out driver status report
|
||||
*********************************************************/
|
||||
static long report(int level)
|
||||
{
|
||||
int card;
|
||||
|
||||
if (MDrive_num_cards <= 0)
|
||||
printf(" No MDrive controllers configured.\n");
|
||||
else
|
||||
{
|
||||
for (card = 0; card < MDrive_num_cards; card++)
|
||||
{
|
||||
struct controller *brdptr = motor_state[card];
|
||||
|
||||
if (brdptr == NULL)
|
||||
printf(" MDrive controller %d connection failed.\n", card);
|
||||
else
|
||||
{
|
||||
struct IM483controller *cntrl;
|
||||
|
||||
cntrl = (struct IM483controller *) brdptr->DevicePrivate;
|
||||
printf(" MDrive controller #%d, port=%s, id: %s \n", card,
|
||||
cntrl->asyn_port, brdptr->ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
static long init()
|
||||
{
|
||||
/*
|
||||
* We cannot call motor_init() here, because that function can do GPIB I/O,
|
||||
* and hence requires that the drvGPIB have already been initialized. That
|
||||
* cannot be guaranteed, so we need to call motor_init from device support
|
||||
*/
|
||||
/* Check for setup */
|
||||
if (MDrive_num_cards <= 0)
|
||||
{
|
||||
Debug(1, "init(): MDrive driver disabled. MDriveSetup() missing from startup script.\n");
|
||||
}
|
||||
return((long) 0);
|
||||
}
|
||||
|
||||
|
||||
static void query_done(int card, int axis, struct mess_node *nodeptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* FUNCTION NAME: set_status
|
||||
*
|
||||
* LOGIC:
|
||||
* Initialize.
|
||||
* Send "Moving Status" query.
|
||||
* Read response.
|
||||
* IF normal response to query.
|
||||
* Set communication status to NORMAL.
|
||||
* ELSE
|
||||
* IF communication status is NORMAL.
|
||||
* Set communication status to RETRY.
|
||||
* NORMAL EXIT.
|
||||
* ELSE
|
||||
* Set communication status error.
|
||||
* ERROR EXIT.
|
||||
* ENDIF
|
||||
* ENDIF
|
||||
*
|
||||
* IF "Moving Status" indicates any motion (i.e. status != 0).
|
||||
* Clear "Done Moving" status bit.
|
||||
* ELSE
|
||||
* Set "Done Moving" status bit.
|
||||
* ENDIF
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
static int set_status(int card, int signal)
|
||||
{
|
||||
struct IM483controller *cntrl;
|
||||
struct mess_node *nodeptr;
|
||||
register struct mess_info *motor_info;
|
||||
/* Message parsing variables */
|
||||
char buff[BUFF_SIZE];
|
||||
int rtnval, rtn_state;
|
||||
double motorData;
|
||||
epicsUInt8 Lswitch;
|
||||
bool plusdir, ls_active = false;
|
||||
msta_field status;
|
||||
|
||||
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
|
||||
input_config *confptr = cntrl->inconfig;
|
||||
motor_info = &(motor_state[card]->motor_info[signal]);
|
||||
nodeptr = motor_info->motor_motion;
|
||||
status.All = motor_info->status.All;
|
||||
|
||||
/* Protect against extra "\r\n" terminator. */
|
||||
rtn_state = recv_mess(card, buff, FLUSH);
|
||||
|
||||
send_mess(card, "PR MV", MDrive_axis[signal]);
|
||||
rtn_state = recv_mess(card, buff, 1);
|
||||
if (rtn_state > 0)
|
||||
{
|
||||
cntrl->status = NORMAL;
|
||||
status.Bits.CNTRL_COMM_ERR = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cntrl->status == NORMAL)
|
||||
{
|
||||
cntrl->status = RETRY;
|
||||
rtn_state = OK;
|
||||
goto exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
cntrl->status = COMM_ERR;
|
||||
status.Bits.CNTRL_COMM_ERR = 1;
|
||||
status.Bits.RA_PROBLEM = 1;
|
||||
rtn_state = 1;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
rtnval = atoi(buff);
|
||||
|
||||
status.Bits.RA_DONE = (rtnval != 0) ? 0 : 1;
|
||||
|
||||
/*
|
||||
* Parse motor position
|
||||
* Position string format: 1TP5.012,2TP1.123,3TP-100.567,...
|
||||
* Skip to substring for this motor, convert to double
|
||||
*/
|
||||
|
||||
send_mess(card, "PR P", MDrive_axis[signal]);
|
||||
recv_mess(card, buff, 1);
|
||||
|
||||
motorData = atof(buff);
|
||||
|
||||
if (motorData == motor_info->position)
|
||||
{
|
||||
if (nodeptr != 0) /* Increment counter only if motor is moving. */
|
||||
motor_info->no_motion_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
epicsInt32 newposition;
|
||||
|
||||
newposition = NINT(motorData);
|
||||
status.Bits.RA_DIRECTION = (newposition >= motor_info->position) ? 1 : 0;
|
||||
motor_info->position = newposition;
|
||||
motor_info->no_motion_count = 0;
|
||||
}
|
||||
|
||||
plusdir = (status.Bits.RA_DIRECTION) ? true : false;
|
||||
|
||||
if (confptr->plusLS == 0 || confptr->minusLS == 0)
|
||||
{
|
||||
status.Bits.RA_PLUS_LS = 0;
|
||||
status.Bits.RA_MINUS_LS = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(buff, "PR I%d", confptr->plusLS);
|
||||
send_mess(card, buff, MDrive_axis[signal]);
|
||||
recv_mess(card, buff, 1);
|
||||
Lswitch = atoi(buff);
|
||||
|
||||
/* Set limit Lswitch error indicators. */
|
||||
if (Lswitch != 0)
|
||||
{
|
||||
status.Bits.RA_PLUS_LS = 1;
|
||||
if (plusdir == true)
|
||||
ls_active = true;
|
||||
}
|
||||
else
|
||||
status.Bits.RA_PLUS_LS = 0;
|
||||
|
||||
sprintf(buff, "PR I%d", confptr->minusLS);
|
||||
send_mess(card, buff, MDrive_axis[signal]);
|
||||
recv_mess(card, buff, 1);
|
||||
Lswitch = atoi(buff);
|
||||
|
||||
if (Lswitch != 0)
|
||||
{
|
||||
status.Bits.RA_MINUS_LS = 1;
|
||||
if (plusdir == false)
|
||||
ls_active = true;
|
||||
}
|
||||
else
|
||||
status.Bits.RA_MINUS_LS = 0;
|
||||
}
|
||||
|
||||
if (confptr->homeLS == 0)
|
||||
status.Bits.RA_HOME = 0;
|
||||
else
|
||||
{
|
||||
sprintf(buff, "PR I%d", confptr->homeLS);
|
||||
send_mess(card, buff, MDrive_axis[signal]);
|
||||
recv_mess(card, buff, 1);
|
||||
Lswitch = atoi(buff);
|
||||
status.Bits.RA_HOME = (Lswitch) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* !!! Assume no closed-looped control!!! */
|
||||
status.Bits.EA_POSITION = 0;
|
||||
|
||||
/* encoder status */
|
||||
status.Bits.EA_SLIP = 0;
|
||||
status.Bits.EA_SLIP_STALL = 0;
|
||||
status.Bits.EA_HOME = 0;
|
||||
|
||||
if (motor_state[card]->motor_info[signal].encoder_present == NO)
|
||||
motor_info->encoder_position = 0;
|
||||
else
|
||||
{
|
||||
send_mess(card, "PR C2", MDrive_axis[signal]);
|
||||
recv_mess(card, buff, 1);
|
||||
motorData = atof(buff);
|
||||
motor_info->encoder_position = (epicsInt32) motorData;
|
||||
}
|
||||
|
||||
status.Bits.RA_PROBLEM = 0;
|
||||
|
||||
/* Get current motor velocity */
|
||||
|
||||
send_mess(card, "PR V", MDrive_axis[signal]);
|
||||
recv_mess(card, buff, 1);
|
||||
motorData = atof(buff);
|
||||
motor_info->velocity = (epicsInt32) motorData;
|
||||
|
||||
if (!status.Bits.RA_DIRECTION)
|
||||
motor_info->velocity *= -1;
|
||||
|
||||
rtn_state = (!motor_info->no_motion_count || ls_active == true ||
|
||||
status.Bits.RA_DONE | status.Bits.RA_PROBLEM) ? 1 : 0;
|
||||
|
||||
/* Test for post-move string. */
|
||||
if ((status.Bits.RA_DONE || ls_active == true) && nodeptr != 0 &&
|
||||
nodeptr->postmsgptr != 0)
|
||||
{
|
||||
strcpy(buff, nodeptr->postmsgptr);
|
||||
send_mess(card, buff, MDrive_axis[signal]);
|
||||
nodeptr->postmsgptr = NULL;
|
||||
}
|
||||
|
||||
exit:
|
||||
motor_info->status.All = status.All;
|
||||
return(rtn_state);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************/
|
||||
/* send a message to the MDrive board */
|
||||
/* send_mess() */
|
||||
/*****************************************************/
|
||||
static RTN_STATUS send_mess(int card, char const *com, const char *const name)
|
||||
{
|
||||
char local_buff[MAX_MSG_SIZE];
|
||||
struct IM483controller *cntrl;
|
||||
int comsize, namesize;
|
||||
size_t nwrite;
|
||||
|
||||
comsize = (com == NULL) ? 0 : strlen(com);
|
||||
namesize = (name == NULL) ? 0 : strlen(name);
|
||||
|
||||
if ((comsize + namesize) > MAX_MSG_SIZE)
|
||||
{
|
||||
errlogMessage("drvMDrive.c:send_mess(); message size violation.\n");
|
||||
return(ERROR);
|
||||
}
|
||||
else if (comsize == 0) /* Normal exit on empty input message. */
|
||||
return(OK);
|
||||
|
||||
if (!motor_state[card])
|
||||
{
|
||||
errlogPrintf("drvMDrive.c:send_mess() - invalid card #%d\n", card);
|
||||
return(ERROR);
|
||||
}
|
||||
|
||||
/* Make a local copy of the string and add the command line terminator. */
|
||||
if (namesize != 0)
|
||||
{
|
||||
strcpy(local_buff, name); /* put in axis */
|
||||
strcat(local_buff, com);
|
||||
}
|
||||
else
|
||||
strcpy(local_buff, com);
|
||||
|
||||
Debug(2, "send_mess(): message = %s\n", local_buff);
|
||||
|
||||
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
|
||||
pasynOctetSyncIO->write(cntrl->pasynUser, local_buff, strlen(local_buff),
|
||||
COMM_TIMEOUT, &nwrite);
|
||||
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************/
|
||||
/* receive a message from the MDrive board */
|
||||
/* recv_mess() */
|
||||
/*****************************************************/
|
||||
static int recv_mess(int card, char *com, int flag)
|
||||
{
|
||||
struct IM483controller *cntrl;
|
||||
const double timeout = 2.0;
|
||||
size_t nread = 0;
|
||||
asynStatus status = asynError;
|
||||
int eomReason;
|
||||
|
||||
/* Check that card exists */
|
||||
if (!motor_state[card])
|
||||
return(ERROR);
|
||||
|
||||
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
|
||||
|
||||
if (flag == FLUSH)
|
||||
pasynOctetSyncIO->flush(cntrl->pasynUser);
|
||||
else
|
||||
status = pasynOctetSyncIO->read(cntrl->pasynUser, com, BUFF_SIZE,
|
||||
timeout, &nread, &eomReason);
|
||||
|
||||
if ((status != asynSuccess) || (nread <= 0))
|
||||
{
|
||||
com[0] = '\0';
|
||||
nread = 0;
|
||||
}
|
||||
|
||||
Debug(2, "recv_mess(): message = \"%s\"\n", com);
|
||||
return(nread);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************/
|
||||
/* Setup system configuration */
|
||||
/* MDriveSetup() */
|
||||
/*****************************************************/
|
||||
RTN_STATUS
|
||||
MDriveSetup(int num_cards, /* maximum number of chains in system. */
|
||||
int scan_rate) /* polling rate - 1/60 sec units. */
|
||||
{
|
||||
int itera;
|
||||
|
||||
if (num_cards < 1 || num_cards > MDrive_NUM_CARDS)
|
||||
MDrive_num_cards = MDrive_NUM_CARDS;
|
||||
else
|
||||
MDrive_num_cards = num_cards;
|
||||
|
||||
/* Set motor polling task rate */
|
||||
if (scan_rate >= 1 && scan_rate <= 60)
|
||||
targs.motor_scan_rate = scan_rate;
|
||||
else
|
||||
targs.motor_scan_rate = SCAN_RATE;
|
||||
|
||||
/*
|
||||
* Allocate space for motor_state structures. Note this must be done
|
||||
* before IM483Config is called, so it cannot be done in motor_init() This
|
||||
* means that we must allocate space for a card without knowing if it
|
||||
* really exists, which is not a serious problem
|
||||
*/
|
||||
motor_state = (struct controller **)
|
||||
malloc(MDrive_num_cards * sizeof(struct controller *));
|
||||
|
||||
for (itera = 0; itera < MDrive_num_cards; itera++)
|
||||
motor_state[itera] = (struct controller *) NULL;
|
||||
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************/
|
||||
/* Configure a controller */
|
||||
/* MDriveConfig() */
|
||||
/*****************************************************/
|
||||
RTN_STATUS
|
||||
MDriveConfig(int card, /* chain being configured */
|
||||
const char *name) /* ASYN port name */
|
||||
{
|
||||
struct IM483controller *cntrl;
|
||||
|
||||
if (card < 0 || card >= MDrive_num_cards)
|
||||
return(ERROR);
|
||||
|
||||
motor_state[card] = (struct controller *) malloc(sizeof(struct controller));
|
||||
motor_state[card]->DevicePrivate = malloc(sizeof(struct IM483controller));
|
||||
cntrl = (struct IM483controller *) motor_state[card]->DevicePrivate;
|
||||
|
||||
strcpy(cntrl->asyn_port, name);
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************/
|
||||
/* initialize all software and hardware */
|
||||
/* This is called from the initialization routine in */
|
||||
/* device support. */
|
||||
/* motor_init() */
|
||||
/*****************************************************/
|
||||
static int motor_init()
|
||||
{
|
||||
struct controller *brdptr;
|
||||
struct IM483controller *cntrl;
|
||||
int card_index, motor_index;
|
||||
char buff[BUFF_SIZE];
|
||||
int total_axis = 0;
|
||||
int status;
|
||||
asynStatus success_rtn;
|
||||
static const char output_terminator[] = "\n";
|
||||
static const char input_terminator[] = "\r\n";
|
||||
|
||||
initialized = true; /* Indicate that driver is initialized. */
|
||||
|
||||
/* Check for setup */
|
||||
if (MDrive_num_cards <= 0)
|
||||
return(ERROR);
|
||||
|
||||
for (card_index = 0; card_index < MDrive_num_cards; card_index++)
|
||||
{
|
||||
if (!motor_state[card_index])
|
||||
continue;
|
||||
|
||||
brdptr = motor_state[card_index];
|
||||
brdptr->ident[0] = (char) NULL; /* No controller ID message. */
|
||||
brdptr->cmnd_response = false;
|
||||
total_cards = card_index + 1;
|
||||
cntrl = (struct IM483controller *) brdptr->DevicePrivate;
|
||||
|
||||
/* Initialize communications channel */
|
||||
success_rtn = pasynOctetSyncIO->connect(cntrl->asyn_port, 0,
|
||||
&cntrl->pasynUser, NULL);
|
||||
|
||||
if (success_rtn == asynSuccess)
|
||||
{
|
||||
pasynOctetSyncIO->setOutputEos(cntrl->pasynUser, output_terminator,
|
||||
strlen(output_terminator));
|
||||
pasynOctetSyncIO->setInputEos(cntrl->pasynUser, input_terminator,
|
||||
strlen(input_terminator));
|
||||
/* Send a message to the board, see if it exists */
|
||||
/* flush any junk at input port - should not be any data available */
|
||||
pasynOctetSyncIO->flush(cntrl->pasynUser);
|
||||
|
||||
for (total_axis = 0; total_axis < MAX_AXES; total_axis++)
|
||||
{
|
||||
int retry = 0;
|
||||
|
||||
/* Try 3 times to connect to controller. */
|
||||
do
|
||||
{
|
||||
send_mess(card_index, "PR VR", MDrive_axis[total_axis]);
|
||||
status = recv_mess(card_index, buff, 1);
|
||||
retry++;
|
||||
} while (status == 0 && retry < 3);
|
||||
|
||||
if (status <= 0)
|
||||
break;
|
||||
else if (total_axis == 0)
|
||||
strcpy(brdptr->ident, buff);
|
||||
}
|
||||
brdptr->total_axis = total_axis;
|
||||
cntrl->inconfig = (input_config *)
|
||||
malloc(sizeof(struct IM483controller) * total_axis);
|
||||
}
|
||||
|
||||
if (success_rtn == asynSuccess && total_axis > 0)
|
||||
{
|
||||
input_config *confptr = cntrl->inconfig;
|
||||
|
||||
brdptr->localaddr = (char *) NULL;
|
||||
brdptr->motor_in_motion = 0;
|
||||
|
||||
for (motor_index = 0; motor_index < total_axis; motor_index++)
|
||||
{
|
||||
int itera;
|
||||
bool encoder = false; /* Default to no encoder detected. */
|
||||
struct mess_info *motor_info = &brdptr->motor_info[motor_index];
|
||||
|
||||
motor_info->status.All = 0;
|
||||
motor_info->no_motion_count = 0;
|
||||
motor_info->encoder_position = 0;
|
||||
motor_info->position = 0;
|
||||
brdptr->motor_info[motor_index].motor_motion = NULL;
|
||||
|
||||
/* Determine if encoder present based last character of "ident". */
|
||||
if (brdptr->ident[strlen(brdptr->ident) - 1] == 'E')
|
||||
encoder = true;
|
||||
else
|
||||
{
|
||||
int size;
|
||||
|
||||
/* Try to get "Part number" (not in old firmware). */
|
||||
send_mess(card_index, "PR PN", MDrive_axis[motor_index]);
|
||||
size = recv_mess(card_index, buff, 1);
|
||||
if (size > 0)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
found |= !strcmp(&buff[size-1],"E");
|
||||
found |= !strcmp(&buff[size-2],"EQ");
|
||||
found |= !strcmp(&buff[size-2],"EE");
|
||||
found |= !strcmp(&buff[size-3],"EJM");
|
||||
found |= !strcmp(&buff[size-5],"EJM-N");
|
||||
encoder = found;
|
||||
}
|
||||
}
|
||||
|
||||
if (encoder == true)
|
||||
{
|
||||
motor_info->pid_present = YES;
|
||||
motor_info->status.Bits.GAIN_SUPPORT = 1;
|
||||
motor_info->encoder_present = YES;
|
||||
motor_info->status.Bits.EA_PRESENT = 1;
|
||||
}
|
||||
else
|
||||
motor_info->encoder_present = NO;
|
||||
|
||||
/* Protect against extra "\r\n" terminator. */
|
||||
status = recv_mess(card_index, buff, 1);
|
||||
|
||||
/* Determine input configuration. */
|
||||
confptr->homeLS = confptr->minusLS = confptr->plusLS = 0;
|
||||
|
||||
for (itera = 1; itera <= 4; itera++)
|
||||
{
|
||||
int type, active;
|
||||
|
||||
sprintf(buff, "PR S%d", itera);
|
||||
send_mess(card_index, buff, MDrive_axis[motor_index]);
|
||||
status = recv_mess(card_index, buff, 1);
|
||||
if (status == 0)
|
||||
{
|
||||
errlogPrintf("Error reading I/O configuration.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
status = sscanf(buff, "%d,%d", &type, &active);
|
||||
switch (type)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case 1: /* Home switch. */
|
||||
confptr->homeLS = itera;
|
||||
break;
|
||||
case 2: /* Plus limit switch. */
|
||||
confptr->plusLS = itera;
|
||||
break;
|
||||
case 3: /* Minus limit switch. */
|
||||
confptr->minusLS = itera;
|
||||
break;
|
||||
default:
|
||||
errlogPrintf("Invalid I/O type: %d.\n", type);
|
||||
|
||||
}
|
||||
}
|
||||
/* Test for missing configuration. */
|
||||
if (confptr->minusLS == 0 || confptr->plusLS == 0)
|
||||
{
|
||||
const char p_label[6] = "Plus", m_label[6] = "Minus";
|
||||
errlogPrintf("MDrive chain #%d, motor #%d %s LS not configured.\n",
|
||||
card_index, motor_index,
|
||||
(confptr->minusLS == 0) ? m_label : p_label);
|
||||
}
|
||||
|
||||
set_status(card_index, motor_index); /* Read status of each motor */
|
||||
}
|
||||
}
|
||||
else
|
||||
motor_state[card_index] = (struct controller *) NULL;
|
||||
}
|
||||
|
||||
any_motor_in_motion = 0;
|
||||
|
||||
mess_queue.head = (struct mess_node *) NULL;
|
||||
mess_queue.tail = (struct mess_node *) NULL;
|
||||
|
||||
free_list.head = (struct mess_node *) NULL;
|
||||
free_list.tail = (struct mess_node *) NULL;
|
||||
|
||||
epicsThreadCreate((char *) "MDrive_motor", epicsThreadPriorityMedium,
|
||||
epicsThreadGetStackSize(epicsThreadStackMedium),
|
||||
(EPICSTHREADFUNC) motor_task, (void *) &targs);
|
||||
|
||||
return(OK);
|
||||
}
|
||||
@@ -21,9 +21,6 @@ OmsAsynSrc_DEPEND_DIRS = MotorSrc
|
||||
DIRS += MotorSimSrc
|
||||
MotorSimSrc_DEPEND_DIRS = MotorSrc
|
||||
|
||||
DIRS += ImsSrc
|
||||
ImsSrc_DEPEND_DIRS = MotorSrc
|
||||
|
||||
DIRS += PIGCS2Src
|
||||
PIGCS2Src_DEPEND_DIRS = MotorSrc
|
||||
|
||||
|
||||
Reference in New Issue
Block a user