Argonne National Laboratory

Experimental Physics and
Industrial Control System

1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  <20112012  2013  2014  2015  2016  2017  2018  2019  2020  Index 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  <20112012  2013  2014  2015  2016  2017  2018  2019  2020 
<== Date ==> <== Thread ==>

Subject: New "model 3" motor driver for Parker ACR controllers
From: "Mark Rivers" <>
To: "Rau, Steffen" <>, "Ron Sluiter" <>
Cc: Daron Chabot <>,, "Kapp, Jens" <>, "Lauer, Kenneth" <>,,
Date: Wed, 23 Mar 2011 17:45:46 -0500



There are now 3 models for writing drivers and device support for the EPICS motor record:


Model 1)  Separate device support and driver support for each controller.  This was the traditional way of doing it.  It has many shortcomings, the most important of which is that the only way to talk to the driver is through the motor record, so there is no good way to use additional records to take advantage of controller-specific features.


Model 2) This was the first version to use asyn device support.  There is a single device-independent asyn device support file.  There is also a single device-independent asyn driver support file.  That driver file calls a device-dependent driver through a function table.  This model is more flexible than model 1.  It is possible to support additional parameters beyond those supported by the motor record (but it is not as easy as it should be).  It is not actually necessary to use the motor record at all, in principle standard EPICS records could be used for all parameters.  But the motor record provides the necessary state logic for most applications.


Model 3) This is a new asyn version.  It consists of a base C++ class, asynMotorDriver, which derives from asynPortDriver in asyn.  Real motor drivers derive from asynMotorDriver.  It is simpler than model 2, and more importantly it is much easier to support additional parameters beyond those in the motor record.


Until this week there was only one Model 3 driver in the motor source code, and that was for the simulation motor.  Today I committed a working version of a new Model 3 driver I wrote for the Parker ACR series controllers.  In addition to support for the motor record parameters, it also supports a jerk parameter (derivative of acceleration with respect to time), and it supports the 32 opto-isolated binary inputs and outputs that the controller provides. 


The driver is a total of 643 lines.


The Parker ACR driver is in the APS Subversion repository:



The ACR support includes: 




iocBoot/iocWithAsyn/st.cmd.acr, motors.subsitututions.acr


I think Model 3 is a nicer way to go for future drivers.  I will be adding an API for coordinated motion, like we do in SNL code for the XPS and MM4005 now, but integrated into the driver so that there is only a single driver talking to the controller.  I plan next to convert the Newport XPS driver from Model 2 to Model 3, and move the trajectory scanning from the SNL program into the driver.


In adding the digital I/O support for the ACR controller I found some missing methods for the asynUInt32Digital interface in asynPortDriver.  I have added those methods, and committed to the asyn Subversion repository:


I would welcome comments on this approach, and in particular on the asynMotorDriver base class.  It would be good to get the class well defined before a lot of drivers get written for it.





From: Rau, Steffen []
Sent: Friday, March 11, 2011 3:07 AM
To: Ron Sluiter; Mark Rivers
Cc: Joe Sullivan; Lauer, Kenneth; Daron Chabot; Kapp, Jens
Subject: AW: PI EPICS drivers - switch on servo control


Hello Mark, Hello Ron,



thank you for these explanations.


Now I'm a big step further with error handling in my driver: After I implemented the EA_POSITION and GAIN_SUPPORT bits of MSTA, CNEN behaves as expected. When my driver detects a "motion error" it can disable CNEN and the user can re-enable it with CNEN also (after removing the obstacle, powering the motor). And CNEN indicates the current status of the stage from begin. It no longer gets disabled "automagically". No additional BI needed.


Currently GAIN_SUPPORT is always set to "TRUE" and POSITION reflects the "servo control" state (enabled or disabled) of the controller. Is this a sensible implementation?


As Daron suggested I use the PROBLEM bit of MSTA to indicate a "motion error". Now I read the documentation (again) and find "SLIP_STALL: Slip/Stall detected (eg. fatal following error) ". Would this be better? Should PROBLEM indicate something different?



