本驱动程序是控制https://blog.csdn.net/yuyuyuliang00/article/details/132483050中描述的模拟电机控制器。其余基于字符串通信方式的电机控制器,都可以使用这个模板进行修改,开发对应的EPICS电机驱动程序。
源程序如下:
头文件vm.h:
#include "asynMotorController.h"
#include "asynMotorAxis.h"
#define MAX_VIRTUAL_MOTOR_AXES 32 /* motor.h设置最大轴数 */
//#define BUFF_SIZE 20 /* 和VirtualMotor之间来回传递字符串的最大长度 */
// 还没有控制器专用参数
#define NUM_VIRTUAL_MOTOR_PARAMS 0
class epicsShareClass VirtualMotorAxis : public asynMotorAxis
{
public:
/* 这些是我们重写自基类的方法 */
/* 参数:1)指向本轴所属的控制器对象的指针 2)本轴的编号 */
VirtualMotorAxis(class VirtualMotorController *pC, int axisNo);
//VirtualMotorAxis(class VirtualMotorController *pC, int axisNo, double stepSize);
void report(FILE *fp, int level);
/* 位置,相对/绝对,最低速度,最高速度,加速度*/
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration);
/* 最低速度,最高速度,加速度 */
asynStatus moveVelocity(double min_velocity, double max_velocity, double acceleration);
//asynStatus home(double min_velocity, double max_velocity, double acceleration, int forwards);
asynStatus stop(double acceleration);
asynStatus poll(bool *moving);
asynStatus setPosition(double position);
//asynStatus setClosedLoop(bool closedLoop);
private:
VirtualMotorController *pC_; /**< 指向本轴所属的控制器对象的指针。因为使用非常频繁,缩写。*/
int axisIndex_;
//double stepsSize_;
// 设定加速度,速度,基速度
asynStatus sendAccelAndVelocity(double accel, double velocity, double baseVelocity);
friend class VirtualMotorController;
};
class epicsShareClass VirtualMotorController : public asynMotorController {
public:
VirtualMotorController(const char *portName, const char *VirtualMotorPortName, int numAxes, double movingPollPeriod, double idlePollPeriod);
void report(FILE *fp, int level);
VirtualMotorAxis* getAxis(asynUser *pasynUser);
VirtualMotorAxis* getAxis(int axisNo);
//private:
// char buff_[BUFF_SIZE];
friend class VirtualMotorAxis;
};
实现文件:
#include
#include
#include
#include
#include
#include
#include
#include "asynMotorController.h"
#include "asynMotorAxis.h"
#include
#include "vm.h"
#define NINT(f) (int)((f)>0 ? (f)+0.5 : (f)-0.5)
/************************************************
* 这些是虚拟电机控制器的方法 *
************************************************/
/** Creates a new VirtualMotorController object.创建一个新的虚拟电机控制器对象,
* 参数[in] portName: 为这个驱动创建的asyn端口的名称
* 参数[in] VirtualMotorPortName 先前创建的连接到虚拟电机控制器的drvAsynSerialPort或drvAsynIPPortConfigure的名称.
* 参数[in] numAxes 这个控制器支持的轴数
* 参数[in] movingPollPeriod 当任何轴在移动时,轮询之间的时间
* 参数[in] idlePollPeriod 当没有轴在移动时,轮询之间的时间
*/
VirtualMotorController::VirtualMotorController(const char *portName, const char *VirtualMotorPortName, int numAxes,
double movingPollPeriod,double idlePollPeriod)
: asynMotorController(portName, numAxes, NUM_VIRTUAL_MOTOR_PARAMS,
0, // 除了基类中接口外,没有其它接口
0, // 除了基类中那些回调外,没有其它回调接口
ASYN_CANBLOCK | ASYN_MULTIDEVICE,
1, // 自动连接
0, 0) // 默认优先级和栈尺寸
{
asynStatus status;
int axis;
VirtualMotorAxis *pAxis;
static const char *functionName = "VirtualMotorController::VirtualMotorController";
/* 连接到虚拟电机控制器 */
status = pasynOctetSyncIO->connect(VirtualMotorPortName, 0, &pasynUserController_, NULL);
if (status) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s: cannot connect to virtual motor controller\n",
functionName);
}
/*
* Controller, NOT axis-specific, initialization can go here 控制器,非轴相关的,初始化在这里进行
*/
// 如果创建轴(每个单位步数),需要其它信息,注释掉以下循环并且从cmd文件并且使用户调用VirtualMotorCreateAxis
for (axis=0; axis0,则将输出每个轴的信息。
* 在输出控制器专用信息后,它调用asynMotorController::report()
*/
void VirtualMotorController::report(FILE *fp, int level)
{
fprintf(fp, "Virtual Motor Controller driver %s\n", this->portName); // 这个驱动的端口名
fprintf(fp, " numAxes=%d\n", numAxes_); // 这个驱动支持的轴数
fprintf(fp, " moving poll period=%f\n", movingPollPeriod_); //电机移动时的轮询时间
fprintf(fp, " idle poll period=%f\n", idlePollPeriod_); //电机运动时的轮询时间
/* 打印在VirtualMotorDriver.h中添加到VirtualMotorController类中的私有变量是个好想法,
* 这使得你可以通过从iocsh运行"dbior"看到发生了什么。
*/
// 调用基类方法
asynMotorController::report(fp, level);
}
/** 返回一个指向一个VirtualMotorAxis对象的指针。如果在pasynUser中编码的轴编号无效,返回NULL。
*
* 参数[in] pasynUser 编码这个轴索引号的asynUser结构体 */
VirtualMotorAxis* VirtualMotorController::getAxis(asynUser *pasynUser)
{
return static_cast(asynMotorController::getAxis(pasynUser));
}
/**返回一个指向一个VirtualMotorAxis对象的指针。如果在pasynUser中编码的轴编号无效,返回NULL。
*
* 参数[in] axisNo Axis索引编码. */
VirtualMotorAxis* VirtualMotorController::getAxis(int axisNo)
{
return static_cast(asynMotorController::getAxis(axisNo));
}
/******************************************
* 这些是轴方法 *
******************************************/
/** Creates a new VirtualMotorAxis object. 创建一个新的虚拟电机轴对象。
* 参数[in] pC :指向这个轴所属的VirtualMotorController的指针
* 参数[in] axisNo : 这个轴的索引编号,范围0到pC->numAxes_-1.
*
* 初始化寄存器数值等
*/
// 注意:如果从iocsh调用VirtualMotorCreateAxis,以下构造器需要被修改,来接收stepSize,
// 这是使用EGU而不是steps控制器必需的。
VirtualMotorAxis::VirtualMotorAxis(VirtualMotorController *pC, int axisNo)
: asynMotorAxis(pC, axisNo),
pC_(pC)
{
//asynStatus status;
axisIndex_ = axisNo + 1;
/*
* 轴特定的初始化,在此进行。编码器位置归零(这似乎只在windows上是一个问题)
*/
setDoubleParam(pC_->motorEncoderPosition_, 0.0);
// 允许CNEN开启/关闭电机
//setIntegerParam(pC->motorStatusGainSupport_, 1);
//setIntegerParam(pC->motorStatusHasEncoder_, 1);
// 使得更改的参数生效
callParamCallbacks();
}
/*
* 如果控制器用EGU而不是整数steps报告位置,并且stepsPerUnit的数值可以在轴之间不同,
* 使用者需要配置每个轴。在本文件末尾的以下函数以及相应的注册代码应该被取消注释。
* 在VirtualMotorDriver.h中的声明也需要被取消注释。VirtualMotorAxis构造器将炫耀被修改来接收(double)stepSize参数。
* Newport XPS支持是一个如何为一个真实控制器做这件事的示例。
*/
/*
extern "C" int VirtualMotorCreateAxis(const char *VirtualMotorName, int axisNo, const char *setpsPerUnit)
{
VirtualMotorController *pC;
double stepSize;
static const char *functionName = "VirtualMotorCreateAxis";
pC = (VirtualMotorController*) findAsynPortDriver(VirtualMotorName);
if (!pC)
{
printf("Error port %s not found\n", VirtualMotorName);
return asynError;
}
stepSize = strtod(stepsPerUnit, NULL);
pC->lock();
new VirtualMotorAxis(pC, axisNo, 1./stepSize);
pC->unlock();
return asynSuccess;
}
*/
/** 报告这个轴的状态
* 参数[in] fp 文件指针,报告信息将写入这个文件
* 参数[in] level 所需报告细节的层度
* 在打印设备相信息后,调用asynMotorAxis:report()
*/
void VirtualMotorAxis::report(FILE *fp, int level)
{
if (level > 0) {
fprintf(fp, " Axis #%d\n", axisNo_);
fprintf(fp, " axisIndex_=%d\n", axisIndex_);
}
/* 打印添加到VirtualMotorDriver.h中VirtualMotorAxis中的私有变量是一个好想法,
* 这使得你通过从iocsh运行"dbior"能够看到发生了什么
*/
// 调用基类方法
asynMotorAxis::report(fp, level);
}
/*
* sendAccelAndVelocity()被VirtualMotorAxis方法调用,其导致电机移动:move(), moveVelocity(), home()
* motor记录字段中的参数:
* baseVelocity (steps/s) = VBAS / abs(MRES)
* velocity (step/s) = VELO / abs(MRES)
* acceleration (step/s/s) = (velocity - baseVelocity) / ACCL
* VBAS MRES VELO ACCL都是motor记录中的字段
*/
asynStatus VirtualMotorAxis::sendAccelAndVelocity(double acceleration, double velocity, double baseVelocity)
{
asynStatus status;
// static const char *functionName = "VirtualMotor::sendAccelAndVelocity";
// 发送基速度
sprintf(pC_->outString_, "%d BAS %f", axisIndex_, baseVelocity);
status = pC_->writeReadController();
// 发送速度
sprintf(pC_->outString_, "%d VEL %f", axisIndex_, velocity);
status = pC_->writeReadController();
// 发送加速度
sprintf(pC_->outString_, "%d ACC %f", axisIndex_, acceleration);
status = pC_->writeReadController();
return status;
}
/*
* 当请求绝对或相对移动时,move()被asynMotor设备支持调用。
* 如果BDST > 0 或 RTRY > 0,它可以被调用多次。
*
* motor记录字段中的参数:
* position (steps) = RVAL = DVAL / MRES
* baseVelocity (steps/s) = VBAS / abs(MRES)
* velocity (step/s) = VELO / abs(MRES)
* acceleration (step/s/s) = (velocity - baseVelocity) / ACCL
*/
asynStatus VirtualMotorAxis::move(double position, int relative, double minVelocity, double maxVelocity, double acceleration)
{
asynStatus status;
// static const char *functionName = "VirtualMotorAxis::move";
status = sendAccelAndVelocity(acceleration, maxVelocity, minVelocity);
printf("position: %f; relative:%d; minVelocity=%f,maxVelocity=%f, acceleration=%f\n", position, relative,
minVelocity, maxVelocity, acceleration);
// Set the target position
if (relative) {
sprintf(pC_->outString_, "%d MR %d", axisIndex_, NINT(position));
} else {
sprintf(pC_->outString_, "%d MV %d", axisIndex_, NINT(position));
}
status = pC_->writeReadController();
// 如果控制器有一个"go"命令,在这里发送
return status;
}
/*
* 当请求一个home时,asynMotor设备支持调用home()。
* 注意:forwards是由设备支持设置,不是由motor记录。
*
* motor记录字段中的参数:
* minVelocity (steps/s) = VBAS / abs(MRES)
* maxVelocity (step/s) = HVEL / abs(MRES)
* acceleration (step/s/s) = (maxVelocity - minVelocity) / ACCL
* forwards = 1 if HOMF was pressed, 0 if HOMR was pressed
* 如果HOMF被按下,forwords=1,如果HOMR被按下,forwards=0
*/
/*
asynStatus VirtualMotorAxis::home(double minVelocity, double maxVelocity, double acceleration, int forwards)
{
// static const char *functionName = "VirtualMotorAxis::home";
// 当前没有实现Homing
return asynSuccess;
}
*/
/*
* 当请求jog时,asynMotor调用moveVelocity
* 如果控制器没有一个jog命令,这个jog在此处被模拟。
* Arguments in terms of motor record fields:
* minVelocity (steps/s) = VBAS / abs(MRES)
* maxVelocity (step/s) = (jog_direction == forward) ? JVEL * DIR / MRES : -1 * JVEL * DIR / MRES
* acceleration (step/s/s) = JAR / abs(EGU)
*/
asynStatus VirtualMotorAxis::moveVelocity(double minVelocity, double maxVelocity, double acceleration)
{
asynStatus status;
//static const char *functionName = "VirtualMotorAxis::moveVelocity";
// 调用这个设置最大当前和加速度。
status = sendAccelAndVelocity(acceleration, maxVelocity, minVelocity);
sprintf(pC_->outString_, "%d JOG %f", axisIndex_, maxVelocity);
status = pC_->writeReadController();
return status;
}
/*
* 当用户按下stop按钮时,stop()被asynMotor设备支持调用。当jog按钮被释放时,它也被调用。
*
*/
asynStatus VirtualMotorAxis::stop(double acceleration)
{
asynStatus status;
//static const char *functionName = "VirtualMotorAxis::stop";
sprintf(pC_->outString_, "%d AB", axisIndex_);
status = pC_->writeReadController();
return status;
}
/*
* 当一个位置被重新定义时,asynMotor设备支持调用setPosition().
* It is also required for autosave to restore a position to the controller at iocInit.
* position (steps) = DVAL / MRES = RVAL
*/
asynStatus VirtualMotorAxis::setPosition(double position)
{
asynStatus status;
//static const char *functionName = "VirtualMotorAxis::setPosition";
sprintf(pC_->outString_, "%d POS %d", axisIndex_, NINT(position));
status = pC_->writeReadController();
return status;
}
/*
* 当一个用户启用或者禁用torque,asynMotor设备支持调用setClosedLoop(),通常从motorx_all.adl,
* 但只用于设置以下参数为1的驱动。
* pC->motorStatusGainSupport_
* pC->motorStatusHasEncoder_
* 在此实现实际实现什么根据控制器特点而不同。
*
* 记录字段中参数:
* closedLoop = CNEN
*/
/*
asynStatus VirtualMotorAxis::setClosedLoop(bool closedLoop)
{
asynStatus status;
//static const char *functionName = "VirtualMotorAxis::setClosedLoop";
if (closedLoop)
{
// 构建"Enable"命令
sprintf(pC_->outString_, "%d EN", axisIndex_);
}
else
{
// 构建"Disable" 命令
sprintf(pC_->outString_, "%d DI", axisIndex_);
}
// 发送命令
status = pC_->writeController();
return status;
}
*/
/** 查询这个轴。
* 这个函数读取电机位置,limit状态,home状态,移动状态以及驱动上电状态。
* 为它轮询的每项调用setIntegerParam() 和 setDoubleParam(),并且接着在末尾调用callParamCallbacks()。
*
* 参数[out] moving : 一个标记,设置它来表明这个轴正在移动(true)或结束(false)。
*/
asynStatus VirtualMotorAxis::poll(bool *moving)
{
int position;
int status;
int done;
int direction;
int limit;
asynStatus comStatus;
// 读取当前电机位置
sprintf(pC_->outString_, "%d POS?", axisIndex_);
comStatus = pC_->writeReadController();
if (comStatus)
goto skip;
// 响应字符串格式为"0.00000"
position = atof((const char *) &pC_->inString_);
setDoubleParam(pC_->motorPosition_, position);
// 读取这个电机的移动状态
sprintf(pC_->outString_, "%d ST?", axisIndex_);
comStatus = pC_->writeReadController();
if (comStatus)
goto skip;
// 响应字符串格式是 "1"
status = atoi((const char *) &pC_->inString_);
/*
状态位
方向: 0x1
结束移动: 0x2
移动中: 0x4
高限制: 0x8
低限位: 0x10
寻home: 0x20
home限位: 0x40
已经找到home: 0x80
错误: 0x100
*/
// 读取方向
direction = (status & 0x1) ? 1 : 0;
setIntegerParam(pC_->motorStatusDirection_, direction);
// 读取移动状态
done = (status & 0x2) ? 1 : 0;
setIntegerParam(pC_->motorStatusDone_, done);
setIntegerParam(pC_->motorStatusMoving_, !done);
*moving = done ? false:true;
// 读取限位状态
limit = (status & 0x8) ? 1 : 0;
setIntegerParam(pC_->motorStatusHighLimit_, limit);
limit = (status & 0x10) ? 1 : 0;
setIntegerParam(pC_->motorStatusLowLimit_, limit);
// 读取home状态
// TODO: 实现homing
// Read the drive power on status 对状态读取驱动电源
//driveOn = (status & 0x100) ? 0 : 1;
//setIntegerParam(pC_->motorStatusPowerOn_, driveOn);
skip:
setIntegerParam(pC_->motorStatusProblem_, comStatus ? 1:0);
callParamCallbacks();
return comStatus ? asynError : asynSuccess;
}
/** 注册ioc */
static const iocshArg VirtualMotorCreateControllerArg0 = {"Port name", iocshArgString};
static const iocshArg VirtualMotorCreateControllerArg1 = {"VMC port name", iocshArgString};
static const iocshArg VirtualMotorCreateControllerArg2 = {"Number of axes", iocshArgInt};
static const iocshArg VirtualMotorCreateControllerArg3 = {"Moving poll period (ms)", iocshArgInt};
static const iocshArg VirtualMotorCreateControllerArg4 = {"Idle poll period (ms)", iocshArgInt};
static const iocshArg * const VirtualMotorCreateControllerArgs[] = {&VirtualMotorCreateControllerArg0,
&VirtualMotorCreateControllerArg1,
&VirtualMotorCreateControllerArg2,
&VirtualMotorCreateControllerArg3,
&VirtualMotorCreateControllerArg4};
static const iocshFuncDef VirtualMotorCreateControllerDef = {"VirtualMotorCreateController", 5, VirtualMotorCreateControllerArgs};
static void VirtualMotorCreateContollerCallFunc(const iocshArgBuf *args)
{
VirtualMotorCreateController(args[0].sval, args[1].sval, args[2].ival, args[3].ival, args[4].ival);
}
/* VirtualMotorCreateAxis */
/*
static const iocshArg VirtualMotorCreateAxisArg0 = {"Controller port name", iocshArgString};
static const iocshArg VirtualMotorCreateAxisArg1 = {"Axis number", iocshArgInt};
static const iocshArg VirtualMotorCreateAxisArg2 = {"stepsPerUnit", iocshArgString};
static const iocshArg * const VirtualMotorCreateAxisArgs[] = {&VirtualMotorCreateAxisArg0,
&VirtualMotorCreateAxisArg1,
&VirtualMotorCreateAxisArg2};
static const iocshFuncDef VirtualMotorCreateAxisDef = {"VirtualMotorCreateAxis", 3, VirtualMotorCreateAxisArgs};
static void VirtualMotorCreateAxisCallFunc(const iocshArgBuf *args)
{
VirtualMotorCreateAxis(args[0].sval, args[1].ival, args[2].sval);
}
*/
static void VirtualMotorRegister(void)
{
iocshRegister(&VirtualMotorCreateControllerDef, VirtualMotorCreateContollerCallFunc);
//iocshRegister(&VirtualMotorCreateAxisDef, VirtualMotorCreateAxisCallFunc);
}
extern "C" {
epicsExportRegistrar(VirtualMotorRegister);
}
注册文件vm.db:
registrar(VirtualMotorRegister)
Makefile文件:
...
# motorvm.dbd will be made up from these files:
motorvm_DBD += base.dbd
motorvm_DBD += asyn.dbd
motorvm_DBD += drvAsynIPPort.dbd
motorvm_DBD += motorSupport.dbd
motorvm_DBD += asSupport.dbd
motorvm_DBD += vm.dbd
# Include dbd files from all support applications:
#motorvm_DBD += xxx.dbd
# Add all the support libraries needed by this IOC
motorvm_LIBS += asyn
motorvm_LIBS += motor
motorvm_LIBS += autosave
# motorvm_registerRecordDeviceDriver.cpp derives from motorvm.dbd
motorvm_SRCS += vm.cpp
...
motor记录的模板文件vmc.substitutions:
file "$(MOTOR)/db/asyn_motor.db"
{
pattern
{N, M, DTYP, PORT, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, DHLM, DLLM, INIT}
{1, "m$(N)", "asynMotor", VMC1, 0, "motor $(N)", mm, Pos, 1, .1, .2, 0, 1, .2, 0.0025, 4, 100, -100, ""}
{2, "m$(N)", "asynMotor", VMC1, 1, "motor $(N)", mm, Pos, 1, .1, .2, 0, 1, .2, 0.0025, 4, 100, -100, ""}
{3, "m$(N)", "asynMotor", VMC1, 2, "motor $(N)", mm, Pos, 1, .1, .2, 0, 1, .2, 0.0025, 4, 100, -100, ""}
{4, "m$(N)", "asynMotor", VMC1, 3, "motor $(N)", deg, Pos, 1, .1, .2, 0, 1, .2, 0.01, 4, 100, -100, ""}
{5, "m$(N)", "asynMotor", VMC1, 4, "motor $(N)", deg, Pos, 1, .1, .2, 0, 1, .2, 0.01, 4, 100, -100, ""}
{6, "m$(N)", "asynMotor", VMC1, 5, "motor $(N)", deg, Pos, 1, .1, .2, 0, 1, .2, 0.01, 4, 100, -100, ""}
{7, "m$(N)", "asynMotor", VMC1, 6, "motor $(N)", deg, Pos, 1, .1, .2, 0, 1, .2, 0.01, 4, 100, -100, ""}
{8, "m$(N)", "asynMotor", VMC1, 7, "motor $(N)", deg, Pos, 1, .1, .2, 0, 1, .2, 0.01, 4, 100, -100, ""}
}
启动脚本:
#!../../bin/linux-aarch64/motorvm
#- You may have to change motorvm to something else
#- everywhere it appears in this file
< envPaths
cd "${TOP}"
## Register all support components
dbLoadDatabase "dbd/motorvm.dbd"
motorvm_registerRecordDeviceDriver pdbbase
# 定义IOC前缀:
epicsEnvSet("PREFIX", "MotorVM:")
drvAsynIPPortConfigure("VMC_ETH","192.168.50.234:6666", 0, 0, 0)
# 显示通信
#!asynSetTraceMask("VMC_ETH", 0, 3)
# 只显示错误
asynSetTraceMask("VMC_ETH", 0, 1)
# 保留选择ascii,使得用单次点击开启跟踪
asynSetTraceIOMask("VMC_ETH", 0, 1)
# 设置字符串终止符
asynOctetSetInputEos("VMC_ETH",0,"\r\n")
asynOctetSetOutputEos("VMC_ETH",0,"\r")
# 1秒种空闲轮询
VirtualMotorCreateController("VMC1", "VMC_ETH", 8, 250, 1000)
cd "${TOP}/iocBoot/${IOC}"
## 装载记录实例
dbLoadTemplate("vmc.substitutions", "P=$(PREFIX)")
iocInit
编译以上程序,并且运行启动脚本:
root@orangepi5:/usr/local/EPICS/program/motorvm/iocBoot/iocmotorvm# ../../bin/linux-aarch64/motorvm st.cmd
#!../../bin/linux-aarch64/motorvm
< envPaths
epicsEnvSet("IOC","iocmotorvm")
epicsEnvSet("TOP","/usr/local/EPICS/program/motorvm")
epicsEnvSet("SUPPORT","/usr/local/EPICS/synApps/support")
epicsEnvSet("ASYN","/usr/local/EPICS/synApps/support/asyn")
epicsEnvSet("MOTOR","/usr/local/EPICS/synApps/support/motor")
epicsEnvSet("AUTOSAVE","/usr/local/EPICS/synApps/support/autosave")
epicsEnvSet("EPICS_BASE","/usr/local/EPICS/base")
cd "/usr/local/EPICS/program/motorvm"
## Register all support components
dbLoadDatabase "dbd/motorvm.dbd"
motorvm_registerRecordDeviceDriver pdbbase
# 定义IOC前缀:
epicsEnvSet("PREFIX", "MotorVM:")
drvAsynIPPortConfigure("VMC_ETH","192.168.50.234:6666", 0, 0, 0)
# Show communication
#!asynSetTraceMask("VMC_ETH", 0, 3)
# Only show errors
asynSetTraceMask("VMC_ETH", 0, 1)
# Leave ascii selected so traces can be turned on with a single click
asynSetTraceIOMask("VMC_ETH", 0, 1)
# Set end-of-string terminators
asynOctetSetInputEos("VMC_ETH",0,"\r\n")
asynOctetSetOutputEos("VMC_ETH",0,"\r")
# 1-second idle polling
VirtualMotorCreateController("VMC1", "VMC_ETH", 8, 250, 1000)
cd "/usr/local/EPICS/program/motorvm/iocBoot/iocmotorvm"
## Load record instances
dbLoadTemplate("vmc.substitutions", "P=MotorVM:")
iocInit
Starting iocInit
############################################################################
## EPICS R7.0.7
## Rev. 2023-05-26T09:07+0000
## Rev. Date build date/time:
############################################################################
iocRun: All initialization complete
## Start any sequence programs
#seq sncxxx,"user=orangepi"
epics>
使用dbl查看加载的记录实例,可见加载了八个电机轴:
epics> dbl
...
MotorVM:m1
MotorVM:m2
MotorVM:m3
MotorVM:m4
MotorVM:m5
MotorVM:m6
MotorVM:m7
MotorVM:m8
使用通道访问命令设置轴的位置,并且实时监控其所在位置:
[blctrl@main-machine adls]$ caput MotorVM:m1 15;camonitor MotorVM:m1.RBV
Old : MotorVM:m1 10
New : MotorVM:m1 15
MotorVM:m1.RBV 2023-09-15 03:55:16.445524 10
MotorVM:m1.RBV 2023-09-15 03:55:16.708532 10.1725
MotorVM:m1.RBV 2023-09-15 03:55:16.977737 10.4425
MotorVM:m1.RBV 2023-09-15 03:55:17.240218 10.705
MotorVM:m1.RBV 2023-09-15 03:55:17.503565 10.97
MotorVM:m1.RBV 2023-09-15 03:55:17.769194 11.2325
MotorVM:m1.RBV 2023-09-15 03:55:18.078248 11.5425
MotorVM:m1.RBV 2023-09-15 03:55:18.339768 11.805
MotorVM:m1.RBV 2023-09-15 03:55:18.600618 12.065
MotorVM:m1.RBV 2023-09-15 03:55:18.862169 12.3275
MotorVM:m1.RBV 2023-09-15 03:55:19.123034 12.5875
MotorVM:m1.RBV 2023-09-15 03:55:19.383936 12.85
MotorVM:m1.RBV 2023-09-15 03:55:19.644173 13.11
MotorVM:m1.RBV 2023-09-15 03:55:19.904203 13.37
MotorVM:m1.RBV 2023-09-15 03:55:20.168548 13.63
MotorVM:m1.RBV 2023-09-15 03:55:20.455850 13.9175
MotorVM:m1.RBV 2023-09-15 03:55:20.763345 14.2275
MotorVM:m1.RBV 2023-09-15 03:55:21.024398 14.49
MotorVM:m1.RBV 2023-09-15 03:55:21.285069 14.56
MotorVM:m1.RBV 2023-09-15 03:55:21.546239 14.685
MotorVM:m1.RBV 2023-09-15 03:55:21.807113 14.8775
MotorVM:m1.RBV 2023-09-15 03:55:22.067684 15
使用medm连接其中的一个电机轴,进行图形显示:
[blctrl@main-machine adls]$ medm -x -macro "P=MotorVM:,M=m1" motorx_all.adl &
通过更改MoveAbs可以改变虚拟轴的位置: