A first sort of working version of a asyn driver for the Nanotoec SMCI

motors. Homing still needs more testing
This commit is contained in:
2015-07-30 14:12:45 +02:00
parent fd4b41d131
commit cdf309114a
10 changed files with 631 additions and 2 deletions

View File

@ -23,7 +23,8 @@ CHECK_RELEASE = YES
# To install files into a location other than $(TOP) define # To install files into a location other than $(TOP) define
# INSTALL_LOCATION here. # INSTALL_LOCATION here.
INSTALL_LOCATION=/usr/local/epics/sinqApp #INSTALL_LOCATION=/usr/local/epics/sinqApp
INSTALL_LOCATION=/afs/psi.ch/project/sinqdev/sinqepicsapp
# Set this when your IOC and the host use different paths # Set this when your IOC and the host use different paths
# to access the application. This will be needed to boot # to access the application. This will be needed to boot

View File

@ -0,0 +1,6 @@
epicsEnvSet("ARCH","linux-x86-debug")
epicsEnvSet("IOC","iocsinqEPICS")
epicsEnvSet("TOP","/afs/psi.ch/project/sinqdev/sinqepicsapp")
epicsEnvSet("EPICS_BASE","/usr/local/epics")
epicsEnvSet("ASYN","/usr/local/epics/support/asyn-4-18")
epicsEnvSet("MOTOR","/usr/local/epics/support/motor-6-7")

View File

@ -0,0 +1,8 @@
file "$(MOTOR)/db/basic_asyn_motor.db"
{
pattern
{P, N, M, DTYP, PORT, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, DHLM, DLLM, INIT}
{KM36:nano:, 1, "m$(N)", "asynMotor", nano, 1, "m1", mm, Pos, 2.0, 0.1, .2, 0, 1, .2, .0001, 3, 30, -30, "1"}
{KM36:nano:, 2, "m$(N)", "asynMotor", nano, 2, "m2", mm, Pos, 2.0, 0.1, .2, 0, 1, .2, .0001, 3, 60, -60, "10"}
{KM36:nano:, 3, "m$(N)", "asynMotor", nano, 3, "m3", mm, Pos, 2.0, 0.1, .2, 0, 1, .2, .0001, 3, 100, -100, "9"}
}

View File

@ -0,0 +1,37 @@
#!../../bin/linux-x86-debug/sinqEPICS
## You may have to change sinqEPICS to something else
## everywhere it appears in this file
< envPaths
cd ${TOP}
## Register all support components
dbLoadDatabase "dbd/sinqEPICS.dbd"
#dbLoadDatabase "dbd/sinq.dbd"
sinqEPICS_registerRecordDeviceDriver pdbbase
## Load record instances
#dbLoadRecords("db/xxx.db","user=koenneckeHost")
#---------- load Nanotec motor controller
#drvAsynIPPortConfigure("serial1", "narziss-ts:3002",0,0,0)
drvAsynIPPortConfigure("serial1", "localhost:2020",0,0,0)
NanotecCreateController("nano","serial1",3,"1,10,9");
### Motors
dbLoadRecords("$(ASYN)/db/asynRecord.db","P=KM36:,R=serial1,PORT=serial1,ADDR=0,OMAX=80,IMAX=80")
cd ${TOP}/iocBoot/${IOC}
dbLoadTemplate "motor.substitutions.nanotec"
iocInit
## Start any sequence programs
#seq sncxxx,"user=koenneckeHost"

View File

@ -26,6 +26,7 @@ sinqEPICS_LIBS += motor asyn std anc350 anc350AsynMotor
sinqEPICS_SRCS += sinqEPICS_registerRecordDeviceDriver.cpp sinqEPICS_SRCS += sinqEPICS_registerRecordDeviceDriver.cpp
sinqEPICS_SRCS += EL734Driver.cpp devScalerEL737.c pmacAsynIPPort.c sinqEPICS_SRCS += EL734Driver.cpp devScalerEL737.c pmacAsynIPPort.c
sinqEPICS_SRCS += pmacController.cpp pmacAxis.cpp sinqEPICS_SRCS += pmacController.cpp pmacAxis.cpp
sinqEPICS_SRCS += NanotecDriver.cpp stptok.cpp
# Build the main IOC entry point on workstation OSs. # Build the main IOC entry point on workstation OSs.