I was a bit confused how to set the single bits in the MSTA field from the C++ class. It seems that MSTA.POSITION corresponds to motorStatusPowerOn. I did not find out where the relation ship between the parameters in the C++ class and the MSTA field is established.

In drvMotorAsyn.c I found this comment:


/* Status bits split out */


motorStatusDone, motorStatusHighLimit, motorStatusAtHome,

motorStatusSlip, motorStatusPowerOn, motorStatusFollowingError,

motorStatusHome, motorStatusHasEncoder, motorStatusProblem,

motorStatusMoving, motorStatusGainSupport, motorStatusCommsError,

motorStatusLowLimit, motorStatusHomed, motorStatusLast,


Can I take this as a reference? The order of the variables is the order of the bits in MSTA?



Best regards






Von: Ron Sluiter []
Gesendet: Donnerstag, 10. März 2011 23:30
An: Mark Rivers
Cc: Joe Sullivan; Lauer, Kenneth; Daron Chabot; Rau, Steffen
Betreff: Re: PI EPICS drivers - switch on servo control


>From motorRecord.html,

This field is active only if the GAIN_SUPPORT bit in the MSTA is true.  This field is set by both the user and device support.

At IOC boot the CNEN should reflect the state of the controller's
closed-loop control system; on or off.

both true at IOC boot, then CNEN should be set true.

This works with the asyn motor version of the MM4000.


On 3/9/2011 2:40 PM, Mark Rivers wrote:


I am writing a new driver using model 3 (C++ class) for the Parker ACR series controllers.  I’ve got it basically working, though more testing is needed once I get limit switches wired up to my motor.

One thing I noticed related to the thread below:

- When the IOC boots the CNEN field is set to 0, so the medm display shows the drive as Disabled.  My driver correctly implements the command to turn the driver power on and off.  But it appears that although CNEN is set to 0, the record does not send the command to disable the drive as part of initialization.  How is this supposed to be handled?  Is my driver supposed to report that the power is on or off, and the CNEN field gets initialized appropriately from that?  Right now when I boot if the drive is powered on the record shows that it is not, which is confusing to the user.



From: Mark Rivers
Sent: Friday, March 04, 2011 8:30 AM
To: Daron Chabot; Rau, Steffen
Cc: Joe Sullivan; Lauer, Kenneth
Subject: RE: PI EPICS drivers - switch on servo control

It is a general principle in EPICS that the driver cannot directly access the record, only device support can do that.  I don't think the standard asyn motor record device support provides a way for the driver do a callback to change CNEN.

But there is an easy way to do it.  Add another parameter like RENABLE to your driver and connect a bi record to it with SCAN=I/O Intr.  When your driver does a callback to that record with a value of 1 then that record causes 1 to be written into the motor record CNEN field.


From: on behalf of Daron Chabot
Sent: Fri 3/4/2011 8:19 AM
To: Rau, Steffen
Cc: Mark Rivers; Joe Sullivan; Lauer, Kenneth
Subject: Re: PI EPICS drivers - switch on servo control

Hi Steffen,

On Fri, Mar 4, 2011 at 5:50 AM, Rau, Steffen <> wrote:


I can successfully enaböe/disable servo cotnrol e.g. by calling "m6.CNEN = 0" or "m6.CNEN = 1" from the python shell. But at some point in time, CNEN becomes 0 again. How do I set CNEN from the asyn motor C++ class?

I see the problem and will look into this today.

-- dc

I need this to indicate an "motion error" as Daron pointed out in his message. I have already implemented setting the problem bit and re-enabling by the user using "CNEN = 1", but I do not (yet) see how to influence this bit from the asyn class.

Best regards


Von: [] Im Auftrag von Daron Chabot
Gesendet: Montag, 28. Februar 2011 16:48
An: Rau, Steffen
Cc: Mark Rivers; Joe Sullivan; Lauer, Kenneth
Betreff: Re: PI EPICS drivers - switch on servo control

On Mon, Feb 28, 2011 at 10:21 AM, Rau, Steffen <> wrote:


