EPICS motor驱动程序实例

news2025/2/25 21:09:27

本驱动程序是控制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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

#include <iocsh.h>
#include <epicsThread.h>

#include <asynOctetSyncIO.h>

#include "asynMotorController.h"
#include "asynMotorAxis.h"

#include <epicsExport.h>
#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; axis<numAxes; axis++) {
    pAxis = new VirtualMotorAxis(this, axis);
  }

  startPoller(movingPollPeriod, idlePollPeriod, 2);
}


/** 创建一个新的虚拟电机控制器对象。
  *  配置的命令,直接被调用或者从iocsh
  * 参数[in] portName                 将为这个驱动创建的asyn端口的名称
  * 参数[in] VirtualMotorPortName     先前被创建来连接到虚拟电机控制器的drvAsynSerialPort或drvAsynIPPortConfigure的名称
  * 参数[in] numAxes                  这个控制器支持的轴数
  * 参数[in] movingPollPeriod         当任何轴在移动时,轮询之间的时间(ms)
  * 参数[in] idlePollPeriod           当没有轴在移动时,轮询之间的时间(ms)
  */
extern "C" int VirtualMotorCreateController(const char *portName, const char *VirtualMotorPortName, int numAxes,
                                   int movingPollPeriod, int idlePollPeriod)
{
  VirtualMotorController *pVirtualMotorController
    = new VirtualMotorController(portName, VirtualMotorPortName, numAxes, movingPollPeriod/1000., idlePollPeriod/1000.);
  pVirtualMotorController = NULL;
  return(asynSuccess);
}


/** 报告驱动的状态。
  * 参数[in] fp       文件指针, 报告将写入这个文件
  * 参数[in] level    所需报告细节的等级
  * 如果detail>0,则将输出每个轴的信息。
  * 在输出控制器专用信息后,它调用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<VirtualMotorAxis*>(asynMotorController::getAxis(pasynUser));
}


/**返回一个指向一个VirtualMotorAxis对象的指针。如果在pasynUser中编码的轴编号无效,返回NULL。
  *
  * 参数[in] axisNo Axis索引编码. */
