Main Page | Data Structures | File List | Data Fields | Globals

mechanism.c File Reference

Mechanism Motion Control API. More...

#include "siutils.h"
#include "mechanism.h"

Functions

void MechInitTable (si_mechanism_t *mech)
 Initialize the data members of an si_mechanism struct.

int MechGetPos (si_mechanism_t *mech, char *reply)
 Query the Si microstep drive for the current mechanism position.

int MechSetProfile (si_mechanism_t *mech, char *reply)
 Upload the motion profile to the microstep drive.

int MechMoveAbs (si_mechanism_t *mech, int req_pos, char *reply)
 Move a mechanism to an absolute step-count position.

int MechMoveRel (si_mechanism_t *mech, int delta, char *reply)
 Move a mechanism by a relative step-count distance from its current position.

int MechMoveTo (si_mechanism_t *mech, int req_pos, int offset, char *reply)
 Move to an indexed mechanism to a selected indexed position.

int MechFindPos (si_mechanism_t *mech, char *reply)
 Seek a valid indexed position starting from an invalid (in-between) position.

int MechBrake (si_mechanism_t *mech, int action, char *reply)
 Set the mechanism brake.

void MechAbort (si_mechanism_t *mech)
 Abort a move in progress.

int MechEncToDev (int encpos, int npos, int offset, int topology)
 Convert a raw encoder position into an offset device position in an indexed mechanism (Convenience Function).


Detailed Description

Mechanism Motion Control API.

This is a set of API functions for operating a mechanism driven by a stepper motor using an Applied Motion Products Si indexing microstep drive.

More details to follow...

Author:
R. Pogge, OSU Astronomy Dept. (pogge@astronomy.ohio-state.edu)
Date:
2004 June 13
Modification History:
2004 Jun 16 - added MechBrake() function, and added brake operations
              to the MechMoveXXX functions [rwp/osu]
2004 Jul 16 - added StepsPerMM to MechInitTable() [rwp/osu]

Function Documentation

void MechInitTable si_mechanism_t mech  ) 
 

Initialize the data members of an si_mechanism struct.

Parameters:
mech pointer to an si_mechanism struct to be initialized
Initializes all of the data members of an si_mechanism struct to sensible default values. Some of these default values are defined in the mechanism.h header file, others are just common-sense.

Debug Behavior: None.

int MechGetPos si_mechanism_t mech,
char *  reply
 

Query the Si microstep drive for the current mechanism position.

Parameters:
mech pointer to the si_mechanism struct for this device
reply string to contain the position status, or an error message as required.
Returns:
MECH_OK if successful, MECH_ERR on errors. reply contains either an (engineering) status message if successful, or an error message if not.
Query the Si microstep drive to read the mechanism's current position, loading the relevant data members of the si_mechanism struct. Reads both the relative and absolute positions, the former by reading and interpreting the input bits (IS command), the latter by reading the immediate device position (IP command, via SCLGetPos() to traslate from hexadecimal, the value of which is loaded into si_mechanism::Position).

If the mechanism uses position encoders, it interprets the bit string returned by the IS command and convert this into an encoder data value (integer) and the relevant flags, loading the following si_mechanism struct data members:

  si_mechanism::Inputs   = Input sensor bit string
  si_mechanism::EncPos   = Decimal representation of the position encoder bits
  si_mechanism::InPos    = In-Position flag (in-/out-of-position), if used
  si_mechanism::AtHome   = Home Position Flag (at home/away), if used
  si_mechanism::CWlimit  = CW limit switch state, if used
  si_mechanism::CCWlimit = CCW limit switch state, if used
  si_mechanism::Occupied = Position occupation state (occupied/empty) if used.
  
On success, the reply string contains an IMPv2-conformal position status string reporting the mechanism position in keyword=value pairs as follows (for example)
  RawIn=11111011 Inputs=11000011 ISMask=11000111 StepCounts=16172
  
The calling routine can do something with this string or not as required. It is also free to interpret/use the various si_mechanism struct data members as required. The string above contains minimal engineering-level information. The usual use is to print it to stdout when the client is running in Verbose or Debug (super-verbose) mode.

Debug Behavior:
Setting the si_mechanism::Debug flag provides detailed engineering output to trace the device query progress.
Calls:
SCLSendQuery() and SCLReadStatus()

int MechSetProfile si_mechanism_t mech,
char *  reply
 

Upload the motion profile to the microstep drive.