View File

@ -0,0 +1,451 @@
/*
FILENAME... NanotecDriver.cpp
USAGE... Motor driver support for the Nanotec SMCI controllers
connected to a RS-485 bus.
Mark Koennecke
July 2015
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <time.h>
#include <iocsh.h>
#include <epicsThread.h>
#include <errlog.h>
#include <asynOctetSyncIO.h>
#include "stptok.h"
#include "NanotecDriver.h"
#include <epicsExport.h>
#define IDLEPOLL 60
/** Creates a new NanotecController object.
* \param[in] portName The name of the asyn port that will be created for this driver
* \param[in] NanotecPortName The name of the drvAsynSerialPort that was created previously to connect to the Nanotec controller
*/
NanotecController::NanotecController(const char *portName, const char *NanotecPortName, int motCount, const char *bus)
: asynMotorController(portName, motCount+1, 0,
0, // No additional interfaces beyond those in base class
0, // No additional callback interfaces beyond those in base class
ASYN_CANBLOCK | ASYN_MULTIDEVICE,
1, // autoconnect
0, 0) // Default priority and stack size
{
int axis, busAddress;
asynStatus status;
NanotecAxis *pAxis;
static const char *functionName = "NanotecController::NanotecController";
char *pPtr, busNoString[20];
/* Connect to Nanotec controller */
status = pasynOctetSyncIO->connect(NanotecPortName, 0, &pasynUserController_, NULL);
if (status) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s: cannot connect to Nanotec controller\n",
functionName);
}
pasynOctetSyncIO->setOutputEos(pasynUserController_,"\r",strlen("\r"));
pasynOctetSyncIO->setInputEos(pasynUserController_,"\r",strlen("\r"));
axis = 1;
pPtr = (char *)bus;
while((pPtr = stptok(pPtr,busNoString,sizeof(busNoString),",")) != NULL){
busAddress = atoi(busNoString);
pAxis = new NanotecAxis(this, axis,busAddress);
errlogPrintf("Axis %d, busAddress = %d\n", axis, busAddress);
axis++;
}
startPoller(1000./1000., IDLEPOLL, 2);
}
/** Creates a new NanotecController object.
* Configuration command, called directly or from iocsh
* \param[in] portName The name of the asyn port that will be created for this driver
* \param[in] NanotecPortName The name of the drvAsynIPPPort that was created previously to connect to the Nanotec controller
*/
extern "C" int NanotecCreateController(const char *portName, const char *NanotecPortName, int numMot, const char *busAddresses)
{
NanotecController *pNanotecController
= new NanotecController(portName, NanotecPortName,numMot,busAddresses);
pNanotecController = NULL;
return(asynSuccess);
}
/** Reports on status of the driver
* \param[in] fp The file pointer on which report information will be written
* \param[in] level The level of report detail desired
*
* If details > 0 then information is printed about each axis.
* After printing controller-specific information it calls asynMotorController::report()
*/
void NanotecController::report(FILE *fp, int level)
{
fprintf(fp, "Nanotec motor driver %s, numAxes=%d\n",
this->portName, numAxes_);
// Call the base class method
asynMotorController::report(fp, level);
}
/** Returns a pointer to an NanotecAxis object.
* Returns NULL if the axis number encoded in pasynUser is invalid.
* \param[in] pasynUser asynUser structure that encodes the axis index number. */
NanotecAxis* NanotecController::getAxis(asynUser *pasynUser)
{
return static_cast<NanotecAxis*>(asynMotorController::getAxis(pasynUser));
}
/** Returns a pointer to an NanotecAxis object.
* Returns NULL if the axis number encoded in pasynUser is invalid.
* \param[in] axisNo Axis index number. */
NanotecAxis* NanotecController::getAxis(int axisNo)
{
return static_cast<NanotecAxis*>(asynMotorController::getAxis(axisNo));
}
// These are the NanotecAxis methods
/** Creates a new NanotecAxis object.
* \param[in] pC Pointer to the NanotecController to which this axis belongs.
* \param[in] axisNo Index number of this axis, range 0 to pC->numAxes_-1.
*
* Initializes register numbers, etc.
*/
NanotecAxis::NanotecAxis(NanotecController *pC, int axisNo, int busAddress)
: asynMotorAxis(pC, axisNo),
pC_(pC)
{
this->busAddress = busAddress;
}
/** Reports on status of the axis
* \param[in] fp The file pointer on which report information will be written
* \param[in] level The level of report detail desired
*
* After printing device-specific information calls asynMotorAxis::report()
*/
void NanotecAxis::report(FILE *fp, int level)
{
if (level > 0) {
fprintf(fp, " axis %d\n",
axisNo_);
}
// Call the base class method
//asynMotorAxis::report(fp, level);
}
asynStatus NanotecController::transactController(char command[COMLEN], char reply[COMLEN])
{
asynStatus status;
size_t in, out;
int reason;
pasynOctetSyncIO->flush(pasynUserController_);
status = pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
reply,COMLEN, 1.,&out,&in,&reason);
if(status != asynSuccess){
return status;
}
/*
check for Nanotec errors
*/
if(strstr(reply,"?") != NULL){
errlogSevPrintf(errlogMajor, "Bad command %s", command);
return asynError;
}
if(strlen(reply) < 1) {
errlogSevPrintf(errlogMajor, "No reply received for %s", command);
return asynError;
}
return status;
}
asynStatus NanotecAxis::move(double position, int relative, double minVelocity, double maxVelocity, double acceleration)
{
asynStatus status;
//static const char *functionName = "NanotecAxis::move";
char command[COMLEN], reply[COMLEN];
size_t in, out;
int reason;
// status = sendAccelAndVelocity(acceleration, maxVelocity);
if (relative) {
position += this->position;
}
homing = 0;
/*
set mode
*/
snprintf(command,sizeof(command),"#%dp2",busAddress);
status = pC_->transactController(command,reply);
if(status != asynSuccess){
return status;
}
/*
set target
*/
snprintf(command,sizeof(command),"#%ds%d",busAddress, (int)position);
status = pC_->transactController(command,reply);
if(status != asynSuccess){
return status;
}
/*
and start..
*/
snprintf(command,sizeof(command),"#%dA",busAddress);
status = pC_->transactController(command,reply);
if(status != asynSuccess){
return status;
}
next_poll = -1;
return status;
}
asynStatus NanotecAxis::home(double minVelocity, double maxVelocity, double acceleration, int forwards)
{
asynStatus status;
//static const char *functionName = "NanotecAxis::home";
char command[COMLEN], reply[COMLEN];
/*
set mode
*/
snprintf(command,sizeof(command),"#%dp4",busAddress);
status = pC_->transactController(command,reply);
if(status != asynSuccess){
return status;
}
/*
reset positioning errors
*/
snprintf(command,sizeof(command),"#%dD",busAddress);
status = pC_->transactController(command,reply);
if(status != asynSuccess){
return status;
}
/*
set direction
*/
snprintf(command,sizeof(command),"#%dd0",busAddress);
status = pC_->transactController(command,reply);
if(status != asynSuccess){
return status;
}
/*
and start..
*/
snprintf(command,sizeof(command),"#%dA",busAddress);
status = pC_->transactController(command,reply);
if(status != asynSuccess){
return status;
}
homing = 1;
next_poll= -1;
return status;
}
asynStatus NanotecAxis::moveVelocity(double minVelocity, double maxVelocity, double acceleration)
{
asynStatus status;
//static const char *functionName = "NanotecAxis::moveVelocity";
double target;
// asynPrint(pasynUser_, ASYN_TRACE_FLOW,
// "%s: minVelocity=%f, maxVelocity=%f, acceleration=%f\n",
// functionName, minVelocity, maxVelocity, acceleration);
if (maxVelocity > 0.) {
/* This is a positive move */
pC_->getDoubleParam(axisNo_,pC_->motorHighLimit_,&target);
} else {
/* This is a negative move */
pC_->getDoubleParam(axisNo_,pC_->motorLowLimit_,&target);
}
status = move(target,0,0,0,0);
return status;
}
asynStatus NanotecAxis::stop(double acceleration )
{
asynStatus status;
//static const char *functionName = "NanotecAxis::stop";
char command[COMLEN], reply[COMLEN];
sprintf(command, "#%dS1", busAddress);
status = pC_->transactController(command,reply);
errlogPrintf("Sent STOP on Axis %d\n", axisNo_);
return status;
}
asynStatus NanotecAxis::setPosition(double position)
{
asynStatus status;
//static const char *functionName = "NanotecAxis::setPosition";
char command[COMLEN], reply[COMLEN];
sprintf(command, "#%dD%d", busAddress, (int)position);
status = pC_->transactController(command,reply);
next_poll = -1;
return status;
}
asynStatus NanotecAxis::setClosedLoop(bool closedLoop)
{
//static const char *functionName = "NanotecAxis::setClosedLoop";
/*
This belongs into the Kingdom of Electronics.
We do not do this.
*/
return asynError;
}
/** Polls the axis.
* This function reads the motor position, the limit status, the home status, the moving status,
* and the drive power-on status.
* It calls setIntegerParam() and setDoubleParam() for each item that it polls,
* and then calls callParamCallbacks() at the end.
* \param[out] moving A flag that is set indicating that the axis is moving (true) or done (false). */
asynStatus NanotecAxis::poll(bool *moving)
{
asynStatus comStatus;
char command[COMLEN], reply[COMLEN];
char *pPtr;
int posVal, statVal;
// protect against excessive polling
if(time(NULL) < next_poll){
*moving = false;
return asynSuccess;
}
// Read the current motor position
sprintf(command,"#%dC", busAddress);
comStatus = pC_->transactController(command,reply);
if(comStatus) goto skip;
pPtr = strchr(reply,'C');
pPtr++;
posVal = atoi(pPtr);
errlogPrintf("Axis %d, reply %s, position %d\n", axisNo_, reply, posVal);
setDoubleParam(pC_->motorPosition_, (double)posVal);
//setDoubleParam(pC_->motorEncoderPosition_, position);
// Read the moving status of this motor
sprintf(command,"#%d$",busAddress);
comStatus = pC_->transactController(command,reply);
if(comStatus) goto skip;
pPtr = strchr(reply,'$');
pPtr++;
statVal = atoi(pPtr);
errlogPrintf("Axis %d, reply %s, statVal = %d\n",
axisNo_, reply, statVal);
setIntegerParam(pC_->motorStatusDone_, false);
*moving = true;
if(homing){
switch(statVal) {
case 164:
setPosition(.0);
break;
case 163:
case 161:
*moving = false;
setIntegerParam(pC_->motorStatusAtHome_, true);
setIntegerParam(pC_->motorStatusDone_, true);
break;
default :
if(statVal & 1) {
*moving = false;
setIntegerParam(pC_->motorStatusAtHome_, true);
setIntegerParam(pC_->motorStatusDone_, true);
}
break;
}
} else {
if(statVal & 1) {
*moving = false;
setIntegerParam(pC_->motorStatusDone_, true);
}
}
if(*moving == true){
next_poll = -1;
}
skip:
setIntegerParam(pC_->motorStatusProblem_, comStatus ? 1:0);
callParamCallbacks();
return comStatus ? asynError : asynSuccess;
}
/** Code for iocsh registration */
static const iocshArg NanotecCreateControllerArg0 = {"Port name", iocshArgString};
static const iocshArg NanotecCreateControllerArg1 = {"Nanotec port name", iocshArgString};
static const iocshArg NanotecCreateControllerArg2 = {"Number of axes", iocshArgInt};
static const iocshArg NanotecCreateControllerArg3 = {"Komma separated list of bus addresses", iocshArgString};
static const iocshArg * const NanotecCreateControllerArgs[] = {&NanotecCreateControllerArg0,
&NanotecCreateControllerArg1,
&NanotecCreateControllerArg2,
&NanotecCreateControllerArg3
};
static const iocshFuncDef NanotecCreateControllerDef = {"NanotecCreateController", 4, NanotecCreateControllerArgs};
static void NanotecCreateContollerCallFunc(const iocshArgBuf *args)
{
NanotecCreateController(args[0].sval, args[1].sval, args[2].ival,args[3].sval);
}
static void NanotecRegister(void)
{
iocshRegister(&NanotecCreateControllerDef, NanotecCreateContollerCallFunc);
}
extern "C" {
epicsExportRegistrar(NanotecRegister);
}

