1) 创建一个用户存放这个IOC程序结构的目录:
root@orangepi4-lts:/usr/local/EPICS/program# mkdir ortec974A
root@orangepi4-lts:/usr/local/EPICS/program# cd ortec974A/
root@orangepi4-lts:/usr/local/EPICS/program/ortec974A# ls
2)使用makeBaseApp.pl构建这个IOC程序架构:
root@orangepi4-lts:/usr/local/EPICS/program/ortec974A# makeBaseApp.pl -t ioc ortec974A
root@orangepi4-lts:/usr/local/EPICS/program/ortec974A# makeBaseApp.pl -i -t ioc ortec974A
Using target architecture linux-aarch64 (only one available)
The following applications are available:
ortec974A
What application should the IOC(s) boot?
The default uses the IOC's name, even if not listed above.
Application name?
root@orangepi4-lts:/usr/local/EPICS/program/ortec974A# ls
configure iocBoot Makefile ortec974AApp
3)编辑configure/RELEASE文件,添加这个IOC程序所需要的其它依赖模块:
...
SUPPORT=/usr/local/EPICS/synApps/support
ASYN=$(SUPPORT)/asyn
AUTOSAVE=$(SUPPORT)/autosave
CALC=$(SUPPORT)/calc
SCALER=$(SUPPORT)/scaler
...
4) 进入源文件目录ortec974AApp/src目录下:
a) 编写974A驱动程序源文件:
// drvOrtec974A.cpp
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <asynPortDriver.h>
#include <asynOctetSyncIO.h>
#include <epicsEvent.h>
#include <epicsThread.h>
#include <epicsTime.h>
#include <errlog.h>
#include <iocsh.h>
#include <epicsExport.h> /* Defines epicsExportSharedSymbols */
#include "devScalerAsyn.h"
#define MAX_CHANNELS 4
#define timeOut 0.1
static const char *driverName= "Scaler974A";
class Scaler974A:public asynPortDriver
{
public:
Scaler974A(const char *portName, const char *serialPort, int serialAddr, int poll);
virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
virtual asynStatus readInt32Array(asynUser *pasynUser,epicsInt32 *value, size_t nElements, size_t *nIn);
virtual void report(FILE *fp, int details);
void eventThread();
private:
int scalerReset;
int scalerChannels;
int scalerRead;
int scalerReadSingle;
int scalerPreset;
int scalerArm;
int scalerDone;
double polltime;
epicsEventId eventId;
asynUser *pasynUserScaler;
asynStatus sendCommand(const char *command, char *statusString, size_t maxStatusLen, size_t *statusLen);
};
static void eventThreadC(void *pPvt)
{
Scaler974A *pScaler974A = (Scaler974A *)pPvt;
pScaler974A->eventThread();
}
Scaler974A::Scaler974A(const char *portName, const char *serialPort, int serialAddr, int poll)
:asynPortDriver(portName, MAX_CHANNELS,
asynInt32Mask | asynInt32ArrayMask | asynDrvUserMask,
asynInt32Mask,
/* Should also be ASYN_CANBLOCK, but device support does not work with asynchronous devices */
ASYN_MULTIDEVICE,1,0,0)
{
int i;
asynStatus status;
static const char *functionName="Scaler974A";
if (poll==0) poll=100;
this->polltime=poll / 1000.;
this->eventId = epicsEventCreate(epicsEventEmpty);
status = pasynOctetSyncIO->connect(serialPort, serialAddr, &this->pasynUserScaler, NULL);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s:%s: error connecting to port %s address %d\n",
driverName, functionName, serialPort, serialAddr);
return;
}
createParam(SCALER_RESET_COMMAND_STRING, asynParamInt32, &this->scalerReset);
createParam(SCALER_CHANNELS_COMMAND_STRING, asynParamInt32, &this->scalerChannels);
createParam(SCALER_READ_COMMAND_STRING, asynParamInt32Array,&this->scalerRead);
createParam(SCALER_READ_SINGLE_COMMAND_STRING, asynParamInt32, &this->scalerReadSingle);
createParam(SCALER_PRESET_COMMAND_STRING, asynParamInt32, &this->scalerPreset);
createParam(SCALER_ARM_COMMAND_STRING, asynParamInt32, &this->scalerArm);
createParam(SCALER_DONE_COMMAND_STRING, asynParamInt32, &this->scalerDone);
setIntegerParam(scalerChannels, MAX_CHANNELS);
setIntegerParam(scalerDone, 1);
for (i=0; i<MAX_CHANNELS; i++) setIntegerParam(i, scalerReadSingle, 0);
epicsThreadCreate("Scaler974A",
epicsThreadPriorityMedium,
epicsThreadGetStackSize(epicsThreadStackMedium),
(EPICSTHREADFUNC)eventThreadC,this);
}
asynStatus Scaler974A::sendCommand(const char *command, char *statusString, size_t maxStatusLen, size_t *statusLen)
{
size_t nWrite;
asynStatus status;
double timeout = 1.0;
int eomReason;
static const char *functionName = "sendCommand";
/*
* asynStatus (*writeRead)(asynUser *pasynUser,
const char *write_buffer, size_t write_buffer_len,
char *read_buffer, size_t read_buffer_len,
double timeout,
size_t *nbytesOut, size_t *nbytesIn, int *eomReason);
要写的字符串,要写的字符串长度,读出缓存,要读的字节数目,超时时间,实际写出的字节数,实际读入的字符数,结束原因
asynStatus (*write)(asynUser *pasynUser, char const *buffer, size_t buffer_len,
double timeout,size_t *nbytesTransfered);
asynStatus (*read)(asynUser *pasynUser, char *buffer, size_t buffer_len,
double timeout, size_t *nbytesTransfered,int *eomReason);
*
* */
status = pasynOctetSyncIO->write(this->pasynUserScaler, command, strlen(command), timeout, &nWrite);
// printf("In sendCommand write status: %d,write content:%s,write length:%d\n", status, command, nWrite);
if (status != asynSuccess) goto done;
epicsThreadSleep(.1);
status = pasynOctetSyncIO->read(this->pasynUserScaler,statusString, maxStatusLen, timeout, statusLen, &eomReason);
// printf("In sendCommand read status: %d, statusString:%s\n", status, statusString);
done:
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s:%s: writing command %s, error=%s\n",
driverName, functionName, command, this->pasynUserScaler->errorMessage);
}
return(status);
}
asynStatus Scaler974A::writeInt32(asynUser *pasynUser, epicsInt32 value)
{
int function = pasynUser->reason;
char response[256];
size_t responseLen;
asynStatus status = asynSuccess;
static const char *functionName="writeInt32";
setIntegerParam(function, value);
if(function==this->scalerReset)
{
this->sendCommand("STOP\r", response, sizeof(response), &responseLen);
//printf("STOP:%s\n", response);
this->sendCommand("CLEAR_ALL\r", response, sizeof(response), &responseLen);
//printf("CLEAR_ALL:%s\n", response);
asynPrint(pasynUser, ASYN_TRACE_FLOW, "%s:%s scalerReset\n",driverName, functionName);
}
else if(function==this->scalerArm)
{
if(value !=0)
{
/* Start counting */
this->sendCommand("START\r", response, sizeof(response), &responseLen);
// printf("START:%s\n", response);
setIntegerParam(scalerDone, 0);
epicsEventSignal(this->eventId);
}
else
{
/* Stop counting */
status = this->sendCommand("STOP\r", response, sizeof(response), &responseLen);
// printf("STOP:%s\n", response);
setIntegerParam(scalerDone, 1);
}
asynPrint(pasynUser, ASYN_TRACE_FLOW, "%s:%s scalerArm=%d\n", driverName, functionName,value);
}
else if(function==this->scalerPreset)
{
int m,n;
char newstr[25];
n=(int)log10(double(value));
m=(int)(value/pow(10.0,n));
sprintf(newstr, "SET_COUNT_PRESET %d,%d\r", m, n);
this->sendCommand(newstr, response, sizeof(response), &responseLen);
// printf("SET_COUNT_PRESET:%s\n", response);
asynPrint(pasynUser, ASYN_TRACE_FLOW, "%s;%s scalerPreset channel", driverName, functionName);
}
else
{
asynPrint(pasynUser,ASYN_TRACE_ERROR,"%s:%s got illegal function %d\n", driverName, functionName, function);
}
callParamCallbacks();
return(status);
}
asynStatus Scaler974A::readInt32Array(asynUser *pasynUser, epicsInt32 *value, size_t maxChannel, size_t *nIn)
{
static const char *functionName="readInt32Array";
int function = pasynUser->reason;
int temp;
int i;
if (maxChannel > MAX_CHANNELS) maxChannel = MAX_CHANNELS;
*nIn = maxChannel;
if (function==scalerRead)
{
for (i=0; i<(int)maxChannel; i++)
{
getIntegerParam(i, scalerReadSingle, &temp);
value[i] = temp;
}
asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
"%s:%s: value=%d %d %d %d\n",
driverName, functionName, value[0], value[1], value[2], value[3]);
} else {
asynPrint(pasynUser,ASYN_TRACE_ERROR,"%s:%s got illegal function %d\n", driverName, functionName, function);
return(asynError);
}
return(asynSuccess);
}
void Scaler974A::report(FILE *fp, int details)
{
asynPortDriver::report(fp, details);
}
void Scaler974A::eventThread()
{
int done, presetCount;
char response[100], statusString[256];
size_t responseLen, statusLen;
int counts[MAX_CHANNELS];
int i;
asynStatus status;
static const char *functionName="eventThread";
while(1)
{
epicsEventMustWait(this->eventId);
while(1)
{
status = this->sendCommand("SHOW_COUNTS\r", statusString, sizeof(statusString), &statusLen);
sscanf(statusString, "%d;%d;%d;%d;",
&counts[0], &counts[1], &counts[2], &counts[3]);
// printf("%d;%d;%d;%d\n", counts[0],counts[1],counts[2],counts[3]);
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
"%s:%s status=%d, counts=%d %d %d %d\n",
driverName, functionName, status, counts[0], counts[1], counts[2], counts[3]);
this->lock();
/* Get value of done in case scaler was stopped by scalerArm(0) */
getIntegerParam(scalerDone, &done);
getIntegerParam(scalerPreset, &presetCount);
if (!done && (counts[0] >= presetCount)) done = 1;
setIntegerParam(scalerDone, done);
for (i=0; i<MAX_CHANNELS; i++) {
setIntegerParam(i, scalerReadSingle, counts[i]);
callParamCallbacks(i, i);
}
this->unlock();
if (done) break;
epicsThreadSleep(this->polltime/1000.0);
}
}
}
extern "C" int initScaler974A(const char *portName, const char *serialPort, int serialAddr, int poll)
{
new Scaler974A(portName, serialPort, serialAddr, poll);
return(asynSuccess);
}
/* iocsh function */
static const iocshArg initArg0 = {"Port Name", iocshArgString};
static const iocshArg initArg1 = {"IPPort", iocshArgString};
static const iocshArg initArg2 = {"Addr", iocshArgString};
static const iocshArg initArg3 = {"Poll", iocshArgInt};
static const iocshArg *const initArgs[] = {&initArg0,
&initArg1,
&initArg2,
&initArg3
};
static const iocshFuncDef initFuncDef = {"initScaler974A",4, initArgs};
static void initCallFunc(const iocshArgBuf *args)
{
initScaler974A(args[0].sval, args[1].sval, args[2].ival, args[3].ival);
}
void Scaler974ARegister(void)
{
iocshRegister(&initFuncDef,initCallFunc);
}
extern "C" {
epicsExportRegistrar(Scaler974ARegister);
}
b) 添加一个dbd文件ortec974ASupport.dbd:
registrar(Scaler974ARegister)
c) 修改同一目录下Makefile文件,指定编译中需要包含的库文件和源文件:
TOP=../..
include $(TOP)/configure/CONFIG
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
#=============================
#=============================
# Build the IOC application
PROD_IOC = ortec974A
# ortec974A.dbd will be created and installed
DBD += ortec974A.dbd
# ortec974A.dbd will be made up from these files:
ortec974A_DBD += base.dbd
ortec974A_DBD += ortec974ASupport.dbd
# Include dbd files from all support applications:
#ortec974A_DBD += xxx.dbd
ortec974A_DBD += drvAsynIPPort.dbd
ortec974A_DBD += asyn.dbd
ortec974A_DBD += scalerSupport.dbd
ortec974A_DBD += calcSupport.dbd
ortec974A_DBD += asSupport.dbd
# Include dbd files from all support applications:
#ortec974A_DBD += xxx.dbd
# Add all the support libraries needed by this IOC
ortec974A_LIBS += asyn
ortec974A_LIBS += scaler
ortec974A_LIBS += calc
ortec974A_LIBS += autosave
# Add all the support libraries needed by this IOC
#ortec974A_LIBS += xxx
ortec974A_SRCS += drvOrtec974A.cpp
# ortec974A_registerRecordDeviceDriver.cpp derives from ortec974A.dbd
ortec974A_SRCS += ortec974A_registerRecordDeviceDriver.cpp
# Build the main IOC entry point on workstation OSs.
ortec974A_SRCS_DEFAULT += ortec974AMain.cpp
ortec974A_SRCS_vxWorks += -nil-
# Add support from base/src/vxWorks if needed
#ortec974A_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary
# Finally link to the EPICS Base libraries
ortec974A_LIBS += $(EPICS_BASE_IOC_LIBS)
#===========================
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE
5) 进入ortec974AApp/Db目录,编写一个用于自动保存的文件scaler4_settings.req:
$(P)$(S).TP
$(P)$(S).TP1
$(P)$(S).CONT
$(P)$(S).DLY1
$(P)$(S).RAT1
$(P)$(S).PREC
$(P)$(S).FREQ
$(P)$(S).RATE
$(P)$(S).DLY
$(P)$(S).COUT
file "scaler_channelN_settings.req", P=$(P), S=$(S), Ch=1
file "scaler_channelN_settings.req", P=$(P), S=$(S), Ch=2
file "scaler_channelN_settings.req", P=$(P), S=$(S), Ch=3
file "scaler_channelN_settings.req", P=$(P), S=$(S), Ch=4
在同一目录下的Makefile中添加一行:DB += scaler4_settings.req
6) 退出到这个IOC的顶层目录,执行make进行程序编译。
7)进入到启动目录iocBoot/iocortec974A,创建两个目录:
root@orangepi4-lts:/usr/local/EPICS/program/ortec974A/iocBoot/iocortec974A# mkdir -p req/ortec974A
root@orangepi4-lts:/usr/local/EPICS/program/ortec974A/iocBoot/iocortec974A# mkdir -p autosave/ortec974A
在req/ortec974A下编写一个用于自动保存的文件auto_settings.req,内容如下:
file scaler4_settings.req P=$(P) S=$(S)
编写启动脚本:
#!../../bin/linux-aarch64/ortec974A
#- You may have to change ortec974A to something else
#- everywhere it appears in this file
< envPaths
cd "${TOP}"
## Register all support components
dbLoadDatabase "dbd/ortec974A.dbd"
ortec974A_registerRecordDeviceDriver pdbbase
drvAsynIPPortConfigure("ortec974A", "192.168.3.30:4001", 0, 0 ,1)
initScaler974A("tcportec974A","ortec974A",0,10)
dbLoadRecords("${SCALER}/db/scaler.db", "P=Ortec974A:,S=Scaler1,OUT=@asyn(tcportec974A 0 0),DTYP=Asyn Scaler,FREQ=10,TP=1,TP1=0.5,PR1=1,CONT=1")
set_requestfile_path("$(TOP)/db")
set_requestfile_path("$(SCALER)/db")
set_requestfile_path("$(TOP)/iocBoot/$(IOC)/req/ortec974A")
# 通过调用set_savefile_path函数指定你想要.sav文件被写到哪个目录中。
set_savefile_path("$(TOP)/iocBoot/$(IOC)/autosave/ortec974A")
# 使用set_pass<N>_restoreFile()函数
# 指定哪些save文件要在记录初始化前(pass 0)前被恢复,以及哪些save文件在记录初始化后(pass 1)被恢复
set_pass1_restoreFile("auto_settings.sav")
save_restoreSet_numSeqFiles(3)
save_restoreSet_SeqPeriodInSeconds(600)
save_restoreSet_RetrySeconds(60)
save_restoreSet_CAReconnect(1)
save_restoreSet_CallbackTimeout(-1)
dbLoadRecords("$(ASYN)/db/asynRecord.db","P=Ortec974A:Scaler1:,R=Asyn,PORT=ortec974A,ADDR=0,IMAX=100,OMAX=100")
cd "${TOP}/iocBoot/${IOC}"
iocInit
create_monitor_set("auto_settings.req",5,"P=Ortec974A:,S=Scaler1")
8) 用以上启动脚本启动这个IOC程序:
root@orangepi4-lts:/usr/local/EPICS/program/ortec974A/iocBoot/iocortec974A# ../../bin/linux-aarch64/ortec974A st.cmd
9)查看这个IOC加载的记录:
epics> dbl
Ortec974A:Scaler1:Asyn
Ortec974A:Scaler1
10) 用CSS编写操作客户端:
11) 在97A的3号输入端口接入一个3MHz的TTL信号,将计数时间设置为1.0秒,点击Count按钮,进行1秒钟计数:
通道3能够读出计数器的正确计数值。