Parameters:
mech Pointer to the si_mechanism struct for this device
reply String for status or error messages
Returns:
MECH_OK if successful, MECH_ERR on errors. reply contains either a success status message, or an error message as appropriate.
Upload the motion profile: step size, speed, accel/deceleration rates, and motor current, and sets other relevant microstep drive parameters for handling limits. These ensure that if the drive has been power cycled without our knowing it, the current (presumably safe) drive profile will be restored. The parameter set are as follows:
  si_mechanism::Speed       = speed (velocity) in rev/s    - VE 
  si_mechanism::Accel       = acceleration rate in rev/s/s - AC 
  si_mechanism::Decel       = deceleration rate in rev/s/s - DE 
  si_mechanism::Distance    = move distance in steps       - DI
  si_mechanism::Current     = motor current in Amps        - CC 
  si_mechanism::IdleCurrent = motor idle current in Amps   - CI
  si_mechanism::Idle        = Enable/Disable motor idle    - ME/MD
  
In addition, we set these parameters:
  if si_mechanism::Limits = MECH_NORMALLY_CLOSED
    DL1 - if limit switch is sinking/normally-closed
                          or sourcing/normally-open
  if si_mechanism::Limits = MECH_NORMALLY_OPEN
    DL2 - if limit sensor is sinking/normally-open 
                          or sourcing/normally-closed
  if si_mechanism::Limits = MECH_NOLIMITS
    DL3 - limit switches disabled

  JD  - disable Jog Inputs (maybe unnecessary)
  JA1 - set the Jog Acceleration to 1 rev/s/s
  
When the drive is power cycled, it loses all knowledge of its position (all parameters are volatile except for the power-on current - PC - and power-up communications mode - PM). The accelerations, speed, etc. are reset to device defaults. To provide a check, we always set the Jog Acceleration parameter (JA) to 1. If we ever see it set to SI_JA_DEFAULT, we will have some indication that the controller has been power-cycled. This is important for being able to operate linear continuous drive mechanisms in step-counting mode to tell the position without resort to an absolute position encoder. For example, the MechMoveAbs() command checks the value of JA before uploading the profile.

Debug Behavior:
If the mechanism si_mechanism::Debug flag is enabled, this function timestamps queries to help evaluation microstep drive transaction times.
Calls:
SCLTimeStamp() and SCLSendCommand()

int MechMoveAbs si_mechanism_t mech,
int  req_pos,
char *  reply
 

Move a mechanism to an absolute step-count position.

Parameters:
mech Pointer to the si_mechanism struct for this device
req_pos Requested absolute position in steps from device home (zero)
reply String for status or error messages
Returns:
MECH_OK on success, MECH_ERR on error. reply contains an engineering status message on success, or an error message if not.
Instructs the controller to advance the mechanism until the internal step count reachs the requested absolute position req_pos. It is assumed that if the range of absolute position is restricted and the calling program knows this, it has already validated req_pos before calling MechMoveAbs(). This function does no validation of req_pos.

The move takes place in the following sequence:

  1. Query the drive JA (Jog Acceleration) and make sure it is not SI_JA_DEFAULT. If it is, this means the controller has likely been power-cycled and the step count has been lost.
  2. If JA is OK, upload the motion profile calling MechSetProfile()
  3. Set the target absolute position passed by req_pos using a DI command.
  4. If the mechanism has a brake, release the brake.
  5. Launch the move using a FP (Feed-to-Position) command.
  6. Poll during the move to monitor progress, at a polling cadence set by si_mechanism::Polltime.
  7. When the move completes:
    1. Set the brake, if used.
    2. Check the actual position against the requested position.

Debug Behavior:
Setting the si_mechanism::Debug flag provides detailed engineering output to trace the device query progress.
Calls:
SCLReqStatus(), MechGetPos(), SCLSendCommand(), SCLTimeStamp(), and SCLSleep()
See also:
MechMoveRel(), MechSelectPos(), MechHome()

int MechMoveRel si_mechanism_t mech,
int  delta,
char *  reply
 

Move a mechanism by a relative step-count distance from its current position.

Parameters:
mech Pointer to the si_mechanism struct for this device
delta Requested relative distance to move (+/-) relative to the current position.
reply String for status or error messages
Returns:
MECH_OK on success, MECH_ERR on error. reply contains an engineering status message on success, or an error message if not.
Instructs the controller to move the mechanism delta steps from the current position.

The move takes place in the following sequence:

  1. Make sure the drive is ready.
  2. Query the drive for the current position.
  3. Set the relative move distance by delta (DI)
  4. If the mechanism has a brake, release it.
  5. Launch the move (FL = Feed-to-Length)
  6. Poll during the move to monitor progress, at the polling cadence defined by si_mechanism::Polltime.
  7. When the move completes:
    1. Set the brake, if used.
    2. Check the difference in position and make sure it moved.