View File

@ -0,0 +1,57 @@
/*
FILENAME... NanotecDriver.h
USAGE... Motor driver support for the Nanotec SMCI controller.
Mark Koennecke
July 2015
*/
#include "asynMotorController.h"
#include "asynMotorAxis.h"
#define COMLEN 80
#define MAXMOT 99
class NanotecAxis : public asynMotorAxis
{
public:
/* These are the methods we override from the base class */
NanotecAxis(class NanotecController *pC, int axis, int busAddress);
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:
NanotecController *pC_; /**< Pointer to the asynMotorController to which this axis belongs.
* Abbreviated because it is used very frequently */
double position;
int homing;
time_t next_poll;
int busAddress;
friend class NanotecController;
};
class NanotecController : public asynMotorController {
public:
NanotecController(const char *portName, const char *NanotecPortName, int numMot, const char *busAddresses);
void report(FILE *fp, int level);
NanotecAxis* getAxis(asynUser *pasynUser);
NanotecAxis* getAxis(int axisNo);
friend class NanotecAxis;
private:
asynUser *pasynUserController_;
asynStatus transactController(char command[COMLEN], char reply[COMLEN]);
};

View File

@ -2,6 +2,7 @@
# SINQ specific DB definitions # SINQ specific DB definitions
#--------------------------------------------- #---------------------------------------------
registrar(EL734Register) registrar(EL734Register)
registrar(NanotecRegister)
addpath "/usr/local/epics/support/asyn-4-18/dbd" addpath "/usr/local/epics/support/asyn-4-18/dbd"
addpath "/usr/local/epics/dbd" addpath "/usr/local/epics/dbd"
addpath "/usr/local/epics/support/motor-6-7/dbd" addpath "/usr/local/epics/support/motor-6-7/dbd"
@ -13,4 +14,5 @@ include "motorSupport.dbd"
include "anc350AsynMotor.dbd" include "anc350AsynMotor.dbd"
include "scalerRecord.dbd" include "scalerRecord.dbd"
device(scaler,INST_IO,devScalerEL737,"asynScalerEL737") device(scaler,INST_IO,devScalerEL737,"asynScalerEL737")