There is an issue with recovery. When the stage hits an obstacle and the controller detects this, servo control is switched off. This is done inside the controller by monitoring the target position from the trajectory and the current position. If the position error gets too large, the controller sets "MOTION_ERROR" and switches off the servo control. All calls to MOV after this will fail until servo control is enabled again. I currently do not see how to signal this state to the record. And I do not see how to re-enable servo control from the user.

Hi Steffen,

>From the Motor Record field descriptions at
    "CNEN is set to Disable by device support when it detects a motion controller error; e.g. maximum following error exceeded."

It seems to me that the logical approach may be to report "MOTION_ERROR" to the console (regardless of debugging "level" or asynPrint mask), flip the MSTA.RA_PROBLEM bit on, and set the CNEN field to "Disable".

When I set the parameter "motorStatusGainSupport", if shows as GAIN_SUPPORT ("motor supports closed-loop position control") flag in the MSTA bit mask. But this is true all the time. Is there a flag indicating the current state? How to enable/disable servo control? I set a breakpoint in my code, but I could not call the function "motorSetClosedLoop" by playing with fields in the motor record. How is this triggered? How should user-code using only the motor record enable servo control?

The Motor Record (MR) itself is capable of "closing the loop". In this mode, the MR fetches a commanded position from its DOL field if and only if the OMSL field is set to "closed_loop".

NOTE: the descriptions at URL given above indicate that this mode of operation is not well-tested. More importantly, it is not relevant for hardware that can do the same job, only better !


Should the driver enable servo control before each MOV call? But then the hardware might get damaged if the stage really has hit an obstacle and the motor is switched on again and again and keeps on pushing against it. Is this something that needs to be implemented by an additional interface?

No to both questions. For this scenario where the following error has been exceeded, it's sufficient to disable the axis and get the user's attention, drawing attention to the nature of the error (eg: printf("Following error exceeded !! Disabling axis %d\n", axis->axis_id) ), and possibly also indicating what needs to be done to recover control of the axis.


-- dc


Von: Lauer, Kenneth []
Gesendet: Freitag, 25. Februar 2011 22:48
An: Rau, Steffen
Betreff: RE: PI EPICS drivers

Hi Steffen,

Another issue I ran into this afternoon is that it is difficult to recover a motor. It happens in other cases,

but in a repeatable instance, the stage was pressing against a piece in the system (by accident, of course),

and there was no way to stop it from the interface. I repeatedly hit stop/pause/etc. without any luck.

I've seen this happen several times before -- the status showing 'moving' and me not able to do anything

about it -- but I haven't figured out what scenarios caused it the other times.  Any thoughts?

Some decent progress on my end, if you're curious -- I have a basic soft motor configuration setup with the PI motors.

Three linear stages are connected, and though there are issues, I can basically control the motor positions with

a center position and a theta rotation (and the reverse) in EPICS.



From: Rau, Steffen []
Sent: Fri 2/25/2011 5:29 AM
To: Lauer, Kenneth
Subject: AW: PI EPICS drivers

Hi Ken,

Just a status update.

There's one fundamental difference when logging is used during the config...() calls.

The asynUser* used as logSink is created by my code.

During normal operation the asynUser* is given by the calling asyn code.

I need to read more to understand how an asynUser is created properly. Maybe I did not initialize it correclty.

Best regards


Von: Lauer, Kenneth []
Gesendet: Donnerstag, 24. Februar 2011 18:17
An: Rau, Steffen
Cc: Chabot, Daron
Betreff: RE: PI EPICS drivers

Hi Steffen,

(And sorry, Daron, for all of the spam...)

Looking further into asynPrint, it does appear to be the culprit. Just when commenting it out in sendAndReceive, it crashed upon printing after returning to the caller.

From asynDriver.h:

