Merge pull request #95 from kmpeters/AMCI_ANF2

Added support for AMCI ANF controllers
This commit is contained in:
rsluiter
2018-04-20 08:44:26 -05:00
committed by GitHub
10 changed files with 1508 additions and 0 deletions
+3
View File
@@ -117,6 +117,9 @@ Channel support):
<li>
Phytron I1AM01 Stepper Motor Controller.
</li>
<li>
AMCI ANG1 Stepper Motor Controller/Driver, ANF1E/ANF1/ANF2E/ANF2 Stepper Motor Controllers.
</li>
</ul>
The record maintains two coordinate systems for motor position ("user" and "dial
" coordinates); displays drive and readback values; enforces limits to motor
+107
View File
@@ -0,0 +1,107 @@
# 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)
@@ -0,0 +1,54 @@
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}
}
+2
View File
@@ -0,0 +1,2 @@
include "ANG1Support.dbd"
include "ANF2Support.dbd"
File diff suppressed because it is too large Load Diff
+151
View File
@@ -0,0 +1,151 @@
/*
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
@@ -0,0 +1,2 @@
#registrar(ANF2MotorRegister)
registrar(ANF2Register)
+3
View File
@@ -12,10 +12,13 @@ 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
+70
View File
@@ -0,0 +1,70 @@
# 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
@@ -0,0 +1,28 @@
# 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")
#}