View File

@ -0,0 +1,50 @@
/*
** stptok() -- public domain by Ray Gardner, modified by Bob Stout
**
** You pass this function a string to parse, a buffer to receive the
** "token" that gets scanned, the length of the buffer, and a string of
** "break" characters that stop the scan. It will copy the string into
** the buffer up to any of the break characters, or until the buffer is
** full, and will always leave the buffer null-terminated. It will
** return a pointer to the first non-breaking character after the one
** that stopped the scan.
*/
#include <string.h>
#include <stdlib.h>
char *stptok(char *s, char *tok, size_t toklen, char *brk)
{
char *lim, *b;
if (!*s)
return NULL;
lim = tok + toklen - 1;
while (*s && tok < lim) {
for (b = brk; *b; b++) {
if (*s == *b) {
*tok = 0;
return (char *) (s + 1);
}
}
*tok++ = *s++;
}
*tok = 0;
return (char *) s;
}
/*---------------------------------------------------------------------------*/
char *SkipSpace(char *pText)
{
char *pRes;
pRes = pText;
while (*pRes) {
if ((*pRes != ' ') && (*pRes != '\t') && (*pRes != '\r')) {
return pRes;
}
pRes++;
}
return NULL;
}

16
sinqEPICSApp/src/stptok.h Normal file
View File

@ -0,0 +1,16 @@
/*
** stptok() -- public domain by Ray Gardner, modified by Bob Stout
**
** You pass this function a string to parse, a buffer to receive the
** "token" that gets scanned, the length of the buffer, and a string of
** "break" characters that stop the scan. It will copy the string into
** the buffer up to any of the break characters, or until the buffer is
** full, and will always leave the buffer null-terminated. It will
** return a pointer to the first non-breaking character after the one
** that stopped the scan.
*/
#ifndef STPSTPTOK
#define STPSTPTOK
char *stptok(char *s, char *tok, size_t toklen, char *brk);
#endif