Support for the Acromag IP330ADC

Authors: Mark Rivers, Joe Sullivan and Marty Kraimer

Release 1.5

The support for the Ip330 is very general and flexible. It is based on a 2-layer model. At the lower level is class Ip330 which knows how to talk to the IP330 hardware, but is application independent. Above class Ip330 are the application-specific classes. There are currently 3 such classes:

Ip330Scan, Ip330Sweep, and Ip330PID can all be running simultaneously, and can even all be talking to the same input channels. It is easy to add additional application-specific classes as the need arises.

The Release notes at the end of this document describe how to convert from the existing Ip330Scan support to this package.

The software is available as a tar file.

Class Ip330

At the lowest level there is a class called Ip330. This class knows how to talk to the IP330 hardware, but does not know how it will be used. The only parameters of the IP330 module which are not configurable in this class are the output data format (which is set to Straight Binary) and the Interrupt Control (which is set to interrupt after conversion of all selected channels). All other parameters of the IP330 can be configured (first and last channels to convert, scan mode, scan rate, trigger signal direction, etc.) An Ip330Config server is provided together with device support for modifying configuration parameters. Currently it only provides the ability to request calibrationrequest calibration. MPF servers and EPICS device support exist for the application-specific classes which sit above Ip330.

Ip330 public interface

This is the public interface of the Ip330 class:

class Ip330
{
public:
    static Ip330 * init(
        const char *moduleName, const char *carrierName, const char *siteName,
        const char *type, const char *range, int firstChan, int lastChan,
        int maxClients, int intVec);
    int config(scanModeType scanMode, const char *triggerString, int
        microSecondsPerScan, int secondsBetweenCalibrate);
    int getCorrectedValue(int channel);
    int correctValue(int channel, int raw);
    int getRawValue(int channel);
    int setGain(int gain,int channel);
    int setScanMode(scanModeType scanMode);
    int setTrigger(triggerType trigger);
    float setMicroSecondsPerScan(float microSeconds);
    float getMicroSecondsPerScan();
    void setSecondsBetweenCalibrate(int seconds);
    int registerCallback(Ip330Callback callback, void *pvt);

Brief description of these functions:

Ip330 configuration

The Ip330 is configured by calling the following functions from the vxWorks startup file.

extern "C" Ip330 *initIp330(
    const char *moduleName, const char *carrierName, const char *siteName,
    const char *typeString, const char *rangeString,
    int firstChan, int lastChan,
    int maxClients, int intVec)

# function return value  = pointer to the Ip330 object, needed by configIp330
#                          and to initialize the application-specific classes
# serverName  = name to give this server
# carrierName = name of IPAC carrier from initIpacCarrier above
# siteName    = name of IP site, e.g. "IP_a"
# typeString  = "D" or "S" for differential or single-ended
# rangeString = "-5to5","-10to10","0to5", or "0to10"
#               This value must match hardware setting selected
# firstChan   = first channel to be digitized.  This must be in the range:
#               0 to 31 (single-ended)
#               0 to 15 (differential)
# lastChan    = last channel to be digitized
# maxClients =  Maximum number of Ip330 tasks which will attach to this
#               Ip330 module.  For example Ip330Scan, Ip330Sweep, etc.  This
#               does not refer to the number of EPICS clients.  A value of
#               10 should certainly be safe.
# intVec        Interrupt vector

extern "C" int configIp330(
    Ip330 *pIp330,
    scanModeType scanMode, const char *triggerString,
    int microSecondsPerScan, int secondsBetweenCalibrate)

# pIp330      = pointer to the Ip330 object, returned by initIp330 above
# scanMode    = scan mode:
#               0 = disable
#               1 = uniformContinuous
#               2 = uniformSingle
#               3 = burstContinuous (normally recommended)
#               4 = burstSingle
#               5 = convertOnExternalTriggerOnly
# triggerString = "Input" or "Output". Selects the direction of the external
#               trigger signal.
# microSecondsPerScan = repeat interval to digitize all channels
#               The minimum theoretical time is 15 microseconds times the
#               number of channels, but a practical limit is probably 100
#               microseconds.  Larger values reduce CPU usage, but decrease
#               the number of callbacks per second to the application classes.
#               This will reduce the number of measurments averages in the
#               ip330Scan class,  increase the granularity in the time per
#               point for the ip330Sweep class, and decrease the number of
#               feedback cycles per second for the ip330PID class.
# secondsBetweenCalibrate = number of seconds between calibration cycles.
#               If zero then there will be no periodic calibration, but
#               one calibration will still be done at initialization.
#               If less than zero then no calibration is done.
#               NOTE: setGain also causes a calibration.

Note that the reason for having configIp330, rather than just initIp330 is simply that there are more than 10 configurable parameters, but vxWorks only allows 10 arguments to be passed to functions which are called from the shell.

IP330 Hardware setup

The DIP Switch Settings column correspond to the value of switches 1 - 10. A value of 1011000010 corresponds to switches 1,3,4,9 on and 2,5,6,7,8,10 off.

ADC Range  DIP Switch     {GAIN}        {FULL VALUE}    {LOW VALUE}

-5to5      1011000010        0               5              -5
-5to5      1011000010        1               2.5            -2.5
-5to5      1011000010        2               1.25           -1.25
-5to5      1011000010        3               0.625          -0.625
-10to10    0100110010        0              10             -10
-10to10    0100110010        1               5              -5
-10to10    0100110010        2               2.5            -2.5
-10to10    0100110010        3               1.25           -1.25
0to5       1010100100        0               5               0
0to5       1010100100        1               2.5             0
0to5       1010100100        2               1.25            0
0to5       1010100100        3               0.625           0
0to10      1011001000        0              10               0
0to10      1011001000        1               5               0
0to10      1011001000        2               2.5             0
0to10      1011001000        3               1.25            0

Ip330Config Server

A server and device support are provided to dynamically change configuration value. Currently this only provides support for changing setSecondsBetweenCalibrate.

Ip330Config server configuration

extern "C" int initIp330Config(
    Ip330 *pIp330, const char *serverName, int queueSize)
# pIp330     = pointer returned by initIp330 above
# serverName = name to give this server.  Must match the INP parm field in
#              EPICS records
# queueSize  = size of output queue for MPF. Make this the maximum number 
#              of records attached to this server.

Ip330Config EPICS Device Support

The current device support is for a longout record. It sends an Int32Messages with the following info:

Server returns:

Standard output record type format is for longout records