Debug Behavior:
Setting the si_mechanism::Debug flag provides detailed engineering output to trace the device query progress.
Calls:
SCLReqStatus(), MechGetPos(), SCLSendCommand(), SCLTimeStamp(), and SCLSleep()
See also:
MechMoveAbs(), MechSelectPos(), MechHome()

int MechMoveTo si_mechanism_t mech,
int  req_pos,
int  offset,
char *  reply
 

Move to an indexed mechanism to a selected indexed position.

Parameters:
mech Pointer to the si_mechanism struct for this device
req_pos Address (1..si_mechanism::NPos) of the requested indexed position to move to.
offset Offset between the encoder and true position (e.g., the in-beam or load position)
reply String for status or error messages
Returns:
MECH_OK on success, MECH_ERR on error. reply contains an engineering status message on success, or an error message if not.
Simple move command - uses the current motion profile (acceleration, speed, deceleration, and number of steps/position) to move an indexed mechanism to a selected indexed position. The operations required are as follows:
  1. Query the mechanism for the current position (MechGetPos() call)
  2. Compute the move vector. The algorithm depends on the drive topology:
      if si_mechanism::Topology = MECH_ROTARY
         move is the least-distance from present position to target
      if si_mechanism::Topology = MECH_LINEAR
         move is npos=(req_pos-current_pos)
      
  3. If the mechanism has a brake, release it.
  4. Launch the move with an FL command (Feed to Length).
  5. Query the drive every si_mechanism::Polltime msec to monitor progress
  6. Query the mechanism position again and make sure it moved.
  7. Sends an MD to turn off the motor idle as required by this mechanism.
  8. Set the brake if used.
Encapsulates a number of low-level commands to make API programming simpler.

The rules for determining "success" of a requested move are as follows:

  1. The mechanism must start from a valid position
  2. The mechanism must end up in a valid position

Position Offsets
On various indexed mechanisms, packaging restrictions (e.g., avoiding obstructions in the instrument beam) require that the location of the position encoders does not exactly align with the operating position of the mechanism. For example, in a filter wheel, the in-beam and load positions are along different radial directions from the center from the radius with the position encoders.

All of our devices use positions numbered 1..si_mechanism::NPos, (1-relative), but the encoder values run 0..si_mechanism::NPos-1 (0-relative). This does sometimes lead to confusion, so beware!

To avoid errors, the MechEncToDev() function is provided to make this computation simple and consistent. See MechEncToDev() for details.

Rotary Indexed Mechanisms
To move a rotary indexed mechanism in an efficient way, we use a least-path algorithm that determines the motion vector +/- of the current position that will move the mechanism to the desired position with the least number of steps. The algorithm is sufficiently easy to get wrong (and we did, many different times in development) that it is embedded here so applications don't have to get their modular arithmetic right

The algorithm to make a least-path move is as follows:

Nmove = (req_pos - cur_pos) % Npos; if (Nmove == 0) { ... done, already there } else if (abs(Nmove) > (Npos/2)) { if (Nmove < 0) Nmove += Npos; else Nmove -= Npos; } move_vec = Nmove * StepPos;
where cur_pos is the current position (e.g., Filter 1), req_pos is the requested position (e.g., Filter 4), Npos is the number of valid positions in the rotary mechanism (si_mechanism::NPos), and StepPos is the number of steps between valid positions (si_mechanism::StepPos). The code below validates that the requested position is indeed within the valid index limits.

Linear Indexed Mechanisms
Linear indexed mechanisms are those constrained to move between fixed positions between two absolute limits. In this case, the motion vector is simply:
move_vec = StepPos * (req_pos - cur_pos);
Again, cur_pos is the current position, req_pos is the requested position, and StepPos is the number of steps between valid positions (si_mechanism::StepPos), and req_pos is validated below before computing the move vector.

Debug Behavior:
Setting the si_mechanism::Debug flag provides detailed engineering output to trace the device query progress.
Calls:
SCLReqStatus(), MechGetPos(), SCLSendCommand(), SCLTimeStamp(), and SCLSleep()
See also:
MechMoveAbs(), MechMoveRel()

int MechFindPos si_mechanism_t mech,
char *  reply
 

Seek a valid indexed position starting from an invalid (in-between) position.

Parameters:
mech Pointer to the si_mechanism struct for this device
reply String for status or error messages
Returns:
MECH_OK on success, MECH_ERR on error. reply contains an engineering status message on success, or an error message if not.
If an indexed mechanism is located at an in-between position ("out-of-position"), the in-position bit (si_mechanism::InPosAddr) is set LOW (si_mechanism::InPos=0). This function instructs the microstep drive to seek a valid position by moving the mechanism slowly in the positive direction until the in-position bit is asserted HIGH. The low-level SCL command for this is "FYnH", where "n" = the address of the in-position bit (si_mechanism::InPosAddr).