/* asynPrint and asynPrintIO are macros that act like
   int asynPrint(asynUser *pasynUser,int reason, const char *format, ... );
   int asynPrintIO(asynUser *pasynUser,int reason,
        const char *buffer, size_t len, const char *format, ... );
typedef struct asynTrace {
    /* lock/unlock are only necessary if caller performs I/O other then*/
    /* by calling asynTrace methods                                    */
    asynStatus (*lock)(asynUser *pasynUser);
    asynStatus (*unlock)(asynUser *pasynUser);
    asynStatus (*setTraceMask)(asynUser *pasynUser,int mask);
    int        (*getTraceMask)(asynUser *pasynUser);
    asynStatus (*setTraceIOMask)(asynUser *pasynUser,int mask);
    int        (*getTraceIOMask)(asynUser *pasynUser);
    asynStatus (*setTraceFile)(asynUser *pasynUser,FILE *fp);
    FILE       *(*getTraceFile)(asynUser *pasynUser);
    asynStatus (*setTraceIOTruncateSize)(asynUser *pasynUser,size_t size);
    size_t     (*getTraceIOTruncateSize)(asynUser *pasynUser);
    int        (*print)(asynUser *pasynUser,int reason, const char *pformat, ...);
    int        (*vprint)(asynUser *pasynUser,int reason, const char *pformat, va_list pvar);
    int        (*printIO)(asynUser *pasynUser,int reason,
               const char *buffer, size_t len,const char *pformat, ...);
    int        (*vprintIO)(asynUser *pasynUser,int reason,
               const char *buffer, size_t len,const char *pformat, va_list pvar);
epicsShareExtern asynTrace *pasynTrace;

#define asynPrint(pasynUser,reason,format...) \
   ((pasynTrace->getTraceMask((pasynUser))&(reason)) \
    ? pasynTrace->print(pasynUser,reason,format) \
    : 0)

I'm afraid I don't understand the internals of asyn enough to know why the trace elements would not be properly set. In st.cmd, I do set the trace mask for each axis.
I've wrapped all of the logSink and asynPrint-related code with something like:

    if (logSink && pasynTrace->print)

