TAM Tama – Real Time Programming

Tama

Triamec servo drives, I/O controllers and adapters are freely programmable for customer-specific applications. Two tasks are available for this purpose:

  • Isochronous task in 10kHz
  • Asynchronous Task
TAMA programming on Triamec products - real-time environments can also be used on the host PC instead of the .NET framework
TAMA programming on Triamec products - real-time environments can also be used on the host PC instead of the .NET framework

Development is done with Microsoft Visual Studio in C#, which translates the code into the intermediate language CIL. A virtual machine (TamaVM) is installed on the Triamec devices, which interprets this standardized language and executes it in real time.

A Tama program can be loaded into the devices at runtime and then executed. For stand-alone applications without PC connection Tama programs can be stored persistently in the devices. Tama programs have access to all registers of the executing device and to cyclically transmitted coupling data of other devices.

Features of TAMA programs

  • Robust runtime environment: Virtual machine inside the device ("crash-proof")
  • Two tasks: One 10kHz real-time and one asynchronous task
  • Hard real-time in 10kHz
  • Strict type safety
  • Data types: int, float, bool, enum, struct, object, array
  • Access to device registers with IntelliSense function of Visual Studio
  • Mathematical functions
  • Interaction with PC application or Tama programs on other devices
  • New programs can be loaded and executed at runtime
  • Persistence allows autonomous operation without PC

Typical applications

  • Homing sequences, touch detection
  • Axis couplings (e.g. portals)
  • Kinematics calculations (e.g. parallel kinematics of a delta robot)
  • Sequence controls for simple stand-alone applications (e.g. force guided press)
  • Dual Loop Control (also together with other Servo Drives)
  • Parameter adaptation (for control, gain scheduling)
  • Monitoring functions and other real-time machine reactions

Tama Program Development

Tama Program Development in VisualStudio
Tama Program Development in VisualStudio

Tama programs can be developed in any Microsoft® .NET language (Visual C#, C++ and Visual Basic) with Microsoft Visual Studio®. Tama programs can be executed on all Triamec Servo Drives and I/O controllers.

Microsoft® provides Visual Studio® with different languages in the Express Editions for free.

Installation

The Tama compiler and sample programs are installed on the PC with the TAM SDK.

Tama Program Examples

In the following section you can view three Tama sample programs and get an insight into Tama programming. More examples and the Tama Compiler are installed with the TAM SDK.

Example 1: Homing with asynchronous task. (simple search of the encoder index, SetPosition, Move)
Example 2: Homing standalone with asynchronous and isochronous task. (DriveControl, Move, ...) 
Example 3: Timers with asynchronous and isochronous task

  • Tama Homing

    Homing Program Example

    #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

    Standalone Homing Program Example

    #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

    Timers Program Example

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