VirtualMotorAxis* VirtualMotorController::getAxis(int axisNo)
{
  return static_cast<VirtualMotorAxis*>(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可以改变虚拟轴的位置: 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1027533.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python实现2048小游戏

简介 《2048》 [1] 是一款比较流行的数字游戏&#xff0c;最早于2014年3月20日发行。原版2048首先在GitHub上发布&#xff0c;原作者是Gabriele Cirulli&#xff0c;后被移植到各个平台。这款游戏是基于《1024》和《小3传奇》的玩法开发而成的新型数字游戏。以下是Python代码…

WPF handyControl 学习样例

​​​​​​​【1.8 HandyControl&#xff1a;80余种控件使用案例】WPF案例代码解析 - 知乎 给大家推荐一个学习handyControl的一个案例&#xff0c;虽然只是一个网站&#xff0c;但里面可以下载源码&#xff0c;&#xff0c; 注意的是&#xff1a;一定要把demo和GalaSoft.M…

G. Best ACMer Solves the Hardest Problem

Problem - G - Codeforces 有一天&#xff0c;一位优秀的ACMer将离开这个领域&#xff0c;面对新的挑战&#xff0c;就像前辈们所做的一样。他们中的一些人接管了家族企业&#xff0c;一些人在失业的边缘挣扎。一些人有勇气展示自己&#xff0c;成为专业的Ingress玩家&#xff…

YMatrix 5 社区版本安装介绍

本文描述YMatrix 5.1版本的安装过程&#xff0c;由于使用的操作系统为CentOS 7&#xff0c;具体步骤参考官网https://www.ymatrix.cn/doc/5.0/install/mx5_cluster/mx5_cluster_centos7 一. 安装准备 1 下载YMatrix社区版安装包 下载地址&#xff1a;https://www.ymatrix.cn…

视频播放器的技术组成

Qt视频播放器实现(目录) 什么是视频 我们这里讲的视频,通常也包括了音频。因为没有声音的画面播放几乎是不可接受的。 这样暗含了一个事实,那就是视频总是包括视频数据和音频数据两部分。 Video 表示视频; Audio 表示音频; 视频播放器播放什么 如同本专栏介绍描述…

python+nodejs+php+springboot+vue 基于数据元标准的教材征订管理系统

教材征订管理系统主要实现角色有管理员和用户,管理员在后台管理学院模块、学生模块、用户表模块、token表模块、教材选定模块、教材入库模块、教材分配模块、教材订购模块、教材模块、配置文件模块、出版社模块。 拟开发的教材征订管理系统通过测试,确保在最大负载的情况下稳定…

比特币 ZK 赏金系列:第 1 部分——支付解密密钥

以前&#xff0c;我们使用零知识赏金 (ZKB) 来支付比特币上的数独解决方案。在本系列中&#xff0c;我们将使用 ZKB 来解决范围更广的更实际的问题。 在第 1 部分中&#xff0c;我们应用 ZKB 来支付解密密钥。假设 Alice 使用对称密钥 K 加密她的文件。为了安全起见&#xff0…

行为树的基本概念和C++库

一 说明 行为树是计算机科学、机器人技术、控制系统和视频游戏中使用的计划执行的数学模型。它们以模块化方式描述一组有限任务之间的切换。他们的优势来自于他们能够创建由简单任务组成的非常复杂的任务&#xff0c;而不用担心简单任务是如何实现的。行为树与分层状​​态机有…

nuxt使用echarts

直接在页面写 bug1&#xff1a;安装包报错&#xff0c;就更换版本 bug2&#xff1a;图表出不来&#xff1a;需要定义宽高 bug3&#xff1a;需要resize大小 安装 npm install echarts4.9.0 plugins文件夹下新建echarts.js import Vue from vue import echarts from echarts /…

Windows 下 MySQL 8.1 图形化界面安装、配置详解

首先我们下载安装包 官方下载链接&#xff1a; MySQL :: Begin Your Download 网盘链接: https://pan.baidu.com/s/1FOew6-93XpknB-bYDhDYPw 提取码: brys 外网下载慢的同学可以使用上述网盘链接 下载完成后我们双击安装包即可进入安装界面 点击next 勾选同意协议&#…

【AIGC】图片生成的原理与应用

前言 近两年 AI 发展非常迅速&#xff0c;其中的 AI 绘画也越来越火爆&#xff0c;AI 绘画在很多应用领域有巨大的潜力&#xff0c;AI 甚至能模仿各种著名艺术家的风格进行绘画。 目前比较有名商业化的 AI 绘画软件有 Midjourney、DALLE2、以及百度出品的文心一格&#xff1a;…

解锁前端Vue3宝藏级资料 第五章 Vue 组件应用 4 ( provide 和 inject )

5.5 provide 和 inject 前面的知识告诉我们vue中组件之间传递值需要使用props来完成&#xff0c;但是props也有一定局限性。这个时候在vue3中还有另外的解决方法。那就是使用 provide 和 inject 允许父组件将数据传递给所有后代组件&#xff0c;而不管组件层次结构有多深。你要…

【linux基础(八)】计算机体系结构--冯诺依曼系统操作系统的再理解

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; 计算机体系结构 1. 前言2. 冯…

eBPF深度探索: 高效DNS监控实现

eBPF可以灵活扩展Linux内核机制&#xff0c;本文通过实现一个DNS监控工具为例&#xff0c;介绍了怎样开发实际的eBPF应用。原文: A Deep Dive into eBPF: Writing an Efficient DNS Monitoring eBPF[1]是内核内置的虚拟机&#xff0c;在Linux内核内部提供了高层库、指令集以及执…

Javascript小案例--树形菜单(菜单数据为对象)

理论上菜单层级可以无限多&#xff0c;因为是递归渲染。 gif效果图&#xff1a; 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initi…

RHCE---Linux的计划任务

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 如果想要让自己设计的备份程序可以自动地在系统下面运行&#xff0c;而不需要手动来启动它&#xff0c;这是该如何处理&#xff1f;这些任务可以分为【单一】任务和【循环】任务&#xff0…

集简云票税通,高效、管理销项发票,满足多样化开票需求

随着数字化时代的到来&#xff0c;传统的纸质发票已经逐渐被电子发票所替代。然而&#xff0c;对于许多企业来说&#xff0c;管理和开具大量的销项发票仍然是一项繁琐的任务&#xff1a; 票税处理成本高&#xff0c;手工开票效率低。部分企业手工开票量大&#xff0c;耗费大量财…

问题:conda删除虚拟环境,报错no package names supplied

用conda 用 conda remove -n ScratchDet_20200114 删除虚拟 环境ScratchDet_20200114时报错 conda remove -n ScratchDet_20200114CondaValueError: no package names supplied,try "conda remove -h" for more details 解决方法&#xff0c;用下面的命令 conda env…

C++——vector(3)

作者&#xff1a;几冬雪来 时间&#xff1a;2023年9月20日 内容&#xff1a;C部分——vector内容讲解 目录 前言&#xff1a; erase&#xff1a; erase和insert总结&#xff1a; resize&#xff1a; 深拷贝&#xff1a; 赋值&#xff1a; 结尾&#xff1a; 前言&…

CentOS7在磁盘 Disk /dev/vdb 格式化为xfs, 创建逻辑卷saas,并挂载到/home/saas

CentOS7在磁盘 Disk /dev/vdb 格式化为xfs&#xff0c; 创建逻辑卷saas&#xff0c;并挂载到/home/saas CentOS7在磁盘 Disk /dev/vdb 格式化为xfs&#xff0c; 创建逻辑卷saas&#xff0c;并挂载到/home/saasCentOS7在磁盘 Disk /dev/vdb 格式化为xfs&#xff0c;并挂载到/ho…