 field(DTYP,"ip330Config")

 field(OUT,"#C{card} S{signal} @{servername},{cmd}
 card   =       The location of the server
 signal =       Not currently used. Just make the value 0
 servername     Must match the serverName specified with initIp330Config
 cmd		Default is 0,which for now is the only valid value.

 field(VAL)     When the record is processed the value of the VAL field
                is setSecondsBetweenCalibrate. See above for meaning.
A typical use is to have a passive longout record. If the VAL field is 0, then each time the record is processed calibration will be performed.

Class Ip330Scan

This class provides the functions an averaging A/D converter. Together with class Ip330 it provides the same functionality as Ip330ScanApp in previous releases of MPF.

Ip330Scan public interface

This is the public interface of the Ip330Scan class:

class Ip330Scan
{
public:
    static Ip330Scan * init(Ip330 *pIp330, int firstChan, int lastChan,
                            int milliSecondsToAverage);
    int getValue(int channel);
    int setGain(int gain,int channel);
    void setMilliSecondsToAverage(int milliSeconds);

Brief description of these functions:

Ip330Scan server configuration

extern "C" int initIp330Scan(
    Ip330 *pIp330, const char *serverName, int firstChan, int lastChan,
    int milliSecondsToAverage, int queueSize)
# pIp330     = pointer returned by initIp330 above
# serverName = name to give this server.  Must match the INP parm field in
#              EPICS records
# firstChan  = first channel to be used by Ip330Scan.  This must be in the
#              range firstChan to lastChan specified in initIp330
# lastChan   = last channel to be used by Ip330Scan.  This must be in the range
#              firstChan to lastChan specified in initIp330
# milliSecondsToAverage = number of milliseconds to average readings
# queueSize  = size of output queue for MPF. Make this the maximum number 
#              of ai records attached to this server.

Ip330Scan EPICS Device Support

Device support sends Int32Messages with the following info:

Server returns:

Standard input record type format is for ai records

 field(SCAN,"1 second")

 field(DTYP,"ip330Scan")

 field(INP,"#C{card} S{signal} @{servername},{gain}
 card   =       The location of the server
 signal =       The input channel of the ip330ADC
                Differential inputs 0 - 15 are valid
                Single ended inputs 0 - 31 are valid
 servername     Must match the serverName specified with initIp330Scan
 gain           Optional. If given must be 0,1,2,or 3. Default is 0.

 field(EGUF,"{FULL VALUE}")
 {FULL VALUE} = See table under class Ip330.

 field(EGUL,"{LOW VALUE}")
 {LOW VALUE} = See table under class Ip330.

 field(LINR,"LINEAR")
 Mandatory

Class Ip330Sweep

This class provides a waveform recorder or transient digitizer, i.e. it collects voltage as a function of time. Time increments as short as 100 microseconds are possible if one does not use all of the channels.

Ip330Sweep public interface

This is the public interface of the Ip330Sweep class:

class Ip330Sweep
{
public:
    static Ip330Sweep * init(Ip330 *pIp330, int firstChan, int lastChan,
                            int maxPoints);
    int setGain(int gain,int channel);
    void startAcquire();
    void stopAcquire();
    void erase();
    float setMicroSecondsPerPoint(float microSeconds);
    float getMicroSecondsPerPoint();
    int setNumPoints(int numPoints);
    int getNumPoints();
    int setPresetTime(float time);
    float getElapsedTime();
    int getStatus();
    void getData(int channel, int *data);

Ip330Sweep is a more complex application than Ip330Scan. It acquires waveform data into a local buffer, which can be returned to EPICS. Waveform acquisition is started by startAcquire. Acquisition stops whenever one of the following occurs:

Ip330Sweep can acquire multiple waveforms simultaneously, one for each of the input channels. Acquisition is started and stopped for all channels simultaneously, and erase() erases all waveforms.

Brief description of these functions:

Ip330Sweep server configuration

initIp330Sweep(Ip330 *pIp330, char *serverName, int firstChan, int lastChan,
               int maxPoints, int queueSize)
# pIp330     = pointer returned by initIp330 above
# serverName = name to give this server
# firstChan  = first channel to be used by Ip330Sweep.  This must be in the
#              range firstChan to lastChan specified in initIp330
# lastChan   = last channel to be used by Ip330Sweep.  This must be in the
#              range firstChan to lastChan specified in initIp330
# maxPoints  = maximum number of points in a sweep.  The amount of memory
#              allocated will be maxPoints*(lastChan-firstChan+1)*4 bytes
# queueSize  = size of output queue for EPICS

Ip330Sweep EPICS Device Support

Device support can send the following messages (MSG_XXX are defined in mcaApp/src/mca.h):

Float64Messages with the following info:

Preset live time and preset real time are ignorred if they are zero. There is no difference between live and real time, but acquisition will stop whenever either preset time is reached.

Standard input record type format is for MCA records. Note that the MCA record is not part of EPICS base. The MPF device support for the MCA record, and the MCA record itself are available separately in the mcaApp tar file.

 field(DTYP,"MPF MCA")

 field(NMAX, nchannels)
 nchannels = maximum number of channels (time points) to use for this record.
             NMAX cannot be changed after iocInit.  NMAX should normally be 
             set to the same value specified for maxPoints in initIp330Sweep

 field(NUSE, nchannels)
 nchannels = actual number of channels (time points) to use for this record.
             NUSE can be changed at any time, however it cannot be greater 
             than NMAX or maxPoints specified in initIp330Sweep

 field(DWEL, scan_time)
 scan_time = time per point in seconds (floating point).  Values as small as
             as 1.e-4 (100 microseconds) can be used if 6 or fewer channels 
             are digitized.  The DWEL field will be set to the actual time
             per point, in case the value requested is not available either
             because it is too short or too long, or because the requested
             time was not an integer multiple of the value of 
             microSecondsPerScan specified in the call to configIp330.

 field(INP,"#C{card} S{signal} @{servername})
 card   =       The location of the server
 signal =       The input channel of the ip330ADC
                Differential inputs 0 - 15 are valid
                Single ended inputs 0 - 31 are valid
 servername     Must match the serverName specified with initIp330Sweep

Class Ip330PID

This class provides very fast PID (feedback) software. It reads its input from a single channel of the Ip330 and writes its output to a single DAC channel on the Systran DAC (dac128V which is in MPF). It can do feedback faster than 1 kHz. It uses exactly the same feedback algorithm as the soft record device support in the EPID (Enhanced PID) EPICS record.

Ip330 public interface

This is the public interface of the Ip330PID class:

class Ip330PID
{
public:
    static Ip330PID * init(Ip330 *pIp330, int ADCChannel,
                           DAC128V *pDAC128V, int DACChannel);
    static void config(Ip330PID *pIp330PID, double KP, double KI, double KD,
                       int interval, int feedbackOn,
                       int lowLimit, int highLimit);
    float setMicroSecondsPerScan(float microSeconds);
    float getMicroSecondsPerScan();
    int setPoint;
    int actual;
    double error;
    double KP;
    double KI;
    double KD;
    double P;
    double I;
    double D;
    int feedbackOn;
    int lowLimit;
    int highLimit;
    int output;

This is a brief description of this public interface. The public variables (setPoint, actual, etc.) are marked as R/W for Read/Write or RO for Read Only. The R/W varibles can be changed by the server at any time.

Ip330PID server configuration

extern "C" Ip330PID *initIp330PID(const char *serverName,
         Ip330 *pIp330, int ADCChannel, DAC128V *pDAC128V, int DACChannel,
         int queueSize)
# serverName = name to give this server
# pIp330     = pointer returned by initIp330 above
# ADCChannel = ADC channel to be used by Ip330PID as its readback source.  
#              This must be in the range firstChan to lastChan specified in 
#              initIp330
# pDAC128V   = pointer returned by initDAC128V
# DACChannel = DAC channel to be used by Ip330PID as its control output.  This
#              must be in the range 0-7.
# queueSize  = size of output queue for EPICS

extern "C" int configIp330PID(Ip330PID *pIp330PID,
         double KP, double KI, double KD,
         int interval, int feedbackOn, int lowLimit, int highLimit)
# pIp330PID  = pointer returned by initIp330PID above
# KP         = proportional gain
# KI         = integral gain
# KD         = derivative gain
# interval   = microseconds per feedback loop
# feedbackOn = 0 for feedback off, 1 for feedback on
# lowLimit   = low limit on DAC output
# highLimit  = high limit on DAC output

Note that the reason for having configIp330PID, rather than just initIp330PID is simply that there are more than 10 configurable parameters, but vxWorks only allows 10 arguments to be passed to functions which are called from the shell.

Ip330PID EPICS Device Support

Device support sends Float64Messages with the following info:

Standard input record type format is for EPID records. Note that the EPID record is not part of EPICS base, and thus the lines to compile devEpidIp330.cc are commented out in Makefile.Vx, and the line for EPID device support is commented out in devIp330.dbd. The EPID record is available from Mark Rivers or from the APS Beamline Controls and Data Acquisition group.

 field(DTYP,"MPF EPID")

 field(INP,"#C{card} S0 @{servername})
 card   =       The location of the server
 servername     Must match the serverName specified with initIp330PID

 field(SCAN,".1 second")
           = interval process the record.  Each time the EPID record processes
             it sends any new parameters (KI, KP, KD, setPoint, etc.) and
             reads back the parameters from the server (P, I, D, error, output,
             etc.)  Thus if SCAN=.1 second then the EPID record provides 10 Hz
             snapshots of the PID feedback, which may actually be occuring at
             1 kHz or faster.  The Ip330Sweep support can be used together with
             the EPICS MCA record to capture the actual ADC input as fast as it
             is digitized, so the complete error information is available.

 field(KP, kp)
 kp        = proportional gain

 field(KI, ki)
 ki        = integral gain

 field(KD, kd)
 kd        = derivative gain

 field(DT, scan_time)
 scan_time = time per feedback loop in seconds

 field(VAL, setPoint)
 setPoint = setpoint in IP330 ADC units

 field(DRVL, lowLimit)
 lowLimit = low output limit in DAC128CV DAC units

 field(DRVH, highLimit)
 highLimit = high output limit in DAC128CV DAC units

Restrictions on Scan Timing

In an earlier release of this package (1.0) each of the application-specific classes (Ip330Scan, Ip330Sweep, Ip330PID) had a function to control the scan rate of the Ip330. However, there is only a single interval timer in the IP330 hardware, and so if one class changed the scan rate it would change the scan rate for all of the other classes. This interaction was undesirable, and so the behavior has been changed, starting with release 1.01. Now the value of microSecondsPerScan specified in configIp330 is never changed by any application classes. Each application class will have its callback routine executed at this time interval. The application class will determine what to do when it is called back. For example, in the ip330Sweep class if microSecondsPerPoint is 1000, and ip330->microSecondsPerScan is 500, then the ip330Sweep callback routine returns immediately without doing anything on every other call. This behaviour eliminates any interaction between the timing for the different application classes. The new limitation is that the granularity of the time intervals available to an application class is now limited to the value of microSecondsPerScan specified in configIp330, whereas previously the granularity was 8 microseconds. Thus, for example, if microSecondsPerScan is 500 microseconds then the dwell times available in the ip330Sweep class are limited to multiples of 500 microseconds.

Release notes

Release 1.0 November 15, 1999

This support replaces the previous ip330ScanApp. That support did the following:

The new support can be configured to function exactly as the previous support was intended to function with commands like the following in the MPF server startup file:

  pIp330 = initIp330("c-Ip330",carrier,"IP_c","D","-5to5",0,15,10,120)
  configIp330(pIp330,3,"Input",1000,60)
  initIp330Scan(pIp330,"c-Ip330Scan",0,15,200,20)

The initIp330 command creates a task called "c-Ip330", with the following settings:

The configIp330 command configures Ip330 with the following settings:

The initIp330Scan command creates a server called "c-Ip330Scan" with the following settings:

Release 1.01 April 20, 2000

Changed the timing logic. See the discussion in Restrictions on Scan Timing.

Release 1.02 September 10, 2000

Changed ip330SweepServer.cc, removed Ip330SweepServer.h, and replaced devMcaIp330.cc with a device-independent layer, devMcaMpf.cc. devMcaMpf.cc is part of mcaApp, not part of Ip330, since it is no longer specific to the Ip330. The messages accepted by Ip330SweepServer have been changed to accomodate the new device-independent layer.

Changes to the PID algorithm in ip330PID.cc to improve its operation. See the comments in ip330PID.cc for details.

Release 1.1 November 1, 2000 (Marty Kraimer)

Release 1.2 January 16, 2001

Release 1.3 August 28, 2001

Release 1.4 October 12, 2001

Release 1.5 July 31, 2002 (Carl Lionberger and Eric Snow at SNS)

 


Suggestions and Comments to:
Mark Rivers : (rivers@cars.uchicago.edu)
Last modified: January 16, 2001