TAM Tama – Echtzeit-Programmierung in C#

Tama

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.

Entwickelt wird mit C# in 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 Entwicklung

Tama Programm Entwicklung in VisualStudio
Tama Programm Entwicklung in VisualStudio

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

Microsoft® stellt das Visual Studio® mit verschiedenen Sprachen in den Express-Editionen gratis zur Verfügung.

Installation

Der Tama Compiler und Beispielprogramme werden mit dem TAM Software 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 Software 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

    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 }
  • Tama Homing Standalone

    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 }
  • Tama Timers

    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; } } } }