Triamec > Produkte > Software > TAM Tama

TAM Tama – Echtzeit-Programmierung

Die Triamec Servo-Drives, I/O-Controller und Adapter sind für kundenspezifische Anwendungen frei programmierbar. Dazu stehen zwei Tasks zur Verfügung:

  • Isochroner Task im 10kHz Takt
  • Asynchroner Task
TAMA auf Triamec Produkten
TAMA Programmierung auf Triamec Produkten - auf dem Host-PC können auch Echtzeit-Umgebungen statt des .NET-Frameworks eingesetzt werden.

Für die Programmierung kann der Anwender eine Sprache aus dem Microsoft .NET-Framework wählen: C#, C++, J# oder VisualBasic. Entwickelt wird mit Microsoft Visual Studio, das den Code in die Zwischensprache CIL übersetzt. Auf den Triamec Geräten ist eine Virtuelle Maschine (TamaVM) installiert, die diese genormte Sprache interpretiert und in Echtzeit ausführt.

Ein Tama-Programm kann zur Laufzeit in die Geräte geladen und danach zur Ausführung gebracht werden. Für eigenständige Applikationen ohne PC-Verbin­dung können Tama-Programme auch in den Geräten persistent gespeichert werden. Tama-Programme haben Zugriff auf alle Register des ausführenden Gerätes sowie auf zyklisch übermittelte Koppeldaten anderer Geräte.

Eigenschaften von TAMA Programmen:

  • Robuste Laufzeitumgebung: Virtuelle Maschine im Gerät ("Absturzsicher")
  • Zwei Tasks: Ein 10kHz Echtzeit- und ein asynchroner Task
  • Harte Echtzeit im 10kHz-Takt
  • Strenge Typ-Sicherheit
  • Datentypen: int, float, bool, enum, struct, object, array
  • Zugriff auf Geräte-Register mit IntelliSense-Funktion von Visual Studio
  • Mathematische Funktionen
  • Interaktion mit PC-Anwendung oder Tama-Programmen auf anderen Geräten
  • Neue Programme können zur Laufzeit geladen und ausgeführt werden
  • Persistenz erlaubt autonomen Betrieb ohne PC

Typische Anwendungen

  • Homing Sequenzen, Anschlag-Erkennung
  • Achskopplungen (z.B. Portale)
  • Kinematik-Berechnungen (z.B. Parallel-Kinematik eines Delta-Roboters)
  • Ablaufsteuerungen für einfache Stand-alone Anwendungen (z.B. Kraftgeführte Presse)
  • Dual Loop Regelungen (auch zusammen mit anderen Servo-Drives)
  • Parameter-Adaption (für die Regelung, Gain-Scheduling)
  • Überwachungsfunktionen und andere Echtzeit­reaktionen der Maschine

Tama Programm Entwickung

Tama
Tama Programm Entwicklung in VisualStudio

