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 request calibrationrequest calibration. 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 # 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.
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 value. 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 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: 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:
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 = initial number of milliseconds to average readings. This value is # replaced by the actual scan time of the associated ai record when the record # processes. The actual scan time of the record is sent to the server in the # "extra" field of the message. # queueSize = size of output queue for MPF. Make this the maximum number # of ai records attached to this server.
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
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.
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:
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 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
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.
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.
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.
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
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.
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.