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.
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 specify the time between calibrations. MPF servers and EPICS device support exist for the application-specific classes which sit above Ip330.
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:
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 # moduleName = name to give this module # carrierName = name of IPAC carrier from initIpacCarrier # 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 with DIP switches # 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 measurement 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.
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
A server and device support are provided to dynamically change configuration values. Currently this only provides support for changing setSecondsBetweenCalibrate.
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.
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.
This class provides the functions of an averaging A/D converter. Together with class Ip330 it provides the same functionality as Ip330ScanApp in previous releases of MPF.
This is the public interface of the Ip330Scan class:
class Ip330Scan { public: Ip330Scan(Ip330 *pIp330, int firstChan, int lastChan); int getValue(int channel); int setGain(int gain,int channel);
Brief description of these functions:
extern "C" int initIp330Scan( Ip330 *pIp330, const char *serverName, int firstChan, int lastChan, 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 # queueSize = size of output queue for MPF. Make this the maximum number # of ai records attached to this server.
Note that the "millisecondsToAverage" argument which was present in releases prior to 1.6 has been removed because the averaging algorithm has changed.
Device support sends Int32Messages with the following info:
Server returns:
Note that sending the value returned by the server is the average from the time the previous message was sent until this message was sent. In other words, sending a message returns the current average and resets the average (sum and counter).
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
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 is a subclass of fastSweep, which is an abstract base class. This is the public interface of the Ip330Sweep class:
class Ip330Sweep { public: ip330Sweep(Ip330 *pIp330, int firstChan, int lastChan, int maxPoints); void startAcquire(); void stopAcquire(); void erase(); double setMicroSecondsPerPoint(double microSeconds); double getMicroSecondsPerPoint(); void nextPoint(int *newData) int setNumPoints(int numPoints); int getNumPoints(); int setRealTime(double time); int setLiveTime(double time); double getElapsedTime(); int getStatus(); void getData(int channel, int *data); int firstChan; int lastChan;
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:
Note that microSecondsPerPoint can be greater than microSecondsPerScan in Ip330, i.e. the waveform sampling time can be an integer multiple N of the Ip330 acquisition time. In this case, Ip330Sweep does averaging, each point in the waveform will be the average of N samples of the input signal. This feature was introduced in release 1.7.
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
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 ignored 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
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). 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.
Ip330PID is a subclass of fastPID, which is an abstract base class. This is the public interface of the Ip330PID class:
class Ip330PID { public: Ip330PID(Ip330 *pIp330, int ADCChannel, DAC128V *pDAC128V, int DACChannel); void doPID(double actual); double setMicroSecondsPerScan(double microSeconds); double getMicroSecondsPerScan(); double readOutput(); void writeOutput(double output); double setPoint; double actual; double error; double KP; double KI; double KD; double P; double I; double D; double lowLimit; double highLimit; double output; int feedbackOn; int prevFeedbackOn; double prevError;
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 variables can be changed by the server at any time.
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
Note that prior to release 1.7 there was an additional function, configIp330PID, that was used to configure parameters such as KP, KI, etc. This function has been eliminated, because it is not possible to pass floating point arguments from the vxWorks shell on the PowerPC architecture, and because these parameters are set at iocInit by device support anyway.
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. 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
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 behavior 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.
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:
Changed the timing logic. See the discussion in Restrictions on Scan Timing.
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.
- Change the independent variable
- Process the ai record: this resets the average
- Wait for the period of time you want to average for
- Process the ai record again, and read the value
This release requires modifying the call to initIp330Scan() in the startup script, since the milliSecondsToAverage parameter no longer exists.