Experimentally, the values we use for seeking a valid position are as follows:

  speed (VE) = 20% normal speed (si_mechanism::Speed)
  accel (AC) = 1 rev/sec/sec (minimum)
  decel (DE) = 1 rev/sec/sec (minimum)
  distance until change (DC) = 2*abs(si_mechanism::StepPos) = 2 index distances
  distance to move (DI) = R*VE^2/(2*DE)
     where R = the microstep resolution.
  
If the in-position bit doesn't assert after moving 2 whole index positions, we have problems. The FY (Feed to sensor with safety distance) will Timeout and return a "!" if the in-position sensor is not seen after moving DC steps. DI steps are computed to allow the motor to ramp down after seeing the sensor but not miss it.

Before the move, we turn the idle ON (ME command) to avoid getting pulled into a kinematic docking detent during a slow move, and once we get into position, we turn idle OFF (MD command) to drop into the position detent.

Debug Behavior:
Setting the si_mechanism::Debug flag provides detailed engineering output to trace the device query progress.
Calls:
SCLTimeStamp(), MechGetPos(), SCLSendCommand(), MechSetProfile(), SCLSendQuery(), and SCLSleep()

int MechBrake si_mechanism_t mech,
int  action,
char *  reply
 

Set the mechanism brake.

Parameters:
mech Pointer to the si_mechanism struct for this device
action Action to perform, either MECH_SET_BRAKE or MECH_RELEASE_BRAKE
reply String for status or error messages
Returns:
MECH_OK on success, MECH_ERR on error. reply contains an engineering status message on success, or an error message if not.
action tells it to set/release the brake by setting the specified output high (MECH_SET_BRAKE) or low (MECH_RELEASE_BRAKE), respectively, using the SO command. The address of the drive output connected to the brake is defined by si_mechanism::BrakeAddr. Note that if we are not using the brake, (si_mechanism::BrakeAddr=0), this function returns immediately with no action.

On completion, sets the si_mechanism::Brake data member. If the brake state is unknown, it is set to -1. After a successful braking action, it sleeps for si_mechanism::Polltime milliseconds to allow things to settle mechanically before returning.

Debug Behavior:
Setting the si_mechanism::Debug flag provides simple status printout ("setting/releasing brake").
Calls:
SCLSendCommand()

void MechAbort si_mechanism_t mech  ) 
 

Abort a move in progress.

Parameters:
mech Pointer to the si_mechanism struct for this device
Sends an SK command to the Si microstep drive to abort any moves in progress, and sets the si_mechanism::Abort flag. It can be used, for example, as a callback for a SIGINT handler for an interactive command shell, or called in response to an abort directive.

Calls:
SCLSendCommand()

int MechEncToDev int  encpos,
int  npos,
int  offset,
int  topology
 

Convert a raw encoder position into an offset device position in an indexed mechanism (Convenience Function).

Parameters:
encpos Current encoder position
npos Number of valid positions
offset Offset between the true and encoder positions
topology Mechanism Topology, one of MECH_ROTARY or MECH_LINEAR
Returns:
The position corresponding to encpos, or MECH_ERR if the encpos is invalid (e.g., out-of-range), or if the topology was incorrectly specified.
The location of the position encoders in indexed mechanisms need not always aligned be with the "active" (beam or load) positions, depending on the device packaging details. MechEncToDev() converts the current position into the specified, sometimes offset, device position, performing the correct arithmetic for the specific mechanism topology. This operation is sufficiently common (and sufficiently easy to get wrong) that we encapsulate it in a separate convenience function to avoid needless repetition of code (and mistakes).

Algorithms:
For rotary indexed mechanism, the device position is computed by this algorithm:
if (offset < 0) offset += npos; pos = 1 + (encpos + offset) % npos ; return pos;
Key features are that all rotary indexed mechanisms we build have fixed positions that are numbered 1-relative (i.e., 1..si_mechanism::NPos, not 0..si_mechanism::NPos-1). A negative offset has to be "wrapped" using the number of positions. You'd be very surprised how long it took to get this right. Modular arithmetic in a rotary (cyclic) mechanism makes everyone's head hurt.

For linear indexed mechanisms, the device position algorithm is much simpler, but easy to mis-code:

pos = 1 + (encpos + offset);
Again, like rotary mechanisms, the logical device positions are always numbered 1..si_mechanism::NPos.


Generated on Mon Jul 19 16:32:37 2004 for Si Microstep Controller API by doxygen 1.3.7