Removed AMCISrc; Added motorAMCI submodule

This commit is contained in:
kpetersn
2019-04-12 10:12:27 -05:00
parent 895257ee13
commit 6c4619d784
19 changed files with 5 additions and 2405 deletions
+3
View File
@@ -76,3 +76,6 @@
[submodule "modules/motorPI"]
path = modules/motorPI
url = https://github.com/epics-motor/motorPI.git
[submodule "modules/motorAMCI"]
path = modules/motorAMCI
url = https://github.com/epics-motor/motorAMCI.git
-107
View File
@@ -1,107 +0,0 @@
# ANF2 motors command file example (run in iocsh)
#
### Note: Modbus support (the EPICS modbus module) is required to be included in the
### EPICS application where the ANF2 support will be loaded. This file is an
### example of how to load the ANF2 support, in an ioc that is built with the
### EPICS modbus module.
epicsEnvSet("PORT1", "ANF2_C1")
epicsEnvSet("PORT2", "ANF2_C2")
# drvAsynIPPortConfigure("portName", "hostInfo", priority, noAutoConnect, noProcessEos);
drvAsynIPPortConfigure("$(PORT1)_IP","192.168.0.50:502",0,0,1)
drvAsynIPPortConfigure("$(PORT2)_IP","192.168.0.51:502",0,0,1)
# modbusInterposeConfig("portName", linkType, timeoutMsec, writeDelayMsec)
modbusInterposeConfig("$(PORT1)_IP",0,2000,0)
modbusInterposeConfig("$(PORT2)_IP",0,2000,0)
# NOTE: modbusLength = 10 * number of axes
# drvModbusAsynConfigure("portName", "tcpPortName", slaveAddress, modbusFunction,
# modbusStartAddress, modbusLength, dataType, pollMsec, "plcType")
drvModbusAsynConfigure("$(PORT1)_In", "$(PORT1)_IP", 0, 4, 0, 120, 0, 100, "ANF2_stepper")
drvModbusAsynConfigure("$(PORT2)_In", "$(PORT2)_IP", 0, 4, 0, 60, 0, 100, "ANF2_stepper")
# NOTE: modbusLength = 10 * number of axes
# drvModbusAsynConfigure("portName", "tcpPortName", slaveAddress, modbusFunction,
# modbusStartAddress, modbusLength, dataType, pollMsec, "plcType")
drvModbusAsynConfigure("$(PORT1)_Out", "$(PORT1)_IP", 0, 16, 1024, 120, 6, 1, "ANF2_stepper")
drvModbusAsynConfigure("$(PORT2)_Out", "$(PORT2)_IP", 0, 16, 1024, 60, 6, 1, "ANF2_stepper")
# Asyn traces for debugging
#!asynSetTraceIOMask "$(PORT1)_In",0,4
#!asynSetTraceMask "$(PORT1)_In",0,9
#!asynSetTraceIOMask "$(PORT1)_Out",0,4
#!asynSetTraceMask "$(PORT1)_Out",0,9
#!asynSetTraceInfoMask "$(PORT1)_Out",0,15
# Asyn records for debugging
#!dbLoadRecords("$(ASYN)/db/asynRecord.db","P=IOC:,R=asyn:c1ip,PORT=$(PORT1)_IP,ADDR=0,OMAX=256,IMAX=256")
#!dbLoadRecords("$(ASYN)/db/asynRecord.db","P=IOC:,R=asyn:c1in,PORT=$(PORT1)_In,ADDR=0,OMAX=256,IMAX=256")
#!dbLoadRecords("$(ASYN)/db/asynRecord.db","P=IOC:,R=asyn:c1out,PORT=$(PORT1)_Out,ADDR=0,OMAX=256,IMAX=256")
#!dbLoadRecords("$(ASYN)/db/asynRecord.db","P=IOC:,R=asyn:c1,PORT=$(PORT1),ADDR=0,OMAX=256,IMAX=256")
# Load the motor records
dbLoadTemplate("templates/motor.substitutions.ANF2")
# AMCI ANF2 stepper controller driver support
#
# ANF2CreateController(
# portName, The name of the asyn port that will be created by this driver
# ANF2InPortName, The name of the In drvAsynIPPPort to read from the ANF2 controller
# ANF2OutPortName, The name of the Out drvAsynIPPPort to write to the ANF2 controller
# numAxes) The number of axes in the stack (max=12)
#
# ANF2CreateAxis(
# ANF2Name, The controller's asyn port
# axis, The axis to be configured (zero-based numbering)
# hexConfig, The desired hex configuration (see manual & AMCI Net Configurator for details)
# baseSpeed, The base speed (steps/second; min=1, max=1,000,000)
# homingTimeout) The homing timeout (integer number of seconds; min=0, max=300)
#
# Note: The base speed can't be changed using the VBAS field of the motor record, but the driver
# does correct the acceleration sent by the motor record to give the desired acceleration time.
# Controller 1 (One ANF2E, Five ANF2's)
ANF2CreateController("$(PORT1)", "$(PORT1)_In", "$(PORT1)_Out", 12)
# Axes for Controller 1
ANF2CreateAxis("$(PORT1)", 0, "0x86280000", 100, 0)
ANF2CreateAxis("$(PORT1)", 1, "0x86000000", 100, 0)
ANF2CreateAxis("$(PORT1)", 2, "0x84000000", 100, 0)
ANF2CreateAxis("$(PORT1)", 3, "0x84000000", 100, 0)
ANF2CreateAxis("$(PORT1)", 4, "0x84000000", 100, 0)
ANF2CreateAxis("$(PORT1)", 5, "0x84000000", 100, 0)
ANF2CreateAxis("$(PORT1)", 6, "0x84000000", 100, 0)
ANF2CreateAxis("$(PORT1)", 7, "0x84000000", 100, 0)
ANF2CreateAxis("$(PORT1)", 8, "0x84000000", 100, 0)
ANF2CreateAxis("$(PORT1)", 9, "0x84000000", 100, 0)
ANF2CreateAxis("$(PORT1)", 10, "0x84000000", 100, 0)
ANF2CreateAxis("$(PORT1)", 11, "0x84000000", 100, 0)
# Controller 2 (One ANF1E, Five ANF1's)
ANF2CreateController("$(PORT2)", "$(PORT2)_In", "$(PORT2)_Out", 6)
# Axes for Controller 2
ANF2CreateAxis("$(PORT2)", 0, "0x84000000", 100, 0)
ANF2CreateAxis("$(PORT2)", 1, "0x84000000", 100, 0)
ANF2CreateAxis("$(PORT2)", 2, "0x84000000", 100, 0)
ANF2CreateAxis("$(PORT2)", 3, "0x84000000", 100, 0)
ANF2CreateAxis("$(PORT2)", 4, "0x84000000", 100, 0)
ANF2CreateAxis("$(PORT2)", 5, "0x84000000", 100, 0)
# NOTE: the poller needs to be started after iocInit
##########
# iocInit
##########
# ANF2StartPoller(
# portName, The controller's asyn port
# movingPollPeriod, The time in ms between polls when any axis is moving
# idlePollPeriod) The time in ms between polls when no axis is moving
#
ANF2StartPoller("$(PORT1)", 200, 1000)
ANF2StartPoller("$(PORT2)", 200, 1000)
@@ -1,54 +0,0 @@
file "$(MOTOR)/motorApp/Db/asyn_motor.db"
{
pattern
{P, M, DTYP, PORT, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, DHLM, DLLM, INIT, RTRY}
{IOC:, "m1", "asynMotor", "ANF2_C1", 0, "ANF2 C1 M1", steps, Pos, 100, 0, .2, 0, 50, .2, 1, 5, 1e9, -1e9, ""}
{IOC:, "m2", "asynMotor", "ANF2_C1", 1, "ANF2 C1 M2", steps, Pos, 100, 0, .2, 0, 50, .2, 1, 5, 1e9, -1e9, ""}
{IOC:, "m3", "asynMotor", "ANF2_C1", 2, "ANF2 C1 M3", steps, Pos, 100, 0, .2, 0, 50, .2, 1, 5, 1e9, -1e9, ""}
{IOC:, "m4", "asynMotor", "ANF2_C1", 3, "ANF2 C1 M4", steps, Pos, 100, 0, .2, 0, 50, .2, 1, 5, 1e9, -1e9, ""}
{IOC:, "m5", "asynMotor", "ANF2_C1", 4, "ANF2 C1 M5", steps, Pos, 100, 0, .2, 0, 50, .2, 1, 5, 1e9, -1e9, ""}
{IOC:, "m6", "asynMotor", "ANF2_C1", 5, "ANF2 C1 M6", steps, Pos, 100, 0, .2, 0, 50, .2, 1, 5, 1e9, -1e9, ""}
{IOC:, "m7", "asynMotor", "ANF2_C1", 6, "ANF2 C1 M7", steps, Pos, 100, 0, .2, 0, 50, .2, 1, 5, 1e9, -1e9, ""}
{IOC:, "m8", "asynMotor", "ANF2_C1", 7, "ANF2 C1 M8", steps, Pos, 100, 0, .2, 0, 50, .2, 1, 5, 1e9, -1e9, ""}
{IOC:, "m9", "asynMotor", "ANF2_C1", 8, "ANF2 C1 M9", steps, Pos, 100, 0, .2, 0, 50, .2, 1, 5, 1e9, -1e9, ""}
{IOC:, "m10", "asynMotor", "ANF2_C1", 9, "ANF2 C1 M10", steps, Pos, 100, 0, .2, 0, 50, .2, 1, 5, 1e9, -1e9, ""}
{IOC:, "m11", "asynMotor", "ANF2_C1", 10, "ANF2 C1 M11", steps, Pos, 100, 0, .2, 0, 50, .2, 1, 5, 1e9, -1e9, ""}
{IOC:, "m12", "asynMotor", "ANF2_C1", 11, "ANF2 C1 M12", steps, Pos, 100, 0, .2, 0, 50, .2, 1, 5, 1e9, -1e9, ""}
{IOC:, "m13", "asynMotor", "ANF2_C2", 0, "ANF2 C2 M1", steps, Pos, 100, 0, .2, 0, 50, .2, 1, 5, 1e9, -1e9, ""}
{IOC:, "m14", "asynMotor", "ANF2_C2", 1, "ANF2 C2 M2", steps, Pos, 100, 0, .2, 0, 50, .2, 1, 5, 1e9, -1e9, ""}
{IOC:, "m15", "asynMotor", "ANF2_C2", 2, "ANF2 C2 M3", steps, Pos, 100, 0, .2, 0, 50, .2, 1, 5, 1e9, -1e9, ""}
{IOC:, "m16", "asynMotor", "ANF2_C2", 3, "ANF2 C2 M4", steps, Pos, 100, 0, .2, 0, 50, .2, 1, 5, 1e9, -1e9, ""}
{IOC:, "m17", "asynMotor", "ANF2_C2", 4, "ANF2 C2 M5", steps, Pos, 100, 0, .2, 0, 50, .2, 1, 5, 1e9, -1e9, ""}
{IOC:, "m18", "asynMotor", "ANF2_C2", 5, "ANF2 C2 M6", steps, Pos, 100, 0, .2, 0, 50, .2, 1, 5, 1e9, -1e9, ""}
}
file "$(MOTOR)/motorApp/Db/ANF2Aux.template"
{
pattern
{P, R, PORT, ADDR}
{IOC:, m1:, "ANF2_C1", 0}
{IOC:, m2:, "ANF2_C1", 1}
{IOC:, m3:, "ANF2_C1", 2}
{IOC:, m4:, "ANF2_C1", 3}
{IOC:, m5:, "ANF2_C1", 4}
{IOC:, m6:, "ANF2_C1", 5}
{IOC:, m7:, "ANF2_C1", 6}
{IOC:, m8:, "ANF2_C1", 7}
{IOC:, m9:, "ANF2_C1", 8}
{IOC:, m10:, "ANF2_C1", 9}
{IOC:, m11:, "ANF2_C1", 10}
{IOC:, m12:, "ANF2_C1", 11}
{IOC:, m13:, "ANF2_C2", 0}
{IOC:, m14:, "ANF2_C2", 1}
{IOC:, m15:, "ANF2_C2", 2}
{IOC:, m16:, "ANF2_C2", 3}
{IOC:, m17:, "ANF2_C2", 4}
{IOC:, m18:, "ANF2_C2", 5}
}
@@ -1,17 +0,0 @@
file "$(MOTOR)/motorApp/Db/basic_asyn_motor.db"
{
pattern
{P, N, M, DTYP, PORT, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, DHLM, DLLM, INIT, RTRY}
{IOC:, 1, "m$(N)", "asynMotor", "ANG1_1_1", 0, "AMCI ANG1 1", steps, Pos, 100, 0, .2, 0, 50, .2, 1, 5, 1e9, -1e9, ""}
{IOC:, 2, "m$(N)", "asynMotor", "ANG1_1_2", 0, "AMCI ANG1 2", steps, Pos, 100, 0, .2, 0, 50, .2, 1, 5, 1e9, -1e9, ""}
}
file "$(MOTOR)/motorApp/Db/ANG1Aux.template"
{
pattern
{P, R, PORT, ADDR}
{IOC:, m1:, ANG1_1_1, 0}
{IOC:, m2:, ANG1_1_2, 0}
}
-74
View File
@@ -1,74 +0,0 @@
# ANG1 motors Command file Example
#
### Note: Modbus support (the EPICS modbus module) is required to be included in the
### EPICS application where the ANG1 support will be loaded. This file is an
### example of how to load the ANG1 support, in an ioc that is built with the
### EPICS modbus module.
# Use the following commands for TCP/IP
#drvAsynIPPortConfigure(const char *portName,
# const char *hostInfo,
# unsigned int priority,
# int noAutoConnect,
# int noProcessEos);
# One per controller. One controller can support up to six drivers.
drvAsynIPPortConfigure("ang1_1","164.54.53.107:502",0,0,1)
#drvAsynIPPortConfigure("ang1_2","164.54.53.23:502",0,0,1)
#drvAsynIPPortConfigure("ang1_3","164.54.xxx.xxx:502",0,0,1)
#modbusInterposeConfig(const char *portName,
# modbusLinkType linkType,
# int timeoutMsec,
# int writeDelayMsec)
# One per controller. One controller can support up to six drivers.
modbusInterposeConfig("ang1_1",0,2000,0)
#modbusInterposeConfig("ang1_2",0,2000,0)
# Word access at Modbus address 0
# Access 1 words as inputs.
# Function code=3
# default data type unsigned integer.
# drvModbusAsynConfigure("portName", "tcpPortName", slaveAddress, modbusFunction, modbusStartAddress, modbusLength, dataType, pollMsec, "plcType")
# One per axis. Note: "ANG1" is the AMCI model. "ANG1_1" is the controller. "ANG1_1_1" is the axis.
drvModbusAsynConfigure("ANG1_1_1_In_Word", "ang1_1", 0, 4, 0, 10, 0, 100, "ANG1_stepper")
drvModbusAsynConfigure("ANG1_1_2_In_Word", "ang1_1", 0, 4, 10, 10, 0, 100, "ANG1_stepper")
#drvModbusAsynConfigure("ANG1_1_3_In_Word", "ang1_1", 0, 3, 20, 10, 0, 100, "ANG1_stepper")
#drvModbusAsynConfigure("ANG1_1_4_In_Word", "ang1_1", 0, 3, 30, 10, 0, 100, "ANG1_stepper")
#drvModbusAsynConfigure("ANG1_1_5_In_Word", "ang1_1", 0, 3, 40, 10, 0, 100, "ANG1_stepper")
#drvModbusAsynConfigure("ANG1_1_6_In_Word", "ang1_1", 0, 3, 50, 10, 0, 100, "ANG1_stepper")
# Access 1 words as outputs.
# Either function code=6 (single register) or 16 (multiple registers) can be used, but 16
# is better because it is "atomic" when writing values longer than 16-bits.
# Default data type unsigned integer.
# drvModbusAsynConfigure("portName", "tcpPortName", slaveAddress, modbusFunction, modbusStartAddress, modbusLength, dataType, pollMsec, "plcType")
# Not sure why the outputs can't be configured for Modbus data type 4?
drvModbusAsynConfigure("ANG1_1_1_Out_Word", "ang1_1", 0, 6, 1024, 10, 0, 1, "ANG1_stepper")
drvModbusAsynConfigure("ANG1_1_2_Out_Word", "ang1_1", 0, 6, 1034, 10, 0, 1, "ANG1_stepper")
#drvModbusAsynConfigure("ANG1_1_3_Out_Word", "ang1_1", 0, 6, 1044, 10, 0, 1, "ANG1_stepper")
#drvModbusAsynConfigure("ANG1_1_4_Out_Word", "ang1_1", 0, 6, 1054, 10, 0, 1, "ANG1_stepper")
#drvModbusAsynConfigure("ANG1_1_5_Out_Word", "ang1_1", 0, 6, 1064, 10, 0, 1, "ANG1_stepper")
#drvModbusAsynConfigure("ANG1_1_6_Out_Word", "ang1_1", 0, 6, 1074, 10, 0, 1, "ANG1_stepper")
# Second ANG1 controller...
#drvModbusAsynConfigure("ANG1_2_1_Out_Word_0", "ang1_2", 0, 6, 1024, 1, 0, 1, "ANG1_stepper")
dbLoadTemplate("ANG1_motors.substitutions")
# AMCI ANG1 stepper controller/driver support
# portName The name of the asyn port that will be created for this driver
# ANG1InPortName The name of the In drvAsynIPPPort that was created previously to connect to the ANG1 controller
# ANG1OutPortName The name of the Out drvAsynIPPPort that was created previously to connect to the ANG1 controller
# numAxes The number of axes that this controller supports
# movingPollPeriod The time in ms between polls when any axis is moving
# idlePollPeriod The time in ms between polls when no axis is moving
#
# One per axis. Note: "ANG1" is the AMCI model. "ANG1_1" is the controller. "ANG1_1_1" is the axis.
# Also, ANG1_1_1 is the port name needed for motor.substitutions.
ANG1CreateController(ANG1_1_1, ANG1_1_1_In_Word, ANG1_1_1_Out_Word, 1, 100, 0)
ANG1CreateController(ANG1_1_2, ANG1_1_2_In_Word, ANG1_1_2_Out_Word, 1, 100, 0)
#ANG1CreateController(ANG1_1, ANG1_1_3_In_Word, ANG1_1_3_Out_Word, 1, 100, 0)
#ANG1CreateController(ANG1_1, ANG1_1_4_In_Word, ANG1_1_4_Out_Word, 1, 100, 0)
#ANG1CreateController(ANG1_1, ANG1_1_5_In_Word, ANG1_1_5_Out_Word, 1, 100, 0)
#ANG1CreateController(ANG1_1, ANG1_1_6_In_Word, ANG1_1_6_Out_Word, 1, 100, 0)
+1
View File
@@ -5,6 +5,7 @@ include $(TOP)/configure/CONFIG
#SUBMODULES += motorVendor
SUBMODULES += motorOms
SUBMODULES += motorNewport
SUBMODULES += motorAMCI
SUBMODULES += motorACR
SUBMODULES += motorAcs
SUBMODULES += motorAcsTech80
+1
Submodule modules/motorAMCI added at 0bbd957f21
-2
View File
@@ -1,2 +0,0 @@
include "ANG1Support.dbd"
include "ANF2Support.dbd"
File diff suppressed because it is too large Load Diff
-151
View File
@@ -1,151 +0,0 @@
/*
FILENAME... ANF2Driver.h
USAGE... Motor driver support for the AMCI ANF2 controller.
Kevin Peterson
Based on the AMCI ANG1 Model 3 device driver written by Kurt Goetze
*/
#include "asynMotorController.h"
#include "asynMotorAxis.h"
//#include <asynInt32Array.h>
#include <asynInt32ArraySyncIO.h>
#define MAX_AXES 12
#define MAX_INPUT_REGS 10
#define MAX_OUTPUT_REGS 10
#define AXIS_REG_OFFSET 10
/*** Input CMD Registers (16-bit) ***/
#define STATUS_1 0
#define STATUS_2 1
#define POS_RD_UPR 2
#define POS_RD_LWR 3
#define EN_POS_UPR 4
#define EN_POS_LWR 5
#define EN_CAP_UPR 6
#define EN_CAP_LWR 7
// Not used must equal zero #define RESERVED 8
#define NET_CONN 9
/*** Output Command Registers (32-bit) ***/
#define COMMAND 0
#define POSITION 1
#define SPEED 2
#define ACCEL_DECEL 3
#define CMD_REG_4 4
/*** Output Configuration Registers (32-bit) ***/
#define CONFIGURATION 0
#define BASE_SPEED 1
#define HOME_TIMEOUT 2
#define CONFIG_REG_3 3
#define CONFIG_REG_4 4
// No. of controller-specific parameters
#define NUM_ANF2_PARAMS 2
/** drvInfo strings for extra parameters that the ACR controller supports */
#define ANF2ResetErrorsString "ANF2_RESET_ERRORS"
#define ANF2GetInfoString "ANF2_GET_INFO"
//#define ANF2ReconfigString "ANF2_RECONFIG"
class ANF2Axis : public asynMotorAxis
{
public:
/* These are the methods we override from the base class */
ANF2Axis(class ANF2Controller *pC, int axisNo, epicsInt32 config, epicsInt32 baseSpeed, epicsInt32 homingTimeout);
void report(FILE *fp, int level);
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration);
asynStatus moveVelocity(double min_velocity, double max_velocity, double acceleration);
asynStatus home(double min_velocity, double max_velocity, double acceleration, int forwards);
asynStatus stop(double acceleration);
asynStatus poll(bool *moving);
asynStatus setPosition(double position);
asynStatus setClosedLoop(bool closedLoop);
private:
ANF2Controller *pC_; /**< Pointer to the asynMotorController to which this axis belongs.
* Abbreviated because it is used very frequently */
asynStatus sendAccelAndVelocity(double accel, double velocity);
double correctAccel(double minVelocity, double maxVelocity, double acceleration);
asynStatus resetErrors();
void getInfo();
epicsInt32 inputReg_[10];
//void reconfig(epicsInt32 value);
void zeroRegisters(epicsInt32 *reg);
asynUser *pasynUserForceRead_;
int axisNo_;
epicsInt32 baseSpeed_;
epicsInt32 homingTimeout_;
epicsInt32 config_;
epicsInt32 motionReg_[5];
epicsInt32 confReg_[5];
epicsInt32 zeroReg_[5];
bool jogging_;
// Configuration bits
short CaptInput_;
short ExtInput_;
short HomeInput_;
short CWInput_;
short CCWInput_;
short BHPO_;
short QuadEnc_;
short DiagFbk_;
short OutPulse_;
short HomeOp_;
short CardAxis_;
short OpMode_;
// LSW
short CaptInputAS_;
short ExtInputAS_;
short HomeInputAS_;
short CWInputAS_;
short CCWInputAS_;
friend class ANF2Controller;
};
class ANF2Controller : public asynMotorController {
public:
ANF2Controller(const char *portName, const char *ANF2InPortName, const char *ANF2OutPortName, int numAxes);
void doStartPoller(double movingPollPeriod, double idlePollPeriod);
void report(FILE *fp, int level);
ANF2Axis* getAxis(asynUser *pasynUser);
ANF2Axis* getAxis(int axisNo);
asynUser *pasynUserInReg_[MAX_AXES][MAX_INPUT_REGS];
asynUser *pasynUserOutReg_[MAX_AXES];
/* These are the methods that we override from asynMotorDriver */
asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
//asynStatus writeFloat64(asynUser *pasynUser, epicsFloat64 value);
//void report(FILE *fp, int level);
/* These are the methods that are new to this class */
protected:
int ANF2ResetErrors_; /** Reset Errors parameter index */
int ANF2GetInfo_; /**< Get Info parameter index */
//int ANF2Reconfig_; /**< Reconfig parameter index */
private:
asynStatus writeReg16(int, int, int, double);
asynStatus writeReg32(int, int, int, double);
asynStatus writeReg32Array(int, epicsInt32*, int, double);
asynStatus readReg16(int, int, epicsInt32*, double);
asynStatus readReg32(int, int, epicsInt32*, double);
char *inputDriver_;
int numAxes_;
int numModules_;
int axesPerModule_;
double movingPollPeriod_;
double idlePollPeriod_;
int axesCreated_;
friend class ANF2Axis;
};
-2
View File
@@ -1,2 +0,0 @@
#registrar(ANF2MotorRegister)
registrar(ANF2Register)
-640
View File
@@ -1,640 +0,0 @@
/*
FILENAME... ANG1Driver.cpp
USAGE... Motor record driver support for the AMCI ANG1 stepper motor controller over Modbus/TCP.
Kurt Goetze
Based on the ACS MCB-4B Model 3 device driver written by Mark Rivers
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <errno.h>
#include <iocsh.h>
#include <epicsThread.h>
#include <epicsString.h>
#include <asynInt32SyncIO.h>
#include "ANG1Driver.h"
#include <epicsExport.h>
#define NINT(f) (int)((f)>0 ? (f)+0.5 : (f)-0.5)
static const char *driverName = "ANG1MotorDriver";
/*** Input Registers ***/
#define STATUS_1 0
#define STATUS_2 1
#define POS_RD_UPR 2
#define POS_RD_LWR 3
#define EN_POS_UPR 4
#define EN_POS_LWR 5
#define EN_CAP_UPR 6
#define EN_CAP_LWR 7
#define MOT_CUR 8 // programmed motor current (x10)
#define JERK_RD 9
/*** Output Registers ***/
#define CMD_MSW 0 // module 0 starts at register address 1024. This is set in drvModbusAsynConfigure.
#define CMD_LSW 1
#define POS_WR_UPR 2
#define POS_WR_LWR 3
#define SPD_UPR 4
#define SPD_LWR 5
#define ACCEL 6
#define DECEL 7
// Not used must equal zero #define RESERVED 8
#define JERK 9
/** Constructor, Creates a new ANG1Controller object.
* \param[in] portName The name of the asyn port that will be created for this driver
* \param[in] ANG1InPortName The name of the drvAsynSerialPort that was created previously to connect to the ANG1 controller
* \param[in] ANG1OutPortName The name of the drvAsynSerialPort that was created previously to connect to the ANG1 controller
* \param[in] numAxes The number of axes that this controller supports
* \param[in] movingPollPeriod The time between polls when any axis is moving
* \param[in] idlePollPeriod The time between polls when no axis is moving
*/
ANG1Controller::ANG1Controller(const char *portName, const char *ANG1InPortName, const char *ANG1OutPortName, int numAxes,
double movingPollPeriod, double idlePollPeriod)
: asynMotorController(portName, numAxes, NUM_ANG1_PARAMS,
0, // No additional interfaces beyond those in base class
0, // No additional callback interfaces beyond those in base class
ASYN_CANBLOCK | ASYN_MULTIDEVICE,
1, // autoconnect
0, 0) // Default priority and stack size
{
int axis, i;
asynStatus status;
ANG1Axis *pAxis;
static const char *functionName = "ANG1Controller::ANG1Controller";
inputDriver_ = epicsStrDup(ANG1InPortName); // Set this before calls to create Axis objects
// Create controller-specific parameters
createParam(ANG1JerkString, asynParamInt32, &ANG1Jerk_);
/* Connect to ANG1 controller */
for (i=0; i<MAX_INPUT_REGS; i++) {
status = pasynInt32SyncIO->connect(ANG1InPortName, i, &pasynUserInReg_[i], NULL);
}
for (i=0; i<MAX_OUTPUT_REGS; i++) {
status = pasynInt32SyncIO->connect(ANG1OutPortName, i, &pasynUserOutReg_[i], NULL);
}
if (status) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s: cannot connect to ANG1 controller\n",
functionName);
}
for (axis=0; axis<numAxes; axis++) {
pAxis = new ANG1Axis(this, axis);
}
startPoller(movingPollPeriod, idlePollPeriod, 2);
}
/** Creates a new ANG1Controller object.
* Configuration command, called directly or from iocsh
* \param[in] portName The name of the asyn port that will be created for this driver
* \param[in] ANG1InPortName The name of the drvAsynIPPPort that was created previously to connect to the ANG1 controller
* \param[in] ANG1OutPortName The name of the drvAsynIPPPort that was created previously to connect to the ANG1 controller
* \param[in] numAxes The number of axes that this controller supports
* \param[in] movingPollPeriod The time in ms between polls when any axis is moving
* \param[in] idlePollPeriod The time in ms between polls when no axis is moving
*/
extern "C" int ANG1CreateController(const char *portName, const char *ANG1InPortName, const char *ANG1OutPortName, int numAxes,
int movingPollPeriod, int idlePollPeriod)
{
ANG1Controller *pANG1Controller
= new ANG1Controller(portName, ANG1InPortName, ANG1OutPortName, numAxes, movingPollPeriod/1000., idlePollPeriod/1000.);
pANG1Controller = NULL;
return(asynSuccess);
}
/** Reports on status of the driver
* \param[in] fp The file pointer on which report information will be written
* \param[in] level The level of report detail desired
*
* If details > 0 then information is printed about each axis.
* After printing controller-specific information it calls asynMotorController::report()
*/
void ANG1Controller::report(FILE *fp, int level)
{
fprintf(fp, "ANG1 motor driver %s, numAxes=%d, moving poll period=%f, idle poll period=%f\n",
this->portName, numAxes_, movingPollPeriod_, idlePollPeriod_);
// Call the base class method
asynMotorController::report(fp, level);
}
/** Returns a pointer to an ANG1Axis object.
* Returns NULL if the axis number encoded in pasynUser is invalid.
* \param[in] pasynUser asynUser structure that encodes the axis index number. */
ANG1Axis* ANG1Controller::getAxis(asynUser *pasynUser)
{
// ? return static_cast<ANG1Axis*>(asynMotorController::getAxis(pANG1Axis methodsasynUser));
return static_cast<ANG1Axis*>(asynMotorController::getAxis(pasynUser));
}
/** Returns a pointer to an ANG1Axis object.
* Returns NULL if the axis number encoded in pasynUser is invalid.
* \param[in] No Axis index number. */
ANG1Axis* ANG1Controller::getAxis(int axisNo)
{
return static_cast<ANG1Axis*>(asynMotorController::getAxis(axisNo));
}
/** Called when asyn clients call pasynInt32->write().
* Extracts the function and axis number from pasynUser.
* Sets the value in the parameter library (?)
* If the function is ANG1Jerk_ it sets the jerk value in the controller.
* Calls any registered callbacks for this pasynUser->reason and address.
* For all other functions it calls asynMotorController::writeInt32.
* \param[in] pasynUser asynUser structure that encodes the reason and address.
* \param[in] value Value to write. */
asynStatus ANG1Controller::writeInt32(asynUser *pasynUser, epicsInt32 value)
{
int function = pasynUser->reason;
asynStatus status = asynSuccess;
ANG1Axis *pAxis = getAxis(pasynUser);
static const char *functionName = "writeInt32";
/* Set the parameter and readback in the parameter library. */
status = setIntegerParam(pAxis->axisNo_, function, value);
if (function == ANG1Jerk_)
{
// Jerk in units steps/sec/sec/sec (0 - 5000)
printf("Jerk = %d\n", value);
status = writeReg16(JERK, value, DEFAULT_CONTROLLER_TIMEOUT);
// sprintf(outString_, "%s JOG JRK %f", pAxis->axisName_, value);
// status = writeController();
} else {
/* Call base class method */
status = asynMotorController::writeInt32(pasynUser, value);
}
/* Do callbacks so higher layers see any changes */
pAxis->callParamCallbacks();
if (status)
asynPrint(pasynUser, ASYN_TRACE_ERROR,
"%s:%s: error, status=%d function=%d, value=%d\n",
driverName, functionName, status, function, value);
else
asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
"%s:%s: function=%d, value=%d\n",
driverName, functionName, function, value);
return status;
}
asynStatus ANG1Controller::writeReg16(int reg, int output, double timeout)
{
asynStatus status;
//printf("writeReg16: writing %d to register %d\n", output, reg);
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,"writeReg16: writing %d to register %d\n", output, reg);
status = pasynInt32SyncIO->write(pasynUserOutReg_[reg], output, timeout);
epicsThreadSleep(0.01);
return status ;
}
asynStatus ANG1Controller::writeReg32(int reg, int output, double timeout)
{
//.. break 32-bit integer into 2 pieces
//.. write the pieces into ANG1 registers
asynStatus status;
float fnum;
int lower,upper;
fnum = (output / 1000.0);
upper = (int)fnum;
fnum = fnum - upper;
fnum = NINT(fnum * 1000);
lower = (int)fnum;
//could write the words this way
// status = pasynInt32SyncIO->write(pasynUserOutReg_[reg], upper, timeout);
// status = pasynInt32SyncIO->write(pasynUserOutReg_[reg+1], lower, timeout);
//or this way
// writeReg16(piece1 ie MSW ...
status = writeReg16(reg, upper, DEFAULT_CONTROLLER_TIMEOUT);
// writeReg16(piece2 ie LSW ...
reg++;
status = writeReg16(reg, lower, DEFAULT_CONTROLLER_TIMEOUT);
return status ;
}
asynStatus ANG1Controller::readReg16(int reg, epicsInt32 *input, double timeout)
{
asynStatus status;
//printf("reg = %d\n", reg);
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,"readReg16 reg = %d\n", reg);
status = pasynInt32SyncIO->read(pasynUserInReg_[reg], input, timeout);
return status ;
}
asynStatus ANG1Controller::readReg32(int reg, epicsInt32 *combo, double timeout)
{
//.. read 2 16-bit words from ANG1 registers
//.. assemble 2 16-bit pieces into 1 32-bit integer
asynStatus status;
// float fnum;
epicsInt32 lowerWord32, upperWord32; // only have pasynInt32SyncIO, not pasynInt16SyncIO ,
epicsInt16 lowerWord16, upperWord16; // so we need to get 32-bits and cast to 16-bit integer
//printf("calling readReg16\n");
status = readReg16(reg, &upperWord32, timeout); //get Upper Word
upperWord16 = (epicsInt16)upperWord32;
//printf("upperWord16: %d\n", upperWord16);
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,"readReg32 upperWord16: %d\n", upperWord16);
// if status != 1 :
reg++;
status = readReg16(reg, &lowerWord32, timeout); //get Lower Word
lowerWord16 = (epicsInt16)lowerWord32;
//printf("lowerWord16: %d\n", lowerWord16);
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,"readReg32 lowerWord16: %d\n", lowerWord16);
*combo = NINT((upperWord16 * 1000) + lowerWord16);
return status ;
}
// ANG1Axis methods Here
// These are the ANG1Axis methods
/** Creates a new ANG1Axis object.
* \param[in] pC Pointer to the ANG1Controller to which this axis belongs.
* \param[in] axisNo Index number of this axis, range 0 to pC->numAxes_-1.
*
* Initializes register numbers, etc.
*/
ANG1Axis::ANG1Axis(ANG1Controller *pC, int axisNo)
: asynMotorAxis(pC, axisNo),
pC_(pC)
{
int status;
//status = pasynInt32SyncIO->connect(myModbusInputDriver, 0, &pasynUserForceRead_, "MODBUS_READ");
status = pasynInt32SyncIO->connect(pC_->inputDriver_, 0, &pasynUserForceRead_, "MODBUS_READ");
if (status) {
//printf("%s:%s: Error, unable to connect pasynUserForceRead_ to Modbus input driver %s\n", pC_->inputDriver_, pC_->functionName, myModbusInputDriver);
printf("%s: Error, unable to connect pasynUserForceRead_ to Modbus input driver\n", pC_->inputDriver_);
}
printf("ANG1Axis::ANG1Axis : pasynUserForceRead_->reason=%d\n", pasynUserForceRead_->reason);
// set position to 0
setPosition(0);
}
/** Reports on status of the axis
* \param[in] fp The file pointer on which report information will be written
* \param[in] level The level of report detail desired
*
* After printing device-specific information calls asynMotorAxis::report()
*/
void ANG1Axis::report(FILE *fp, int level)
{
if (level > 0) {
fprintf(fp, " axis %d\n",
axisNo_);
}
// Call the base class method
asynMotorAxis::report(fp, level);
}
// SET VEL & ACCEL
asynStatus ANG1Axis::sendAccelAndVelocity(double acceleration, double velocity)
{
asynStatus status;
// static const char *functionName = "ANG1::sendAccelAndVelocity";
// Send the velocity in egus
//sprintf(pC_->outString_, "%1dVA%f", axisNo_ + 1, (velocity*stepSize_));
//status = pC_->writeController();
status = pC_->writeReg32(SPD_UPR, NINT(velocity), DEFAULT_CONTROLLER_TIMEOUT);
// Send the acceleration in egus/sec/sec
//printf(" velocity: %f\n", velocity);
//printf(" acceleration: %f\n", acceleration);
// ANG1 acceleration range 1 to 5000 steps/ms/sec
// Therefore need to limit range received by motor record from 1000 to 5e6 steps/sec/sec
if (acceleration < 1000) {
// print message noting that accel has been capped low
acceleration = 1000;
}
if (acceleration > 5000000) {
// print message noting that accel has been capped high
acceleration = 5000000;
}
// ANG1 acceleration units are steps/millisecond/second, so we divide by 1000 here
status = pC_->writeReg16(ACCEL, NINT(acceleration/1000.0), DEFAULT_CONTROLLER_TIMEOUT);
status = pC_->writeReg16(DECEL, NINT(acceleration/1000.0), DEFAULT_CONTROLLER_TIMEOUT);
return status;
}
// MOVE
asynStatus ANG1Axis::move(double position, int relative, double minVelocity, double maxVelocity, double acceleration)
{
asynStatus status;
int velo, distance, move_bit;
printf(" ** ANG1Axis::move called, relative = %d\n", relative);
status = sendAccelAndVelocity(acceleration, maxVelocity);
//velo = maxVelocity * SOME_SCALE_FACTOR
velo = NINT(maxVelocity);
if (relative) {
printf(" ** relative move called\n");
//status = pC_->writeReg32(SPD_UPR, velo, DEFAULT_CONTROLLER_TIMEOUT);
//distance = position * SOM_OTHER_SCALE_FACTOR;
distance = NINT(position);
status = pC_->writeReg32(POS_WR_UPR, distance, DEFAULT_CONTROLLER_TIMEOUT);
move_bit = 0x0;
status = pC_->writeReg16(CMD_MSW, move_bit, DEFAULT_CONTROLLER_TIMEOUT);
move_bit = 0x2;
status = pC_->writeReg16(CMD_MSW, move_bit, DEFAULT_CONTROLLER_TIMEOUT);
} else {
// absolute
printf(" ** absolute move called\n");
//status = pC_->writeReg32(SPD_UPR, velo, DEFAULT_CONTROLLER_TIMEOUT);
//distance = position * SOM_OTHER_SCALE_FACTOR;
distance = NINT(position);
printf(" ** distance = %d\n", distance);
status = pC_->writeReg32(POS_WR_UPR, distance, DEFAULT_CONTROLLER_TIMEOUT);
move_bit = 0x0;
status = pC_->writeReg16(CMD_MSW, move_bit, DEFAULT_CONTROLLER_TIMEOUT);
move_bit = 0x1;
status = pC_->writeReg16(CMD_MSW, move_bit, DEFAULT_CONTROLLER_TIMEOUT);
}
// Delay the first status read, give the controller some time to return moving status
epicsThreadSleep(0.05);
return status;
}
// HOME (needs work)
asynStatus ANG1Axis::home(double minVelocity, double maxVelocity, double acceleration, int forwards)
{
asynStatus status;
int home_bit;
// static const char *functionName = "ANG1Axis::home";
//status = sendAccelAndVelocity(acceleration, maxVelocity);
if (forwards) {
printf(" ** HOMING FORWARDS **\n");
home_bit = 0x20;
status = pC_->writeReg16(CMD_MSW, home_bit, DEFAULT_CONTROLLER_TIMEOUT);
} else {
home_bit = 0x40;
status = pC_->writeReg16(CMD_MSW, home_bit, DEFAULT_CONTROLLER_TIMEOUT);
}
return status;
}
// JOG
asynStatus ANG1Axis::moveVelocity(double minVelocity, double maxVelocity, double acceleration)
{
asynStatus status;
int velo, distance, move_bit;
static const char *functionName = "ANG1Axis::moveVelocity";
asynPrint(pasynUser_, ASYN_TRACE_FLOW,
"%s: minVelocity=%f, maxVelocity=%f, acceleration=%f\n",
functionName, minVelocity, maxVelocity, acceleration);
velo = NINT(fabs(maxVelocity));
status = sendAccelAndVelocity(acceleration, velo);
/* ANG1 does not have jog command. Move 1 million steps */
if (maxVelocity > 0.) {
/* This is a positive move in ANG1 coordinates */
//printf(" ** relative move (JOG pos) called\n");
distance = 1000000;
status = pC_->writeReg32(POS_WR_UPR, distance, DEFAULT_CONTROLLER_TIMEOUT);
move_bit = 0x0;
status = pC_->writeReg16(CMD_MSW, move_bit, DEFAULT_CONTROLLER_TIMEOUT);
move_bit = 0x2;
status = pC_->writeReg16(CMD_MSW, move_bit, DEFAULT_CONTROLLER_TIMEOUT);
} else {
/* This is a negative move in ANG1 coordinates */
//printf(" ** relative move (JOG neg) called\n");
distance = -1000000;
status = pC_->writeReg32(POS_WR_UPR, distance, DEFAULT_CONTROLLER_TIMEOUT);
move_bit = 0x0;
status = pC_->writeReg16(CMD_MSW, move_bit, DEFAULT_CONTROLLER_TIMEOUT);
move_bit = 0x2;
status = pC_->writeReg16(CMD_MSW, move_bit, DEFAULT_CONTROLLER_TIMEOUT);
}
// Delay the first status read, give the controller some time to return moving status
epicsThreadSleep(0.05);
return status;
}
// STOP
asynStatus ANG1Axis::stop(double acceleration)
{
asynStatus status;
int stop_bit;
//static const char *functionName = "ANG1Axis::stop";
printf("\n STOP \n\n");
stop_bit = 0x0;
status = pC_->writeReg16(CMD_MSW, stop_bit, DEFAULT_CONTROLLER_TIMEOUT);
// stop_bit = 0x10; Immediate stop
stop_bit = 0x4; // Hold move
status = pC_->writeReg16(CMD_MSW, stop_bit, DEFAULT_CONTROLLER_TIMEOUT);
return status;
}
// SET
asynStatus ANG1Axis::setPosition(double position)
{
asynStatus status;
int set_position, set_bit;
//static const char *functionName = "ANG1Axis::setPosition";
//status = writeReg32(SPD_UPR, velo, DEFAULT_CONTROLLER_TIMEOUT);
//distance = position * SOM_OTHER_SCALE_FACTOR;
set_position = NINT(position);
status = pC_->writeReg32(POS_WR_UPR, set_position, DEFAULT_CONTROLLER_TIMEOUT);
set_bit = 0x200;
status = pC_->writeReg16(CMD_MSW, set_bit, DEFAULT_CONTROLLER_TIMEOUT);
set_bit = 0x0;
status = pC_->writeReg16(CMD_MSW, set_bit, DEFAULT_CONTROLLER_TIMEOUT);
return status;
}
// ENABLE TORQUE
asynStatus ANG1Axis::setClosedLoop(bool closedLoop)
{
asynStatus status;
int enable = 0x8000;
int disable = 0x0000;
int cmd;
printf(" ** setClosedLoop called \n");
if (closedLoop) {
printf("setting enable %X\n", enable);
// Let's reset errors first
cmd = 0x0;
status = pC_->writeReg16(CMD_MSW, cmd, DEFAULT_CONTROLLER_TIMEOUT);
cmd = 0x400;
status = pC_->writeReg16(CMD_MSW, cmd, DEFAULT_CONTROLLER_TIMEOUT);
cmd = 0x0;
status = pC_->writeReg16(CMD_MSW, cmd, DEFAULT_CONTROLLER_TIMEOUT);
status = pC_->writeReg16(CMD_LSW, enable, DEFAULT_CONTROLLER_TIMEOUT);
setIntegerParam(pC_->motorStatusPowerOn_, 1);
} else {
printf("setting disable %X\n", disable);
status = pC_->writeReg16(CMD_LSW, disable, DEFAULT_CONTROLLER_TIMEOUT);
setIntegerParam(pC_->motorStatusPowerOn_, 0);
}
return status;
}
// POLL
/** Polls the axis.
* This function reads motor position, limit status, home status, and moving status
* It calls setIntegerParam() and setDoubleParam() for each item that it polls,
* and then calls callParamCallbacks() at the end.
* \param[out] moving A flag that is set indicating that the axis is moving (true) or done (false). */
asynStatus ANG1Axis::poll(bool *moving)
{
int done;
int limit;
int enabled;
double position;
asynStatus status;
epicsInt32 read_val; // don't use a pointer here. The _address_ of read_val should be passed to the read function.
// Force a read operation
//printf(" . . . . . Calling pasynInt32SyncIO->write\n");
//printf("Calling pasynInt32SyncIO->write(pasynUserForceRead_, 1, TIMEOUT), pasynUserForceRead_->reason=%d\n", pasynUserForceRead_->reason);
status = pasynInt32SyncIO->write(pasynUserForceRead_, 1, DEFAULT_CONTROLLER_TIMEOUT);
//printf(" . . . . . status = %d\n", status);
// if status goto end
// Read the current motor position
//
//readReg32(int reg, epicsInt32 *combo, double timeout)
status = pC_->readReg32(POS_RD_UPR, &read_val, DEFAULT_CONTROLLER_TIMEOUT);
printf("ANG1Axis::poll: Motor position raw: %d\n", read_val);
position = (double) read_val;
setDoubleParam(pC_->motorPosition_, position);
printf("ANG1Axis::poll: Motor position: %f\n", position);
// Read the moving status of this motor
//
status = pC_->readReg16(STATUS_1, &read_val, DEFAULT_CONTROLLER_TIMEOUT);
//printf("status 1 is 0x%X\n", read_val);
// Done logic
done = ((read_val & 0x8) >> 3); // status word 1 bit 3 set to 1 when the motor is not in motion.
setIntegerParam(pC_->motorStatusDone_, done);
*moving = done ? false:true;
printf("done is %d\n", done);
// Read the limit status
//
status = pC_->readReg16(STATUS_2, &read_val, DEFAULT_CONTROLLER_TIMEOUT);
printf("status 2 is 0x%X\n", read_val);
limit = (read_val & 0x1); // a cw limit has been reached
setIntegerParam(pC_->motorStatusHighLimit_, limit);
//printf("+limit %d\n", limit);
if (limit) { // reset error and set position so we can move off of the limit
// Reset error
setClosedLoop(1);
// Reset position
//printf(" Reset Position\n");
setPosition(position);
}
limit = (read_val & 0x2); // a ccw limit has been reached
setIntegerParam(pC_->motorStatusLowLimit_, limit);
//printf("-limit %d\n", limit);
if (limit) { // reset error and set position so we can move off of the limit
// Reset error
setClosedLoop(1);
// Reset position
setPosition(position);
}
// test for home
// Should be in init routine? Allows CNEN to be used.
setIntegerParam(pC_->motorStatusGainSupport_, 1);
// Check for the torque status and set accordingly.
enabled = (read_val & 0x8000);
if (enabled)
setIntegerParam(pC_->motorStatusPowerOn_, 1);
else
setIntegerParam(pC_->motorStatusPowerOn_, 0);
// Notify asynMotorController polling routine that we're ready
callParamCallbacks();
return status;
}
/** Code for iocsh registration */
static const iocshArg ANG1CreateControllerArg0 = {"Port name", iocshArgString};
static const iocshArg ANG1CreateControllerArg1 = {"ANG1 In port name", iocshArgString};
static const iocshArg ANG1CreateControllerArg2 = {"ANG1 Out port name", iocshArgString};
static const iocshArg ANG1CreateControllerArg3 = {"Number of axes", iocshArgInt};
static const iocshArg ANG1CreateControllerArg4 = {"Moving poll period (ms)", iocshArgInt};
static const iocshArg ANG1CreateControllerArg5 = {"Idle poll period (ms)", iocshArgInt};
static const iocshArg * const ANG1CreateControllerArgs[] = {&ANG1CreateControllerArg0,
&ANG1CreateControllerArg1,
&ANG1CreateControllerArg2,
&ANG1CreateControllerArg3,
&ANG1CreateControllerArg4,
&ANG1CreateControllerArg5,};
static const iocshFuncDef ANG1CreateControllerDef = {"ANG1CreateController", 6, ANG1CreateControllerArgs};
static void ANG1CreateContollerCallFunc(const iocshArgBuf *args)
{
ANG1CreateController(args[0].sval, args[1].sval, args[2].sval, args[3].ival, args[4].ival, args[5].ival);
}
static void ANG1Register(void)
{
iocshRegister(&ANG1CreateControllerDef, ANG1CreateContollerCallFunc);
}
extern "C" {
epicsExportRegistrar(ANG1Register);
}
-88
View File
@@ -1,88 +0,0 @@
/*
FILENAME... ANG1Driver.h
USAGE... Motor driver support for the AMCI ANG1 controller.
Based on MCB-4B driver written by
Mark Rivers
March 1, 2012
K. Goetze 2014-03-24
*/
#include "asynMotorController.h"
#include "asynMotorAxis.h"
#define MAX_ANG1_AXES 1
#define MAX_INPUT_REGS 10
#define MAX_OUTPUT_REGS 10
// No. of controller-specific parameters
#define NUM_ANG1_PARAMS 1
/** drvInfo strings for extra parameters that the ACR controller supports */
#define ANG1JerkString "ANG1_JERK"
//#define ACRReadBinaryIOString "ACR_READ_BINARY_IO"
//#define ACRBinaryInString "ACR_BINARY_IN"
//#define ACRBinaryOutString "ACR_BINARY_OUT"
//#define ACRBinaryOutRBVString "ACR_BINARY_OUT_RBV"
class ANG1Axis : public asynMotorAxis
{
public:
/* These are the methods we override from the base class */
ANG1Axis(class ANG1Controller *pC, int axis);
void report(FILE *fp, int level);
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration);
asynStatus moveVelocity(double min_velocity, double max_velocity, double acceleration);
asynStatus home(double min_velocity, double max_velocity, double acceleration, int forwards);
asynStatus stop(double acceleration);
asynStatus poll(bool *moving);
asynStatus setPosition(double position);
asynStatus setClosedLoop(bool closedLoop);
private:
ANG1Controller *pC_; /**< Pointer to the asynMotorController to which this axis belongs.
* Abbreviated because it is used very frequently */
asynStatus sendAccelAndVelocity(double accel, double velocity);
asynUser *pasynUserForceRead_;
friend class ANG1Controller;
};
class ANG1Controller : public asynMotorController {
public:
ANG1Controller(const char *portName, const char *ANG1InPortName, const char *ANG1OutPortName, int numAxes, double movingPollPeriod, double idlePollPeriod);
void report(FILE *fp, int level);
ANG1Axis* getAxis(asynUser *pasynUser);
ANG1Axis* getAxis(int axisNo);
asynUser *pasynUserInReg_[MAX_INPUT_REGS];
asynUser *pasynUserOutReg_[MAX_OUTPUT_REGS];
// asynUser *pasynUserForceRead_;
/* These are the methods that we override from asynMotorDriver */
asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
//asynStatus writeFloat64(asynUser *pasynUser, epicsFloat64 value);
//void report(FILE *fp, int level);
//ACRAxis* getAxis(asynUser *pasynUser);
//ACRAxis* getAxis(int axisNo);
/* These are the methods that are new to this class */
protected:
int ANG1Jerk_; /**< Jerk time parameter index */
private:
asynStatus writeReg16(int, int, double);
asynStatus writeReg32(int, int, double);
asynStatus readReg16(int, epicsInt32*, double);
asynStatus readReg32(int, epicsInt32*, double);
char *inputDriver_;
friend class ANG1Axis;
};
-2
View File
@@ -1,2 +0,0 @@
#registrar(ANG1MotorRegister)
registrar(ANG1Register)
-30
View File
@@ -1,30 +0,0 @@
TOP=../..
include $(TOP)/configure/CONFIG
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
#=============================
#==================================================
# Build an IOC support library
LIBRARY_IOC += AMCI
# motorRecord.h will be created from motorRecord.dbd
# install devMotorSoft.dbd into <top>/dbd
DBD += AMCISupport.dbd
DBD += ANG1Support.dbd
DBD += ANF2Support.dbd
# The following are compiled and added to the Support library
AMCI_SRCS += ANG1Driver.cpp
AMCI_SRCS += ANF2Driver.cpp
AMCI_LIBS += motor
AMCI_LIBS += asyn
AMCI_LIBS += $(EPICS_BASE_IOC_LIBS)
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE
-49
View File
@@ -1,49 +0,0 @@
AMCI ANG1
=========
Asyn model 3 driver support for the AMCI ANG1 stepper motor
controller/driver.
Modbus/TCP communication, using Mark Rivers' modbus module
Currently some basic parameters, such as IP address, motor current,
etc., will need to be set up on the controller using AMCI's PC/Win software.
Limits and Home also need to be configured with this software: *
+ Limit -> ANG1 Input 1
- Limit -> ANG1 Input 2
Home -> ANG1 Input 3
ANG1 configuration software is available from AMCI's website:
www.amci.com/product-software.asp
Eventually I would like to implement the "Configuration Mode" options
down in the driver, making the AMCI PC software necessary only for
initial comms (IP) setup.
The ANG1 controller/driver is available with or without an ethernet
port. A group of controller/drivers can contain up to 6 modules, one of
which needs to have the ethernet option. A single-channel
implementation would need the module with ethernet. This software is
intended to support configurations of multiple groups of 1 to 6 modules.
Again, each group will need 1 ethernet enabled module (ANG1-E).
asyn model 3 driver files:
--------------------------
ANG1Driver.cpp
ANG1Driver.h
ANG1Support.dbd
* Note: 1.) At some point I would like to add the capability to set these
parameters at boot-time, using the ANG1 "configuration mode".
2.) VBAS can only be set in configuration mode (which is not
currently supported) or by using the PC configuration software.
To use VBAS, set the desired value for the axis using the AMCI
PC configuration interface, then set the VBAS field in the
motor record accordingly. That way this value will be used by
the record to correctly calculate acceleration.
-70
View File
@@ -1,70 +0,0 @@
# AMCI ANF2
Asyn model 3 driver support for the AMCI ANF2 stepper motor controller
Modbus/TCP communication, using Mark Rivers' modbus module
## Supported Controller Models
The following ANF controller versions are supported:
```
ANF1E: 1-axis stepper controller, modbus tcp/ip
ANF1: 1-axis stepper controller, no network interface
ANF2E: 2-axis stepper controller, modbus tcp/ip
ANF2: 2-axis stepper controller, no network interface
```
A stack of controller can contain up to 6 modules, one of
which needs to have the ethernet option. A single-channel
implementation would need the module with ethernet.
## Vendor Software
ANF2 configuration software is available from AMCI's website:
www.amci.com/product-software.asp
Note: The AMCI Net Configurator only works (allows a motor to be moved) if
the controller is configured for EtherNet/IP, however, the EPICS support
requires the device to be configured for Modbus-TCP.
## Controller Quirks
* The controller doesn't allow absolute moves when a limit is active;
The only way to move a motor off of a limit is by **jogging**.
* The base speed is set when an axis is configured. This driver corrects
the acceleration sent by the motor record (VBAS isn't guaranteed to match
the base speed) and sends the acceleration necessary to achieve the desired
acceleration time.
* The controller doesn't remember its configuration after it is power-cycled.
* Sending the configuration to the controller invalidates the position
requiring either a home search or the redefinition of the current position.
* The configuration can't be read after a configuration is accepted by the
controller, after which it automatically switches into command mode.
* The command to stop an abosolute move generates an error if a jog is in
progress. The command to stop a jog doesn't stop an absolute move.
## Controller Configuration
The AMCI Net Configurator can be used to:
* Change the ip address from the default (192.168.0.50)
* Change the protocol to Modbus-TCP
* Determine hex config strings for each axis
### Example axis configuration
```
0x86000000 - Step & Direction pulses, Diagnostic Feedback, No home switch, No limits
0x86280000 - Step & Direction pulses, Diagnostic Feedback, No home switch, Active-low CW/CCW limits
0x84000000 - Step & Direction pulses, No Feedback, No home switch, No Limits
0x84280000 - Step & Direction pulses, No Feedback, No home switch, Active-low CW/CCW limits
0x842C0004 - Step & Direction pulses, No Feedback, Active-high home switch, Active-low CW/CCW limits
0x85280000 - Step & Direction pulses, Quadrature Feedback, No home switch, Active-low CW/CCW limits
```
-28
View File
@@ -1,28 +0,0 @@
# Database for extra PVs for AMCI ANG1 controllers
record(bo,"$(P)$(R)ResetErrors") {
field(DESC,"Reset Errors")
field(PINI, "0")
field(VAL,"0")
field(DTYP, "asynInt32")
field(OUT,"@asyn($(PORT),$(ADDR))ANF2_RESET_ERRORS")
field(ZNAM, "Done")
field(ONAM, "Reset")
}
record(bo,"$(P)$(R)GetInfo") {
field(DESC,"Get Info")
field(PINI, "0")
field(VAL,"0")
field(DTYP, "asynInt32")
field(OUT,"@asyn($(PORT),$(ADDR))ANF2_GET_INFO")
}
# Reconfig isn't yet implemented in a generally-useful way
#record(longout,"$(P)$(R)Reconfig") {
# field(DESC,"Reconfig")
# field(PINI, "0")
# field(VAL,"0")
# field(DTYP, "asynInt32")
# field(OUT,"@asyn($(PORT),$(ADDR))ANF2_RECONFIG")
#}
-3
View File
@@ -27,9 +27,6 @@ PC6KSrc_DEPEND_DIRS = MotorSrc
DIRS += SmarActMCSSrc
SmarActMCSSrc_DEPEND_DIRS = MotorSrc
DIRS += AMCISrc
AMCISrc_DEPEND_DIRS = MotorSrc
endif
# Install the edl files