423 lines
20 KiB
C
423 lines
20 KiB
C
/*
|
|
* SmarAct MCS2 programming example: Movement
|
|
*
|
|
* This programming example shows you how to
|
|
* find available MCS2 devices to connect to
|
|
* and how to perform different movement commands.
|
|
*
|
|
* For a full command reference see the MCS2 Programmers Guide.
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include "SmarActControl.h"
|
|
|
|
#if defined(_WIN32)
|
|
#define strtok_r strtok_s
|
|
#endif
|
|
|
|
void calibrate(int8_t channel);
|
|
void findReference(int8_t channel);
|
|
void move(int8_t channel, int32_t moveMode, int8_t direction);
|
|
void stop(int8_t channel);
|
|
|
|
SA_CTL_DeviceHandle_t dHandle;
|
|
|
|
void exitOnError(SA_CTL_Result_t result) {
|
|
if (result != SA_CTL_ERROR_NONE) {
|
|
SA_CTL_Close(dHandle);
|
|
// Passing an error code to "SA_CTL_GetResultInfo" returns a human readable string
|
|
// specifying the error.
|
|
printf("MCS2 %s (error: 0x%04x).\nPress return to exit.\n", SA_CTL_GetResultInfo(result), result);
|
|
getchar();
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void printMenu(void) {
|
|
printf("\n*******************************************************\n");
|
|
printf("WARNING: make sure the positioner can move freely\n \
|
|
without damaging other equipment!\n");
|
|
printf("*******************************************************\n");
|
|
printf("\nEnter command and return:\n");
|
|
printf("[?] print this menu\n");
|
|
printf("[c] calibrate\n");
|
|
printf("[f] find reference\n");
|
|
printf("[+] perform movement in positive direction\n");
|
|
printf("[-] perform movement in negative direction\n");
|
|
printf("[s] stop\n");
|
|
printf("[p] get current position\n");
|
|
printf("[0] set move mode: closed loop absolute move\n");
|
|
printf("[1] set move mode: closed loop relative move\n");
|
|
printf("[2] set move mode: open loop scan absolute*\n");
|
|
printf("[3] set move mode: open loop scan relative*\n");
|
|
printf("[4] set move mode: open loop step*\n");
|
|
printf("[5] set control mode: standard mode*\n");
|
|
printf("[6] set control mode: quiet mode*\n");
|
|
printf(" * not available for Magnetic Driver channels\n");
|
|
printf("[q] quit\n");
|
|
}
|
|
|
|
int main() {
|
|
|
|
SA_CTL_Result_t result;
|
|
|
|
printf("*******************************************************\n");
|
|
printf("* SmarAct MCS2 Programming Example (Movement) *\n");
|
|
printf("*******************************************************\n");
|
|
|
|
// Read the version of the library
|
|
// Note: this is the only function that does not require the library to be initialized.
|
|
const char *version = SA_CTL_GetFullVersionString();
|
|
printf("SmarActCTL library version: %s.\n", version);
|
|
|
|
// MCS2 devices are identified with locator strings.
|
|
// To list all available devices we use the "SA_CTL_FindDevices" function.
|
|
// The returned device list contains the locators of all found MCS2 devices separated by newlines.
|
|
// The "ioDeviceListLen" parameter must contain the size of the passed deviceList buffer and
|
|
// contains the length of the deviceList when the function returns.
|
|
// In case the deviceList size exceed the provided buffer size an "SA_CTL_QUERYBUFFER_SIZE_ERROR" is returned.
|
|
char deviceList[1024];
|
|
size_t ioDeviceListLen = sizeof(deviceList);
|
|
result = SA_CTL_FindDevices("", deviceList, &ioDeviceListLen);
|
|
if (result != SA_CTL_ERROR_NONE) {
|
|
printf("MCS2 failed to find devices.\n");
|
|
}
|
|
exitOnError(result);
|
|
if (strlen(deviceList) == 0) {
|
|
printf("MCS2 no devices found. Exit.\n");
|
|
getchar();
|
|
exit(1);
|
|
} else {
|
|
printf("MCS2 available devices:\n%s\n", deviceList);
|
|
}
|
|
// Open the first MCS2 device from the list
|
|
char *ptr;
|
|
strtok_r(deviceList, "\n",&ptr);
|
|
char *locator = deviceList;
|
|
result = SA_CTL_Open(&dHandle, locator, "");
|
|
if (result != SA_CTL_ERROR_NONE) {
|
|
printf("MCS2 failed to open \"%s\".\n",locator);
|
|
}
|
|
exitOnError(result);
|
|
printf("MCS2 opened \"%s\".\n", locator);
|
|
|
|
// There are three commands which trigger a movement of the positioner:
|
|
// "SA_CTL_Calibrate", "SA_CTL_Reference" and "SA_CTL_Move".
|
|
// The command "SA_CTL_Stop" aborts any ongoing movement.
|
|
|
|
// Note that movement commands are always non-blocking. This means that the function call
|
|
// returns immediately without waiting for the actual movement to complete. In addition,
|
|
// they do not return an error in case the movement can not be processed for some reason,
|
|
// e.g. if there is no positioner connected for the given channel.
|
|
// The channel state can be polled to determine the moment when the movement finished.
|
|
// Alternatively, channel events can be used to receive "commandFinished" events.
|
|
// Both methods are not show in this example. Instead, refer to the further programming examples.
|
|
|
|
// Generally the appropriate movement properties (like mode, velocity, acceleration, etc.)
|
|
// should be set before starting the actual movement.
|
|
|
|
// The following code shows the calibration sequence, the find reference function, closed loop
|
|
// movement and open loop movement.
|
|
// See the corresponding functions "calibrate","findReference" and "move" at the
|
|
// end of this file for more information on the specific properties.
|
|
|
|
// We assume that there is a linear positioner with integrated sensor connected to channel 0.
|
|
int8_t channel = 4;
|
|
int32_t type;
|
|
// We start by setting some general channel properties.
|
|
result = SA_CTL_GetProperty_i32(dHandle, channel, SA_CTL_PKEY_CHANNEL_TYPE, &type, 0);
|
|
exitOnError(result);
|
|
if (type == SA_CTL_STICK_SLIP_PIEZO_DRIVER) {
|
|
// Set max closed loop frequency (maxCLF) to 6 kHz. This properties sets a limit for the maximum actuator driving frequency.
|
|
// The maxCLF is not persistent thus set to a default value at startup.
|
|
result = SA_CTL_SetProperty_i32(dHandle, channel, SA_CTL_PKEY_MAX_CL_FREQUENCY, 6000);
|
|
exitOnError(result);
|
|
// The hold time specifies how long the position is actively held after reaching the target.
|
|
// This property is also not persistent and set to zero by default.
|
|
// A value of 0 deactivates the hold time feature, a value of SA_CTL_INFINITE (0xffffffff) sets the time to infinite.
|
|
// (Until manually stopped by "SA_CTL_Stop") Here we set the hold time to 1000 ms.
|
|
result = SA_CTL_SetProperty_i32(dHandle, channel, SA_CTL_PKEY_HOLD_TIME, 1000);
|
|
exitOnError(result);
|
|
} else if (type == SA_CTL_MAGNETIC_DRIVER) {
|
|
// Enable the amplifier (and start the phasing sequence).
|
|
result = SA_CTL_SetProperty_i32(dHandle, channel, SA_CTL_PKEY_AMPLIFIER_ENABLED, SA_CTL_ENABLED);
|
|
exitOnError(result);
|
|
}
|
|
// The move mode states the type of movement performed when sending the "SA_CTL_Move" command.
|
|
int32_t moveMode = SA_CTL_MOVE_MODE_CL_ABSOLUTE;
|
|
int64_t position;
|
|
char key;
|
|
printMenu();
|
|
do {
|
|
key = getchar();
|
|
switch (key) {
|
|
case 'c':
|
|
calibrate(channel);
|
|
break;
|
|
case 'f':
|
|
findReference(channel);
|
|
break;
|
|
case '+':
|
|
move(channel, moveMode, 0);
|
|
break;
|
|
case '-':
|
|
move(channel, moveMode, 1);
|
|
break;
|
|
case 's':
|
|
stop(channel);
|
|
break;
|
|
case 'p':
|
|
result = SA_CTL_GetProperty_i64(dHandle, channel, SA_CTL_PKEY_POSITION, &position,0);
|
|
exitOnError(result);
|
|
printf("MCS2 position: %ld pm.\n", position);
|
|
break;
|
|
case '0':
|
|
moveMode = SA_CTL_MOVE_MODE_CL_ABSOLUTE;
|
|
result = SA_CTL_SetProperty_i32(dHandle, channel, SA_CTL_PKEY_MOVE_MODE, moveMode);
|
|
exitOnError(result);
|
|
printf("MCS2 set closed-loop absolute move mode\n");
|
|
break;
|
|
case '1':
|
|
moveMode = SA_CTL_MOVE_MODE_CL_RELATIVE;
|
|
result = SA_CTL_SetProperty_i32(dHandle, channel, SA_CTL_PKEY_MOVE_MODE, moveMode);
|
|
exitOnError(result);
|
|
printf("MCS2 set closed-loop relative move mode\n");
|
|
break;
|
|
case '2':
|
|
moveMode = SA_CTL_MOVE_MODE_SCAN_ABSOLUTE;
|
|
result = SA_CTL_SetProperty_i32(dHandle, channel, SA_CTL_PKEY_MOVE_MODE, moveMode);
|
|
exitOnError(result);
|
|
printf("MCS2 set open-loop scan absolute move mode\n");
|
|
break;
|
|
case '3':
|
|
moveMode = SA_CTL_MOVE_MODE_SCAN_RELATIVE;
|
|
result = SA_CTL_SetProperty_i32(dHandle, channel, SA_CTL_PKEY_MOVE_MODE, moveMode);
|
|
exitOnError(result);
|
|
printf("MCS2 set open-loop scan relative move mode\n");
|
|
break;
|
|
case '4':
|
|
moveMode = SA_CTL_MOVE_MODE_STEP;
|
|
result = SA_CTL_SetProperty_i32(dHandle, channel, SA_CTL_PKEY_MOVE_MODE, moveMode);
|
|
exitOnError(result);
|
|
printf("MCS2 set open-loop step move mode\n");
|
|
break;
|
|
case '5':
|
|
// In the "normal" actuator mode the control loop generates actuator driving signals
|
|
// with a maximum frequency limited by the max closed loop frequency (maxCLF).
|
|
result = SA_CTL_SetProperty_i32(dHandle, channel, SA_CTL_PKEY_ACTUATOR_MODE, SA_CTL_ACTUATOR_MODE_NORMAL);
|
|
exitOnError(result);
|
|
printf("MCS2 set normal actuator mode\n");
|
|
break;
|
|
case '6':
|
|
// The "quiet" actuator mode allows positioner movement with minimal noise emission.
|
|
result = SA_CTL_SetProperty_i32(dHandle, channel, SA_CTL_PKEY_ACTUATOR_MODE, SA_CTL_ACTUATOR_MODE_QUIET);
|
|
exitOnError(result);
|
|
printf("MCS2 set quiet actuator mode\n");
|
|
break;
|
|
case 'q':
|
|
break;
|
|
case '?':
|
|
printMenu();
|
|
break;
|
|
}
|
|
} while (key != 'q');
|
|
|
|
// Before closing the program the connection to the device must be closed by calling "SA_CTL_Close".
|
|
SA_CTL_Close(dHandle);
|
|
printf("MCS2 close.\n");
|
|
printf("*******************************************************\n");
|
|
return 0;
|
|
}
|
|
|
|
// CALIBRATION
|
|
// The calibration sequence is used to increase the precision of the position calculation. This function
|
|
// should be called once for each channel if the mechanical setup changes.
|
|
// (e.g. a different positioner was connected to the channel, the positioner type was set to a different type)
|
|
// The calibration data will be saved to non-volatile memory, thus it is not necessary to perform the calibration sequence
|
|
// on each initialization.
|
|
// Note: the "SA_CTL_CH_STATE_BIT_IS_CALIBRATED" in the channel state can be used to determine
|
|
// if valid calibration data is stored for the specific channel.
|
|
|
|
// During the calibration sequence the positioner performs a movement of up to several mm, make sure to not start
|
|
// the calibration near a mechanical endstop in order to ensure proper operation.
|
|
// See the MCS2 Programmers Guide for more information on the calibration.
|
|
void calibrate(int8_t channel) {
|
|
SA_CTL_Result_t result;
|
|
printf("MCS2 start calibration on channel: %d.\n", channel);
|
|
// Set calibration options (start direction: forward)
|
|
result = SA_CTL_SetProperty_i32(dHandle, channel, SA_CTL_PKEY_CALIBRATION_OPTIONS, 0);
|
|
exitOnError(result);
|
|
// Start calibration sequence
|
|
result = SA_CTL_Calibrate(dHandle, channel, 0);
|
|
// Note that the function call returns immediately, without waiting for the movement to complete.
|
|
// The "SA_CTL_CH_STATE_BIT_CALIBRATING" in the channel state can be monitored to determine
|
|
// the end of the calibration sequence.
|
|
exitOnError(result);
|
|
}
|
|
|
|
// FIND REFERENCE
|
|
// Since the position sensors work on an incremental base, the referencing sequence is used to
|
|
// establish an absolute positioner reference for the positioner after system startup.
|
|
// Note: the "SA_CTL_CH_STATE_BIT_IS_REFERENCED" in the channel state can be used to to decide
|
|
// whether it is necessary to perform the referencing sequence.
|
|
void findReference(int8_t channel) {
|
|
SA_CTL_Result_t result;
|
|
printf("MCS2 find reference on channel: %d.\n", channel);
|
|
// Set find reference options.
|
|
// The reference options specify the behaviour of the find reference sequence.
|
|
// The reference flags can be ORed to build the reference options.
|
|
// By default (options = 0) the positioner returns to the position of the reference mark.
|
|
// Note: In contrast to previous controller systems this is not mandatory.
|
|
// The MCS2 controller is able to find the reference position "on-the-fly".
|
|
// See the MCS2 Programmer Guide for a description of the different modes.
|
|
// result = SA_CTL_SetProperty_i32(dHandle, channel, SA_CTL_PKEY_REFERENCING_OPTIONS, 0b00000110);
|
|
// exitOnError(result);
|
|
// Set velocity to 1mm/s
|
|
// result = SA_CTL_SetProperty_i64(dHandle, channel, SA_CTL_PKEY_MOVE_VELOCITY, 2000000000);
|
|
// exitOnError(result);
|
|
// Set acceleration to 10mm/s2.
|
|
// result = SA_CTL_SetProperty_i64(dHandle, channel, SA_CTL_PKEY_MOVE_ACCELERATION, 10000000000);
|
|
// exitOnError(result);
|
|
// Start referencing sequence
|
|
// result = SA_CTL_Reference(dHandle, channel, 0);
|
|
// Note that the function call returns immediately, without waiting for the movement to complete.
|
|
// The "SA_CTL_CH_STATE_BIT_REFERENCING" in the channel state can be monitored to determine
|
|
// the end of the referencing sequence.
|
|
// exitOnError(result);
|
|
|
|
|
|
// result = SA_CTL_SetProperty_i32(dHandle, 2, SA_CTL_PKEY_REFERENCING_OPTIONS, 0b00000000);
|
|
// exitOnError(result);
|
|
// result = SA_CTL_SetProperty_i64(dHandle, 2, SA_CTL_PKEY_MOVE_VELOCITY, 2000000000);
|
|
// exitOnError(result);
|
|
// result = SA_CTL_SetProperty_i64(dHandle, 2, SA_CTL_PKEY_MOVE_ACCELERATION, 10000000000);
|
|
// exitOnError(result);
|
|
// result = SA_CTL_Reference(dHandle, 2, 0);
|
|
// exitOnError(result);
|
|
//
|
|
//
|
|
// result = SA_CTL_SetProperty_i32(dHandle, 5, SA_CTL_PKEY_REFERENCING_OPTIONS, 0b00000000);
|
|
// exitOnError(result);
|
|
// result = SA_CTL_SetProperty_i64(dHandle, 5, SA_CTL_PKEY_MOVE_VELOCITY, 2000000000);
|
|
// exitOnError(result);
|
|
// result = SA_CTL_SetProperty_i64(dHandle, 5, SA_CTL_PKEY_MOVE_ACCELERATION, 10000000000);
|
|
// exitOnError(result);
|
|
// result = SA_CTL_Reference(dHandle, 5, 0);
|
|
// exitOnError(result);
|
|
//
|
|
result = SA_CTL_SetProperty_i32(dHandle, 0, SA_CTL_PKEY_REFERENCING_OPTIONS, 0b00100000);
|
|
exitOnError(result);
|
|
result = SA_CTL_SetProperty_i64(dHandle, 0, SA_CTL_PKEY_MOVE_VELOCITY, 2000000000);
|
|
exitOnError(result);
|
|
result = SA_CTL_SetProperty_i64(dHandle, 0, SA_CTL_PKEY_MOVE_ACCELERATION, 10000000000);
|
|
exitOnError(result);
|
|
result = SA_CTL_Reference(dHandle, 0, 0);
|
|
exitOnError(result);
|
|
|
|
result = SA_CTL_SetProperty_i32(dHandle, 1, SA_CTL_PKEY_REFERENCING_OPTIONS, 0b00100010);
|
|
exitOnError(result);
|
|
result = SA_CTL_SetProperty_i64(dHandle, 1, SA_CTL_PKEY_MOVE_VELOCITY, 2000000000);
|
|
exitOnError(result);
|
|
result = SA_CTL_SetProperty_i64(dHandle, 1, SA_CTL_PKEY_MOVE_ACCELERATION, 10000000000);
|
|
exitOnError(result);
|
|
result = SA_CTL_Reference(dHandle, 1, 0);
|
|
exitOnError(result);
|
|
|
|
}
|
|
|
|
// MOVE
|
|
// The move command instructs a positioner to perform a movement.
|
|
// The given "moveValue" parameter is interpreted according to the previously configured move mode.
|
|
// It can be a position value (in case of closed loop movement mode), a scan value (in case of scan move mode)
|
|
// or a number of steps (in case of step move mode).
|
|
void move(int8_t channel, int32_t moveMode, int8_t direction) {
|
|
SA_CTL_Result_t result;
|
|
int64_t moveValue;
|
|
// Set move mode depending properties for the next movement.
|
|
switch (moveMode) {
|
|
case SA_CTL_MOVE_MODE_CL_ABSOLUTE:
|
|
// Set move velocity [in pm/s].
|
|
result = SA_CTL_SetProperty_i64(dHandle, channel, SA_CTL_PKEY_MOVE_VELOCITY, 1000000000);
|
|
exitOnError(result);
|
|
// Set move acceleration [in pm/s2].
|
|
result = SA_CTL_SetProperty_i64(dHandle, channel, SA_CTL_PKEY_MOVE_ACCELERATION, 1000000000);
|
|
exitOnError(result);
|
|
// Specify absolute position [in pm].
|
|
if (direction) moveValue = -2000000000;
|
|
else moveValue = 1000000000;
|
|
printf("MCS2 move channel %d to absolute position: %ld pm.\n", channel, moveValue);
|
|
break;
|
|
case SA_CTL_MOVE_MODE_CL_RELATIVE:
|
|
// Set move velocity [in pm/s].
|
|
result = SA_CTL_SetProperty_i64(dHandle, channel, SA_CTL_PKEY_MOVE_VELOCITY, 500000000);
|
|
exitOnError(result);
|
|
// Set move acceleration [in pm/s2].
|
|
result = SA_CTL_SetProperty_i64(dHandle, channel, SA_CTL_PKEY_MOVE_ACCELERATION, 10000000000);
|
|
exitOnError(result);
|
|
// Specify relative position distance [in pm] and direction.
|
|
moveValue = 500000000;
|
|
if (direction) moveValue = -moveValue;
|
|
printf("MCS2 move channel %d relative: %ld pm.\n", channel, moveValue);
|
|
break;
|
|
case SA_CTL_MOVE_MODE_SCAN_ABSOLUTE:
|
|
// Set scan velocity [in dac increments/s].
|
|
// Valid range: 1 to 65535000000
|
|
result = SA_CTL_SetProperty_i64(dHandle, channel, SA_CTL_PKEY_SCAN_VELOCITY, (65535*2));
|
|
exitOnError(result);
|
|
// Specify absolute scan target to which to scan to [in dac increments].
|
|
// Valid range: 0 to 65535 corresponding to 0 to 100V piezo voltage
|
|
if (direction) moveValue = 0;
|
|
else moveValue = 65535;
|
|
printf("MCS2 scan channel %d absolute to: %ld.\n", channel, moveValue);
|
|
break;
|
|
case SA_CTL_MOVE_MODE_SCAN_RELATIVE:
|
|
// Set scan velocity [in dac increments/s].
|
|
result = SA_CTL_SetProperty_i64(dHandle, channel, SA_CTL_PKEY_SCAN_VELOCITY, 65535);
|
|
exitOnError(result);
|
|
// Specify relative scan target and direction to which to scan to [in dac increments].
|
|
// Valid range: -65535 to 65535 corresponding to 0 to 100V piezo voltage
|
|
// If the resulting absolute scan target exceeds the valid range the scan movement will stop at the boundary.
|
|
moveValue = 65535;
|
|
if (direction) moveValue = -moveValue;
|
|
printf("MCS2 scan channel %d relative: %ld.\n", channel, moveValue);
|
|
break;
|
|
case SA_CTL_MOVE_MODE_STEP:
|
|
// Set step frequency [in Hz].
|
|
// Valid range: 1 to 20000 Hz
|
|
result = SA_CTL_SetProperty_i32(dHandle, channel, SA_CTL_PKEY_STEP_FREQUENCY, 1000);
|
|
exitOnError(result);
|
|
// Set maximum step amplitude [in dac increments].
|
|
// valid range: 0 to 65535 corresponding to 0 to 100V piezo voltage
|
|
// Lower amplitude values result in smaller step width.
|
|
result = SA_CTL_SetProperty_i32(dHandle, channel, SA_CTL_PKEY_STEP_AMPLITUDE, 65535);
|
|
exitOnError(result);
|
|
// Specify the number of steps to perform and the direction.
|
|
moveValue = 500;
|
|
if (direction) moveValue = -moveValue;
|
|
printf("MCS2 open loop step move, channel %d, steps: %ld.\n", channel, moveValue);
|
|
break;
|
|
}
|
|
// Start actual movement.
|
|
result = SA_CTL_Move(dHandle, channel, moveValue, 0);
|
|
// Note that the function call returns immediately, without waiting for the movement to complete.
|
|
// The "SA_CTL_CH_STATE_BIT_ACTIVELY_MOVING" (and "SA_CTL_CH_STATE_BIT_CLOSED_LOOP_ACTIVE") in the channel state
|
|
// can be monitored to determine the end of the movement.
|
|
exitOnError(result);
|
|
}
|
|
|
|
// STOP
|
|
// This command stops any ongoing movement. It also stops the hold position feature of a closed loop command.
|
|
// Note for closed loop movements with acceleration control enabled:
|
|
// The first "stop" command sent while moving triggers the positioner to come to a halt by decelerating to zero.
|
|
// A second "stop" command triggers a hard stop ("emergency stop").
|
|
void stop(int8_t channel) {
|
|
SA_CTL_Result_t result;
|
|
printf("MCS2 stop channel: %d.\n", channel);
|
|
result = SA_CTL_Stop(dHandle, channel, 0);
|
|
exitOnError(result);
|
|
}
|