Tama Programme können in jeder Microsoft® .NET Sprache (Visual C#, Visual C++, Visual J# und Visual Basic) mit Microsoft Visual Studio® entwickelt werden. Tama Programme lassen sich auf allen Triamec Servo-Drives, I/O Controllern und auf Adaptern mit Prozessor ausführen.

Microsoft® stellt das Visual Studio® mit verschiedenen Sprachen in den Express-Editionen gratis zur Verfügung. Die Installation kann hier bezogen werden.

Installation

Der Tama Compiler und Beispielprogramme werden mit dem TAM SDK auf dem PC installiert.

Tama Beispielprogramme

In der nachfolgenden Sektion können Sie drei Tama Beispielprogramme anschauen und einen Einblick in die Tama-Programmierung erhalten. Weitere Beispiele und der Tama Compiler werden mit dem TAM SDK installiert.

Beispiel 1: Homing mit asynchronem Task. (einfache Suche des Encoder-Index, SetPosition, Move)
Beispiel 2: Homing Standalone mit asynchronem und isochronem Task. (DriveControl, Move, ...) 
Beispiel 3: Timers mit asynchronem und isochronem Task

  • Tama Homing
  • Tama Homing Standalone
  • Tama Timers

Beispielprogramm Homing

#region Fileheader
//
// $Id: Homing.tama.cs 14422 2012-12-20 08:09:49Z ab $
// 
// Copyright © 2012 Triamec Motion AG
// 
#endregion Fileheader

/* Overview
 * --------
 * 
 * This is the source code for the Tama homing programs shipped with the TAM SDK (in the RC subdirectory of the 
 * installation).
 * The axis module offers homing support, when the "Omit homing" parameter is set to false and an appropriate homing
 * Tama program is loaded. After enabling the axis, the homing button will trigger the StartHoming command, and the
 * axis GUI will reflect start and end of the homing sequence. It's posible to override the default search speed, 
 * the homing direction and the index logic. 
 * - The parameter Register.Axes.Axes[0].Parameters.Environment.ReferencePosition will be set at the found index position. 
 * - It is possible to override the 'move speed to use during the homing moves' within Register.Tama.Variables.GenPurposeVar0.
 *   - The default value is 0.1 m/s or rad/s 
 * - It is possible to override the 'move homing direction to use during the homing moves' within Register.Tama.Variables.GenPurposeIntVar0.
 *   - GenPurposeIntVar0 = 0 -> homeDirectionPositive = false -> searches backwards (default)
 *   - GenPurposeIntVar0 > 0 -> homeDirectionPositive = true  -> searches forwards
 * - It is possible to override the 'index Logic definition' within Register.Tama.Variables.GenPurposeIntVar1.
 *   - GenPurposeIntVar1 = 0 -> indexLogicNegative = false -> positive logic (default)
 *   - GenPurposeIntVar1 > 0 -> indexLogicNegative = true  -> negative logic
 * 
 * Further more, during homing, the Stop button will trigger the Stop command.
 * 
 * This file can be used as a basis for custom extensions.
 * 
 * 
 * Support for different register layouts
 * --------------------------------------
 * 
 * In order to support different register layouts with the same lines of code, conditional compilation symbols have been
 * defined. Instead of the usual Debug/Release project and solution configurations, the project defines configurations
 * referring to the different supported register layouts. Using solution batch build, all homing programs may be
 * compiled in one pass.
 * 
 * 
 * Shortcomings
 * ------------
 *
 * Some register layouts (5, 6 and 16), support two axes, while the presented homing program always operates on the
 * first axis. Using an #if conditional directive in every call to an axis register would introduce too much clutter in
 * most cases. Therefore, in order to support the second axis, two versions of homing programs need to be maintained.
 * In order to support both axes simultaneously, the program needed to be refactored accordingle, i.e. additional
 * states and commands.
 * 
 */

using Triamec.TriaLink;
using Triamec.Tam.Modules;
using Triamec.Tama.Vmid5;
#if RLID4
using Triamec.Tama.Rlid4;
#elif RLID5
using Triamec.Tama.Rlid5;
#elif RLID6
using Triamec.Tama.Rlid6;
#elif RLID16
using Triamec.Tama.Rlid16;
#endif

/// <summary>
/// Tama program for the
/// axis of the module catalog.
/// </summary>
[Tama]
static class
#if RLID4
 Homing04
#elif RLID5
 Homing05
#elif RLID6
 Homing06
#elif RLID16
 Homing16
#endif
 {
	#region Fields
	/// <summary>
	/// The next state to set after move is done.
	/// </summary>
	static TamaState stateAfterMoveDone;

	/// <summary>
	/// The original drf to set after homing and touchdown search.
	/// </summary>
	private static float originalDrf;

	/// <summary>
	/// The move speed to use during the homing moves. [m/s or red/s]
	/// It is possible to override this parameter within Register.Tama.Variables.GenPurposeVar0.
	/// The default value is 0.1 m/s or red/s 
	/// </summary>
	private static float searchSpeed;
	private static float searchSpeedDefault = 0.1f;

	/// <summary>
	/// The homing move direction to use during the homing moves. ­
	/// It is possible to override this parameter within Register.Tama.Variables.GenPurposeIntVar0.
	/// The default value is false 
	/// </summary>
	private static bool homeDirectionPositive;

	/// <summary>
	/// The index Logic definition. ­
	/// indexLogicNegative = false -> positive logic (default)
	/// indexLogicNegative = true  -> negative logic
	/// It is possible to override this parameter within Register.Tama.Variables.GenPurposeIntVar1.
	/// </summary>
	private static bool indexLogicNegative;

	
	/// <summary>
	/// The detected index position [rad]
	/// </summary>
	private static float indexPosition;
	private static int indexPositionExtension;

	#endregion Fields

	#region Asynchronous Tama main application
	/// <summary>
	/// The cyclic task.
	/// </summary>
	[TamaTask(Task.AsynchronousMain)]
	static void Homer() {
		#region State machine
		switch ((TamaState)Register.Tama.AsynchronousMainState) {
			#region state idle -> wait for command to execute
			case TamaState.Idle:
				switch ((TamaCommand)Register.Tama.AsynchronousMainCommand) {
					case TamaCommand.StartHoming:
						// homing with search of index requested

						// get the search dynamic parameters and saves the actual DRF value 
						// --------------------------------------------------------------------
						GetSearchDynamicParameters();

						// switch to state 'CheckForHomingAction'
						Register.Tama.AsynchronousMainState = (int)TamaState.CheckForHomingAction;
						// reset the tama command 
						Register.Tama.AsynchronousMainCommand = (int)TamaCommand.NoCommand;

						break;
				}
				break;
			#endregion state idle -> wait for command to execute


			#region state CheckForHomingAction -> checks for needed homing action
			case TamaState.CheckForHomingAction:
				// check for stop command
				if (!stopRequestedOrError()) {
					// homing with search of index requested
					// check if axis is at index initial
					if (isAxisAtIndex()) {
						// axis is at index initial 
						// -> move away from index within velocity move in opposite home direction 
						startSearchMove(false);

						// switch to WaitIndexCleared state
						Register.Tama.AsynchronousMainState = (int)TamaState.WaitIndexCleared;
					} else {
						// axis is not at index
						// enable index latching
						Register.Axes_0.Commands.General.EncoderIndexLatchEnable = true;
								
						// -> start searching index within velocity move in home direction
						startSearchMove(true);
						
						// switch to SearchLatch state
						Register.Tama.AsynchronousMainState = (int)TamaState.SearchIndex;
					}
				}
				break;
			#endregion state CheckForHomingAction -> checks for needed homing action


			#region state wait index cleared state -> move till index is cleared and watch stop command
			case TamaState.WaitIndexCleared:
				// check for stop command
				if (!stopRequestedOrError()) {
					// check if index is cleared	
					if (!isAxisAtIndex()) {
						// index cleared -> stop axis fast (stops axis near index position)
						Register.Axes_0.Commands.PathPlanner.Command = PathPlannerCommand.Stop;

						// switch to state 'wait for move done' and 
						// set state after move done to CheckForHomingAction
						Register.Tama.AsynchronousMainState = (int)TamaState.WaitMoveDone;
						stateAfterMoveDone = TamaState.CheckForHomingAction;

					} else {
						// latch still not cleared -> stay in wait state 
					}
				}
				break;
			#endregion state wait index cleared state -> move till index is cleared and watch stop command


			#region state search index -> search index and watch stop command
			case TamaState.SearchIndex:
				// check for stop command
				if (!stopRequestedOrError()) {
					// watch index latch and stop  
					// --------------------------------------------------------------------
					if (indexLatched()) {
						// at index -> safe index position
						indexPosition = Register.Axes_0.Signals.PositionController.LatchedPosition.Float32;
						indexPositionExtension = Register.Axes_0.Signals.PositionController.LatchedPosition.Extension;

						// drive axis fast to position at index event (stops axis at index position)
						Register.Axes_0.Commands.PathPlanner.Xnew.Float32 = indexPosition;
						Register.Axes_0.Commands.PathPlanner.Xnew.Extension = indexPositionExtension;
						Register.Axes_0.Commands.PathPlanner.Command = PathPlannerCommand.MoveAbsolute;

						// switch to state 'wait for move done' and 
						// set state after move done to setPosition
						// --------------------------------------------------------------------
						Register.Tama.AsynchronousMainState = (int)TamaState.WaitMoveDone;
						stateAfterMoveDone = TamaState.SetPosition;
					}
				}
				break;
			#endregion state search index -> start search move, search index and watch stop command


			#region state SetPosition -> check for consistency, set new PathPlanner position and controller position
			case TamaState.SetPosition:
				// set new axis position to provided reference position value.
				// rem: referencePosition is the position of the index 
				Register.Axes_0.Commands.PathPlanner.Xnew.Float32 = Register.Axes_0.Parameters.Environment.ReferencePosition;
				Register.Axes_0.Commands.PathPlanner.Xnew.Extension = 0;
				// perform setPosition command 	
				Register.Axes_0.Commands.PathPlanner.Command = PathPlannerCommand.SetPosition;

				// set state to waitPosSet
				Register.Tama.AsynchronousMainState = (int)TamaState.WaitPositionSet;
				break;
			#endregion state SetPosition -> set new PathPlanner position and controller position


			#region state WaitPositionSet -> wait until new position is set
			case TamaState.WaitPositionSet:
				// check for origin translation is done 
				// --------------------------------------------------------------------
				if (!Register.Axes_0.Commands.OriginTranslation.TranslatePositions) {
					// move axis to position 0.0
					Register.Axes_0.Commands.PathPlanner.Xnew.Float32 = 0.0f;
					Register.Axes_0.Commands.PathPlanner.Xnew.Extension = 0;
					Register.Axes_0.Commands.PathPlanner.Command = PathPlannerCommand.MoveAbsolute;

					// switch to state 'wait for move done' and 
					// set state after move done to idle
					Register.Tama.AsynchronousMainState = (int)TamaState.WaitMoveDone;
					stateAfterMoveDone = TamaState.Idle;
				}
				break;
			#endregion state WaitPositionSet -> wait until new position is set


			#region state waitMoveDone -> wait for move is done
			case TamaState.WaitMoveDone:
				// check for done 
				// --------------------------------------------------------------------
				if (Register.Axes_0.Signals.PathPlanner.Done) {
					if (stateAfterMoveDone == TamaState.Idle) {
						// restore the Drf and velocity settings
						Register.Axes_0.Parameters.PathPlanner.DynamicReductionFactor = originalDrf;
						Register.Axes_0.Commands.PathPlanner.CommitParameter = true;
					}
					// switch to requested state after move done 
					Register.Tama.AsynchronousMainState = (int)stateAfterMoveDone;
				} else {
					if (!stopRequestedOrError()) { }
				}
				break;
			#endregion state waitMoveDone -> wait for move is done


		}
		#endregion State machine
	}
	#endregion Asynchronous Tama main application


	#region local methods of asynchronous Tama main application

	/// <summary>
	/// checks for stop request or axis error
	/// - performs stop actions and sets the state to WaitMoveDone and nextState to idle
	///   returns true if stop is requested; false otherwise
	/// - checks for error conditions.
	///   - reset orig DRF in case of error, sets nextState to motionError and returns true;
	///   - returns false in no error cases
	/// </summary>
	static bool stopRequestedOrError() {
		if ((TamaCommand)Register.Tama.AsynchronousMainCommand == TamaCommand.Stop) {
			// stop is requested, reset the tama command 
			Register.Tama.AsynchronousMainCommand = (int)TamaCommand.NoCommand;

			// fast stop axis
			Register.Axes_0.Commands.PathPlanner.Command = PathPlannerCommand.Stop;

			// switch to state 'wait for move done' and 
			// set state after move done to idle
			Register.Tama.AsynchronousMainState = (int)TamaState.WaitMoveDone;
			stateAfterMoveDone = TamaState.Idle;
			return true;
		}
		// check for axis error
		if (Register.Axes_0.Signals.General.AxisError != AxisErrorIdentification.None) {
			// any axis error occurred
			// restore the Drf and velocity settings
			Register.Axes_0.Parameters.PathPlanner.DynamicReductionFactor = originalDrf;
			Register.Axes_0.Commands.PathPlanner.CommitParameter = true;
			// immediately set state to error 
			Register.Tama.AsynchronousMainState = (int)TamaState.Idle;
			// reset the tama command 
			Register.Tama.AsynchronousMainCommand = (int)TamaCommand.NoCommand;
			return true;
		}
		return false;
	}

	
	/// <summary>
	/// GetSearchDynamicParameters
	/// gets the search speeed parameter and saves the actual DRF value. 
	/// </summary>
	static void GetSearchDynamicParameters() {
		// get override search speed value from register, if value is > 0.0 
		// --------------------------------------------------------------------
		if (Register.Tama.Variables.GenPurposeVar0 > 0.0f) {
			searchSpeed = Register.Tama.Variables.GenPurposeVar0;
		} else {
			// take the default value
			searchSpeed = searchSpeedDefault;
		}
		// show actual search speed in register
		Register.Tama.Variables.GenPurposeVar0 = searchSpeed;

		// get override homing direction value from register, if value is > 0 
		// --------------------------------------------------------------------
		if (Register.Tama.Variables.GenPurposeIntVar0 > 0) {
			homeDirectionPositive = Register.Tama.Variables.GenPurposeIntVar0 > 0 ? true: false;
		} else {
			// take the default value
			homeDirectionPositive = false;
		}
		// show actual homeDirection in register
		Register.Tama.Variables.GenPurposeIntVar0 = homeDirectionPositive ? 1: 0;
		
		// get override index logic value from register, if value is > 0 
		// --------------------------------------------------------------------
		if (Register.Tama.Variables.GenPurposeIntVar1 > 0) {
			indexLogicNegative = Register.Tama.Variables.GenPurposeIntVar1 > 0 ? true : false;
		} else {
			// take the default value
			indexLogicNegative = false;
		}
		// show actual index logic in register
		Register.Tama.Variables.GenPurposeIntVar1 = indexLogicNegative ? 1 : 0;


		// disable index latching (default state)
		// --------------------------------------------------------------------
		Register.Axes_0.Commands.General.EncoderIndexLatchEnable = false;

		// safe the orig DRF
		// --------------------------------------------------------------------
		originalDrf = Register.Axes_0.Parameters.PathPlanner.DynamicReductionFactor;
		// set DRF for the search moves 
		Register.Axes_0.Parameters.PathPlanner.DynamicReductionFactor = 1.0f;
		Register.Axes_0.Commands.PathPlanner.CommitParameter = true;
	}


	/// <summary>
	/// startSearchMove
	/// sets the searchSpeed depending on parameters moveInHomeDirection and HomeDirectionPositive.
	/// starts the velocity move afterwards. 
	/// <param name="moveInHomeDirection">moves in home direction if value is true.</param>
	/// </summary>
	static void startSearchMove(bool moveInHomeDirection) {
		// start searching within velocity move with defined search speed
		if (homeDirectionPositive) {
			Register.Axes_0.Commands.PathPlanner.Vnew = moveInHomeDirection ? searchSpeed : -searchSpeed;
		} else {
			Register.Axes_0.Commands.PathPlanner.Vnew = moveInHomeDirection ? -searchSpeed : searchSpeed;
		}
		Register.Axes_0.Commands.PathPlanner.Direction = PathPlannerDirection.Current;
		Register.Axes_0.Commands.PathPlanner.Command = PathPlannerCommand.MoveVelocity;
	}


	/// <summary>
	/// isAxisAtIndex
	/// Reads the EncoderIndex register and returns true if axis is at index; returns false otherwise
	/// </summary>
	static bool isAxisAtIndex() {
		// get actual encoderIndex state
		if (indexLogicNegative) {
			 //(bit reset == at index)
			return !Register.Axes_0.Signals.General.EncoderIndex;
		} else {
			 //(bit set == at index)
			return Register.Axes_0.Signals.General.EncoderIndex;
		}
	}

	/// <summary>
	/// indexLatched
	/// Reads the EncoderIndex latch register and returns true if index latch has triggered; returns false otherwise
	/// </summary>
	static bool indexLatched() {
		// get actual encoderIndex latch state (bit set == latched)
		return Register.Axes_0.Signals.General.EncoderIndexLatch;
	}

	#endregion local methods of asynchronous Tama main application

}

Beispielprogramm Homing Standalone

#region Fileheader
//
// $Id: StandaloneHomingAndEndswitches.tama.cs 15871 2013-11-07 09:28:41Z chm $
// 
// Copyright © 2007 Triamec AG
// 
#endregion Fileheader

/* Overview
 * --------
 * 
 * input signals:
 * 
 * din(0) = enable
 * din(1) = home
 * din(2) = move
 * 
 * din(3) = position limit in negative direction reached
 * din(4) = position limit in positive direction reached
 * 
 * din(5) = side
 * 
 * output signals:
 * 
 * dout(1) = 0        -> no error pending
 * dout(1) = 1        -> error pending
 * 
 * dout(2) = 0        -> no action
 * dout(2) = 1        -> standstill 
 * dout(2) = blinking -> moving 
 * 
 * The Triamec I/O Helper is helpful for testing the program.
 * 
 */

using Triamec.Tam.Samples;
using Triamec.Tama.Rlid4;
using Triamec.Tama.Vmid5;
using Triamec.TriaLink;

[Tama]
static class StandaloneHomingAndEndswitches {
	#region Enumerations
	enum State {
		Disabled,
		Enabled,
		Idle,
		MovingToMiddlePositive,
		MovingToMiddleNegative,
		MovingToReferenceStart,
		MovingToReference,
		MovingToHome,
		MovingToPosition1,
		MovingToPosition2,
	}
	#endregion Enumerations

	#region Constants

	// homing velocities [m/s] or [rad/s]
	const float cVelocityToMiddle = 10f;
	const float cVelocityToReference = 1f;

	// homing specific positions
	const float cRelativeReferenceStartPosition = 0.001f;
	const float cDesiredReferencePosition = 0;
	const float cHomePosition = 0;
	const float cPosition1 = -3;
	const float cPosition2 = 3;

	// blinking duration, in seconds
	const float duration = 0.5f;

	#endregion Constants

	#region Fields

	// digital signals
	static bool commitError = true;
	static State state;

	#endregion Fields

	#region Constructor
	/// <summary>
	/// Initializes the <see cref="StandaloneHomingAndEndswitches"/> class.
	/// </summary>
	static StandaloneHomingAndEndswitches() {
		blinkTimer.Start(duration, Register.General.Signals.TriaLinkTimestamp);
	}
	#endregion Constructor

	#region user functions

	static AsynchronousTimer blinkTimer = new AsynchronousTimer();
	static bool blinkFlag;

	static bool Blink() {
		if (blinkTimer.Elapsed(Register.General.Signals.TriaLinkTimestamp)) {
			blinkFlag = !blinkFlag;
			blinkTimer.Start(duration, Register.General.Signals.TriaLinkTimestamp);
		}
		return blinkFlag;
	}

	static bool GetDigitalIn(int channel) {
		return ((Register.General.Signals.InputBits & (1 << channel)) != 0);
	}

	static bool EnableDrive(bool enable) {
		bool done = false;

		if (enable) {
			if (Register.General.Signals.DriveState != DeviceState.Operational) {
				// switch the drive on
				Register.General.Commands.Internals.Event = DeviceEvent.SwitchOn;
			} else {
				// startup system
				switch (Register.Axes_0.Signals.General.AxisState) {

					case AxisState.Disabled:
						if (commitError) {
							// proforma clear axis error
							Register.Axes_0.Commands.General.Event = AxisEvent.ResetError;
							commitError = false;
						} else {
							// enable axis
							Register.Axes_0.Commands.General.Event = AxisEvent.EnableAxis;
						}
						break;

					case AxisState.Startup:
					case AxisState.Enabling:
					case AxisState.ErrorStopping:
						break;

					default:
						// drive is enabled
						done = true;
						break;
				}
			}
		} else {
			// force an axis error reset
			// when enabling the drive next time
			commitError = true;

			if (Register.General.Signals.DriveState == DeviceState.FaultPending) {
				
				// commit pending drive error
				Register.General.Commands.Internals.Event = DeviceEvent.ResetFault;
			} else {
				// shutdown drive
				switch (Register.Axes_0.Signals.General.AxisState) {

					case AxisState.ContinuousMotion:
						// stop axis
						Register.Axes_0.Commands.PathPlanner.Command = PathPlannerCommand.Stop;
						break;

					case AxisState.Stopping:
						// complete stopping (nothing to do)
						break;

					case AxisState.Standstill:
						// standstill reached, disable axis
						Register.Axes_0.Commands.General.Event = AxisEvent.DisableAxis;
						break;

					case AxisState.Disabled:
						// switch off drive
						Register.General.Commands.Internals.Event = DeviceEvent.SwitchOff;
						break;

					case AxisState.Startup:

						switch (Register.General.Signals.DriveState) {
							case DeviceState.Operational:
								// errogenous state
								Register.General.Commands.Internals.Event = DeviceEvent.SwitchOff;
								break;

							default:
								break;

						}
						break;

					default:
						// all other states are directly disabled
						Register.Axes_0.Commands.General.Event = AxisEvent.DisableAxis;
						break;

				}
			}
		}

		return done;

	}

	#endregion user functions

	#region Standalone application
	/// <summary>
	/// The standalone application.
	/// </summary>
	[TamaTask(Task.AsynchronousMain)]
	public static void StandAloneApplication() {

		// digital inputs
		bool enable = GetDigitalIn(0);
		bool home = GetDigitalIn(1);
		bool move = GetDigitalIn(2);
		bool side = GetDigitalIn(5);

		// error handling
		bool axisError = Register.Axes_0.Signals.General.AxisError != AxisErrorIdentification.None;
		bool driveError = (DeviceErrorIdentification)Register.General.Signals.DriveError != DeviceErrorIdentification.None;

		// leds
		Register.General.Commands.Output1 = (axisError && !commitError) || driveError;
		Register.General.Commands.Output2 = (state == State.Idle) || ((state > State.Idle) && Blink());


		if (EnableDrive(enable)) {

			// Drive is enabled
			switch (state) {
				case State.Disabled:
					state = State.Enabled;
					break;

				case State.Enabled:
					if (home) {
						Register.Axes_0.Commands.PathPlanner.Direction = PathPlannerDirection.Current;
						Register.Axes_0.Commands.General.EncoderIndexLatchEnable = false;
						if (side) {
							Register.Axes_0.Commands.PathPlanner.Vnew = cVelocityToMiddle;
							state = State.MovingToMiddlePositive;
						} else {
							Register.Axes_0.Commands.PathPlanner.Vnew = -cVelocityToMiddle;
							state = State.MovingToMiddleNegative;
						}
						Register.Axes_0.Commands.PathPlanner.Command = PathPlannerCommand.MoveVelocity;
					}
					break;

				case State.MovingToMiddleNegative:
					if (side) {

						// middle found -> move to the start of the reference position
						Register.Axes_0.Commands.PathPlanner.Xnew.Float32 =
							Register.Axes_0.Signals.PathPlanner.Position.Float32 +
							cRelativeReferenceStartPosition;
						Register.Axes_0.Commands.PathPlanner.Command = PathPlannerCommand.MoveAbsolute_Vel;
						state = State.MovingToReferenceStart;
					}
					break;

				case State.MovingToMiddlePositive:
					if (!side) {

						// middle found -> move to the start of the reference position
						Register.Axes_0.Commands.PathPlanner.Xnew.Float32 =
							Register.Axes_0.Signals.PathPlanner.Position.Float32 +
							cRelativeReferenceStartPosition;
						Register.Axes_0.Commands.PathPlanner.Command = PathPlannerCommand.MoveAbsolute_Vel;
						state = State.MovingToReferenceStart;
					}
					break;

				case State.MovingToReferenceStart:
					if (Register.Axes_0.Signals.PathPlanner.Done) {

						// start search of the encoder reference
						Register.Axes_0.Commands.PathPlanner.Vnew = cVelocityToReference;
						Register.Axes_0.Commands.General.EncoderIndexLatchEnable = true;
						Register.Axes_0.Commands.PathPlanner.Direction = PathPlannerDirection.Negative;
						Register.Axes_0.Commands.PathPlanner.Command = PathPlannerCommand.MoveVelocity;
						state = State.MovingToReference;
					}
					break;

				case State.MovingToReference:

					// the DSP accepted the latch command
					if (Register.Axes_0.Signals.General.EncoderIndexLatch == true) {

						// reference found -> move to home position
						Register.Axes_0.Commands.PathPlanner.Xnew.Float32 =
							cHomePosition + Register.Axes_0.Signals.PositionController.LatchedPosition.Float32 -
							cDesiredReferencePosition;
						Register.Axes_0.Commands.PathPlanner.Command = PathPlannerCommand.MoveAbsolute;
						state = State.MovingToHome;
					}
					break;

				case State.MovingToHome:
					if (Register.Axes_0.Signals.PathPlanner.Done) {

						// home reached -> set the new position reference
						Register.Axes_0.Commands.PathPlanner.Xnew.Float32 = cHomePosition;
						Register.Axes_0.Commands.PathPlanner.Command = PathPlannerCommand.SetPosition;
						state = State.Idle;
					}
					break;

				case State.Idle:
					if (move) {

						// go to position 1
						state = State.MovingToPosition1;
						Register.Axes_0.Commands.PathPlanner.Xnew.Float32 = cPosition1;
						Register.Axes_0.Commands.PathPlanner.Command = PathPlannerCommand.MoveAbsolute;
					}
					break;

				case State.MovingToPosition1:
					if (!move) {

						// go to home position
						state = State.Idle;
						Register.Axes_0.Commands.PathPlanner.Xnew.Float32 = cHomePosition;
						Register.Axes_0.Commands.PathPlanner.Command = PathPlannerCommand.MoveAbsolute;
					} else {
						if (Register.Axes_0.Signals.PathPlanner.Done) {

							// go to position 2
							state = State.MovingToPosition2;
							Register.Axes_0.Commands.PathPlanner.Xnew.Float32 = cPosition2;
							Register.Axes_0.Commands.PathPlanner.Command = PathPlannerCommand.MoveAbsolute;
						}
					}
					break;

				case State.MovingToPosition2:
					if (!move) {

						// go to home position
						state = State.Idle;
						Register.Axes_0.Commands.PathPlanner.Xnew.Float32 = cHomePosition;
						Register.Axes_0.Commands.PathPlanner.Command = PathPlannerCommand.MoveAbsolute;
					} else {
						if (Register.Axes_0.Signals.PathPlanner.Done) {

							// go to position 1
							state = State.MovingToPosition1;
							Register.Axes_0.Commands.PathPlanner.Xnew.Float32 = cPosition1;
							Register.Axes_0.Commands.PathPlanner.Command = PathPlannerCommand.MoveAbsolute;
						}
					}
					break;
			}
		} else {
			// Drive is not ready
			state = State.Disabled;
		}

		Register.Tama.AsynchronousMainState = (int)state;
	}
	#endregion Standalone application

	#region Endpoint detection
	/// <summary>
	/// Stops the drive depending on two end switches.
	/// </summary>
	[TamaTask(Task.IsochronousMain)]
	static void DetectEndpoints() {
		bool lowLimit = GetDigitalIn(3);
		bool highLimit = GetDigitalIn(4);

		AxisState axisState = Register.Axes_0.Signals.General.AxisState;

		// Let the path planner execute a commanded error stop
		if (axisState == AxisState.ErrorStopping) return;

		if (axisState > AxisState.Standstill) {
			// When moving...

			// Issue a full dynamics stop when lower limit is reached and moving in negative direction, or
			// when higher limit is reached and moving in positive direction.
			if ((lowLimit && (Register.Axes_0.Signals.PathPlanner.Velocity < 0f)) ||
				(highLimit && (Register.Axes_0.Signals.PathPlanner.Velocity > 0f))) {

				Register.Axes_0.Commands.PathPlanner.Command = PathPlannerCommand.EmergencyStop;
			}
		} else {
			// When stand still...

			switch (Register.Axes_0.Commands.PathPlanner.Command) {
				case PathPlannerCommand.MoveAbsolute:
				case PathPlannerCommand.MoveAbsolute_Vel:
				case PathPlannerCommand.MoveAbsolute_VelAcc:

					// Prohibit moving in negative direction in the lower limit,
					// and prohibit moving in positive direction in the higher limit.
					if ((lowLimit && (Register.Axes_0.Commands.PathPlanner.Xnew.Float32 < Register.Axes_0.Signals.PositionController.ActualPosition.Float32)) ||
						(highLimit && (Register.Axes_0.Commands.PathPlanner.Xnew.Float32 > Register.Axes_0.Signals.PositionController.ActualPosition.Float32))) {

						Register.Axes_0.Commands.PathPlanner.Command = PathPlannerCommand.NoCommand;
					}
					break;

				case PathPlannerCommand.MoveDirectCoupled:
				case PathPlannerCommand.MoveCoupled:

					if (lowLimit || highLimit) {

						// Coupling is prohibited.
						Register.Axes_0.Commands.PathPlanner.Command = PathPlannerCommand.NoCommand;
					}
					break;

				case PathPlannerCommand.MoveVelocity:
				case PathPlannerCommand.MoveVelocity_Acc:

					// Prohibit moving in negative direction in the lower limit,
					// and prohibit moving in positive direction in the higher limit.
					if ((lowLimit && (Register.Axes_0.Commands.PathPlanner.Vnew < 0f)) ||
						(highLimit && (Register.Axes_0.Commands.PathPlanner.Vnew > 0f))) {

						Register.Axes_0.Commands.PathPlanner.Command = PathPlannerCommand.NoCommand;
					}
					break;

				// not implemented
				//case PathPlannerCommand.MoveRelative:
				//case PathPlannerCommand.MoveRelative_Vel:
				//case PathPlannerCommand.MoveRelative_VelAcc:
				//case PathPlannerCommand.MoveAdditive:
				//case PathPlannerCommand.MoveAdditive_Vel:
				//case PathPlannerCommand.MoveAdditive_VelAcc:
				//case PathPlannerCommand.TorqueControl:
				//case PathPlannerCommand.TorqueControl_Vel:
				//case PathPlannerCommand.TorqueControl_VelAcc:
				//case PathPlannerCommand.CoupleOut:

				// no reaction required
				//case PathPlannerCommand.NoCommand:
				//case PathPlannerCommand.Stop:
				//case PathPlannerCommand.Stop_Acc:
				//case PathPlannerCommand.EmergencyStop:
				//case PathPlannerCommand.SetPosition:
				//case PathPlannerCommand.SetPositionRelative:
				//case PathPlannerCommand.Init:
				default:
					break;
			}
		}
	}
	#endregion Endpoint detection
}

Beispielprogramm Timers

#region Fileheader
//
// $Id: Timers.tama.cs 12764 2011-12-27 14:32:04Z chm $
// Copyright © 2008 Triamec Motion AG
//
#endregion Fileheader

using Triamec.Tama.Rlid4;
using Triamec.Tama.Vmid5;

/* Timer Sample
 * ------------
 * 
 * This sample demonstrates the usage of the two library timers, AsynchronousTimer and IsochronousTimer.
 * 
 * As the names suggest, use the isochronous timer in the isochronous main task and the asynchronous timer in the
 * asynchronous main task.
 * 
 * The sample code statically creates a timer for each task and then listens on the main command register for the
 * start signal.
 * When started, the main state is set to 0. After the duration specified in general purpose int or float variables 0 or
 * 1, respectively, the main state is reset to 1.
 * 
 */

namespace Triamec.Tam.Samples {
	[Tama]
	static class Timers {
		static readonly IsochronousTimer isotimer = new IsochronousTimer(Register.General.Parameters.CycleTimePathPlanner);
		static readonly AsynchronousTimer asytimer = new AsynchronousTimer(); 

		[TamaTask(Task.IsochronousMain)]
		static void Iso() {
			switch (Register.Tama.IsochronousMainCommand) {
				case 1:
					isotimer.Start(Register.Tama.Variables.GenPurposeIntVar0);
					Register.Tama.IsochronousMainState = 0;
					Register.Tama.IsochronousMainCommand = 3;
					break;

				case 2:
					isotimer.Start(Register.Tama.Variables.GenPurposeVar0);
					Register.Tama.IsochronousMainState = 0;
					Register.Tama.IsochronousMainCommand = 3;
					break;

				case 3:
					if (isotimer.Tick()) {
						Register.Tama.IsochronousMainState = 1;
						Register.Tama.IsochronousMainCommand = 0;
					}
					break;
			}
		}

		[TamaTask(Task.AsynchronousMain)]
		static void Asy() {
			switch (Register.Tama.AsynchronousMainCommand) {
				case 0:
					break;

				case 1:
					asytimer.Start(Register.Tama.Variables.GenPurposeIntVar1, Register.General.Signals.TriaLinkTimestamp);
					Register.Tama.AsynchronousMainState = 0;
					Register.Tama.AsynchronousMainCommand = 3;
					break;

				case 2:
					asytimer.Start(Register.Tama.Variables.GenPurposeVar1, Register.General.Signals.TriaLinkTimestamp);
					Register.Tama.AsynchronousMainState = 0;
					Register.Tama.AsynchronousMainCommand = 3;
					break;

				case 3:
					if (asytimer.Elapsed(Register.General.Signals.TriaLinkTimestamp)) {
						Register.Tama.AsynchronousMainState = 1;
						Register.Tama.AsynchronousMainCommand = 0;
					}
					break;
			}
		}
	}
}