NAME mac_hdw - macro hardware facility
DESCRIPTION
The macro hardware facility links user-defined macro functions
with spec's built-in C code for motor and counter
control. The facility also provides a new route to implement
calculational pseudomotors, such as might be used for a slit's gap
and offset or a table's height, pitch and roll.
For the configured macro hardware devices, spec
will call user-defined macro functions to perform specific low-level
tasks. When reading the hardware config file, for each macro
hardware "controller" unit and each macro hardware channel,
spec will call an associated user-defined macro
function. For each low-level command needed to implement a motor or
counter control function, spec will make calls to
another associated user-defined macro function. In addition,
spec will call one other user-defined macro
function to set or get values for associated motor and counter
parameters.
Calculational pseudomotors are pseudomotors whose positions
depend on the positions of real motors. For such a macro hardware
pseudomotor, spec will call a user- defined macro
function that will calculate the position of each pseudomotor based
on the real motor positions or will calculate the real motor
positions based on a target pseudomotor position.
To enable a macro motor or counter, configure a macro controller
on the Devices screen of configuration editor, as follows:
|
MOTORS DEVICE ADDR <>MODE NUM <>TYPE
YES
motxx
- 5
Macro Motor
SCALERS DEVICE ADDR <>MODE NUM <>TYPE
YES
cntxx
- 5
Macro Counter YES
cntzz
- 5
Macro Counter/Timer
|
Here, the motxx , cntxx or
cntzz entries for DEVICE will be used by
spec as a prefix for the macros associated with the
particular macro hardware controller.
The ADDR field is optional, but may contain an
arbitrary string value. The value can be retrieved within
spec using
motor_par(mne , "address") where
mne is the mnemonic of any motor or counter
channel associated with the controller (as of spec
release 5.06.04-4). In addition, within the macro functions
described below, the value will assigned to a local variable named
prefix_ADDR (as of spec release
5.06.04-8).
The Macro Functions
For standard macro motors and macro counters, the three possible
user-defined macro functions have names formed by prepending the
prefix defined in the config file to _config() ,
_cmd() and _par() . For calculational
pseudomotors, a _calc() function is needed rather than
the _cmd() function.
For all the macro functions, the first argument is the motor or
counter number if the call applies to a single channel or the string
".." if the call applies to all channels associated
with the controller. The second argument is a string that contains a
key specific to the command. Remaining arguments, if any, contain
parameters.
As explained in the previous section, if an optional
ADDR string is included in the config file for
the associated controller, the string will be assigned to a variable
named prefix_ADDR . The variable is of local
scope and is only visible within the macro functions.
To send an error back to spec from the macro
functions, return the special string ".error." .
The _config() Function
The _config() function is called after reading the
config file. On startup, the function is called after the
initial command files have been read, so that macros defined in the
initial command files can be used to set up the macro hardware. For
regular macro motors and counters, this function is optional and
need not exist.
The _config macro function is called as follows:
prefix_config(".." , "ctrl",
p1 , p2 )
- Called for each macro hardware controller unit. The parameter
p1 is the unit number of the controller, while
p2 is the number of channels set as NUM on the
Devices screen of the configuration editor. If the function
returns the string ".error." , spec
will consider the controller unresponsive and won't call the macro
functions for the associated channels.
prefix_config( mne, "mot",
unit, module,
chan)
- Called for each macro motor channel, where
mne is the motor mnemonic,
unit is the unit number of the associated
controller, module is the optional module
number and chan is the channel number. For
calculational pseudomotors, this call must return a string
containing a list of mnemonics for the real motors on which the
pseudomotor depends. If the function returns the string
".error." , spec will consider the
channel unusable.
prefix_config( mne, "cnt",
unit, 0, chan)
- Called for each macro counter channel, where
mne is the counter mnemonic,
unit is the unit number of the associated
controller, and chan is the channel number.
The fourth argument is currently always zero. If the function
returns the string ".error." , spec
will consider the channel unusable.
The unit and channel numbers are assigned in the configuration
editor as with other motors and counters. The optional module number
became available in spec release 5.04.03-2. Unit
numbers are assigned consecutively to each controller type. That is,
the first macro motor controller listed is unit zero, as is the
first macro counter controller, both independent of other controller
types. Counter unit and channel numbers are assigned explicitly on
the Scaler screen of the configuration editor. Motor unit,
module and channel numbers can be explicitly assigned in the
unit/channel field of the configuration editor. If left blank, the
channels numbers are assigned automatically in consecutive order.
For macro hardware designed for general purpose applications,
these calls to the _config() macro function can be used
to set up the rest of the macro interface. For simple applications,
this function won't be required.
The _cmd() Function For Motors
The _cmd() function is called to control regular
macro motors. There are many more command keys in the list below
than any particular macro motor would need. Only the command keys
that are relevant to the particular application should be included
in the user-defined macro function.
The syntax of the function call is:
prefix_cmd( mne,
key [, p1 [,
p2]])
Called by the C code for all operations related to motor
control. mne is the string ".." for
keys that apply to all motors. mne is the motor
number for keys that apply to individual motors. Each key is only
called one way or the other. key is a string
containing the particular command. p1 and
p2 , if present, are parameters related to the
command.
In the following, the phrase "sent when changed" means the
_cmd() macro function is only called with the given key
before the first applicable move command or home command after
spec reads the config file, either on
startup or after a reconfig command (included in the
config macro) or after the associated parameter has
been changed using the motor_par() function.
"base_rate"
Sent when changed. The p1 parameter contains
the base rate in units of Hz.
"slew_rate"
Sent when changed. The p1 parameter contains
the slew rate in units of Hz.
"acceleration"
Sent when changed. Also called if the base rate or slew rate
have changed, since some motor controllers need to be told to
recalculate acceleration ramps if the velocity parameters change.
The p1 parameter contains the acceleration time
in units of milliseconds. The p2 parameter
contains the acceleration in units of steps per second per second.
"home_base_rate"
- Sent when changed. The
p1 parameter
contains the home base rate in units of Hz.
"home_slew_rate"
- Sent when changed. The
p1 parameter
contains the home slew rate in units of Hz.
"home_acceleration"
- Sent when changed. The
p1 parameter
contains the home acceleration time in units of milliseconds. The
p2 parameter contains the home acceleration in
units of steps per second per second.
"preread_all"
- Sent prior to a possible read of all the motors. Note,
depending on the configured hardware read modes for the motors,
there may be no subsequent commands to read a motor associated
with this controller. Either
"preread_all" or
"preread_one" (below), but not both, will be called
prior to the "position" call below.
"preread_one"
- Sent prior to reading an individual motor. Either
"preread_all" (above) or "preread_one" ,
but not both, will be called prior to the "position"
call below.
"position"
- For this key, the macro function must return the current motor
position in dial units. This call will be preceded by a call with
a key of either
"preread_one" or
"preread_all" .
"set_position"
- Sent to set the current dial position of the macro motor. The
parameter
p1 contains the position in dial
units.
"prestart_all"
- For regular moves, sent if any motors associated with the
macro motor controller need to be moved.
"prestart_one"
- For regular moves and homing moves, sent for each motor that
needs to be moved. For regular moves, a call of
"prestart_all" comes first.
"magnitude"
- For regular moves, sent with the magnitude of the move in dial
units. The parameter
p1 contains the position
in dial units and includes the sign of the move. The magnitude is
also included with the "start_one" key (below).
"start_one"
- Sent to start a regular move for one motor. The parameter
p1 contains the target position in dial units
to accommodate a controller that requires absolute positions. The
parameter p2 contains the magnitude of the
move in dial units to accommodate a controller that requires
relative positions.
"start_all"
- Sent after all
"start_one" commands to
accommodate controllers that use a simultaneous start.
"get_status"
- Sent to get the move and limit status of individual motors. If
the motor is moving, the macro function must return a value with
bit 0x02 set. If the low limit is active, the return value must
have bit 0x04 set. If the high limit is active, the return value
must have bit 0x08 set. Otherwise, the macro function must return
a value of zero.
"flush_all"
- Sent before the "hard" position synchronization that occurs on
startup and before and after reading the config and
settings files on a
reconfig command.
"flush_one"
- Sent for each motor after the
"flush_all" key
above and before a "get_status" during the position
synchronization.
"abort_one"
- Sent for each active motor when motors are halted, normally
either by a
^C from the keyboard or by a
stop() command.
"abort_all"
- Sent to each macro motor controller that has busy motors when
motor are halted. The key is sent after the
"abort_one" keys are sent. This command accommodates
controllers that allow a single command to halt all its associated
motors.
"search"
- Sent to initiate a home or limit search. A
"prestart_one" call will precede this call. The
parameter p1 indicates the type of search as
follows.
"home"
- Find the home position as appropriate.
"home+"
- Find the home position by moving in the positive direction.
"home-"
- Find the home position by moving in the negative direction.
"lim+"
- Find the positive limit.
"lim-"
- Find the negative limit.
The parameter
p2 contains the position in dial units that
corresponds to the home or limit switch.
"diff_position"
- Sent if the motor is configured for a settle time. To
configure a motor for settle time, both the DC dead-band and the
DC settle-time parameters have to be set (usually from the first
optional motor parameter screen of the configuration editor - get
there by typing an
m from the primary motor screen).
The settle-time parameter is in seconds. spec
will wait for at least as long as the settle time before treating
a move as complete. In addition, spec will wait
until the value returned by this call is less than the dead band,
but for no longer than five seconds. The preferred units for dead
band are steps, but it is only necessary that the units in the
config file agree with the units returned by this call.
Backlash is handled as two separate move commands. If the macro
function will take care of backlash, set the backlash parameter to
zero in the config file.
The default behavior with respect to reading the motor position
is to only ask for the motor position from the controller during
position synchronization or at the end of a move. The motor
parameter "hardware read mode" (on the second optional motor
parameter screen of the configuration editor) can be set to require
spec to ask for the position before each move
and/or for every read_motors() call from user level.
The hardware read mode can also be set so that position
discrepancies are always silently resolved in favor of the value
returned by the controller (or macro function).
A minimal implementation would likely recognize the keys
"start_one" , "get_status" ,
"position" and "set_position" . An example
that does nothing useful follows: |
def motxx_cmd(mne, key, p1, p2) '{
global demo_pos[]
if (key == "set_position") {
demo_pos[mne]
= p1
return
} if (key
== "position") {
return(demo_pos[mne])
} if (key
== "start_one") {
demo_pos[mne]
= p1
return
} if (key
== "get_status") {
return(0)
} }'
|
The _par() Function For Motors
The _par() macro function is called when various
motor parameters are set, and when the motor_par()
function is used to retrieve a user-defined parameter. The
mne argument will always be a motor number and
never the string ".." that is used with the other
user-defined macro hardware functions.
The function will be called as:
prefix_par( mne,
key, "get")
- The function should return a value for the parameter named as
key for motor number mne .
The macro function will never be called to get a parameter value
when key is a parameter name that is built
into the spec C code.
prefix_par( mne,
key, "set", p1 [,
p2])
- Called when various parameters change their value, as
described below.
The built-in parameter names are as follows. The
_par() function will never be called with
"get" for these parameters, as their values are
maintained internally.
The first set below are called only when motor_par()
is executed from user-level.
"acceleration"
p1 contains the acceleration time in
milliseconds. p2 contains the acceleration
value in steps per second per second.
"backlash"
p1 contains the new backlash value in
steps.
"backlash_rate"
p1 contains the new backlash rate in Hz.
"base_rate"
p1 contains the new base rate in Hz.
"disable"
p1 contains 1 or 0, depending on whether
the motor was disabled or un- disabled (available as of
spec release 5.06.03-8).
"slew_rate" or "velocity"
p1 contains the new slew rate
in Hz.
"step_size"
p1 contains the new step-size parameter.
The following two keys are called when an associated function is
executed from user level.
"limits"
p1 contains the low limit in dial units.
p2 contains the high limit in dial units.
Called when the user-level set_lim() function is
executed.
"offset"
- Called when the user-level
chg_offset() function
is executed. p1 contains the offset in user
units.
The following optional motor parameters generate a call to the
user-defined macro hardware function when the values are read from
the config file and when the values are set with the
motor_par() function. See the motors
help file for additional information on the parameters.
"home_slew_rate"
"home_base_rate"
"home_acceleration"
"dc_dead_band"
"dc_settle_time"
"dc_proportional_gain"
"dc_derivative_gain"
"dc_integral_gain"
"dc_integration_imit"
"dc_following_error"
"dc_sampling_interval"
"encoder_step_size"
"step_mode" "slop"
"read_mode"
"deceleration"
"torque"
"misc_1"
"misc_2"
"misc_3"
"misc_4"
"misc_5"
"misc_6"
There are a number of valid arguments to motor_par()
which will not generate a call to the _par() macro
function at all. These include "disable" ,
"unit" , "channel" ,
"responsive" , "controller" ,
"device_id" , "active" ,
"status" , "config_step_size" ,
"config_acceleration" , "config_velocity" ,
"config_base_rate" , "config_backlash" ,
"low_limit" and "high_limit" .
Arguments to motor_par() that are not recognized by
the built-in C code will be passed on, as is, to the
_par() user-defined macro function.
Calculational Pseudo Motors
For calculational pseudomotors, two macro functions must be be
provided with names formed by prepending the prefix from the
config file to _config() and
_calc() .
The _config() function, when called with the key
equal to "mot" , must return a string that contains a
space-delimited list of mnemonics for the real motors on which motor
mne depends.
The _calc() macro function will be called as
follows:
prefix_calc( mne,
mode)
- When called with
mode equal to zero, the
function should assign a value to
A[mne ] corresponding to the
current position of the pseudomotor mne . When
called with mode equal to one, the function
should assign a value to A[mne ]
corresponding to the target position of the real motor
mne .
When called to calculate the real motor positions for a move
(with mode equal 1), the function will first be
called with mne set to the string
".." , then called with each of the real motor mnemonics
as arguments, in turn. One can use the initial call to calculate
quantities that depend on the current positions of the real motors
before new values are assigned in subsequent calls.
The _calc() function should only include commands to
calculate pseudomotor positions from real motor positions or vice
versa. The function should not contain calls to do hardware access.
In fact, calls to the built-in functions wait() or
read_motors() will return immediately if called from
the _calc() macro function, to avoid possible recursion
as those built-in functions can subsequently call the invoking
_calc() macro function.
With spec release 5.06.04-4, a special
motor_par() option called "chan0" is
available for macro motors. The command
motor_par(mne , "chan0")
will return the motor number of the macro motor with the same
controller unit and module number of the macro motor with mnemonic
mne . This feature allows for simplified
implementation of general-purpose calculational pseudomotors.
The following examples implements calculational pseudomotors for
a slit. The slit has two blades whose real motors have mnemonics
sl2t and sl2b (slit 2 top and bottom). The
pseudomotors are the slit gap and the slit offset position with
mnemonics sl2g and sl2o , respectively.
The Devices screen of the configuration editor should look
as follows for the controller: |
MOTORS DEVICE
ADDR <>MODE NUM
<>TYPE YES
slit2
2 Macro
Motor
|
The macros would be as follows: |
def slit2_config(mne, type, unit, module, chan) '{
if (type == "mot")
return
"sl2t sl2b" }' def slit2_calc(mne, mode) '{
if (mode == 0) {
if
(mne == sl2g)
A[mne]
= A[sl2t] + A[sl2b]
if
(mne == sl2o)
A[mne]
= (A[sl2t] - A[sl2b])/2 }
else {
if
(mne == sl2t)
A[mne]
= -A[sl2o] + A[sl2g]/2
if
(mne == sl2b)
A[mne]
= A[sl2o] + A[sl2g]/2 }
}'
|
The following make_slit_macs macro can be used to
generate macros such as the above. |
def make_slit_macs '{
local name, file
local l, r, g, o
file = getval("File for macros",
"tty") name = getval("Name of slit",
"Slit1") r = getval("Mnemonic for
right/top slit", "s1r") l =
getval("Mnemonic for left/bottom slit", "s1l")
g = getval("Mnemonic for gap", "s1vg")
o = getval("Mnemonic for offset",
"s1vo")
fprintf(file, "\n\ def
%s_config(mne, type, unit, module, chan) \'{\n\
if (type == \"mot\")\n\
return
\"%s %s\"\n\ }\'\n\ def %s_calc(mne, mode) \'{\n\
if (mode == 0) {\n\
if
(mne == %s)\n\
A[mne]
= A[%s] + A[%s]\n\
if
(mne == %s)\n\
A[mne]
= (A[%s] - A[%s])/2\n\ } else {\n\
if
(mne == %s)\n\
A[mne]
= -A[%s] + A[%s]/2\n\
if
(mne == %s)\n\
A[mne]
= A[%s] + A[%s]/2\n\ } }\'\n",
name,l,r, name,g,l,r, o,r,l, l,o,g, r,o,g) }'
|
The following example implements a table-height pseudomotor with
mnemonic t1z that is the average height of the three
real motors t1f , t1b1 and
t1b2 that correspond to the table legs. When the height
is moved, each leg is moved by an amount equal to the difference of
the current height and the target height. The current average height
needs to be calculated from the current real-motor positions before
the new positions are assigned. The feature where the
_calc() function is called with mne set to
the string ".." before being called with the real motor
mnemonics is used to save the average position in a global variable
to be used in the subsequent calls. |
def tab1_config(mne, type, unit, module, chan) '{
global tab1_ave
if (type == "mot")
return
"t1f t1b1 t1b2" }' def TE1_vert_jack_calc(mne, mode) '{
if (mode == 0) {
if
(mne == t1z)
A[mne]
= (A[t1f] + A[t1b1] + A[t1b2])/3
} else {
if
(mne == "..")
tab1_ave
= (A[t1f]+A[t1b1]+A[t1b2])/3
else
if (mne == t1f)
A[mne]
+= A[t1z] - tab1_ave
else
if (mne == t1b1)
A[mne]
+= A[t1z] - tab1_ave
else
if (mne == t1b2)
A[mne]
+= A[t1z] - tab1_ave }
}'
|
This last example shows how an energy pseudomotor can be
readily created that ties in with the standard spec
energy macros from the energy.mac distribution file. Such a
pseudomotor can then be scanned using the standard motor scans.
Note, the existing energy macros, such as Escan ,
moveE , etc., will still work.
The _config() function below makes use of the
monochromator mnemonic conventions set in energy.mac.
|
def Energy_config(mne, type, unit, module, chan) '{
if (type ==
"mot") {
if (mono_type == 1)
return(motor_mne(Mono))
if (mono_type == 2)
return("mono mon_y mon_z")
if (mono_type == 3)
return("monu mond montrav")
if (mono_type == 4)
return("monu mond montrav monoff")
} }'
def Energy_calc(mne, mode) '{
if (mode ==
0) {
calcE
A[energy] = hc_over_e / LAMBDA
} else {
calcM A[energy]
calcE }
}'
|
The prefix Energy and motor mnemonic
energy are, as always, arbitrary, but must match the
configured device name on the Devices screen and motor
mnemonic on the Motor screen.
The _cmd() Function For Counters
The _cmd() function for counters will be called in
response to spec's built-in timer/counter
functions. The function will be called with the keys to start
counters in response to spec's
tcount() , mcount() and
move_cnt commands. The count-mode codes used below are
as follows: |
- 1 - count to a monitor preset -
mcount() 2 -
count to a time preset - tcount() 3 - just count
until the counters are halted - move_cnt
|
The syntax of the function call is:
prefix_cmd( mne,
key [, p1 [,
p2]])
Called by the C code for all operations related to counter/timer
control. mne is the string ".." for
keys that apply to all counters. mne is the
counter number for keys that apply to individual counters. Each key
is only called one way or the other. key is a
string containing the particular command. p1 and
p2 , if present, are parameters related to the
command.
Possible keys are as follows:
"prestart_all"
Sent to the controller before any counters are started.
p1 will contain the count preset, either in
seconds or monitor counts. p2 contains the count
mode (1, 2 or 3), as in the above list.
"start_one"
Sent to start each counter at the start of the counting period.
The function will be called for the regular counting channels before
being called for the master timer channel. p1 is
the count time in seconds if counting to time or in counts if
counting to monitor. If mne is the master timer,
p2 will contain 1, 2 or 3 to indicate the
counting mode. If mne is not the master,
p2 will be zero.
"get_status"
Sent after counters have been started, but only to the master
timer/counter, if there is one. Must return nonzero if the timer is
busy or zero if the counting is finished.
"counts"
Called when spec's built-in
getcounts function is executed. The macro function must
return the current counts for scaler channel number
mne . Note, all the scalers channels associated
with real counters will have been read before the macro function
call, so the values is the S[] counter array will be
current for the non-macro counters.
"halt_all"
Sent to each macro counter controller when counting is halted.
The key is sent before the "halt_one" keys are sent.
"halt_one"
Sent to each active counter when counting is halted.
Commands to halt counters are sent when a preset count time
elapses, if a ^C is typed from the keyboard, when a
stop() or sync command is encountered from
user-level or after motors have stopped when the
move_cnt command is used.
The _par() Function For Counters
There are currently no built-in keywords that produce calls to
the _par() macro function for counters. The macro
function will only be called when spec's user-level
counter_par() function is called to set or retrieve an
otherwise unrecognized parameter.
The function will be called as:
prefix_par( mne,
key, "get")
- The function should return a value for the user-defined
parameter named as
key for counter number
mne .
prefix_par( mne,
key, "set", p1)
- Called to set the user-defined parameter
key to p1 for counter
number mne .
The mne argument will always be a counter
number and never the string ".." .
Built-in arguments to counter_par() that can't be
passed to a user-defined macro function include
"disable" , "unit" , "channel" ,
"responsive" , "controller" ,
"device_id" , "scale" ,
"monitor" , and "timer" .
Debugging
Setting the spec DEBUG level to 128 will display
the macro function calls, which may help make clear when and in what
order the various macro functions are called. For example, a move
command (with the default hardware read mode) produces the
following: |
161.FOURC> debug 128 162.FOURC> mv m0 15
motxx_cmd("..", "preread_all") # from read_motors()
motxx_cmd("..", "preread_all") # internal pre-move sync
motxx_cmd("..", "prestart_all") motxx_cmd(m0,
"prestart_one") motxx_cmd(m0, "base_rate", 200)
motxx_cmd(m0, "slew_rate", 2000) motxx_cmd(m0,
"acceleration", 125, 14400) motxx_cmd(m0, "magnitude", 5)
motxx_cmd(m0, "start_one", 5, 15) motxx_cmd("..",
"start_all")
|
... Meeting the software needs of scientists since 1985
...
Last Formatted Aug 5, 2006
Last Updated 02/02/06 Send comments, queries,
suggestions to info@certif.com
© 1995-2003 Certified Scientific Software. All
rights reserved
| |