and added a simple getLogSink to the PIGCSController:

    asynUser* getLogSink() {
        if (m_pCurrentLogSink)
            return m_pCurrentLogSink;
            return pInterface;

That had no effect. I assume something must be just uninitialized.
Disabling all of the asynPrint statements with the code below has resulted in a stable build:

#undef asynPrint
#define asynPrint(user,reason,format...) 0

Attached you'll find my IOC startup scripts.



From: Rau, Steffen []
Sent: Thu 2/24/2011 10:51 AM
To: Lauer, Kenneth
Cc: Chabot, Daron
Subject: AW: PI EPICS drivers

Hello Ken,

Hmm, I cannot crash my test IOC. You callstack has hints, that the pointer to asynUser given to asynPrint as the first argument may not be valid. Can you just for test purposes disable the asynPrint calls in the sendAndReceive methods?

Best regards


Von: Lauer, Kenneth []
Gesendet: Donnerstag, 24. Februar 2011 15:43
An: Rau, Steffen
Cc: Chabot, Daron
Betreff: RE: PI EPICS drivers

Hi Steffen,

Hah, a Schrödingbug -- exactly.

I had been adding a RTRY column to your substitution file, but oddly enough, after looking into basic_asyn_motor.db again, you'll see that there's no $(RTRY).

I tried it several times and the crashes were definitely related to it. But since they have no bearing on the records' contents, I have absolutely no idea why

it's occurring now.


From: Rau, Steffen []
Sent: Thu 2/24/2011 3:21 AM
To: Lauer, Kenneth
Cc: Chabot, Daron
Subject: AW: PI EPICS drivers

Hello Ken,

the rounding issue is strange. I'm sure to have seen motion to non-integral targets. But now I know about the bug, I cannot move there any more... (I think this is called a Schrödingbug...). I already fixed it and will upload the code as soon as the RTRY issue is fixed.

How can I set RTRY to 0 at boot time? I'm not en EPICS expert and this motor driver is everything I have seen. I put together an IOC with Joe Sullivan which starts my driver. This is everthing I use to test. There is a file called "asynMotor.substitutions", which is loaded by dbLoadTemplate(). But when I try to modify this, RTRY is still set to 10 after boot.

Best regards


Von: Lauer, Kenneth []
Gesendet: Mittwoch, 23. Februar 2011 19:20
An: Rau, Steffen
Betreff: RE: PI EPICS drivers

I sent the last message a bit too quickly. There's also a slight issue with setting maximum retry (RTRY) to 0 on boot. I haven't figured out the exact value, but if it's

less than roughly 10, the program will segfault. I can just set the retry count to 0 after boot-up, so it's not a major problem. If you do get around to looking into it, here's the backtrace:

Program terminated with signal 11, Segmentation fault.
#0  0x00547edd in findDpCommon (puserPvt=0x202c30ec) at ../../asyn/asynDriver/asynManager.c:424
424     ../../asyn/asynDriver/asynManager.c: No such file or directory.
        in ../../asyn/asynDriver/asynManager.c
(gdb) bt
#0  0x00547edd in findDpCommon (puserPvt=0x202c30ec) at ../../asyn/asynDriver/asynManager.c:424
#1  0x00547f47 in findTracePvt (puserPvt=0x202c30ec) at ../../asyn/asynDriver/asynManager.c:434
#2  0x0054d791 in getTraceMask (pasynUser=0x202c3130) at ../../asyn/asynDriver/asynManager.c:2180
#3  0x0099ffaf in PIGCSController::sendAndReceive (pInterface=0x8e201fc, outputBuff=0xbffd5ec8 "SPA? 1 14", inputBuff=0xbffd5dc9 "G3", inputSize=99, logSink=0x202c3130) at ../PIGCSController.cpp:81
#4  0x009a02fc in PIGCSController::sendAndReceive (this=0x8e20968, output=0xbffd5ec8 "SPA? 1 14", inputBuff=0xbffd5dc9 "G3", inputSize=99) at ../PIGCSController.cpp:145
#5  0x009a14b9 in PIGCSController::getGCSParameter (this=0x8e20968, pAxis=0x8e20bb0, paramID=14,
value=@0xbffd5f70) at ../PIGCSController.cpp:448
#6  0x009a2b29 in PIGCSMotorController::getResolution (this=0x8e20968, pAxis=0x8e20bb0,
resolution=@0xbffd5fd8) at ../PIGCSMotorController.cpp:98
#7  0x0099e074 in PIasynController::configAxis (this=0x8e25170, axis=0, hiHardLimit=189998, lowHardLimit=0, home=0, start=0) at ../PIasynDriver.cpp:377
#8  0x0099f698 in PIasynConfigAxis (portName=0x8e20371 "PIPort1", axis=0, hiHardLimit=189998, lowHardLimit=0, home=0, start=0) at ../PIasynDriver.cpp:608
#9  0x0099f7b6 in PIasynConfigAxisCallFunc (args=0x8dc2de8) at ../PIasynDriver.cpp:657
#10 0x001b2d97 in iocshBody (pathname=0x8dc2ba0 "asynMotor.cmd", commandLine=0x0) at ../../../src/libCom/iocsh/iocsh.cpp:743
#11 0x001b2c5f in iocshBody (pathname=0xbffd82b4 "st.cmd", commandLine=0x0) at ../../../src/libCom/iocsh/iocsh.cpp:707
#12 0x001b3062 in iocsh (pathname=0xbffd82b4 "st.cmd") at ../../../src/libCom/iocsh/iocsh.cpp:801
#13 0x0804b89b in main (argc=2, argv=0xbffd62d4) at ../pitestMain.cpp:17




Navigate by Date:
Prev: epicsQt Szalata, Zenon M.
Next: Re: epics on FreeBSD: broadcast problem Gerrit Kühn
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  <20112012  2013  2014  2015  2016  2017  2018  2019  2020 
Navigate by Thread:
Prev: Driver for Tektronix AFG 3000 series? Ralph Lange
Next: PyEpics 3.1.0 Matt Newville
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  <20112012  2013  2014  2015  2016  2017  2018  2019  2020 
ANJ, 18 Nov 2013 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·