1)构建这个IOC程序的程序框架:
orangepi@orangepi5:/usr/local/EPICS/program/simScope$ ls
bin configure db dbd iocBoot lib Makefile simScopeApp
2)修改configure下RELEASE文件,添加所需的支持模块,此IOC程序需要asyn模块支持,因此添加以下两行:
...
# Variables and paths to dependent modules:
#MODULES = /path/to/modules
#MYMODULE = $(MODULES)/my-module
SUPPORT=/usr/local/EPICS/synApps/support
ASYN=$(SUPPORT)/asyn
...
3) 进入simScopeApp/src/目录下,编写以下以下三个文件:
simScope.cpp simScope.h simScopeSupport.dbd
a) simScope.h:
#include <asynPortDriver.h>
#define NUM_VERT_SELECTIONS 4
/* drvInfo string*/
#define P_RunString "SCOPE_RUN" /* asynInt32 r/w start/stop backgroud thread */
#define P_MaxPointsString "SCOPE_MAX_POINTS" /* asynInt32 r/o max number of data points */
#define P_TimePerDivString "SCOPE_TIME_PER_DIV" /* asynFloat64 r/w Time Per Div */
#define P_TimePerDivSelectString "SCOPE_TIME_PER_DIV_SELECT" /* asynInt32 r/w Time Per Div Choices */
#define P_VertGainString "SCOPE_VERT_GAIN" /* asynFloat64 r/w Vert Gain */
#define P_VertGainSelectString "SCOPE_VERT_GAIN_SELECT" /* asynInt32 r/w Vert Gain Choices */
#define P_VoltsPerDivString "SCOPE_VOLTS_PER_DIV" /* asynFloat64 r/w Volts Per Div */
#define P_VoltsPerDivSelectString "SCOPE_VOLTS_PER_DIV_SELECT" /* asynInt32 r/w Volts Per Div Choices */
#define P_VoltOffsetString "SCOPE_VOLT_OFFSET" /* asynFloat64 r/w Volt Offset */
#define P_TriggerDelayString "SCOPE_TRIGGER_DELAY" /* asynFloat64 r/w Trigger Delay */
#define P_NoiseAmplitudeString "SCOPE_NOISE_AMPLITUDE" /* asynFloat64 r/w Noise Amplitude */
#define P_UpdateTimeString "SCOPE_UPDATE_TIME" /* asynFloat64 r/w Update Time */
#define P_WaveformString "SCOPE_WAVEFORM" /* asynFloat64Array r/o waveform data array */
#define P_TimeBaseString "SCOPE_TIME_BASE" /* asynFloat64Array r/o time base array */
#define P_MinValueString "SCOPE_MIN_VALUE" /* asynFloat64 r/o min data */
#define P_MaxValueString "SCOPE_MAX_VALUE" /* asynFloat64 r/o max data */
#define P_MeanValueString "SCOPE_MEAN_VALUE" /* asynFloat64 r/o mean data */
class simScope : public asynPortDriver
{
public:
simScope(const char * portName, int maxArraySize);
/* There method overwritten from asynPortDriver */
virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
virtual asynStatus writeFloat64(asynUser *pasynUser, epicsFloat64 value);
virtual asynStatus readFloat64Array(asynUser *pasynUser, epicsFloat64 *value, size_t nElements, size_t *nIn);
virtual asynStatus readEnum(asynUser *pasynUser, char *strings[], int values[], int severties[], size_t nElements, size_t *nIn);
void simTask(void);
protected:
/* index for parameter in the parameter library */
int P_Run;
int P_MaxPoints;
int P_TimePerDiv;
int P_TimePerDivSelect;
int P_VertGain;
int P_VertGainSelect;
int P_VoltsPerDiv;
int P_VoltsPerDivSelect;
int P_VoltOffset;
int P_TriggerDelay;
int P_NoiseAmplitude;
int P_UpdateTime;
int P_Waveform;
int P_TimeBase;
int P_MinValue;
int P_MaxValue;
int P_MeanValue;
private:
/* event to send sigal to background thread */
epicsEventId eventId_;
/* waveform data array */
epicsFloat64 *pData_;
/* waveform timebase array */
epicsFloat64 *pTimeBase_;
/* string array for Volts Per Div Choices*/
char *voltsPerDivStrings_[NUM_VERT_SELECTIONS];
/* value array for Volts Per Div Choices */
int voltsPerDivValues_[NUM_VERT_SELECTIONS];
/* severities array for Volts Per Div Choices */
int voltsPerDivSeverities_[NUM_VERT_SELECTIONS];
/* set Vert Gain */
void setVertGain();
/* set Volts Per Div according to voltsPerDivSelect */
void setVoltsPerDiv();
/* set Time Per Div according to TimePerDivSelect */
void setTimePerDiv();
};
b) simScope.cpp:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <math.h>
#include <epicsTypes.h>
#include <epicsTime.h>
#include <epicsThread.h>
#include <epicsString.h>
#include <epicsTimer.h>
#include <epicsMutex.h>
#include <epicsEvent.h>
#include <iocsh.h>
#include "simScope.h"
#include <epicsExport.h>
#define FREQUENCY 1000 /* volt signal frequency */
#define AMPLITUDE 1.0 /* volt signal amplitude */
#define NUM_DIVISIONS 10 /* horizontal and vertical axis' divisions */
#define MIN_UPDATE_TIME 0.05 /* the min update time for the waveform data */
#define MAX_ENUM_STRING_SIZE 20
/* This driver's Name */
static const char * driverName = "simScope";
static int allVoltsPerDivSelections[NUM_VERT_SELECTIONS] = {1,2,5,10};
/* For C Call */
void simTask(void * pdrvPvt);
simScope::simScope(const char * portName, int maxPoints)
:asynPortDriver(portName,
1,
asynInt32Mask | asynFloat64Mask | asynFloat64ArrayMask | asynEnumMask | asynDrvUserMask,
asynInt32Mask | asynFloat64Mask | asynFloat64ArrayMask | asynEnumMask,
0, /* asynFlags */
1, /* autoconnect*/
0, /* Default priority */
0) /* Default stack size */
{
asynStatus status;
int i;
const char *functionName = "simScope";
/* make sure the number of points at least 100 points */
if (maxPoints < 100){
maxPoints = 100;
}
printf("Come to Contruct the simScope instance\n");
/* allocate space for waveform data */
pData_ = (epicsFloat64 *)calloc( maxPoints, sizeof(epicsFloat64));
/* allocate space for timebase */
pTimeBase_ = (epicsFloat64 *)calloc(maxPoints, sizeof(epicsFloat64));
/* set the interval between pTimeBase_ elements */
for (i = 0; i < maxPoints; i++){ // the number of divisions is NUM_DIVISIONS
pTimeBase_[i] = (epicsFloat64)i/ (maxPoints - 1) * NUM_DIVISIONS ;
}
// create the event for sending signal to the background thread
eventId_ = epicsEventCreate(epicsEventEmpty);
printf("Come before create parameters\n");
/* create the parameters for communicating with epics Records */
createParam(P_RunString, asynParamInt32, &P_Run);
createParam(P_MaxPointsString, asynParamInt32, &P_MaxPoints);
createParam(P_TimePerDivString, asynParamFloat64, &P_TimePerDiv);
createParam(P_TimePerDivSelectString, asynParamInt32, &P_TimePerDivSelect);
createParam(P_VertGainString, asynParamFloat64, &P_VertGain);
createParam(P_VertGainSelectString, asynParamInt32, &P_VertGainSelect);
createParam(P_VoltsPerDivString, asynParamFloat64, &P_VoltsPerDiv);
createParam(P_VoltsPerDivSelectString, asynParamInt32, &P_VoltsPerDivSelect);
createParam(P_VoltOffsetString, asynParamFloat64, &P_VoltOffset);
createParam(P_TriggerDelayString, asynParamFloat64, &P_TriggerDelay);
createParam(P_NoiseAmplitudeString, asynParamFloat64, &P_NoiseAmplitude);
createParam(P_UpdateTimeString, asynParamFloat64, &P_UpdateTime);
createParam(P_WaveformString, asynParamFloat64Array, &P_Waveform);
createParam(P_TimeBaseString, asynParamFloat64Array, &P_TimeBase);
createParam(P_MinValueString, asynParamFloat64, &P_MinValue);
createParam(P_MaxValueString, asynParamFloat64, &P_MaxValue);
createParam(P_MeanValueString, asynParamFloat64, &P_MeanValue);
/* alloc space for vert per divistion strings */
for (i = 0; i < NUM_VERT_SELECTIONS; i++){
voltsPerDivValues_[i] = 0;
voltsPerDivSeverities_[i] = 0;
voltsPerDivStrings_[i] = (char *)calloc(MAX_ENUM_STRING_SIZE, sizeof(char));
}
setIntegerParam(P_MaxPoints, maxPoints);
setIntegerParam(P_Run, 0);
setIntegerParam(P_VertGainSelect, 10);
/* set the arrays voltsPerDivValues_, voltsPerDivString_ according to P_VertGainSelect parameter*/
setVertGain();
setDoubleParam(P_VoltsPerDiv, 1.0);
setDoubleParam(P_VoltOffset, 0.0);
setDoubleParam(P_TriggerDelay, 0.0);
setDoubleParam(P_NoiseAmplitude, 0.1);
setDoubleParam(P_UpdateTime, 1);
setDoubleParam(P_TimePerDiv, 0.001);
setDoubleParam(P_MinValue, 0.0);
setDoubleParam(P_MaxValue, 0.0);
setDoubleParam(P_MeanValue, 0.0);
printf("Come after Create Parameters and set defaults for these parameters\n");
printf("Come before create background thread\n");
status = (asynStatus)(epicsThreadCreate("simScope",
epicsThreadPriorityMedium,
epicsThreadGetStackSize(epicsThreadStackMedium),
(EPICSTHREADFUNC)::simTask,
(void *)this) == NULL);
printf("Come after create background thread\n");
if (status){
printf("%s:%s: epicsThreadCreate failure\n", driverName, functionName);
return;
}
printf("Come to the end of contructing simScope Intances: portName: %s, maxPoints:%d\n", portName, maxPoints);
}
void simScope::setVertGain()
{
epicsInt32 igain, i;
double gain;
// P_VertGainSelect for mbbo to Vert Gain
getIntegerParam(P_VertGainSelect, &igain);
gain =(epicsFloat64) igain;
setDoubleParam(P_VertGain, gain);
printf("VertGainSelect: %d, VertGain: %lf\n", igain, gain);
for (i = 0; i < NUM_VERT_SELECTIONS; i++){
epicsSnprintf(voltsPerDivStrings_[i], MAX_ENUM_STRING_SIZE, "%.2f",allVoltsPerDivSelections[i] / gain);
voltsPerDivValues_[i] = (int)(allVoltsPerDivSelections[i] / gain * 1000 + 0.5);
printf("%d: V/Div=%d, %s\n", i+1, voltsPerDivValues_[i], voltsPerDivStrings_[i]);
}
//doCallbacksEnum(voltsPerDivStrings_, voltsPerDivValues_, voltsPerDivSeverities_, NUM_VERT_SELECTIONS, P_VoltsPerDivSelect, 0);
doCallbacksEnum(voltsPerDivStrings_, voltsPerDivValues_, voltsPerDivSeverities_, NUM_VERT_SELECTIONS, P_VoltsPerDivSelect, 0);
}
void simScope::setVoltsPerDiv()
{
epicsInt32 mVPerDiv;
getIntegerParam(P_VoltsPerDivSelect, &mVPerDiv);
setDoubleParam(P_VoltsPerDiv, mVPerDiv / 1000.0);
printf("VoltsPerDivSelect: %d, VoltsPerDiv: %lf\n", mVPerDiv, mVPerDiv/1000.0);
}
/*
* P_TimePerDivSelect parameter is in micro second
* P_TimePerDiv parameter is in second
* */
void simScope::setTimePerDiv()
{
epicsInt32 microSecPerDiv;
getIntegerParam(P_TimePerDivSelect, µSecPerDiv);
setDoubleParam(P_TimePerDiv, microSecPerDiv / 1000000.0);
}
asynStatus simScope::writeInt32(asynUser * pasynUser, epicsInt32 value)
{
int function = pasynUser->reason;
asynStatus status = asynSuccess;
const char * paramName;
const char * functionName = "writeInt32";
/* set the value for the parameter */
status = (asynStatus) setIntegerParam(function, value);
/* get name of the parameter */
getParamName(function, ¶mName);
if (function == P_Run){
if (value) epicsEventSignal(eventId_);
}
else if (function == P_VertGainSelect){
setVertGain();
}
else if (function == P_VoltsPerDivSelect){
setVoltsPerDiv();
}
else if (function == P_TimePerDivSelect){
setTimePerDiv(); // set P_TimePerDiv parameter according to P_TimePerDivSelect parameter
}
else{
}
status = (asynStatus) callParamCallbacks();
if (status){
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, "%s:%s: status=%d, functino=%d,parameter name=%s, value=%d", driverName, functionName, status, function, paramName, value);
}
else{
asynPrint(pasynUser, ASYN_TRACEIO_DRIVER, "%s:%s: function=%d,parameter name=%s, value=%d", driverName, functionName, function, paramName, value);
}
return status;
}
asynStatus simScope::writeFloat64(asynUser * pasynUser, epicsFloat64 value)
{
int function = pasynUser->reason;
asynStatus status;
epicsInt32 run;
const char * paramName;
const char * functionName = "writeFloat64";
status = (asynStatus)setDoubleParam(function, value);
getParamName(function, ¶mName);
if (function == P_UpdateTime){
if (value < MIN_UPDATE_TIME){
asynPrint(pasynUser, ASYN_TRACE_WARNING, "%s:%s warning, update time too small, changed from %f to %f\n", driverName, functionName, value, MIN_UPDATE_TIME);
value = MIN_UPDATE_TIME;
setDoubleParam(P_UpdateTime, value);
}
getIntegerParam(P_Run, &run);
}
else{
}
status = (asynStatus) callParamCallbacks();
if (status){
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, "%s:%s: status=%d, function=%d, parameter name=%s, value=%f",
driverName, functionName, status, function, paramName, value);
}
else{
asynPrint(pasynUser, ASYN_TRACEIO_DRIVER, "%s:%s: function=%d, parameter name = %s, value = %f\n", driverName, functionName, function, paramName, value);
}
return status;
}
asynStatus simScope::readFloat64Array(asynUser *pasynUser, epicsFloat64 *value , size_t nElements, size_t *nIn)
{
int function = pasynUser->reason;
size_t nCopy;
epicsInt32 maxPoints;
asynStatus status = asynSuccess;
epicsTimeStamp timeStamp;
const char *functionName = "readFloat64Array";
const char * paramName;
getTimeStamp(&timeStamp);
pasynUser->timestamp = timeStamp;
getIntegerParam(P_MaxPoints, &maxPoints);
nCopy = maxPoints;
getParamName(function, ¶mName);
if (nElements < nCopy) nCopy = nElements;
if (function == P_Waveform){
memcpy(value, pData_, nCopy * sizeof(epicsFloat64));
* nIn = nCopy;
}
else if (function == P_TimeBase){
memcpy(value, pTimeBase_, nCopy * sizeof(epicsFloat64));
* nIn = nCopy;
}
if(status){
epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, "%s:%s: status=%d, function=%d, parameter name=%s", driverName, functionName, status, function, paramName);
}
else{
asynPrint(pasynUser, ASYN_TRACEIO_DRIVER, "%s:%s: function=%d, parameter name=%s\n", driverName, functionName, function, paramName);
}
return status;
}
asynStatus simScope::readEnum(asynUser * pasynUser, char *strings[], int values[], int severities[], size_t nElements, size_t *nIn)
{
int function = pasynUser->reason;
size_t i;
if (function == P_VoltsPerDivSelect){
for (i = 0; ((i < NUM_VERT_SELECTIONS) && (i < nElements)); i++){
if (strings[i]) free(strings[i]);
strings[i] = epicsStrDup(voltsPerDivStrings_[i]);
values[i] = voltsPerDivValues_[i];
severities[i] = 0;
}
}
else{
*nIn = 0;
return asynError;
}
*nIn = i;
return asynSuccess;
}
void simScope::simTask()
{
double timePerDiv, voltsPerDiv, voltOffset, triggerDelay, noiseAmplitude;
double updateTime, minValue, maxValue, meanValue;
double time, timeStep;
double noise, yScale;
epicsInt32 run, i, maxPoints;
double pi = 3.1415926;
printf("Enter in the simTask() functinon:\n");
lock();
printf("Enter in loop forever:\n");
while (1){
getDoubleParam(P_UpdateTime, &updateTime);
getIntegerParam(P_Run, &run);
printf("updateTime: %f, run: %d\n", updateTime, run);
unlock();
if (run) {
epicsEventWaitWithTimeout(eventId_, updateTime);
}
else{
printf("Wait for running signal\n");
epicsEventWait(eventId_);
printf("Receive the running signal\n");
}
lock();
getIntegerParam(P_Run, &run);
if (!run){
continue;
}
getIntegerParam(P_MaxPoints, &maxPoints);
getDoubleParam(P_TimePerDiv, &timePerDiv);
getDoubleParam(P_VoltsPerDiv, &voltsPerDiv);
getDoubleParam(P_VoltOffset, &voltOffset);
getDoubleParam(P_TriggerDelay, &triggerDelay);
getDoubleParam(P_NoiseAmplitude, &noiseAmplitude);
getDoubleParam(P_UpdateTime, &updateTime);
printf("maxpoints:%d, timeperdiv:%f, voltsPerDiv:%f, voltoffset:%f, triggerdelay:%f, noiseampitude:%f, updatetime:%f\n",
maxPoints, timePerDiv, voltsPerDiv, voltOffset, triggerDelay, noiseAmplitude, updateTime);
time = triggerDelay;
timeStep = timePerDiv * NUM_DIVISIONS / maxPoints;
minValue = 1e6;
maxValue = -1e6;
meanValue = 0.;
yScale = 1.0 / voltsPerDiv;
printf("timeStep:%f\n", timeStep);
for (i = 0; i < maxPoints; i++){
noise = noiseAmplitude * (rand() / (double)RAND_MAX - 0.5);
pData_[i] = AMPLITUDE * (sin(time * FREQUENCY * 2 * pi)) + noise;
if (pData_[i] < minValue){
minValue = pData_[i];
}
if (pData_[i] > maxValue){
maxValue = pData_[i];
}
meanValue += pData_[i];
pData_[i] = NUM_DIVISIONS / 2 + yScale * (pData_[i] + voltOffset);
time += timeStep;
}
updateTimeStamp();
meanValue = meanValue / maxPoints;
setDoubleParam(P_MinValue, minValue);
setDoubleParam(P_MaxValue, maxValue);
setDoubleParam(P_MeanValue, meanValue);
printf("min:%f, max:%f, mean:%f\n", minValue, maxValue, meanValue);
callParamCallbacks();
doCallbacksFloat64Array(pData_, maxPoints, P_Waveform, 0);
}
}
void simTask(void * drvPvt)
{
simScope * pPvt = (simScope *)drvPvt;
pPvt->simTask();
}
extern "C"{
int simScopeConfigure(const char * portName, int maxPoints)
{
new simScope(portName, maxPoints);
return asynSuccess;
}
static const iocshArg initArg0 = {"portName", iocshArgString};
static const iocshArg initArg1 = {"max points", iocshArgInt};
static const iocshArg * const initArgs[] = {&initArg0, &initArg1};
static const iocshFuncDef initFuncDef = {"simScopeConfigure", 2, initArgs};
static void initCallFunc(const iocshArgBuf * args)
{
simScopeConfigure(args[0].sval, args[1].ival);
}
void simScopeRegister(void)
{
iocshRegister(&initFuncDef, initCallFunc);
}
epicsExportRegistrar(simScopeRegister);
}
c) simScopeSupport.dbd:
registrar("simScopeRegister")
d) 修改所编写源文件目录下的Makefile文件,指定需要链接的库文件以及源文件及支持文件:
TOP=../..
include $(TOP)/configure/CONFIG
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
#=============================
#=============================
# Build the IOC application
PROD_IOC = simScope
# simScope.dbd will be created and installed
DBD += simScope.dbd
# simScope.dbd will be made up from these files:
simScope_DBD += base.dbd
simScope_DBD += simScopeSupport.dbd
# Include dbd files from all support applications:
simScope_DBD += asyn.dbd
# Add all the support libraries needed by this IOC
simScope_LIBS += asyn
simScope_SRCS += simScope.cpp
# simScope_registerRecordDeviceDriver.cpp derives from simScope.dbd
simScope_SRCS += simScope_registerRecordDeviceDriver.cpp
# Build the main IOC entry point on workstation OSs.
simScope_SRCS_DEFAULT += simScopeMain.cpp
simScope_SRCS_vxWorks += -nil-
# Add support from base/src/vxWorks if needed
#simScope_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary
# Finally link to the EPICS Base libraries
simScope_LIBS += $(EPICS_BASE_IOC_LIBS)
#===========================
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE
4) 3) 进入simScopeApp/Db/目录下下编写记录支持数据库文件simScope.db并且将其添加到相同路径下的Makefile文件中:
# These records control run/stop --> P_Run
record(bo, "$(P)$(R)Run")
{
field(PINI, "1")
field(DTYP, "asynInt32")
field(OUT, "@asyn($(PORT), $(ADDR), $(TIMEOUT))SCOPE_RUN")
field(ZNAM, "Stop")
field(ONAM, "Run")
}
record(bi, "$(P)$(R)Run_RBV")
{
field(PINI, "1")
field(DTYP, "asynInt32")
field(INP, "@asyn($(PORT), $(ADDR), $(TIMEOUT))SCOPE_RUN")
field(ZNAM, "Done")
field(ONAM, "Running")
field(OSV, "MINOR")
field(SCAN, "I/O Intr")
}
## This record is the number of points in the data array ---> P_MaxPoints
## readonly, set in startup
record(longin, "$(P)$(R)MaxPoints_RBV")
{
field(PINI, "1")
field(DTYP, "asynInt32")
field(INP, "@asyn($(PORT), $(ADDR), $(TIMEOUT))SCOPE_MAX_POINTS")
field(SCAN, "I/O Intr")
}
# There recrods are the choices for the Time Per Div Select ---> P_TimePerDivSelect
record(mbbo, "$(P)$(R)TimePerDivSelect")
{
field(PINI, "1")
field(DTYP, "asynInt32")
field(OUT, "@asyn($(PORT), $(ADDR), $(TIMEOUT))SCOPE_TIME_PER_DIV_SELECT")
field(ZRST, "0.01 msec")
field(ZRVL, "10")
field(ONST, "0.02 msec")
field(ONVL, "20")
field(TWST, "0.05 msec")
field(TWVL, "50")
field(THST, "0.1 msec")
field(THVL, "100")
field(FRST, "0.2 msec")
field(FRVL, "200")
field(FVST, "0.5 msec")
field(FVVL, "500")
field(SXST, "1 msec")
field(SXVL, "1000")
field(SVST, "2 msec")
field(SVVL, "2000")
field(EIST, "5 msec")
field(EIVL, "5000")
field(NIST, "10 msec")
field(NIVL, "10000")
field(TEST, "20 msec")
field(TEVL, "20000")
field(ELST, "50 msec")
field(ELVL, "50000")
field(TVST, "100 msec")
field(TVVL, "100000")
}
#
## P_TimePerDiv
record(ai, "$(P)$(R)TimePerDiv_RBV")
{
field(PINI, "1")
field(DTYP, "asynFloat64")
field(INP, "@asyn($(PORT), $(ADDR), $(TIMEOUT))SCOPE_TIME_PER_DIV")
field(PREC, "5")
field(SCAN, "I/O Intr")
}
#
## These records is for Vert Gain
## P_VertGainSelect
record(mbbo, "$(P)$(R)VertGainSelect")
{
field(PINI, "1")
field(DTYP, "asynInt32")
field(OUT, "@asyn($(PORT), $(ADDR), $(TIMEOUT))SCOPE_VERT_GAIN_SELECT")
field(ZRST, "1 X")
field(ZRVL, "1")
field(ONST, "2 X")
field(ONVL, "2")
field(TWST, "5 X")
field(TWVL, "5")
field(THST, "10 X")
field(THVL, "10")
field(FRST, "100 X")
field(FRVL, "100")
}
# P_VertGain
record(ai, "$(P)$(R)VertGain_RBV")
{
field(PINI, "1")
field(INP, "@asyn($(PORT), $(ADDR), $(TIMEOUT))SCOPE_VERT_GAIN")
field(DTYP, "asynFloat64")
field(PREC, "1")
field(SCAN, "I/O Intr")
}
#
#
record(mbbo, "$(P)$(R)VoltsPerDivSelect")
{
field(PINI, "1")
field(DTYP, "asynInt32")
field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))SCOPE_VOLTS_PER_DIV_SELECT")
field(ZRST, "Garbage")
field(ZRVL, "0")
}
#
record(mbbi, "$(P)$(R)VoltsPerDivSelect_RBV")
{
field(DTYP, "asynInt32")
field(INP, "@asyn($(PORT),$(ADDR),$(TIMEOUT))SCOPE_VOLTS_PER_DIV_SELECT")
field(ZRST, "Garbage")
field(ZRVL, "0")
field(SCAN, "I/O Intr")
}
#
record(ai, "$(P)$(R)VoltsPerDiv_RBV")
{
field(PINI, "1")
field(DTYP, "asynFloat64")
field(INP, "@asyn($(PORT),$(ADDR),$(TIMEOUT))SCOPE_VOLTS_PER_DIV")
field(PREC, "2")
field(SCAN, "I/O Intr")
}
## This record to set the offset
## P_VoltOffset
record(ao, "$(P)$(R)VoltOffset")
{
field(PINI, "1")
field(DTYP, "asynFloat64")
field(OUT, "@asyn($(PORT), $(ADDR), $(TIMEOUT))SCOPE_VOLT_OFFSET")
field(PREC, "3")
}
record(ai, "$(P)$(R)VoltOffset_RBV")
{
field(PINI, "1")
field(DTYP, "asynFloat64")
field(INP, "@asyn($(PORT), $(ADDR), $(TIMEOUT))SCOPE_VOLT_OFFSET")
field(PREC, "3")
field(SCAN, "I/O Intr")
}
# This record to set trigger delay time
record(ao, "$(P)$(R)TriggerDelay")
{
field(PINI, "1")
field(DTYP, "asynFloat64")
field(OUT, "@asyn($(PORT), $(ADDR), $(TIMEOUT))SCOPE_TRIGGER_DELAY")
field(PREC, "5")
}
record(ai, "$(P)$(R)TriggerDelay_RBV")
{
field(PINI, "1")
field(DTYP, "asynFloat64")
field(INP, "@asyn($(PORT), $(ADDR), $(TIMEOUT))SCOPE_TRIGGER_DELAY")
field(PREC, "5")
field(SCAN, "I/O Intr")
}
# This record to control noise in the signal
# P_NoiseAmplitude
record(ao, "$(P)$(R)NoiseAmplitude")
{
field(PINI, "1")
field(DTYP, "asynFloat64")
field(OUT, "@asyn($(PORT), $(ADDR), $(TIMEOUT))SCOPE_NOISE_AMPLITUDE")
field(PREC, "3")
}
record(ai, "$(P)$(R)NoiseAmplitude_RBV")
{
field(PINI, "1")
field(DTYP, "asynFloat64")
field(INP, "@asyn($(PORT), $(ADDR), $(TIMEOUT))SCOPE_NOISE_AMPLITUDE")
field(PREC, "3")
field(SCAN, "I/O Intr")
}
#
# This record to control the update time
record(ao, "$(P)$(R)UpdateTime")
{
field(PINI, "1")
field(DTYP, "asynFloat64")
field(OUT, "@asyn($(PORT), $(ADDR), $(TIMEOUT))SCOPE_UPDATE_TIME")
field(PREC, "3")
}
record(ai, "$(P)$(R)UpdateTime_RBV")
{
field(PINI, "1")
field(DTYP, "asynFloat64")
field(INP, "@asyn($(PORT), $(ADDR), $(TIMEOUT))SCOPE_UPDATE_TIME")
field(PREC, "3")
field(SCAN, "I/O Intr")
}
# This record is waveform
record(waveform, "$(P)$(R)Waveform_RBV")
{
field(PINI, "1")
field(DTYP, "asynFloat64ArrayIn")
field(INP, "@asyn($(PORT), $(ADDR), $(TIMEOUT))SCOPE_WAVEFORM")
field(NELM, "$(NPOINTS)")
field(FTVL, "DOUBLE")
field(LOPR, "0")
field(HOPR, "10")
field(SCAN, "I/O Intr")
}
# This record is time base
record(waveform, "$(P)$(R)TimeBase_RBV")
{
field(PINI, "1")
field(DTYP, "asynFloat64ArrayIn")
field(INP, "@asyn($(PORT), $(ADDR), $(TIMEOUT))SCOPE_TIME_BASE")
field(NELM, "$(NPOINTS)")
field(FTVL, "DOUBLE")
field(LOPR, "0")
field(HOPR, "10")
field(SCAN, "I/O Intr")
}
# This record is min value
record(ai, "$(P)$(R)MinValue_RBV")
{
field(PINI, "1")
field(DTYP, "asynFloat64")
field(INP, "@asyn($(PORT), $(ADDR), $(TIMEOUT))SCOPE_MIN_VALUE")
field(PREC, "4")
field(SCAN, "I/O Intr")
}
record(ai, "$(P)$(R)MaxValue_RBV")
{
field(PINI, "1")
field(DTYP, "asynFloat64")
field(INP, "@asyn($(PORT), $(ADDR), $(TIMEOUT))SCOPE_MAX_VALUE")
field(PREC, "4")
field(SCAN, "I/O Intr")
}
record(ai, "$(P)$(R)MeanValue_RBV")
{
field(PINI, "1")
field(DTYP, "asynFloat64")
field(INP, "@asyn($(PORT), $(ADDR), $(TIMEOUT))SCOPE_MEAN_VALUE")
field(PREC, "4")
field(SCAN, "I/O Intr")
}
Makefile中添加:DB += simScope.db
5)回到这个IOC的顶层目录,即:simScope,执行make命令,进行程序编译。
6)进入启动目录iocBoot/iocsimScope,修改启动脚本文件st.cmd:
#!../../bin/linux-aarch64/simScope
#- You may have to change simScope to something else
#- everywhere it appears in this file
< envPaths
cd "${TOP}"
## Register all support components
dbLoadDatabase "dbd/simScope.dbd"
simScope_registerRecordDeviceDriver pdbbase
simScopeConfigure("testSimScope", 1000)
## Load record instances
dbLoadRecords("db/simScope.db","P=SimScope:,R=Device1:,PORT=testSimScope,ADDR=0,TIMEOUT=1, NPOINTS=1000")
cd "${TOP}/iocBoot/${IOC}"
iocInit
7) 启动这个IOC程序:
../../bin/linux-aarch64/simScope st.cmd
8) 查看这个IOC下的所有加载记录:
epics> dbl
SimScope:Device1:TimePerDiv_RBV
SimScope:Device1:VertGain_RBV
SimScope:Device1:VoltsPerDiv_RBV
SimScope:Device1:VoltOffset_RBV
SimScope:Device1:TriggerDelay_RBV
SimScope:Device1:NoiseAmplitude_RBV
SimScope:Device1:UpdateTime_RBV
SimScope:Device1:MinValue_RBV
SimScope:Device1:MaxValue_RBV
SimScope:Device1:MeanValue_RBV
SimScope:Device1:VoltOffset
SimScope:Device1:TriggerDelay
SimScope:Device1:NoiseAmplitude
SimScope:Device1:UpdateTime
SimScope:Device1:Run_RBV
SimScope:Device1:Run
SimScope:Device1:MaxPoints_RBV
SimScope:Device1:VoltsPerDivSelect_RBV
SimScope:Device1:TimePerDivSelect
SimScope:Device1:VertGainSelect
SimScope:Device1:VoltsPerDivSelect
SimScope:Device1:Waveform_RBV
SimScope:Device1:TimeBase_RBV
9) 使用CSS显示以上运行的记录数据库:
在Settings区域,可以选择设置垂直增益,每个刻度电压值,电压偏移,触发延时,正弦波更新周期,以及每个水平刻度表示的时间量。
在User Operate区域中,可以启动/停止波形的产生。
在Running Status区域可以显示运行状态。
在Statics Info区域中显示波形数据的统计信息。