Table of Contents
.
This manual is a work in progress. If you are able to help with writing, editing, or graphic preparation please contact any member of the writing team or join and send an email to emc-users@lists.sourceforge.net.
Copyright (c) 2000-9 LinuxCNC.org
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and one Back-Cover Text: This EMC Handbook is the product of several authors writing for linuxCNC.org. As you find it to be of value in your work, we invite you to contribute to its revision and growth.A copy of the license is included in the section entitled GNU Free Documentation License. If you do not find the license you may order a copy from Free Software Foundation, Inc. 59 Temple Place, Suite 330 Boston, MA 02111-1307
BASE_PERIOD is the "heartbeat" of your EMC computer. Every period, the software step generator decides if it is time for another step pulse. A shorter period will allow you to generate more pulses per second, within limits. But if you go too short, your computer will spend so much time generating step pulses that everything else will slow to a crawl, or maybe even lock up. Latency and stepper drive requirements affect the shortest period you can use.
Worst case latencies might only happen a few times a minute, and the odds of bad latency happening just as the motor is changing direction are low. So you can get very rare errors that ruin a part every once in a while and are impossible to troubleshoot.
The simplest way to avoid this problem is to choose a BASE_PERIOD that is the sum of the longest timing requirement of your drive, and the worst case latency of your computer. This is not always the best choice for example if you are running a drive with a 20uS hold time requirement, and your latency test said you have a maximum latency of 11uS, then if you set the BASE_PERIOD to 20+11 = 31uS and a not-so-nice 16,129 steps per second.
The problem is with the 20uS hold time requirement. That plus the 11uS latency is what forces us to use a slow 31uS period. But the EMC2 software step generator has some parameters that let you increase the various time from one period to several. For example, if steplen is changed from 1 to 2, then it there will be two periods between the beginning and end of the step pulse. Likewise, if dirhold is changed from 1 to 3, there will be at least three periods between the step pulse and a change of the direction pin.
If we can use dirhold to meet the 20uS hold time requirement, then the next longest time is the 4.5uS high time. Add the 11uS latency to the 4.5uS high time, and you get a minimum period of 15.5uS. When you try 15.5uS, you find that the computer is sluggish, so you settle on 16uS. If we leave dirhold at 1 (the default), then the minimum time between step and direction is the 16uS period minus the 11uS latency = 5uS, which is not enough. We need another 15uS. Since the period is 16uS, we need one more period. So we change dirhold from 1 to 2. Now the minimum time from the end of the step pulse to the changing direction pin is 5+16=21uS, and we don’t have to worry about the drive stepping the wrong direction because of latency.
For more information on stepgen see Section ( [sec:Stepgen]).
Servo systems must be "tuned" as they don’t quite work out of the box like a stepper system might. This is because servos don’t "step" in fixed increments like steppers do. PID is the "Black Magic" that makes your servos move where you want them to move and when you want them to move.
PID stand for Proportional, Integral, and Derivative. The Proportional value determines the reaction to the current error, the Integral value determines the reaction based on the sum of recent errors, and the Derivative value determines the reaction based on the rate at which the error has been changing. They are three common mathematical techniques that are applied to the task of getting a working process to follow a set point. In the case of EMC the process we want to control is actual axis position and the set point is the commanded axis position.
By "tuning" the three constants in the PID controller algorithm, the controller can provide control action designed for specific process requirements. The response of the controller can be described in terms of the responsiveness of the controller to an error, the degree to which the controller overshoots the set point and the degree of system oscillation.
The proportional term (sometimes called gain) makes a change to the output that is proportional to the current error value. A high proportional gain results in a large change in the output for a given change in the error. If the proportional gain is too high, the system can become unstable. In contrast, a small gain results in a small output response to a large input error. If the proportional gain is too low, the control action may be too small when responding to system disturbances.
In the absence of disturbances, pure proportional control will not settle at its target value, but will retain a steady state error that is a function of the proportional gain and the process gain. Despite the steady-state offset, both tuning theory and industrial practice indicate that it is the proportional term that should contribute the bulk of the output change.
The contribution from the integral term (sometimes called reset) is proportional to both the magnitude of the error and the duration of the error. Summing the instantaneous error over time (integrating the error) gives the accumulated offset that should have been corrected previously. The accumulated error is then multiplied by the integral gain and added to the controller output.
The integral term (when added to the proportional term) accelerates the movement of the process towards set point and eliminates the residual steady-state error that occurs with a proportional only controller. However, since the integral term is responding to accumulated errors from the past, it can cause the present value to overshoot the set point value (cross over the set point and then create a deviation in the other direction).
The rate of change of the process error is calculated by determining the slope of the error over time (i.e. its first derivative with respect to time) and multiplying this rate of change by the derivative gain.
The derivative term slows the rate of change of the controller output and this effect is most noticeable close to the controller set point. Hence, derivative control is used to reduce the magnitude of the overshoot produced by the integral component and improve the combined controller-process stability.
If the PID controller parameters (the gains of the proportional, integral and derivative terms) are chosen incorrectly, the controlled process input can be unstable, i.e. its output diverges, with or without oscillation, and is limited only by saturation or mechanical breakage. Tuning a control loop is the adjustment of its control parameters (gain/proportional band, integral gain/reset, derivative gain/rate) to the optimum values for the desired control response.
A simple tuning method is to first set the I and D values to zero. Increase the P until the output of the loop oscillates, then the P should be set to be approximately half of that value for a "quarter amplitude decay" type response. Then increase I until any offset is correct in sufficient time for the process. However, too much I will cause instability. Finally, increase D, if required, until the loop is acceptably quick to reach its reference after a load disturbance. However, too much D will cause excessive response and overshoot. A fast PID loop tuning usually overshoots slightly to reach the set point more quickly; however, some systems cannot accept overshoot, in which case an "over-damped" closed-loop system is required, which will require a P setting significantly less than half that of the P setting causing oscillation.
The Real Time Application Interface (RTAI) is used to provide the best Real Time (RT) performance. The RTAI patched kernel lets you write applications with strict timing constraints. RTAI gives you the ability to have things like software step generation which require precise timing.
The Advanced Configuration and Power Interface (ACPI) has a lot of different functions, most of which interfere with RT performance (for example: power management, CPU power down, CPU frequency scaling, etc). The EMC2 kernel (and probably all RTAI-patched kernels) has ACPI disabled. ACPI also takes care of powering down the system after a shutdown has been started, and that’s why you need to push the power button to completely turn off your computer.
The EMC is configured with human readable text files. All of these files can be read and edited in any of the common text file editors available with most any Linux distribution.[1] You’ll need to be a bit careful when you edit these files. Some mistakes will cause the start up to fail. These files are read whenever the software starts up. Some of them are read repeatedly while the CNC is running.
Configuration files include
emcrc::
(((.emcrc))) This file saves user specific information and is created to save the name of the directory when the user first selects an EMC configuration.footnote:[Usually this file is in the users home directory (e.g. /home/user/ ) ]
Items marked (hal) are used only by the sample HAL files and are suggested as a good convention. Other items are used by EMC directly, and must always have the section and item names given.
[1] Don’t confuse a text editor with a word processor. A text editor like gedit or kwrite produce files that are plain text. They also produce lines of text that are separated from each other. A word processor like Open Office produce files with paragraphs and word wrapping and lots of embedded codes that control font size and such. A text editor does none of this.
A typical INI file follows a rather simple layout that includes;
Each of these elements is separated on single lines. Each end of line or newline character creates a new element.
A comment line is started with a ; or a # mark. When the ini reader sees either of these marks at the start a line, the rest of the line is ignored by the software. Comments can be used to describe what some INI element will do.
; This is my little mill configuration file. ; I set it up on January 12, 2006
Comments can also be used to select between several values of a single variable.
DISPLAY = axis # DISPLAY = touchy
In this list, the DISPLAY variable will be set to axis because the other one is commented out. If someone carelessly edits a list like this and leaves two of the lines uncommented, the first one encountered will be used.
Note that inside a variable, the "#" and ";" characters do not denote comments:
INCORRECT = value # and a comment # Correct Comment CORRECT = value
Related parts of an ini file are separated into sections. A section line looks like [THIS_SECTION]. The name of the section is enclosed in brackets. The order of sections is unimportant. The following sections are used by EMC:
A variable line is made up of a variable name, an equals sign(=), and a value. Everything from the first non-white space character after the = up to the end of the line is passed as the value, so you can embed spaces in string symbols if you want to or need to. A variable name is often called a keyword.
The following sections detail each section of the configuration file, using sample values for the configuration lines.
Some of the variables are used by EMC, and must always use the section names and variable names shown. Other variables are used only by HAL, and the section names and variable names shown are those used in the sample configuration files.
Different user interface programs use different options, and not every option is supported by every user interface. The main two interfaces for EMC are AXIS and Touchy. Axis is an interface for use with normal computer and monitor, Touchy is for use with touch screens. Descriptions of the interfaces are in the Interfaces section of the User Manual.
DISPLAY = axis The name of the user interface to use. Valid options may include:
The following [DISPLAY] items are used only if you select AXIS as your user interface program.
[TRAJ]AXES
should be used. This sequence specifies
the order in which the effect
of each axis is applied, with a "-" inverting the sense of the
rotation.
The proper GEOMETRY string depends on the machine configuration and
the kinematics used to control it. The example string GEOMETRY=XYZBCUVW
is for a 5-axis machine where kinematics causes UVW to move in the
coordinate system of the tool and XYZ to move in the coordinate system
of the material. The order of the letters is important, because it
expresses the order in which the different transformations are applied.
For example rotating around C then B is different than rotating around
B then C. Geometry has no effect without a rotary axis.
The following [DISPLAY] items are not used if you select AXIS as your user interface program.
AXIS has the ability to send loaded files through a filter program. This filter can do any desired task: Something as simple as making sure the file ends with M2, or something as complicated as detecting whether the input is a depth image, and generating g-code to mill the shape it defines. The [FILTER] section of the ini file controls how filters work. First, for each type of file, write a PROGRAM_EXTENSION line. Then, specify the program to execute for each type of file. This program is given the name of the input file as its first argument, and must write rs274ngc code to standard output. This output is what will be displayed in the text area, previewed in the display area, and executed by EMC when Run.
If your post processor outputs files in all caps you might want to add the following line:
PROGRAM_EXTENSION = .NGC XYZ Post Processor
The following lines add support for the image-to-gcode converter included with EMC2:
PROGRAM_EXTENSION = .png,.gif Greyscale Depth Image
It is also possible to specify an interpreter:
PROGRAM_EXTENSION = .py Python Script
In this way, any Python script can be opened, and its output is treated as g-code. One such example script is available at nc_files/holecircle.py. This script creates g-code for drilling a series of holes along the circumference of a circle. Many more g-code generators are on the EMC Wiki site http://wiki.linuxcnc.org/cgi-bin/emcinfo.pl.
If the environment variable AXIS_PROGRESS_BAR is set, then lines written to stderr of the form
FILTER_PROGRESS=%d
Sets the AXIS progress bar to the given percentage. This feature should be used by any filter that runs for a long time.
Example:
PARAMETER_FILE = myfile.var
Example:
Example:
SUBROUTINE_PATH = ncsubroutines:/tmp/testsubs:lathesubs:millsubs
Example:
USER_M_PATH = myfuncs:/tmp/mcodes:experimentalmcodes
A search is made for each possible user defined function, typically (M100-M199). The search order is:
The first executable M1xx found in the search is used for each M1xx.
You may find other entries in this section and they should not be changed.
core_stepper.hal
) and
one which specifies the machine pin out (xxx_pinout.hal
)
The [TRAJ] section contains general parameters for the trajectory planning module in EMCMOT.
The default behavior is for EMC to force the user to home the machine before any MDI command or a program is run. Normally jogging only is allowed before homing. Setting NO_FORCE_HOMING = 1 allows the user to make MDI moves and run programs without homing the machine first. Interfaces without homing ability will need to have this option set to
The [AXIS_0], [AXIS_1], etc. sections contains general parameters for the individual components in the axis control module. The axis section names begin numbering at 0, and run through the number of axes specified in the [TRAJ] AXES entry minus 1.
AXIS_8 = W
COMP_FILE_TYPE=1 the second and third values specify the forward trim (how far from nominal while traveling forward) and the reverse trim (how far from nominal while traveling in reverse).
These parameters are Homing related, for a better explanation read the Homing Section ( [sec:Homing]).
The following items are for servo-based systems and servo-like systems. This description assumes that the units of output from the PID component are volts.
0.000 (hal) These two values are the scale and offset factors for the axis output to the motor amplifiers. The second value (offset) is subtracted from the computed output (in volts), and divided by the first value (scale factor), before being written to the D/A converters. The units on the scale value are in true volts per DAC output volts. The units on the offset value are in volts. These can be used to linearize a DAC. Specifically, when writing outputs, the EMC first converts the desired output in quasi-SI units to raw actuator values, e.g., volts for an amplifier DAC. This scaling looks like: The value for scale can be obtained analytically by doing a unit analysis, i.e., units are [output SI units]/[actuator units]. For example, on a machine with a velocity mode amplifier such that 1 volt results in 250 mm/sec velocity, Note that the units of the offset are in machine units, e.g., mm/sec, and they are pre-subtracted from the sensor readings. The value for this offset is obtained by finding the value of your output which yields 0.0 for the actuator output. If the DAC is linearized, this offset is normally 0.0. The scale and offset can be used to linearize the DAC as well, resulting in values that reflect the combined effects of amplifier gain, DAC non-linearity, DAC units, etc. To do this, follow this procedure:
Raw | Measured |
---|---|
-10 | -9.93 |
-9 | -8.83 |
0 | -0.03 |
1 | 0.96 |
9 | 9.87 |
10 | 10.87 |
The following items are Stepper related items.
The spindle will be left on during the tool change when the value is
Homing seems simple enough - just move each joint to a known location, and set EMC’s internal variables accordingly. However, different machines have different requirements, and homing is actually quite complicated.
There are four possible homing sequences, along with the associated configuration parameters as shown in the following table. For a more detailed description of what each configuration parameter does, see the following section.
SEARCH_VEL | LATCH_VEL | USE_INDEX | Homing Type |
---|---|---|---|
nonzero | nonzero | NO | Switch-only |
nonzero | nonzero | YES | Switch + Index |
0 | nonzero | YES | Index-only |
0 | 0 | NO | None |
Other combinations | Error |
There are six pieces of information that determine exactly how the home sequence behaves. They are defined in an [AXIS] section of the inifile.
The default value is zero. A value of zero causes EMC to assume that there is no home switch; the search stage of homing is skipped.
If HOME_SEARCH_VEL is non-zero, then EMC assumes that there is a home switch. It begins by checking whether the home switch is already tripped. If tripped it backs off the switch at HOME_SEARCH_VEL. The direction of the back-off is opposite the sign of HOME_SEARCH_VEL. Then it searches for the home switch by moving in the direction specified by the sign of HOME_SEARCH_VEL, at a speed determined by its absolute value. When the home switch is detected, the joint will stop as fast as possible, but there will always be some overshoot. The amount of overshoot depends on the speed. If it is too high, the joint might overshoot enough to hit a limit switch or crash into the end of travel. On the other hand, if HOME_SEARCH_VEL is too low, homing can take a long time.
Specifies the speed and direction that EMC uses when it makes its final accurate determination of the home switch (if present) and index pulse location (if present). It will usually be slower than the search velocity to maximise accuracy. If HOME_SEARCH_VEL and HOME_LATCH_VEL have the same sign, then the latch phase is done while moving in the same direction as the search phase. (In that case, EMC first backs off the switch, before moving towards it again at the latch velocity.) If HOME_SEARCH_VEL and HOME_LATCH_VEL have opposite signs, the latch phase is done while moving in the opposite direction from the search phase. That means EMC will latch the first pulse after it moves off the switch. If HOME_SEARCH_VEL is zero (meaning there is no home switch), and this parameter is nonzero, EMC goes ahead to the index pulse search. If HOME_SEARCH_VEL is non-zero and this parameter is zero, it is an error and the homing operation will fail. The default value is zero.
It specifies the speed that EMC uses when it makes its move from HOME_OFFSET to the HOME position. If the HOME_FINAL_VEL is missing from the ini file, then the maximum joint speed is used to make this move. The value must a positive number.
Can hold the values YES / NO. This flag determines whether EMC will ignore the limit switch inputs. Some machine configurations do not use a separate home switch, instead they route one of the limit switch signals to the home switch input as well. In this case, EMC needs to ignore that limit during homing. The default value for this parameter is NO.
Specifies whether or not there is an index pulse. If the flag is true (HOME_USE_INDEX = YES), EMC will latch on the rising edge of the index pulse. If false, EMC will latch on either the rising or falling edge of the home switch (depending on the signs of HOME_SEARCH_VEL and HOME_LATCH_VEL). The default value is NO.
Contains the location of the home switch or index pulse, in joint coordinates. It can also be treated as the distance between the point where the switch or index pulse is latched and the zero point of the joint. After detecting the index pulse, EMC sets the joint coordinate of the current point to HOME_OFFSET. The default value is zero.
The position that the joint will go to upon completion of the homing sequence. After detecting the index pulse, and setting the coordinate of that point to HOME_OFFSET, EMC makes a move to HOME as the final step of the homing process. The default value is zero. Note that even if this parameter is the same as HOME_OFFSET, the axis will slightly overshoot the latched position as it stops. Therefore there will always be a small move at this time (unless HOME_SEARCH_VEL is zero, and the entire search/latch stage was skipped). This final move will be made at the joint’s maximum velocity. Since the axis is now homed, there should be no risk of crashing the machine, and a rapid move is the quickest way to finish the homing sequence. [3]
If there is not a separate home switch input for this axis, but a number of momentary switches wired to the same pin, set this value to 1 to prevent homing from starting if one of the shared switches is already closed. Set this value to 0 to permit homing even if the switch is already closed.
Used to define a multi-axis homing sequence HOME ALL and enforce homing order (e.g., Z may not be homed if X is not yet homed). An axis may be homed after all axes with a lower HOME_SEQUENCE have already been homed and are at the HOME_OFFSET. If two axes have the same HOME_SEQUENCE, they may be homed at the same time. If HOME_SEQUENCE is -1 or not specified then this joint will not be homed by the HOME ALL sequence. HOME_SEQUENCE numbers start with 0 and there may be no unused numbers.
See also the manual pages motion(9) and iocontrol(1).
Motion is loaded with the motmod command. A kins should be loaded before motion.
loadrt motmod [base_period_nsec=period] [servo_period_nsec=period] [traj_period_nsec=period] [num_joints=[0-9] ([num_dio=1-64] num_aio=1-16])]
If the number of digital I/O needed is above the default of 4 you can add up to 64 digital I/O by using the num_dio option when loading motmod.
If the number of analog I/O needed is more that the default of 4 you can add up to 16 analog I/O by using the num_aio option when loading motmod.
These pins, parameters, and functions are created by the realtime
motmod
module.
M52 P1
, the
commanded velocity is multiplied by this value. This effect is
multiplicative with the NML-level feed override value and
motion.feed-hold.
M62-65
.
M53 P1
, and this
bit is TRUE, the feed rate is set to 0.
G38.x
uses the value on this pin to determine when the
probe has made
contact. TRUE for probe contact closed (touching), FALSE for probe
contact open.
HAL near
component, by comparing requested and actual spindle speeds.
M3
) direction.
G95
). If your spindle
encoder driver does not have a velocity output, you
can generate a suitable one by sending the spindle position through a
ddt
component.
M3
), negative for spindle reverse (M4
).
M3
), negative for spindle reverse (M4
).
G43
active), or it could come from the gcode (G43.1
active)
G43
active), or it could come from the gcode (G43.1
active)
G43
active), or it could come from the gcode (G43.1
active)
Many of these parameters serve as debugging aids, and are subject to change or removal at any time.
These pins and parameters are created by the realtime motmod
module. These are actually joint values, but the pins and parameters
are still called "axis.N".[4] They are read and updated by the motion-controller function.
These pins are created by the userspace IO controller, usually called
io
.
GladeVCP is an EMC2 component which adds the ability to add a virtual control panel to EMC user interfaces like Axis or Touchy. It is similar to PyVCP; whereas PyVCP panels are created by editing an XML file manually, GladeVCP uses the glade WYSIWYG user interface editor. Therefore, it’s faster and easier to create visually pleasing panels with GladeVCP.
Either modify an existing UI component, or start a new one with running glade. In the left tab, expand the HAL Python components. Create a window as top level window from the Toplevels section, which by default will be named window1. Leave that name as is - gladevcp assumes the top level window has this name. Add a HAL_Box or a HAL_Table from HAL Python to the frame, and pick and place some elements like LED, button etc in its box.
This will look like so:
Select File→Save as, give it a name like myui.ui and make sure it’s saved as GtkBuilder file (radio button left bottom corner in Save dialog). The convention for GtkBuilder file extension is .ui. You can now run it with:
gladevcp myui.ui
While you could run your UI as a separate top level window side-by-side with Axis, having it within the Axis frame as a tab side-by-side with the Preview and DRO tabs is more elegant. To do so, edit your .ini file and add two variables to the DISPLAY section of ini file:
Try it out by running Axis - there should be a new tab called GladeVCP near the DRO tab. Select that tab, you should see the example panel nicely fit within Axis like so:
Touchy also understands EMBED_TAB_NAME/EMBED_TAB_COMMAND variables from INI file so instructions for it are equal to Axis.
To be useful, the pins in your UI need to be linked to the rest of your setup. Usually this is done by running one or several HAL files through the HALFILE and POSTGUI_HALFILE statements in the HAL section of your .ini file.
For gladevcp components this not currently possible because all of these HALFILE statements are run before gladevcp is started, so linking to your UI’s pins from there would refer to pins which do not yet exist. You therefore need to prepare a file of all HAL commands which refer to / link with your UI’s pins and pass it to the gladevcp command in the EMBED_TAB_COMMAND statement like so:
EMBED_TAB_COMMAND = gladevcp -c gladevcp-test -H gladevcp-test.hal -w
There is an example gladevcp-test.hal file in the emc2-dev/lib/python/gladevcp directory. Before using it in the EMBED_TAB_NAME statement, make sure the loadusr statements at the top are commented out.
This is just a very minimal example to convey the idea - for a detailed description on how to program a gladevcp application, see GladeVCPprogramming and HalWidgets.
gladevcp cannot only manipulate or display HAL pins, you can also write regular event handlers in Python. This could be used, among others, to execute MDI commands. Here’s how you do it:
Write a Python module like so and save as e.g. handlers.py:
nhits = 0 def on_button_press(gtkobj,data=None): global nhits nhits += 1 gtkobj.set_label("hits: %d" % nhits)
In glade, define a button or HAL button, select the Signals tab, and in the GtkButton properties select the pressed line. Enter on_button_press there, and save the glade file.
Then add the option -u handlers.py to the gladevcp command line. If your event handlers are spread over several files, just add multiple -u <pyfilename> options.
Now, pressing the button should change its label since it’s set in the callback function.
What the -u flag does is: all Python functions in this file are collected and setup as potential callback handlers for your Gtk widgets - they can be referenced from glade Signals tabs. The callback handlers are called with the particular object instance as parameter, like the GtkButton instance above, so you can apply any GtkButton method from there.
Or do some more useful stuff, like calling an MDI command!
See also man gladevcp . This is the usage message when you run gladevcp -h:
Usage: gladevcp [options] myfile.ui
Options:
Stock gladevcp can be used pretty much in the style of PyVCP - the major difference being that in the case of gladevcp, the glade user interface editor is used to design a screen layout, whereas PyVCP is driven by manually edited XML files. Also, PyVCP uses the TkInter widgets, whereas gladevcp uses the much richer GTK toolkit. Both support HAL widgets - widgets with one or more associated HAL pin which is the interface to the rest of EMC. Stock gladevcp and PyVCP panels are really just a bunch of virtual switches, dials, leds an so forth, wired to the outside world through the HAL layer, designed to set and read ints, bits and floats values.
Most widget sets, and their associated user interface editors, support the concept of callbacks - functions in user-written code which are executed when something happens in the UI - events like mouse clicks, characters typed, mouse movement, timer events, window hiding and exposure and so forth. Both PyVCP and stock gladevcp mainly do two things: mapping events on HAL widgets to actions like a value change of the associated HAL pin, and in the other direction - detecting if HAL pin values changed and updating the associated widget, like a LED, a meter, a bar, or just some label displaying a value. However, neither PyVCP nor stock gladevcp provide support for other actions than changing HAL values. Doing something more complex, like executing MDI commands to call a G-code subroutine, is outside scope.
GladeVcp includes a collection of Gtk widgets with attached HAL pins called HAL Widgets, intended to control, display or otherwise interact with the EMC HAL layer. They are intended to be used with the glade user interface editor. With proper installation, the HAL Widgets should show up in glade’s HAL Python widget group. Many HAL specific fields in the glade General section have an associated mouse-over tool tip.
HAL signals come in two variants, bits and numbers. Bits are off/on signals. Numbers can be "float", "s32" or "u32". For more information on HAL data types see the [→] section. The GladeVcp widgets can either display the value of the signal with an indicator widget, or modify the signal value with a control widget. Thus there are four classes of GladeVcp widgets that you can connect to a HAL signal. Another class of helper widgets allow you to organize and label your panel.
HAL Widgets inherit methods, properties and signals from the underlying Gtk widgets, so it is helpful to consult the Gtk and PyGTK documentation as well.
Most HAL widgets have a single associated HAL pin with the same name as the widget (glade: General→Name). Exceptions to this rule currently are the HAL_Spinbutton, which has two pins: a <widgetname>-f (float) and a <widgetname>-s (s32) pin, and the HAL_ProgressBar, which has a <widgetname> value input pin, and a <widgetname>.scale input pin.
As a general rule, if you need to set a HAL output widget’s value from Python code, do so by calling the underlying Gtk setter (e.g. set_active(), set_value()) - do not try to set the associated pin’s value by halcomp[pinname] = value directly because the widget will not notice.
It might be tempting to set HAL widget input pins programmatically. Note this defeats the purpose of an input pin in the first place - it should be linked to, and react to signals generated by other HAL components. While there is currently no write protection on writing to input pins in HAL Python, this doesn’t make sense. You might use setp pinname value in the associated halfile for testing though.
It is perfectly OK to set an output HAL pin’s value with halcomp[pinname] = value provided this HAL pin is not associated with a widget, that is, has been created by the hal_glib.GPin(halcomp.newpin(<name>,<type>,<direction>) method (see GladeVCProgramming for an example).
Event-driven programming means that the UI tells you when "something happens" - through a callback, like when a button was pressed. The output HAL widgets (those which display a HAL pin’s value) like LED, Bar, VBar, Meter etc, support the hal-pin-changed signal which may cause a callback into your Python code when - well, a HAL pin changes its value. This means there’s no more need for permanent polling of HAL pin changes in your code, the widgets do that in the background and let you know. The example in configs/gladevcp/examples/complex shows how this is handled in Python.
Here is an example how to set a hal-pin-changed signal for a HAL_LED in the glade UI editor:
This group of widget is derived from various Gtk buttons and consists of HAL_Button, HAL_ToggleButton, HAL_RadioButton and CheckButton widgets. All of them have a single output BIT pin named identical to the widget. Buttons have no additional properties compared to their base Gtk classes.
Hint: Defining radio button groups in glade:
See configs/gladevcp/by-widget/radiobutton for a gladevcp application and UI file for working with radio buttons.
HAL_HScale and HAL_VScale are derived from the GtkHScale and GtkVScale respectively. They have one output FLOAT pin with name equal to widget name. Scales have no additional properties.
Hint: To make a scale useful in glade, add an Adjustment (General→Adjustment→New or existing adjustment) and edit the adjustment object. It defines the default/min/max/increment
values. Also, set adjustment Page size and Page increment to zero to avoid warnings.
HAL SpinButton is derived from GtkSpinButton and holds two pins:
Hint: to be useful, Spinbuttons need an adjustment value like scales, see above.
HAL_Label is simple widget based on GtkLabel which represents a HAL pin value in a user-defined format. The pin’s HAL type depends on the label_pin_type property (0:S32, 1:float, 2:U32), see also the tooltip on General→HAL pin type (note this is different from PyVCP which has three label widgets, one for each type).
The text displayed depends on the text_template property - an Python format string to represent the pin value. It defaults to "%s" (values are converted by the str() function) but may contain anything legit in a as argument to Pythons format() method.
Example: Distance: %.03f will display the text and the pin value with 3 fractional digits padded with zeros for a FLOAT pin.
Compared to their Gtk counterparts they have one input BIT pin which controls if their child widgets are sensitive or not. If the pin is low then child widgets are inactive which is the default.
Hint: if you find some part of your gladevcp application is grayed out (insensitive), see whether a container’s pin is unset.
The HAL_Led simulates a real indicator LED . It has a single input BIT pin which controls it’s state: ON or OFF. Leds have several properties which control their look and feel:
As an input widget, LED also supports the hal-pin-changed signal. If you want to get a notification in your code when the LED’s HAL pin was changed, then connect this signal to a handler, for example on_led_pin_changed and provide the handler as follows:
def on_led_pin_changed(self,hal_led,data=None): print "on_led_pin_changed() - HAL pin value:",hal_led.hal_pin.get()
This will be called at any edge of the signal and also during program start up to report the current value.
Example LEDs:
Note: This widget might go away. Use the HAL_HBar and HAL_VBar widgets instead.
The HAL_ProgressBar is derived from gtk.ProgressBar and has two float HAL input pins:
It has the following properties:
HAL_ComboBox is derived from gtk.ComboBox. It enables choice of a value from a dropdown list.
It exports two HAL pins:
It has the following property which can be set in glade:
column: the column index, type S32, defaults to -1, range from -1..100 .
In default mode this widgets sets the pins to the index of the chosen list entry. So if your widget has three labels, it may only assume values 0,1 and 2.
In column mode (column > -1), the value reported is chosen from the ListStore array as defined in Glade. So typically your widget definition would have two columns in the ListStore , one with text displayed in the dropdown, and an int or float value to use for that choice.
There’s an example in configs/gladevcp/by-widget/combobox/combobox.{py,ui} which uses column mode to pick a float value from the ListStore.
If you’re confused like me about how to edit ComboBox ListStores and CellRenderer , see http://www.youtube.com/watch?v=Z5_F-rW2cL8.
HAL Bar and VBar widgets for horizontal and vertical bars representing float values. They have one input FLOAT hal pin. Both bars have the following properties:
Examples:
HAL Meter is widget like PyVCP meter representing float value. It have one input FLOAT hal pin. Widget has following properties:
Examples:
Gremlin is a plot preview widget similar to the Axis preview window. It assumes a running EMC environment like Axis or Touchy. To connect to it, inspects the INI_FILE_NAME environment variable. Gremlin displays the current .ngc file - it does monitor for changes and reloads the ngc file if the file name in Axis/Touchy changes. If you run it in a gladevcp application when EMC is not running you might get a traceback because the Gremlin widget cant find EMC status, like the current file name.
Gremlin does not export any HAL pins. It has the following properties:
Example:
GladeVcp includes a collection of "canned actions" called EMC Action Widgets for the glade user interface editor. Other than HAL widgets, which interact with HAL pins, EMC Actions interact with EMC and the G-code interpreter.
EMC Action Widgets are derived from the Gtk.Action widget. The Action widget in a nutshell:
The appearance of EMC Actions in glade is roughly as follows:
Tooltip hovers provide a description.
These are one-shot type widgets. They implement a single action and are for use in simple buttons, menu entries or radio/check groups.
These are bi-modal widgets. They implement two actions or use a second (usually pressed) state to indicate that currently an action is running. Toggle actions are aimed for use in ToggleButtons, ToggleToolButtons or toggling menu items. A simplex example is the ESTOP toggle button.
Currently the following widgets are available:
The following toggle actions have only one associated command and use the pressed state to indicate that the requested operation is running:
These widgets provide a means to execute arbitrary MDI commands.
Here’s a glade UI file which conveys the basics:
Open it in glade and study how it’s done. Start Axis, and then start this from a terminal window with gladevcp whoareyou.ui . See the hal_action_mdi1 Action and it’s MDI command property - this just executes (MSG, "Hi, I’m an EMC_Action_MDI") so there should be a message popup in Axis like so:
You’ll notice that the button associated with the Action_MDI action is grayed out if the machine is off, in E-Stop or the program is running. It will automatically become active when the machine is turned on and out of E-Stop, and the program is idle.
Optionally, MDI command strings may have parameters substituted before they are passed to the interpreter. Parameters currently may be names of HAL pins in the gladevcp component (in a later release, this will be extended to cover HAL pins of other components as well). This is how it works:
Here’s the example UI file for gladevcp: , and here’s what you get:
It’s perfectly OK to call an O-word subroutine in an MDI command, and pass HAL pin values as actual parameters. Here’s an example UI file upload:owordsub.ui and O-word sub:
Place oword.ngc so Axis can find it, and run gladevcp owordsub.ui from a terminal window. This looks like so:
The EMC G-Code interpreter has a single global set of variables, like feed, spindle speed, relative/absolute mode and others. If you use G code commands or O-word subs, some of these variables might get changed by the command or subroutine - for example, a probing subroutine will very likely set the feed value quite low. With no further precautions, your previous feed setting will be overwritten by the probing subroutine’s value.
To deal with this surprising and undesirable side effect of a given O-word subroutine or G-code statement executed with an EMC ToggleAction_MDI, you might associate pre-MDI and post-MDI handlers with a given EMC ToggleAction_MDI. These handlers are optional and provide a way to save any state before executing the MDI Action, and to restore it to previous values. The signal names are mdi-command-start and mdi-command-stop; the handler names can be set in glade like any other handler.
Here’s an example how a feed value might be saved and restored by such handlers (note that EMC command and status channels are available as self.emc and self.stat through the _EMC_ActionBase class:
def on_mdi_command_start(self, action, userdata=None): action.stat.poll() self.start_feed = action.stat.settings[1]
def on_mdi_command_stop(self, action, userdata=None): action.emc.mdi('F%.1f' % (self.start_feed)) while action.emc.wait_complete() == -1: pass
Only the Action_MDI Toggle widget supports these signals.
Many actions depend on EMC status - is it in manual, MDI or auto mode? is a program running, paused or idle? You cannot start an MDI command while a G-code program is running, so this needs to be taken care of. Many EMC actions take care of this themselves, and related buttons and menu entries are deactivated when the operation is currently impossible.
When using Python event handlers - which are at a lower level than Actions - one needs to take care of dealing with status dependencies oneself. For this purpose, there’s the EMC Stat widget: to associate EMC status changes with event handlers.
EMC Stat has no visible component - you just add it to your UI with glade. Once added, you can associate handlers with its following signals:
As most of the infrastructure to support user-defined actions in glade and the GTK widget set is in place, the new gladevcp version really just provides a way for the user to write a Python module whose class methods - or in the simple case, just functions - can be referred to in glade as event handlers, a way to import this module(s) into gladevcp at runtime and properly link it with the rest of the HAL layer.
We also extended the way the HAL input pins interact with the gladevcp panel. Beyond HAL widgets displaying pin values, there is now a way to attach a value-changed callback to a HAL pin, which fits nicely with the event-driven structure of a typical widget application: every activity, be it mouse click, key, timer expired, or the change of a HAL pin’s value, generates a callback and is handled by the same orthogonal mechanism.
Note that the above refers to explicitly declared HAL pins. HAL widgets come with a pre-defined signal hal-pin-changed, see the HalWidgets page for details.
See the Adding HAL pins section below for details.
A annoying aspect of gladevcp in its earlier form and pyvcp is the fact that you may change values through text entry, sliders, spin boxes, toggle buttons etc, but their settings are not saved and restored at the next run of EMC - they start at the default value as set in the panel or widget definition. Therefore I added an easy-to-use mechanism to save and restore the state of HAL widgets, and program variables (in fact instance attributes) as well. This mechanism uses the popular .ini file syntax and has safeguards against the .ini file and the corresponding user interface or program variables getting out of sync - just imagine renaming, adding or deleting widgets in glade: an .ini file lying around from a previous program version, or an entirely different user interface, would be not be able to restore the state properly. This situation is detected through a signature which depends on all object names and types which are saved and to be restored. In the case of signature mismatch, a new .ini file with default settings is generated.
The overall protocol is as follows:
For simple tasks it’s sufficient to define functions named after the glade signal handlers. These will be called when the corresponding event happens in the widget tree. Here’s a trivial example - it assumes that the pressed signal of a GTK Button or HAL Button is linked to a callback called on_button_press:
nhits = 0 def on_button_press(gtkobj,data=None): global nhits nhits += 1 gtkobj.set_label("hits: %d" % nhits)
Add this function to a Python file and run as follows:
gladevcp -u <myhandler>.py mygui.ui
Note communication between handlers has to go through global variables, which does not scale well and is positively un-pythonic. This is why we came up with the class-based handler model.
The idea here is: handlers are linked to class methods. The underlying class(es) are instantiated and inspected during gladevcp startup and linked to the widget tree as signal handlers. So the task now is to write:
Here is a minimum user-defined handler example module:
Class MyCallbacks : def on_this_signal(self,obj,data=None): print "this_signal happened, obj=",obj def get_handlers(halcomp,builder,useropts): return [MyCallbacks ()]
Now, on_this_signal will be available as signal handler to your widget tree.
If during module inspection gladevcp finds a function get_handlers, it calls it as follows:
get_handlers(halcomp,builder,useropts)
the arguments are:
gladevcp then inspects the list of class instances and retrieves their method names. Qualifying method names are connected to the widget tree as signal handlers. Only method names which do not begin with an _ (underscore) are considered.
Note that regardless whether you’re using the libglade or the new GtkBuilder format for your glade UI, widgets can always be referred to as builder.get_object(<widgetname>). Also, the complete list of widgets is available as builder.get_objects() regardless of UI format.
It is important to know in which state of affairs your get_handlers() function is called so you know what is safe to do there and what not. First, modules are imported and initialized in command line order. After successful import, get_handlers() is called in the following state:
Once all modules have been imported and method names extracted, the following steps happen:
So when your handler class is initialized, all widgets are existent but not yet realized (displayed on screen). And the HAL component isn’t ready as well, so its unsafe to access pins values in your init() method.
If you want to have a callback to execute at program start after it is safe to access HAL pins, then a connect a handler to the realize signal of the top level window1 (which might be its only real purpose). At this point gladevcp is done with all setup tasks, the halfile has been run, and gladevcp is about to enter the gtk main loop.
Within a class, method names must be unique. However, it is OK to have multiple class instances passed to gladevcp by get_handlers() with identically named methods. When the corresponding signal occurs, these methods will be called in definition order - module by module, and within a module, in the order class instances are returned by get_handlers().
Instead of extending gladevcp for any conceivable option which could potentially be useful for a handler class, you may use the -U <useroption> flag (repeatedly if you wish). This flag collects a list of <useroption> strings. This list is passed to the get_handlers() function (useropts argument). Your code is free to interpret these strings as you see fit. An possible usage would be to pass them to the Python exec function in your get_handlers() as follows:
debug = 0 ... def get_handlers(halcomp,builder,useropts): ... global debug # assuming there's a global var for cmd in useropts: exec cmd in globals()
This way you can pass arbitrary Python statements to your module through the gladevcp -U option, for example:
gladevcp -U debug=42 -U "print debug=%d % debug" …
This should set debug to 2 and confirm that your module actually did it.
If you want any of: GTK widget state, HAL widgets output pin’s values and/or class attributes of your handler class to be retained across invocations, proceed as follows:
describe these decisions in your handler class' init() method through a nested dictionary as follows:
def __init__(self, halcomp,builder,useropts): self.halcomp = halcomp self.builder = builder self.useropts = useropts self.defaults = { # the following names will be saved/restored as method attributes # the save/restore mechanism is strongly typed - the variables type will be derived from the type of the # initialization value. Currently supported types are: int, float, bool, string IniFile.vars : { 'nhits' : 0, 'a': 1.67, 'd': True ,'c' : "a string"}, # to save/restore all widget's state which might remotely make sense, add this: IniFile.widgets : widget_defaults(builder.get_objects()) # a sensible alternative might be to retain only all HAL output widgets' state: # IniFile.widgets: widget_defaults(select_widgets(self.builder.get_objects(), hal_only=True,output_only = True)), }
and associate an .ini file with this descriptor:
self.ini_filename = __name__ + '.ini' self.ini = IniFile(self.ini_filename,self.defaults,self.builder) self.ini.restore_state(self)
after restore_state(), self will have attributes set if as running the following:
self.nhits = 0 self.a = 1.67 self.d = True self.c = "a string"
Note that types are saved and preserved on restore. This example assumes that the ini file didn’t exist or had the default values from self.defaults.
After this incantation, you can use the following IniFil methods:
To save the widget and/or variable state on exit, connect a signal handler to the window1 (toplevel) destroy event:
def on_destroy(self,obj,data=None): self.ini.save_state(self)
Next time you start the gladevcp application, the widgets should come up in the state when the application was closed.
You can do that, but note that the values in self.defaults override your edits if there is a syntax or type error in your edit. The error is detected, a console message will hint about that happened, and the bad inifile will be renamed to have the .BAD suffix. Subsequent bad ini files overwrite earlier .BAD files.
If you need HAL pins which are not associated with a specific HAL widget, add them as follows:
import hal_glib ... # in your handler class __init__(): self.example_trigger = hal_glib.GPin(halcomp.newpin('example-trigger', hal.HAL_BIT, hal.HAL_IN))
To get a callback when this pin’s value changes, associate a value-change callback with this pin, add:
self.example_trigger.connect('value-changed', self._on_example_trigger_change)
and define a callback method (or function, in this case leave out the self parameter):
# note '_' - this method will not be visible to the widget tree def _on_example_trigger_change(self,pin,userdata=None): print "pin value changed to:" % (pin.get())
Since gladevcp uses GTK widgets which rely on the GObject base class, the full glib functionally is available. Here is an example for a timer callback:
def _on_timer_tick(self,userdata=None): ... return True # to restart the timer; return False for on-shot ... # demonstrate a slow background timer - granularity is one second # for a faster timer (granularity 1msec), use this: # glib.timeout_add(100, self._on_timer_tick,userdata) # 10Hz glib.timeout_add_seconds(1, self._on_timer_tick)
We believe key handling works OK, but since it is new code, we’re telling about it you so you can watch out for problems; please let us know of errors or odd behavior. This is the story:
Axis uses the TkInter widget set. GladeVCP applications use Gtk widgets and run in a separate process context. They are hooked into Axis with the Xembed protocol. This allows a child application like gladevcp to properly fit in a parent’s window, and - in theory - have integrated event handling.
However, this assumes that both parent and child application properly support the Xembed protocol, which Gtk does, but TkInter doesn’t. A consequence of this is that certain keys would not be forwarded from a gladevcp panel to Axis properly under all circumstances. One of these situations was the case when an Entry, or SpinButton widget had focus: in this case, for instance an Escape key would not have been forwarded to Axis and cause an abort as it should, with potentially disastrous consequences.
Therefore, key events in gladevcp are explicitly handled, and selectively forwarded to Axis, to assure that such situations cannot arise. For details, see the keyboard_forward() function in lib/python/gladevcp/xembed.py.
More detailed information can be found in the man page for halcmd "man halcmd" in a terminal window. To see the HAL configuration and check the status of pins and parameters use the HAL Configuration window on the Machine menu in AXIS. To watch a pin status open the Watch tab and click on each pin you wish to watch and it will be added to the watch window.
The command "loadrt" loads a real time HAL component. Real time component functions need to be added to a thread to be updated at the rate of the thread. You cannot load a user space component into the real time space.
The syntax and an example:
loadrt <component> <options> loadrt mux4 count=1
The command "addf" adds a real time component function to a thread. You have to add a function from a HAL real time component to a thread to get the function to update at the rate of the thread.
If you used the Stepper Config Wizard to generate your config you will have two threads.
The syntax and an example:
addf <component> <thread> addf mux4 servo-thread
The command "loadusr" loads a user space HAL component. User space programs are their own separate processes, which optionally talk to other HAL components via pins and parameters. You cannot load real time components into user space.
Flags may be one or more of the following:
The syntax and examples:
loadusr <component> <options> loadusr halui loadusr -Wn spindle gs2_vfd -n spindle in English it means "loadusr wait for name spindle component gs2_vfd name spindle."
The command "net" creates a "connection" between a signal and and one or more pins. If the signal does not exist net creates the new signal. This replaces the need to use the command newsig. The direction indicators "⇐ and ⇒" are only to make it easier for humans to follow the logic and are not used by the net command.
The syntax and an example:
net <signal-name> <pin-name> <opt-direction> <opt-pin-name> net both-home-y <= parport.0.pin-11-in
Each signal can only have one source (a HAL "Dir out” pin) and as many readers (a HAL "Dir in" pin) as you like. In the Dir column of the HAL Configuration window you can see which pins are "in" and which are "out". The following figure shows the "direction" of the signal from the source, through the signal, and then out to the reader(s).
This example shows the signal xStep with the source being stepgen.0.out and with two readers, parport.0.pin-02-out and parport.0.pin-08-out. Basically the value of stepgen.0.out is send to the signal xStep and that value is then sent to parport.0.pin-02-out and parport.0.pin-08-out.
signal source destination destination net xStep stepgen.0.out => parport.0.pin-02-out parport.0.pin-08-out
Since the signal xStep contains the value of stepgen.0.out (the source) you can use the same signal again to send the value to another reader. To do this just use the signal with the readers on another line.
net xStep <= stepgen.0.out => parport.0.pin-02-out
The so called I/O pins like index-enable do not follow this rule.
The command "setp" sets the value of a pin or parameter. The valid values will depend on the type of the pin or parameter. It is an error if the data types do not match.
Some components have parameters that need to be set before use. Parameters can be set before use or while running as needed. You cannot use setp on a pin that is connected to a signal.
The syntax and an example:
setp <pin/parameter-name> <value> setp parport.0.pin-08-out TRUE
The command "unlinkp" unlinks a pin from the connected signal. If no signal was connected to the pin prior running the command, nothing happens.
The syntax and an example:
unlinkp <pin-name>
unlinkp parport.0.pin-02-out
The command "linksp" creates a "connection" between a signal and one pin.
The syntax and an example:
linksp <signal-name> <pin-name>
linksp X-step parport.0.pin-02-out
The "linksp" command has been superseded by the "net" command.
The command "linkps" creates a "connection" between one pin and one signal. It is the same as linksp but the arguments are reversed.
The syntax and an example:
linkps <pin-name> <signal-name>
linkps parport.0.pin-02-out X-Step
The "linkps" command has been superseded by the "net" command.
the command "newsig" creates a new HAL signal by the name <signame> and the data type of <type>. Type must be "bit", "s32", "u32" or "float". Error if <signame> all ready exists.
The syntax and an example:
newsig <signame> <type>
newsig Xstep bit
More information can be found in the HAL manual or the man pages for halrun.
A bit value is an on or off.
A "float" is a floating point number. In other words the decimal point can move as needed.
For more information on floating point numbers see:
An "s32" number is a whole number that can have a negative or positive value.
If you used the Stepper Config Wizard to generate your config you will have up to three HAL files in your config directory.
Two parameters are automatically added to each HAL component when it is created. These parameters allow you to scope the execution time of a component.
.time(((time))) + .tmax(((tmax)))
Time is the number of CPU cycles it took to execute the function.
Tmax is the maximum number of CPU cycles it took to execute the function. Tmax is a read/write parameter so the user can set it to 0 to get rid of the first time initialization on the function’s execution time.
Hal contains several real time logic components. Logic components follow a "Truth Table" that states what the output is for any given input. Typically these are bit manipulators and follow electrical logic gate truth tables.
The "and2" component is a two input "and" gate. The truth table below shows the output based on each combination of input.
Syntax
and2 [count=N] or [names=name1[,name2...]]
Functions
and2.n
Pins
and2.N.in0 (bit, in) + and2.N.in1 (bit, in) + and2.N.out (bit, out)
Truth Table
in0 | in1 | out |
---|---|---|
False | False | False |
True | False | False |
False | True | False |
True | True | True |
The "not" component is a bit inverter.
Syntax
not [count=n] or [names=name1[,name2...]]
Functions
not.all + not.n
Pins
not.n.in (bit, in) + not.n.out (bit, out)
Truth Table
in | out |
---|---|
True | False |
False | True |
The "or2" component is a two input OR gate.
Syntax
or2[count=n] or [names=name1[,name2...]]
Functions
or2.n
Pins
or2.n.in0 (bit, in) + or2.n.in1 (bit, in) + or2.n.out (bit, out)
Truth Table
in0 | in1 | out |
---|---|---|
True | False | True |
True | True | True |
False | True | True |
False | False | False |
The "xor2" component is a two input XOR (exclusive OR)gate.
Syntax
xor2[count=n] or [names=name1[,name2...]]
Functions
xor2.n
Pins
xor2.n.in0 (bit, in) + xor2.n.in1 (bit, in) + xor2.n.out (bit, out)
Truth Table
in0 | in1 | out |
---|---|---|
True | False | True |
True | True | False |
False | True | True |
False | False | False |
An "and2" example connecting two inputs to one output.
loadrt and2 count=1 + addf and2.0 servo-thread + net my-sigin1 and2.0.in0 <= parport.0.pin-11-in + net my-sigin2 and2.0.in1 <= parport.0.pin-12-in + net both-on parport.0.pin-14-out <= and2.0.out
In the above example one copy of and2 is loaded into real time space and added to the servo thread. Next pin 11 of the parallel port is connected to the in0 bit of the and gate. Next pin 12 is connected to the in1 bit of the and gate. Last we connect the and2 out bit to the parallel port pin 14. So following the truth table for and2 if pin 11 and pin 12 are on then the output pin 14 will be on.
The weighted_sum converts a group of bits to an integer. The conversion is the sum of the "weights" of the bits that are on plus any offset. This is similar to a binary coded decimal but with more options. The "hold" bit stops processing the input changes so the "sum" will not change.
The following syntax is used to load the weighted_sum component.
loadrt weighted_sum wsum_sizes=size[,size,...]
Creates weighted sum groups each with the given number of input bits (size).
To update the weighted_sum you need to attach process_wsums to a thread.
addf process_wsums servo-thread
This updates the weighted_sum component.
In the following example clipped from the HAL Configuration window in Axis the bits "0" and "2" are true and there is no offset. The "weight" of 0 is 1 and the "weight" of 2 is 4 so the sum is 5.
The script halshow can help you find your way around a running HAL. This is a very specialized system and it must connect to a working HAL. It cannot run standalone because it relies on the ability of HAL to report what it knows of itself through the halcmd interface library. It is discovery based. Each time halshow runs with a different EMC configuration it will be different.
As we will soon see, this ability of HAL to document itself is one key to making an effective CNC system.
Halshow is in the AXIS menu under Machine/Show Hal Configuration.
Halshow is in the TkEMC menu under Scripts/Hal Show.
At the left of its display as shown in figure [cap:Halshow-Layout] is a tree view, somewhat like you might see with some file browsers. At the right is a tabbed notebook with tabs for show and watch.
The tree shows all of the major parts of a HAL. In front of each is a small plus (+) or minus (-) sign in a box. Clicking the plus will expand that tree node to display what is under it. If that box shows a minus sign, clicking it will collapse that section of the tree.
You can also expand or collapse the tree display using the Tree View menu at the upper left edge of the display. Under Tree View you will find: Expand Tree, Collapse Tree; Expand Pins, Expand Parameters, Expand Signals; and Erase Watch. (Note that Erase Watch erases’all' previously set watches, you cannot erase just one watch.)
Clicking on the node name, the word "Components" for example, will show you (under the "Show" tab) all that hal knows about the contents of that node. Figure [cap:Halshow-Layout] shows a list exactly like you will see if you click the "Components" name while you are running a standard m5i20 servo card. The information display is exactly like those shown in traditional text based HAL analysis tools. The advantage here is that we have mouse click access, access that can be as broad or as focused as you need.
If we take a closer look at the tree display we can see that the six major parts of a HAL can all be expanded at least one level. As these levels are expanded you can get more focused with the reply when you click on the rightmost tree node. You will find that there are some hal pins and parameters that show more than one reply. This is due to the nature of the search routines in halcmd itself. If you search one pin you may get two, like this:
Component Pins: + Owner Type Dir Value Name + 06 bit -W TRUE parport.0.pin-10-in + 06 bit -W FALSE parport.0.pin-10-in-not
The second pin’s name contains the complete name of the first.
Below the show area on the right is a set of widgets that will allow you to play with the running HAL. The commands you enter here and the effect that they have on the running HAL are not saved. They will persist as long as EMC remains up but are gone as soon as EMC is.
The entry box labeled "Test Hal Command:" will accept any of the commands listed for halcmd. These include:
This little editor will enter a command any time you press <enter> or push the execute button. An error message from halcmd will show below this entry widget when these commands are not properly formed. If you are not certain how to set up a proper command you’ll need to read again the documentation on halcmd and the specific modules that you are working with.
Let’s use this editor to add a differential module to a hal and connect it to axis position so that we could see the rate of change in position, i.e., acceleration. We first need to load a hal module named blocks, add it to the servo thread, then connect it to the position pin of an axis. Once that is done we can find the output of the differentiator in halscope. So let’s go. (Yes, I looked this one up.)
loadrt blocks ddt=1
Now look at the components node and you should see blocks in there someplace.
Loaded HAL Components: + ID Type Name + 10 User halcmd29800 + 09 User halcmd29374 + 08 RT blocks + 06 RT hal_parport + 05 RT scope_rt + 04 RT stepgen + 03 RT motmod + 02 User iocontrol
Sure enough there it is. Notice that its ID is 08. Next we need to find out what functions are available with it so we look at functions:
Exported Functions: + Owner CodeAddr Arg FP Users Name + 08 E0B97630 E0DC7674 YES 0 ddt.0 + 03 E0DEF83C 00000000 YES 1 motion-command-handler + 03 E0DF0BF3 00000000 YES 1 motion-controller + 06 E0B541FE E0DC75B8 NO 1 parport.0.read + 06 E0B54270 E0DC75B8 NO 1 parport.0.write + 06 E0B54309 E0DC75B8 NO 0 parport.read-all + 06 E0B5433A E0DC75B8 NO 0 parport.write-all + 05 E0AD712D 00000000 NO 0 scope.sample + 04 E0B618C1 E0DC7448 YES 1 stepgen.capture-position + 04 E0B612F5 E0DC7448 NO 1 stepgen.make-pulses + 04 E0B614AD E0DC7448 YES 1 stepgen.update-freq
Here we look for owner #08 and see that blocks has exported a function named ddt.0. We should be able to add ddt.0 to the servo thread and it will do its math each time the servo thread is updated. Once again we look up the addf command and find that it uses three arguments like this:
addf <functname> <threadname> [<position>]
We already know the functname=ddt.0 so let’s get the thread name right by expanding the thread node in the tree. Here we see two threads, servo-thread and base-thread. The position of ddt.0 in the thread is not critical. So we add the function ddt.0 to the servo-thread:
addf ddt.0 servo-thread
This is just for viewing, so we leave position blank and get the last position in the thread. Figure [cap:Addf-Command] shows the state of halshow after this command has been issued.
Next we need to connect this block to something. But how do we know what pins are available? The answer is to look under pins. There we find ddt and see this:
Component Pins: + Owner Type Dir Value Name + 08 float R- 0.00000e+00 ddt.0.in + 08 float -W 0.00000e+00 ddt.0.out
That looks easy enough to understand, but what signal or pin do we want to connect to it? It could be an axis pin, a stepgen pin, or a signal. We see this when we look at axis.0:
Component Pins: + Owner Type Dir Value Name + 03 float -W 0.00000e+00 axis.0.motor-pos-cmd ==> Xpos-cmd
So it looks like Xpos-cmd should be a good signal to use. Back to the editor where we enter the following command:
linksp Xpos-cmd ddt.0.in
Now if we look at the Xpos-cmd signal using the tree node we’ll see what we’ve done:
Signals: + Type Value Name + float 0.00000e+00 Xpos-cmd + <== axis.0.motor-pos-cmd + ==> ddt.0.in + ==> stepgen.0.position-cmd
We see that this signal comes from axis.o.motor-pos-cmd and goes to both ddt.0.in and stepgen.0.position-cmd. By connecting our block to the signal we have avoided any complications with the normal flow of this motion command.
The Hal Show Area uses halcmd to discover what is happening in a running HAL. It gives you complete information about what it has discovered. It also updates as you issue commands from the little editor panel to modify that HAL. There are times when you want a different set of things displayed without all of the information available in this area. That is where the Hal Watch Area is of value.
Clicking the watch tab produces a blank canvas. You can add signals and pins to this canvas and watch their values.[5] You can add signals or pins when the watch tab is displayed by clicking on the name of it. Figure [cap:Watch-Display] shows this canvas with several "bit" type signals. These signals include enable-out for the first three axes and two of the three iocontrol "estop" signals. Notice that the axes are not enabled even though the estop signals say that EMC is not in estop. A quick look at TkEMC shows that the condition of EMC is ESTOP RESET. The amp enables do not turn true until the machine has been turned on.
Watch displays bit type (binary) values using colored circles representing LEDs. They show as dark brown when a bit signal or pin is false, and as light yellow whenever that signal is true. If you select a pin or signal that is not a bit type (binary) signal, watch will show it as a numerical value.
Watch will quickly allow you to test switches or see the effect of changes that you make to EMC while using the graphical interface. Watch’s refresh rate is a bit slow to see stepper pulses, but you can use it for these if you move an axis very slowly or in very small increments of distance. If you’ve used IO_Show in EMC, the watch page in halshow can be set up to watch a parport much as IO_Show did.
[5] The refresh rate of the watch display is much lower than Halmeter or Halscope. If you need good resolution of the timing of signals those tools are much more effective.
The preferred way to set up a standard stepper machine is with the Step Configuration Wizard. See the Getting Started Guide.
This chapter describes some of the more common settings for manually setting up a stepper based system. Because of the various possibilities of configuring EMC2, it is very hard to document them all, and keep this document relatively short.
The most common EMC2 usage is for stepper based systems. These systems are using stepper motors with drives that accept step & direction signals.
It is one of the simpler setups, because the motors run open-loop (no feedback comes back from the motors), yet the system needs to be configured properly so the motors don’t stall or lose steps.
Most of this chapter is based on the sample config released along with
EMC2. The config is called stepper, and usually it is
found in /etc/emc2/sample-configs/stepper
.
With software step generation, the maximum step rate is one step per two BASE_PERIODs for step-and-direction output. The maximum requested step rate is the product of an axis' MAX_VELOCITY and its INPUT_SCALE. If the requested step rate is not attainable, following errors will occur, particularly during fast jogs and G0 moves.
If your stepper driver can accept quadrature input, use this mode. With a quadrature signal, one step is possible for each BASE_PERIOD, doubling the maximum step rate.
The other remedies are to decrease one or more of: the BASE_PERIOD (setting this too low will cause the machine to become unresponsive or even lock up), the INPUT_SCALE (if you can select different step sizes on your stepper driver, change pulley ratios, or leadscrew pitch), or the MAX_VELOCITY and STEPGEN_MAXVEL.
If no valid combination of BASE_PERIOD, INPUT_SCALE, and MAX_VELOCITY is acceptable, then consider using hardware step generation (such as with the emc2-supported Universal Stepper Controller, Mesa cards, and others.)
One of the major flaws in EMC was that you couldn’t specify the pinout without recompiling the source code. EMC2 is far more flexible, and now (thanks to the Hardware Abstraction Layer) you can easily specify which signal goes where. See the HAL manual for more detailed information on HAL.
As it is described in the HAL Introduction and tutorial, we have signals, pins and parameters inside the HAL.
The ones relevant for our pinout are[6]:
signals: Xstep, Xdir & Xen
Depending on what you have chosen in your .ini file you are using either standard_pinout.hal or xylotex_pinout.hal. These are two files that instruct the HAL how to link the various signals & pins. Further on we’ll investigate the standard_pinout.hal.
This file contains several HAL commands, and usually looks like this:
# standard pinout config file for 3-axis steppers # using a parport for I/O # # first load the parport driver loadrt hal_parport cfg="0x0378" # # next connect the parport functions to threads # read inputs first addf parport.0.read base-thread 1 # write outputs last addf parport.0.write base-thread -1 # # finally connect physical pins to the signals net Xstep => parport.0.pin-03-out net Xdir => parport.0.pin-02-out net Ystep => parport.0.pin-05-out net Ydir => parport.0.pin-04-out net Zstep => parport.0.pin-07-out net Zdir => parport.0.pin-06-out # create a signal for the estop loopback net estop-loop iocontrol.0.user-enable-out iocontrol.0.emc-enable-in # create signals for tool loading loopback net tool-prep-loop iocontrol.0.tool-prepare iocontrol.0.tool-prepared net tool-change-loop iocontrol.0.tool-change iocontrol.0.tool-changed # connect "spindle on" motion controller pin to a physical pin net spindle-on motion.spindle-on => parport.0.pin-09-out ### ### You might use something like this to enable chopper drives when machine ON ### the Xen signal is defined in core_stepper.hal ### # net Xen => parport.0.pin-01-out ### ### If you want active low for this pin, invert it like this: ### # setp parport.0.pin-01-out-invert 1 ### ### A sample home switch on the X axis (axis 0). make a signal, ### link the incoming parport pin to the signal, then link the signal ### to EMC's axis 0 home switch input pin ### # net Xhome parport.0.pin-10-in => axis.0.home-sw-in ### ### Shared home switches all on one parallel port pin? ### that's ok, hook the same signal to all the axes, but be sure to ### set HOME_IS_SHARED and HOME_SEQUENCE in the ini file. See the ### user manual! ### # net homeswitches <= parport.0.pin-10-in # net homeswitches => axis.0.home-sw-in # net homeswitches => axis.1.home-sw-in # net homeswitches => axis.2.home-sw-in ### ### Sample separate limit switches on the X axis (axis 0) ### # net X-neg-limit parport.0.pin-11-in => axis.0.neg-lim-sw-in # net X-pos-limit parport.0.pin-12-in => axis.0.pos-lim-sw-in ### ### Just like the shared home switches example, you can wire together ### limit switches. Beware if you hit one, EMC will stop but can't tell ### you which switch/axis has faulted. Use caution when recovering from this. ### # net Xlimits parport.0.pin-13-in => axis.0.neg-lim-sw-in axis.0.pos-lim-sw-in
The files starting with # are comments, and their only purpose is to guide the reader through the file.
There are a couple of operations that get executed when the standard_pinout.hal gets executed / interpreted:
If you want to change the standard_pinout.hal file, all you need is a text editor. Open the file and locate the parts you want to change.
If you want for example to change the pin for the X-axis Step & Directions signals, all you need to do is to change the number in the parport.0.pin-XX-out name:
net Xstep parport.0.pin-03-out
can be changed to:
net Xstep parport.0.pin-02-out
or basically any other numbers you like.
Hint: make sure you don’t have more than one signal connected to the same pin.
If external hardware expects an “active low” signal, set the
corresponding -invert
parameter. For instance, to invert the spindle
control signal:
setp parport.0.pin-09-invert TRUE
If your spindle can be controlled by a PWM signal, use the pwmgen
component to create the signal:
loadrt pwmgen output_type=0
This assumes that the spindle controller’s response to PWM is simple:
0% PWM gives 0 RPM, 10% PWM gives 180 RPM, etc. If there is a minimum
PWM required to get the spindle to turn, follow the example in the
nist-lathe sample configuration to use a scale
component.
Some amplifiers (drives) require an enable signal before they accept and command movement of the motors. For this reason there are already defined signals called Xen, Yen, Zen.
To connect them use the following example:
net Xen parport.0.pin-08-out
You can either have one single pin that enables all drives; or several, depending on the setup you have. Note, however, that usually when one axis faults, all the other drives will be disabled as well, so having only one enable signal / pin for all drives is a common practice.
As you can see in [sub:standard_pinout.hal] by default the stepper configuration assumes no external ESTOP button. [8]
To add a simple external button you need to replace the line:
net estop-loop iocontrol.0.user-enable-out iocontrol.0.emc-enable-in
with
net estop-loop parport.0.pin-01-in iocontrol.0.emc-enable-in
This assumes an ESTOP switch connected to pin 01 on the parport. As long as the switch will stay pushed[9], EMC2 will be in the ESTOP state. When the external button gets released EMC2 will imediately switch to the ESTOP-RESET state, and all you need to do is switch to Machine On and you’ll be able to continue your work with EMC2.
[6] Note: we are only presenting one axis to keep it short, all others are similar.
[7] the fastest thread in the EMC2 setup, usually the code gets executed every few microseconds
[8] An extensive explanation of hooking up ESTOP circuitry is explained in the wiki.linuxcnc.org and in the Integrator Manual
[9] make sure you use a maintained switch for ESTOP.
Some of these will have expanded descriptions from the man pages. Some will have limited descriptions. All of the components have man pages. From this list you know what components exist and can use man n name to get additional information. For example in a terminal window type
man 1 axis
to view the information in the man page.
Some of these will have expanded descriptions from the man pages. Some will have limited descriptions. All of the components have man pages. From this list you know what components exist and can use man n name to get additional information in a terminal window.
Compute the absolute value and sign of the input signal
loadrt abs [count=N|names=name1[,name2...]]
addf abs.N|name thread-name
abs.N.in (float in) Input value abs.N.out (float out) Output Value, always positive abs.N.sign (bit out) Sign of Input, false = positive, true = negative
The first abs loaded will be abs.0 and each one after that the "N" number will increment.
Two-input AND gate. For out to be true both inputs must be true
loadrt and2 [count=N|names=name1[,name2...]]
addf and2.N|name thread-name
and2.N.in0 (bit in) Input 0 and2.N.in1 (bit in) Input 1 and2.N.out (bit out) Output
Perform linear interpolation between two values
loadrt blend [count=N|names=name1[,name2...]]
addf blend.N|name thread-name
blend.N.in1 (float in) First input. blend.N.in2 (float in) Second input. blend.N.select (float in) Select input. blend.N.out (float out) Output value.
blend.N.open (bit r/w)
If select is equal to 0.0 output is equal to in1.
If select is equal to 1.0, the output is equal to in2.
For select values between 0.0 and 1.0, the output changes linearly from in1 to in2.
If blend.N.open is true, select values outside the range 0.0 to 1.0 give values outside the range in1 to in2. If false, outputs are clamped to the the range in1 to in2
Create a square-wave for the "charge pump" input of some controller boards
loadrt charge_pump
addf charge-pump
charge-pump.out (bit out) charge-pump.enable (bit in) default = TRUE
Outputs a square wave if enable is TRUE or unconnected, low if enable is FALSE
Two input version of Clarke transform
loadrt clarke2 [count=N|names=name1[,name2...]]
addf clarke2.N | name
clarke2.N.a (float in) phase a input clarke2.N.b (float in) phase b input clarke2.N.x (float out) cartesian components of output clarke2.N.y (float out) cartesian components of output
The Clarke transform can be used to translate a vector quantity from a three phase system (three components 120 degrees apart) to a two phase Cartesian system.
clarke2 implements a special case of the Clarke transform, which only needs two of the three input phases. In a three wire three phase system, the sum of the three phase currents or voltages must always be zero. As a result only two of the three are needed to completely define the current or voltage. clarke2 assumes that the sum is zero, so it only uses phases A and B of the input. Since the H (homopolar) output will always be zero in this case, it is not generated.
Clarke (3 phase to cartesian) transform
loadrt clarke3 [count=N|names=name1[,name2...]]
addf clarke3.N | name
clarke3.N.a (float in) three phase input vector clarke3.N.b (float in) three phase input vector clarke3.N.c (float in) three phase input vector clarke3.N.x (float out) cartesian components of output clarke3.N.y (float out) cartesian components of output clarke3.N.h (float out) homopolar component of output
The Clarke transform can be used to translate a vector quantity from a three phase system (three components 120 degrees apart) to a two phase Cartesian system (plus a homopolar component if the three phases don’t sum to zero).
clarke3 implements the general case of the transform, using all three phases. If the three phases are known to sum to zero, see clarke2 for a simpler version.
Inverse Clarke transform
loadrt clarkeinv [count=N|names=name1[,name2...]]
addf clarkeinv.N | name
clarkeinv.N.x (float in) cartesian components of input clarkeinv.N.y (float in) cartesian components of input clarkeinv.N.h (float in) homopolar component of input (usually zero) clarkeinv.N.a (float out) three phase output vector clarkeinv.N.b (float out) three phase output vector clarkeinv.N.c (float out) three phase output vector
.
The inverse Clarke transform can be used to translate a vector quantity from Cartesian coordinate system to a three phase system (three components 120 degrees apart).
filter noisy digital inputs, for more information see [sec:Debounce]
software counting of quadrature encoder signals, for more information see [sec:Encoder]
Gives six degrees of freedom in position and orientation (XYZABC). The location of the motors is defined at compile time.
Kinematics that can model a general serial-link manipulator with up to 6 angular joints.
HAL driver for the Mesa Electronics 5i20, 5i22, 5i23, 4i65, and 4i68 Anything IO boards, with HostMot2 firmware
Kinematics for a tabletop 5 axis mill named "max" with tilting head (B axis) and horizintal rotary mounted to the table (C axis). Provides UVW motion in the rotated coordinate system. The source file, maxkins.c, may be a useful starting point for other 5-axis systems.
proportional/integral/derivative controller, for more information see [sec:PID]
Hardware driver and firmware for the Pluto-P parallel-port FPGA, for use with servos
Hardware driver and firmware for the Pluto-P parallel-port FPGA, for use with steppers
software PWM/PDM generation, for more information see [sec:PWMgen]
signal generator, for more information see [sec:Siggen]
simulated quadrature encoder, for more information see [sec:Simulated-Encoder]
software step pulse generation, for more information see [sec:Stepgen]
The joints represent the distance of the controlled point from three predefined locations (the motors), giving three degrees of freedom in position (XYZ)
Place a signal on an I/O pin only when enabled, similar to a tristate buffer in electronics
Place a signal on an I/O pin only when enabled, similar to a tristate buffer in electronics
There is a 1:1 correspondence between joints and axes. Most standard milling machines and lathes use the trivial kinematics module.
Hal Meter can be loaded from a terminal or from Axis. Hal Meter is faster than Hal Show displaying values. Hal Meter has two windows, one to pick the pin, signal, or parameter to monitor and one that displays the value. Multiple Hal Meters can be open at the same time. If you use a script to open multiple Hal Meters you can set the position of each one with -g X Y relative to the upper left corner of your screen. See the man page for more options.
loadusr halmeter pin hm2.0.stepgen.00.velocity-fb -g 0 500
This component provides software based generation of step pulses in response to position or velocity commands. In position mode, it has a built in pre-tuned position loop, so PID tuning is not required. In velocity mode, it drives a motor at the commanded speed, while obeying velocity and acceleration limits. It is a realtime component only, and depending on CPU speed, etc, is capable of maximum step rates of 10kHz to perhaps 50kHz. Figure [fig:Stepgen-Block-Diag] shows three block diagrams, each is a single step pulse generator. The first diagram is for step type 0, (step and direction). The second is for step type 1 (up/down, or pseudo-PWM), and the third is for step types 2 through 14 (various stepping patterns). The first two diagrams show position mode control, and the third one shows velocity mode. Control mode and step type are set independently, and any combination can be selected.
emc2$ *halcmd loadrt stepgen step_type=<type-array> [ctrl_type=<ctrl_array>]*
<type-array>
is a series of comma separated decimal integers. Each
number causes a
single step pulse generator to be loaded, the value of the number
determines the stepping type. <ctrl_array>
is a comma separated
series of “p`” or “`v`” characters, to specify position or velocity
mode. `ctrl_type
is optional, if ommitted, all of the step generators
will be position
mode. For example:
emc2# *halcmd loadrt stepgen step_type=0,0,2 ctrl_type=p,p,v*
will install three step generators. The first two use step type 0
(step and direction) and run in position mode. The last one uses step
type 2 (quadrature) and runs in velocity mode. The default value for
<config-array>
is “0,0,0”
which will install three type 0
(step/dir) generators. The maximum
number of step generators is 8 (as defined by MAX_CHAN in stepgen.c).
Each generator is independent, but all are updated by the same
function(s) at the same time. In the following descriptions, <chan>
is the number of a specific generator. The first generator is number 0.
Each step pulse generator will have only some of these pins, depending on the step type and control type selected.
(float)
` stepgen.<chan>.position-cmd` — Desired motor position, in
position units (position mode only).
(float)
` stepgen.<chan>.velocity-cmd` — Desired motor velocity, in
position units per second (velocity
mode only).
(s32) ``stepgen.<chan>.counts
— Feedback position in counts,
updated by capture_position()
.
(float) ``stepgen.<chan>.position-fb
— Feedback position in
position units, updated by capture_position()
.
(bit)
` stepgen.<chan>.enable` — Enables output steps - when false,
no steps are generated.
(bit)
` stepgen.<chan>.step` — Step pulse output (step type 0 only).
(bit)
` stepgen.<chan>.dir` — Direction output (step type 0 only).
(bit) ``stepgen.<chan>.up
— UP pseudo-PWM output (step type 1 only).
(bit) ``stepgen.<chan>.down
— DOWN pseudo-PWM output (step type 1
only).
(bit) ``stepgen.<chan>.phase-A
— Phase A output (step types 2-14
only).
(bit) ``stepgen.<chan>.phase-B
— Phase B output (step types 2-14
only).
(bit) ``stepgen.<chan>.phase-C
— Phase C output (step types 3-14
only).
(bit) ``stepgen.<chan>.phase-D
— Phase D output (step types 5-14
only).
(bit) ``stepgen.<chan>.phase-E
— Phase E output (step types 11-14
only).
(float)
` stepgen.<chan>.position-scale` — Steps per position unit.
This parameter is used for both output
and feedback.
(float)
` stepgen.<chan>.maxvel` — Maximum velocity, in position
units per second. If 0.0, has no
effect.
(float)
` stepgen.<chan>.maxaccel` — Maximum accel/decel rate, in
positions units per second squared.
If 0.0, has no effect.
(float)
` stepgen.<chan>.frequency` — The current step rate, in
steps per second.
(float)
` stepgen.<chan>.steplen` — Length of a step pulse (step
type 0 and 1) or minimum time in a
given state (step types 2-14), in nano-seconds.
(float)
` stepgen.<chan>.stepspace` — Minimum spacing between two
step pulses (step types 0 and 1 only),
in nano-seconds.
(float)
` stepgen.<chan>.dirsetup` — Minimum time from a direction
change to the beginning of the next
step pulse (step type 0 only), in nanoseconds.
(float)
` stepgen.<chan>.dirhold` — Minmum time from the end of a
step pulse to a direction change
(step type 0 only), in nanoseconds.
(float)
` stepgen.<chan>.dirdelay` — Minmum time any step to a step
in the opposite direction (step
types 1-14 only), in nano-seconds.
(s32)
` stepgen.<chan>.rawcounts` — The raw feedback count, updated
by make_pulses()
.
In position mode, the values of maxvel and maxaccel are used by the internal position loop to avoid generating step pulse trains that the motor cannot follow. When set to values that are appropriate for the motor, even a large instantaneous change in commanded position will result in a smooth trapezoidal move to the new location. The algorithm works by measuring both position error and velocity error, and calculating an acceleration that attempts to reduce both to zero at the same time. For more details, including the contents of the “control equation” box, consult the code.
In velocity mode, maxvel is a simple limit that is applied to the commanded velocity, and maxaccel is used to ramp the actual frequency if the commanded velocity changes abruptly. As in position mode, proper values for these parameters ensure that the motor can follow the generated pulse train.
The step generator supports 15 different “step types”. Step type 0 is
the most familiar, standard step and direction. When configured for
step type 0, there are four extra parameters that determine the exact
timing of the step and direction signals. See figure
[fig:StepDir-timing] for the meaning of these parameters. The
parameters are in nanoseconds, but will be rounded up to an integer
multiple of the thread period for the threaed that calls
make_pulses()
. For example, if make_pulses()
is called every 16uS,
and steplen is 20000, then the step pulses will
be 2 x 16 = 32uS long. The default value for all four of the parameters
is 1nS, but the automatic rounding takes effect the first time the code
runs. Since one step requires steplen
nS high and stepspace
nS
low, the maximum frequency is 1,000,000,000 divided by
(steplen+stepspace)
. If maxfreq
is set higher than that limit, it
will be lowered automatically. If
maxfreq is zero, it will remain zero, but the output frequency will
still be limited.
Step and Direction Timing. Step type 1 has two outputs, up and down. Pulses appear on one or the
other, depending on the direction of travel. Each pulse is steplen
nS
long, and the pulses are separated by at least stepspace
nS. The
maximum frequency is the same as for step type 0. If maxfreq
is set
higher than the limit it will be lowered. If maxfreq
is zero, it
will remain zero but the output frequency will still be
limited.
Step types 2 through 14 are state based, and have from two to five
outputs. On each step, a state counter is incremented or decremented.
Figures [fig:Quad-Three-Phase], [fig:Four-Phase], and
[fig:Five-Phase] show the output patterns as a function of the state
counter. The maximum frequency is 1,000,000,000 divided by steplen
,
and as in the other modes, maxfreq
will be lowered if it is above the
limit.
Five-Phase Step Types. The component exports three functions. Each function acts on all of the step pulse generators - running different generators in different threads is not supported.
(funct)
` stepgen.make-pulses` — High speed function to generate
and count pulses (no floating
point).
(funct)
` stepgen.update-freq` — Low speed function does position
to velocity conversion, scaling
and limiting.
(funct)
` stepgen.capture-position` — Low speed function for
feedback, updates latches and scales
position.
The high speed function stepgen.make-pulses
should be run in a very
fast thread, from 10 to 50uS depending on the
capabilities of the computer. That thread’s period determines the
maximum step frequency, since steplen
, stepspace
, dirsetup
,
dirhold
, and dirdelay
are all rounded up to a integer multiple of
the thread periond in
nanoseconds. The other two functions can be called at a much lower
rate.
This component provides software based generation of PWM (Pulse Width Modulation) and PDM (Pulse Density Modulation) waveforms. It is a realtime component only, and depending on CPU speed, etc, is capable of PWM frequencies from a few hundred Hertz at pretty good resolution, to perhaps 10KHz with limited resolution.
emc2$
<config-array>
is a series of comma separated decimal integers.
Each number causes a
single PWM generator to be loaded, the value of the number determines
the output type. For example:
emc2$
will install three PWM generators. The first one will use output type
0 (PWM only), the next uses output type 1 (PWM and direction) and the
last one uses output type 2 (UP and DOWN). There is no default value,
if <config-array>
is not specified, no PWM generators will be
installed. The maximum
number of frequency generators is 8 (as defined by MAX_CHAN in
pwmgen.c). Each generator is independent, but all are updated by the
same function(s) at the same time. In the following descriptions,
<chan>
is the number of a specific generator. The first generator is
number 0.
Each PWM generator will have the following pins:
(float)
` pwmgen.<chan>.value` — Command value, in arbitrary units.
Will be scaled by the scale
parameter (see below).
(bit)
` pwmgen.<chan>.enable` — Enables or disables the PWM
generator outputs.
Each PWM generator will also have some of these pins, depending on the output type selected:
(bit)
` pwmgen.<chan>.pwm` — PWM (or PDM) output, (output types 0
and 1 only).
(bit)
` pwmgen.<chan>.dir` — Direction output (output type 1 only).
(bit) ``pwmgen.<chan>.up
— PWM/PDM output for positive input value
(output type 2 only).
(bit) ``pwmgen.<chan>.down
— PWM/PDM output for negative input
value (output type 2 only).
(float)
` pwmgen.<chan>.scale` — Scaling factor to convert value
from arbitrary units to duty cycle.
(float)
` pwmgen.<chan>.pwm-freq` — Desired PWM frequency, in Hz.
If 0.0, generates PDM instead of
PWM. If set higher than internal limits, next call of update_freq()
will set it to the internal limit. If non-zero, and dither
is false,
next call of update_freq()
will set it to the nearest integer
multiple of the make_pulses()
function period.
(bit)
` pwmgen.<chan>.dither-pwm` — If true, enables dithering to
achieve average PWM frequencies or
duty cycles that are unobtainable with pure PWM. If false, both the PWM
frequency and the duty cycle will be rounded to values that can be
achieved exactly.
(float)
` pwmgen.<chan>.min-dc` — Minimum duty cycle, between 0.0
and 1.0 (duty cycle will go to
zero when disabled, regardless of this setting).
(float)
` pwmgen.<chan>.max-dc` — Maximum duty cycle, between 0.0
and 1.0.
(float)
` pwmgen.<chan>.curr-dc` — Current duty cycle - after all
limiting and rounding (read only).
The PWM generator supports three different “output types”. Type 0 has
a single output pin. Only positive commands are accepted, negative
values are treated as zero (and will be affected by min-dc
if it is
non-zero). Type 1 has two output pins, one for the PWM/PDM
signal and one to indicate direction. The duty cycle on the PWM pin is
based on the absolute value of the command, so negative values are
acceptable. The direction pin is false for positive commands, and true
for negative commands. Finally, type 2 also has two outputs, called up
and down. For positive commands, the PWM signal appears on the up
output, and the down output remains false. For negative commands, the
PWM signal appears on the down output, and the up output remains false.
Output type 2 is suitable for driving most H-bridges.
The component exports two functions. Each function acts on all of the PWM generators - running different generators in different threads is not supported.
(funct)
` pwmgen.make-pulses` — High speed function to generate PWM
waveforms (no floating point).
(funct)
` pwmgen.update` — Low speed function to scale and limit
value and handle other
paremeters.
The high speed function pwmgen.make-pulses
should be run in a very
fast thread, from 10 to 50uS depending on the
capabilities of the computer. That thread’s period determines the
maximum PWM carrier frequency, as well as the resolution of the PWM or
PDM signals. The other function can be called at a much lower rate.
This component provides software based counting of signals from quadrature encoders. It is a realtime component only, and depending on CPU speed, latency, etc, is capable of maximum count rates of 10kHz to perhaps up to 50kHz. Figure [fig:Encoder-Block-Diag] is a block diagram of one channel of encoder counter.
Encoder Counter Block Diagram.
emc2$
<counters>
is the number of encoder counters that you want to
install. If numchan
is not specified, three counters will be
installed. The maximum
number of counters is 8 (as defined by MAX_CHAN in encoder.c). Each
counter is independent, but all are updated by the same function(s) at
the same time. In the following descriptions, <chan>
is the number
of a specific counter. The first counter is number 0.
encoder.<chan>.counter-mode
(bit, I/O) (default: FALSE) — Enables
counter mode. When true, the
counter counts each rising edge of the phase-A input, ignoring the
value on phase-B. This is useful for counting the output of a single
channel (non-quadrature) sensor. When false, it counts in quadrature
mode.
encoder.<chan>.counts
(s32, Out) — Position in encoder counts.
encoder.<chan>.counts-latched
(s32, Out) — Not used at this time.
encoder.<chan>.index-enable
(bit, I/O) — When True, counts
and
position are
reset to zero on next rising edge of Phase Z. At the
same time, index-enable
is reset to zero to indicate that the rising
edge has occoured. The index-enable
pin is bi-directional. If
index-enable
is False, the Phase Z channel of the encoder will be
ignored, and the
counter will count normally. The encoder driver will never set
index-enable
True. However, some other component may do so.
encoder.<chan>.latch-falling
(bit, In) (default: TRUE) — Not used
at this time.
encoder.<chan>.latch-input
(bit, In) (default: TRUE) — Not used at
this time.
encoder.<chan>.latch-rising
(bit, In) — Not used at this time.
encoder.<chan>.min-speed-estimate
(float, in) — Determine the
minimum true velocity magnitude at which
velocity will be estimated as nonzero and postition-interpolated will
be interpolated. The units of min-speed-estimate
are the same as the
units of velocity
. Scale factor, in counts per length unit. Setting
this parameter too
low will cause it to take a long time for velocity to go to 0 after
encoder pulses have stopped arriving.
encoder.<chan>.phase-A
(bit, In) — Phase A of the quadrature
encoder signal.
encoder.<chan>.phase-B
(bit, In) — Phase B of the quadrature
encoder signal.
encoder.<chan>.phase-Z
(bit, In) — Phase Z (index pulse) of the
quadrature encoder signal.
encoder.<chan>.position
(float, Out) — Position in scaled units
(see position-scale
).
encoder.<chan>.position-interpolated
(float, Out) — Position in
scaled units, interpolated between
encoder counts. The position-interpolated
attempts to interpolate
between encoder counts, based on the most
recently measured velocity. Only valid when velocity is approximately
constant and above min-speed-estimate
. Do not be used for position
control, since its value is incorrect at
low speeds, during direction reversals, and during speed changes.
However, it allows a low ppr encoder (including a one pulse per
revolution "encoder") to be used for lathe threading, and may have
other uses as well.
encoder.<chan>.position-latched (float, Out)
— Not used at this
time.
encoder.<chan>.position-scale (float, I/O)
— Scale factor, in
counts per length unit. For example, if
position-scale is 500, then 1000 counts of the encoder will be reported
as a position of 2.0 units.
encoder.<chan>.rawcounts (s32, In)
— The raw count, as determined
by update-counters. This value is
updated more frequently than counts and position. It is also unaffected
by reset or the index pulse.
encoder.<chan>.reset
(bit, In) — When True, force counts
and
position
to zero immediately.
encoder.<chan>.velocity
(float, Out) — Velocity in scaled units per
second. encoder
uses an algorithm that greatly reduces quantization
noise as compared
to simply differentiating the position
output. When the magnitude
of the true velocity is below
min-velocity-estimate, the velocity output is 0.
encoder.<chan>.x4-mode (bit, I/O) (default: TRUE)
— Enables
times-4 mode. When true, the counter counts each edge of
the quadrature waveform (four counts per full cycle). When false, it
only counts once per full cycle. In counter-mode, this parameter is
ignored. The 1x mode is usefull for some jogwheels.
encoder.<chan>.capture-position.time
(s32, RO)
encoder.<chan>.capture-position.tmax (s32, RW)
encoder.<chan>.update-counters.time (s32, RO)
The component exports two functions. Each function acts on all of the encoder counters - running different counters in different threads is not supported.
(funct)
` encoder.update-counters` — High speed function to count
pulses (no floating point).
(funct)
` encoder.capture-position` — Low speed function to update
latches and scale position.
This component provides Proportional/Integeral/Derivative control loops. It is a realtime component only. For simplicity, this discussion assumes that we are talking about position loops, however this component can be used to implement other feedback loops such as speed, torch height, temperature, etc. Figure [fig:PID-block-diag] is a block diagram of a single PID loop.
emc2$
<loops>
is the number of PID loops that you want to install. If
numchan
is not specified, one loop will be installed. The maximum
number of
loops is 16 (as defined by MAX_CHAN in pid.c). Each loop is completely
independent. In the following descriptions, <loopnum>
is the loop
number of a specific loop. The first loop is number 0.
If debug=1
is specified, the component will export a few extra
parameters that
may be useful during debugging and tuning. By default, the extra
parameters are not exported, to save shared memory space and avoid
cluttering the parameter list.
The three most important pins are
(float) ``pid.<loopnum>.command
— The desired position, as
commanded by another system component.
(float) ``pid.<loopnum>.feedback
— The present position, as
measured by a feedback device such as an
encoder.
(float) ``pid.<loopnum>.output
— A velocity command that attempts
to move from the present position
to the desired position.
For a position loop, command and feedback are in position units. For a linear axis, this could be inches, mm, meters, or whatever is relevant. Likewise, for an angular axis, it could be degrees, radians, etc. The units of the output pin represent the change needed to make the feedback match the command. As such, for a position loop Output is a velocity, in inches/sec, mm/sec, degrees/sec, etc. Time units are always seconds, and the velocity units match the position units. If command and feedback are in meters, then output is in meters per second.
Each loop has two pins which are used to monitor or control the general operation of the component.
(float) ``pid.<loopnum>.error
— Equals .command
minus .feedback
.
(bit) ``pid.<loopnum>.enable
— A bit that enables the loop. If
.enable
is false, all integrators are reset, and the output is
forced to
zero. If .enable
is true, the loop operates normally.
Pins used to report saturation. Saturation occurs when the output of the PID block is at its maximum or minimum limit.
(bit)
pid.<loopnum>.saturated — True when output is saturated.
(float)
pid.<loopnum>.saturated_s — The time the output has been
saturated.
(s32)
pid.<loopnum>.saturated_count — The time the output has been
saturated.
The PID gains, limits, and other tunable features of the loop are implemented as parameters.
(float)
` pid.<loopnum>.Pgain` — Proportional gain
(float)
` pid.<loopnum>.Igain` — Integral gain
(float)
` pid.<loopnum>.Dgain` — Derivative gain
(float)
` pid.<loopnum>.bias` — Constant offset on output
(float)
` pid.<loopnum>.FF0` — Zeroth order feedforward - output
proportional to command
(position).
(float)
` pid.<loopnum>.FF1` — First order feedforward - output
proportional to derivative of
command (velocity).
(float)
` pid.<loopnum>.FF2` — Second order feedforward - output
proportional to 2nd derivative
of command (acceleration)[10].
(float)
` pid.<loopnum>.deadband` — Amount of error that will be
ignored
(float)
` pid.<loopnum>.maxerror` — Limit on error
(float)
` pid.<loopnum>.maxerrorI` — Limit on error integrator
(float)
` pid.<loopnum>.maxerrorD` — Limit on error derivative
(float)
` pid.<loopnum>.maxcmdD` — Limit on command derivative
(float)
` pid.<loopnum>.maxcmdDD` — Limit on command 2nd derivative
(float)
` pid.<loopnum>.maxoutput` — Limit on output value
All of the max???
limits are implemented such that if the parameter
value is zero,
there is no limit.
If debug=1
was specified when the component was installed, four
additional
parameters will be exported:
(float)
` pid.<loopnum>.errorI` — Integral of error.
(float)
` pid.<loopnum>.errorD` — Derivative of error.
(float)
` pid.<loopnum>.commandD` — Derivative of the command.
(float)
` pid.<loopnum>.commandDD` — 2nd derivative of the command.
The component exports one function for each PID loop. This function performs all the calculations needed for the loop. Since each loop has its own function, individual loops can be included in different threads and execute at different rates.
(funct)
` pid.<loopnum>.do_pid_calcs` — Performs all calculations
for a single PID loop.
If you want to understand the exact algorithm used to compute the
output of the PID loop, refer to figure [fig:PID-block-diag], the
comments at the beginning of emc2/src/hal/components/pid.c
, and of
course to the code itself. The loop calculations are in the C
function calc_pid()
.
The simulated encoder is exactly that. It produces quadrature pulses with an index pulse, at a speed controlled by a HAL pin. Mostly useful for testing.
emc2$ *halcmd loadrt sim-encoder num_chan=<number>*
<number>
is the number of encoders that you want to simulate. If not
specified, one encoder will be installed. The maximum number is 8 (as
defined by MAX_CHAN in sim_encoder.c).
(float) ``sim-encoder.<chan-num>.speed
— The speed command for the
simulated shaft.
(bit) ``sim-encoder.<chan-num>.phase-A
— Quadrature output.
(bit) ``sim-encoder.<chan-num>.phase-B
— Quadrature output.
(bit) ``sim-encoder.<chan-num>.phase-Z
— Index pulse output.
When .speed
is positive, .phase-A
leads .phase-B
.
speed
.
The default is 1.0, which means that speed
is in revolutions per
second. Change to 60 for RPM, to 360 for
degrees per second, 6.283185 for radians per seconed, etc.
Note that pulses per revolution is not the same as counts per revolution. A pulse is a complete quadrature cycle. Most encoder counters will count four times during one complete cycle.
The component exports two functions. Each function affects all simulated encoders.
(funct)
` sim-encoder.make-pulses` — High speed function to
generate quadrature pulses (no floating
point).
(funct)
` sim-encoder.update-speed` — Low speed function to read
speed
, do scaling, and set up make-pulses
.
Debounce is a realtime component that can filter the glitches created by mechanical switch contacts. It may also be useful in other applications where short pulses are to be rejected.
emc2$ *halcmd loadrt debounce cfg=<config-string>*
<config-string>
is a series of comma separated decimal integers.
Each number installs
a group of identical debounce filters, the number determines how many
filters are in the group. For example:
emc2$ *halcmd loadrt debounce cfg=1,4,2*
will install three groups of filters. Group 0 contains one filter,
group 1 contains four, and group 2 contains two filters. The default
value for <config-string>
is "1"
which will install a single group
containing a single filter. The
maximum number of groups 8 (as defined by MAX_GROUPS in debounce.c).
The maximum number of filters in a group is limited only by shared
memory space. Each group is completely independent. All filters in a
single group are identical, and they are all updated by the same
function at the same time. In the following descriptions, <G>
is the
group number and <F>
is the filter number within the group. The
first filter is group 0,
filter 0.
Each individual filter has two pins.
(bit) ``debounce.<G>.<F>.in
— Input of filter <F>
in group <G>
.
(bit) ``debounce.<G>.<F>.out
— Output of filter <F>
in group <G>
.
Each group of filters has one parameter[11].
(s32) ``debounce.<G>.delay
— Filter delay for all filters in group
<G>
.
The filter delay is in units of thread periods. The minimum delay is
zero. The output of a zero delay filter exactly follows its input - it
doesn’t filter anything. As delay
increases, longer and longer
glitches are rejected. If delay
is 4, all glitches less than or
equal to four thread periods will be
rejected.
Siggen is a realtime component that generates square, triangle, and sine waves. It is primarily used for testing.
emc2$ *halcmd loadrt siggen [num_chan=<chans>]*
<chans>
is the number of signal generators that you want to install.
If numchan
is not specified, one signal generator will be installed.
The maximum
number of generators is 16 (as defined by MAX_CHAN in siggen.c). Each
generator is completely independent. In the following descriptions,
<chan>
is the number of a specific signal generator (the numbers
start at 0).
Each generator has five output pins.
(float) ``siggen.<chan>.sine
— Sine wave output.
(float) ``siggen.<chan>.cosine
— Cosine output.
(float) ``siggen.<chan>.sawtooth
— Sawtooth output.
(float) ``siggen.<chan>.triangle
— Triangle wave output.
(float) ``siggen.<chan>.square
— Square wave output.
All five outputs have the same frequency, amplitude, and offset.
In addition to the output pins, there are three control pins:
(float) ``siggen.<chan>.frequency
— Sets the frequency in Hertz,
default value is 1 Hz.
(float) ``siggen.<chan>.amplitude
— Sets the peak amplitude of the
output waveforms, default is 1.
(float) ``siggen.<chan>.offset
— Sets DC offset of the output
waveforms, default is 0.
For example, if siggen.0.amplitude
is 1.0 and siggen.0.offset
is
0.0, the outputs will swing from -1.0 to +1.0. If siggen.0.amplitude
is 2.5 and siggen.0.offset
is 10.0, then the outputs will swing from
7.5 to 12.5.
None. [12]
[10] FF2 is not currently implemented, but it will be added. Consider this note a “FIXME” for the code
[11] Each individual filter also has an internal state variable. There is a compile time switch that can export that variable as a parameter. This is intended for testing, and simply wastes shared memory under normal circumstances.
[12] Prior to version 2.1, frequency, amplitude, and offset were parameters. They were changed to pins to allow control by other components.
Parport is a driver for the traditional PC parallel port. The port has a total of 17 physical pins. The original parallel port divided those pins into three groups: data, control, and status. The data group consists of 8 output pins, the control group consists of 4 pins, and the status group consists of 5 input pins.
In the early 1990’s, the bidirectional parallel port was introduced, which allows the data group to be used for output or input. The HAL driver supports the bidirectional port, and allows the user to set the data group as either input or output. If configured as output, a port provides a total of 12 outputs and 5 inputs. If configured as input, it provides 4 outputs and 13 inputs.
In some parallel ports, the control group pins are open collectors, which may also be driven low by an external gate. On a board with open collector control pins, the "x" mode allows a more flexible mode with 8 outputs, and 9 inputs. In other parallel ports, the control group has push-pull drivers and cannot be used as an input.footnote:[HAL cannot automatically determine if the "x" mode bidirectional pins are actually open collectors (OC). If they are not, they cannot be used as inputs, and attempting to drive them LOW from an external source can damage the hardware.
To determine whether your port has "open collector" pins, load hal_parport in "x" mode. With no device attached, HAL should read the pin as TRUE. Next, insert a 470 resistor from one of the control pins to GND. If the resulting voltage on the control pin is close to 0V, and HAL now reads the pin as FALSE, then you have an OC port. If the resulting voltage is far from 0V, or HAL does not read the pin as FALSE, then your port cannot be used in "x" mode.
The external hardware that drives the control pins should also use open collector gates (e.g., 74LS05).
On some machines, BIOS settings may affect whether "x" mode can be used. "SPP" mode is most most likely to work. ]
No other combinations are supported, and a port cannot be changed from input to output once the driver is installed. Figure [fig:Parport-block-diag] shows two block diagrams, one showing the driver when the data group is configured for output, and one showing it configured for input. For "x" mode, refer to the pin listing of "halcmd show pin" for pin direction assignment.
The parport driver can control up to 8 ports (defined by MAX_PORTS in hal_parport.c). The ports are numbered starting at zero.
loadrt hal_parport cfg="<config-string>"
I/O addresses below 16 are treated as port indexes. This is the simplest way to install the parport driver and cooperates with the Linux parport_pc driver if it is loaded.
loadrt hal_parport cfg="0"
Will use the address Linux has detected for parport0.
The configure string consists of a hex port address, followed by an optional direction, repeated for each port. The direction is "in", "out", or "x" and determines the direction of the physical pins 2 through 9, and whether to create input HAL pins for the physical control pins. If the direction is not specified, the data group defaults to output. For example:
loadrt hal_parport cfg="0x278 0x378 in 0x20A0 out"
This example installs drivers for one port at 0x0278, with pins 2-9 as
outputs (by default, since neither "in" nor "out" was specified), one
at 0x0378, with pins 2-9 as inputs, and one at 0x20A0, with pins 2-9
explicitly specified as outputs. Note that you must know the base
address of the parallel port to properly configure the driver. For ISA
bus ports, this is usually not a problem, since the port is almost
always at a "well known" address, like 0278 or 0378 which is typically
configured in the system BIOS. The address for a PCI card is usually
shown in "lspci -v" in an "I/O ports" line, or in the kernel message
log after executing "sudo modprobe -a parport_pc
". There is no
default address; if <config-string>
does not contain at least one
address, it is an error.
(bit) ``parport.<portnum>.pin-<pinnum>-out
— Drives a physical
output pin.
(bit) ``parport.<portnum>.pin-<pinnum>-in
— Tracks a physical input
pin.
(bit) ``parport.<portnum>.pin-<pinnum>-in-not
— Tracks a physical
input pin, but inverted.
For each pin, <portnum>
is the port number, and <pinnum>
is the
physical pin number in the 25 pin D-shell connector.
For each physical output pin, the driver creates a single HAL pin, for
example parport.0.pin-14-out
. Pins 2 through 9 are part of the data
group and are output pins if
the port is defined as an output port. (Output is the default.) Pins 1,
14, 16, and 17 are outputs in all modes. These HAL pins control the
state of the corresponding physical pins.
For each physical input pin, the driver creates two HAL pins, for
example parport.0.pin-12-in
and parport.0.pin-12-in-not
. Pins 10,
11, 12, 13, and 15 are always input pins. Pins 2 through 9
are input pins only if the port is defined as an input port. The -in
HAL pin is TRUE if the physical pin is high, and FALSE if the
physical pin is low. The -in-not
HAL pin is inverted — it is FALSE
if the physical pin is high. By
connecting a signal to one or the other, the user can determine the
state of the input. In "x" mode, pins 1, 14, 16, and 17 are also input
pins.
(bit) ``parport.<portnum>.pin-<pinnum>-out-invert
— Inverts an
output pin.
(bit) ``parport.<portnum>.pin-<pinnum>-out-reset
(only for "out"
pins) — TRUE if this pin should be reset when the -reset
function is
executed.
(U32) parport.<portnum>.reset-time
— The time (in nanoseconds)
between a pin is set by write
and reset by the reset
function if it
is enabled.
The -invert
parameter determines whether an output pin is active
high or active
low. If -invert
is FALSE, setting the HAL -out
pin TRUE drives the
physical pin high, and FALSE drives it low. If -invert
is TRUE, then
setting the HAL -out
pin TRUE will drive the physical pin low.
(funct) ``parport.<portnum>.read
-- Reads physical input pins of port
<portnum>
and updates HAL -in
and -in-not
pins.
(funct) ``parport.read-all
— Reads physical input pins of all ports
and updates HAL -in
and -in-not
pins.
(funct) ``parport.<portnum>.write
— Reads HAL -out
pins of port
<portnum>
and updates that port’s physical output pins.
(funct) ``parport.write-all
— Reads HAL -out
pins of all ports
and updates all physical output pins.
(funct) ``parport.<portnum>.reset
— Waits until reset-time
has
elapsed since the associated write
, then resets pins to values
indicated by -out-invert
and -out-invert
settings. reset
must be
later in the same thread as write. `If `-reset
is TRUE, then the
reset
function will set the pin to the value of -out-invert
. This
can be used in conjunction with stepgen’s doublefreq
to produce one
step per period. The stepgen stepspace for that pin
must be set to 0 to enable doublefreq.
The individual functions are provided for situations where one port
needs to be updated in a very fast thread, but other ports can be
updated in a slower thread to save CPU time. It is probably not a good
idea to use both an -all
function and an individual function at the
same time.
If loading the module reports
insmod: error inserting '/home/jepler/emc2/rtlib/hal_parport.ko': + -1 Device or resource busy
then ensure that the standard kernel module parport_pc
is not
loaded[13] and that no other device in the system has claimed the I/O ports.
If the module loads but does not appear to function, then the port
address is incorrect or the probe_parport
module is required.
To setup DoubleStep on the parallel port you must add the function parport.n.reset after parport.n.write and configure stepspace to 0 and the reset time wanted. So that step can be asserted on every period in HAL and then toggled off by parport after being asserted for time specificed by parport.n.reset-time.
For example:
loadrt hal_parport cfg="0x378 out" setp parport.0.reset-time 5000 loadrt stepgen step_type=0,0,0 addf parport.0.read base-thread addf stepgen.make-pulses base-thread addf parport.0.write base-thread addf parport.0.reset base-thread addf stepgen.capture-position servo-thread ... setp stepgen.0.steplen 1 setp stepgen.0.stepspace 0
In modern PCs, the parallel port may require plug and play (PNP)
configuration before it can be used. The probe_parport
module
performs configuration of any PNP ports present, and should be
loaded before hal_parport
. On machines without PNP ports, it may be
loaded but has no effect.
[13] In the EMC packages for Ubuntu, the file
/etc/modprobe.d/emc2
generally prevents parport_pc
from being automatically loaded.
Halui is a HAL based user interface for EMC, it connects HAL pins to NML commands. Most of the functionality (buttons, indicators etc.) that is provided by a traditional GUI (mini, Axis, etc.), is provided by HAL pins in Halui.
The easiest way to add halui is to add the following to the [HAL] section of the ini file.
HALUI = halui
An alternate way to invoke it (especially when using a stepconf generated config file) is to include the following in your custom.hal file. Make sure you use the actual path to your ini file.
loadusr halui -ini /path/to/inifile.ini
in your custom.hal file.
count-enable
pin must be true.
<n> is a number between 0 and 8 and selected.
<n> is a number between 0 and 8 and selected.
The maximum linear velocity can be adjusted from 0 to the MAX_VELOCITY that is set in the [TRAJ] section of the ini file.
Sometimes the user wants to add more complicated tasks to be performed by the activation of a HAL pin. This is possible using the following MDI commands scheme:
The MDI_COMMAND is added to the ini file in the [HALUI] section.
[HALUI] + MDI_COMMAND = G0 X0
count-enable
pin must be true.
All of these examples assume you are starting with a stepconf based configuration and have two threads base-thread and servo-thread. The stepconf wizard will create an empty custom.hal and a custom_postgui.hal file. The custom.hal file will be loaded after the configuration hal file and the custom_postgui.hal file is loaded after the GUI has been loaded.
In this example it is assumed that your "rolling your own" configuration and wish to add the HAL Manual Toolchange window. The HAL Manual Toolchange is primarily useful if you have presettable tools and you store the offsets in the tool table. If you need to touch off for each tool change then it is best just to split up your g code. To use the HAL Manual Toolchange window you basically have to load the hal manualtoolchange component then send the iocontrol "tool change" to the hal manualtoolchange "change" and send the hal manualtoolchange "changed" back to the iocontrol "tool changed".
This is an example of with the HAL Manual Toolchange from the stepconf wizard
loadusr -W hal_manualtoolchange net tool-change iocontrol.0.tool-change => hal_manualtoolchange.change net tool-changed iocontrol.0.tool-changed <= hal_manualtoolchange.changed net tool-number iocontrol.0.tool-prep-number => hal_manualtoolchange.number net tool-prepare-loopback iocontrol.0.tool-prepare => iocontrol.0.tool-prepared
This is an example of without the HAL Manual Toolchange from the stepconf wizard
net tool-number <= iocontrol.0.tool-prep-number net tool-change-loopback iocontrol.0.tool.-change => iocontrol.0.tool-changed net tool-prepare-loopback iocontrol.0.tool-prepare => iocontrol.0.tool-prepared
This example uses "ddt", "mult2" and "abs" to compute the velocity of a single axis. For more information on the real time components see the man pages or the Realtime Components section ( [sec:Realtime-Components]).
The first thing is to check your configuration to make sure you are not using any of the real time components all ready. You can do this by opening up the HAL Configuration window and look for the components in the pin section. If you are then find the .hal file that they are being loaded in and increase the counts and adjust the instance to the correct value. Add the following to your custom.hal file.
Load the realtime components.
loadrt ddt count=1 + loadrt mult2 count =1 + loadrt abs count=1
Add the functions to a thread so it will get updated.
addf ddt.0 servo-thread + addf mult2.0 servo-thread + addf abs.0 servo-thread
Make the connections.
setp mult2.in1 60 + net xpos-cmd ddt.0.in + net X-IPS mult2.0.in0 <= ddt.0.out + net X-ABS abs.0.in <= mult2.0.out + net X-IPM abs.0.out
In this last section we are setting the mult2.0.in1 to 60 to convert the inch per second to inch per minute that we get from the ddt.0.out.
The xpos-cmd sends the comanded position to the ddt.0.in. The ddt computes the derivative of the change of the input.
The ddt2.0.out is multiplyed by 60 to give IPM.
The mult2.0.out is sent to the abs to get the absolute value.
The following figure shows the result when the X axis is moving at 15 IPM in the minus direction. Notice that we can get the absolute value from either the abs.0.out pin or the X-IPM signal.
This example shows how the HAL components "lowpass", "limit2" or "limit3" can be used to limit how fast a signal changes.
In this example we have a servo motor driving a lathe spindle. If we just used the commanded spindle speeds on the servo it will try and go from present speed to commanded speed as fast as it can. This could cause a problem or damage the drive. To slow the rate of change we can send the motion.spindle-speed-out through a limiter before the PID, so that the PID command value varies slowly.
Three built-in components that limit a signal are:
To find more information on these HAL components check the man pages.
Place the following in a text file called softstart.hal. If you’re not familiar with Linux place the file in your home directory.
############################## + loadrt threads period1=1000000 name1=thread + loadrt siggen + loadrt lowpass + loadrt limit2 + loadrt limit3 + net square siggen.0.square => lowpass.0.in limit2.0.in limit3.0.in + net lowpass <= lowpass.0.out + net limit2 <= limit2.0.out + net limit3 <= limit3.0.out + setp siggen.0.frequency .1 + setp lowpass.0.gain .01 + setp limit2.0.maxv 2 + setp limit3.0.maxv 2 + setp limit3.0.maxa 10 + addf siggen.0.update thread + addf lowpass.0 thread + addf limit2.0 thread + addf limit3.0 thread + start + loadusr halscope + ##############################
Open a terminal window and run the file with the following command.
halrun -I softstart.hal
When the HAL Oscilloscope first starts up click "OK" to accept the default thread.
Next you have to add the signals to the channels. Click on channel 1 then select "square" from the Signals tab. Repeat for channels 2-4 and add lowpass, limit2, and limit3.
Next to set up a trigger signal click on the Source None button and select square. The button will change to Source Chan 1.
Next click on Single in the Run Mode radio buttons box. This will start a run and when it finishes you will see your traces.
To separate the signals so you can see them better click on a channel then use the Pos slider in the Vertical box to set the positions.
To see the effect of changing the set point values of any of the components you can change them in the terminal window. To see what different gain settings do for lowpass just type the following in the terminal window and try different settings.
setp lowpass.0.gain .01
After changing a setting run the oscilloscope again to see the change.
When you’re finished type "exit" in the terminal window to shut down halrun and close the halscope. Don’t close the terminal window with halrun running as it might leave some things in memory that could prevent EMC from loading.
For more information on HalScope see the HAL manual.
Python Virtual Control Panel
The pyVCP (python Virtual Control Panel) is designed to give the integrator the ability to customize the AXIS interface with buttons and indicators to do special tasks.
Hardware machine control panels can use up a lot of I/O pins and can be expensive. That is where Virtual Control Panels have the advantage as well as it cost nothing to build a pyVCP.
Virtual control panels can be used for testing or monitoring things to temporarily replace real I/O devices while debugging ladder logic, or to simulate a physical panel before you build it and wire it to an I/O board.
The following graphic displays many of the pyVCP widgets.
The layout of a pyVCP panel is specified with an XML file that contains widget tags between <pyvcp> and </pyvcp>. For example:
<pyvcp>
If you place this text in a file called tiny.xml, and run
halrun -I loadusr pyvcp -c mypanel tiny.xml
pyVCP will create the panel for you, which includes two widgets, a Label with the text "This is a LED indicator", and a LED, used for displaying the state of a HAL BIT signal. It will also create a HAL component named "mypanel" (all widgets in this panel are connected to pins that start with "mypanel."). Since no <halpin> tag was present inside the <led> tag, pyVCP will automatically name the HAL pin for the LED widget mypanel.led.0
For a list of widgets and their tags and options, see the widget reference below.
Once you have created your panel, connecting HAL signals to and from the pyVCP pins is done with the halcmd:
net <signal-name> <pin-name> <opt-direction> <opt-pin-name>signal-name
If you are new to HAL, the HAL basics chapter in the Integrators Manual is a good place to start.
Parts of pyVCP files are evaluated as Python code, and can take any action available to Python programs. Only use pyVCP .xml files from a source that you trust.
Since AXIS uses the same GUI toolkit (Tkinter) as pyVCP, it is possible to include a pyVCP panel on the right side of the normal AXIS user interface. A typical example is explained below.
Place your pyVCP XML file describing the panel in the same directory where your .ini file is. Say we we want to display the current spindle speed using a Bar widget. Place the following in a file called spindle.xml:
<pyvcp>
Here we’ve made a panel with a Label and a Bar widget, specified that the HAL pin connected to the Bar should be named "spindle-speed", and set the maximum value of the bar to 5000 (see widget reference below for all options). To make AXIS aware of this file, and call it at start up, we need to specify the following in the [DISPLAY] section of the .ini file:
PYVCP = spindle.xml
To make our widget actually display the spindle-speed it needs to be hooked up to the appropriate HAL signal. A .hal file that will be run once AXIS and pyVCP have started can be specified in the [HAL] section of the .ini file:
POSTGUI_HALFILE = spindle_to_pyvcp.hal
This change will run the HAL commands specified in "spindle_to_pyvcp.hal". In our example the contents could look like this:
net spindle-rpm-filtered => pyvcp.spindle-speed
assuming that a signal called "spindle-rpm-filtered" already exists. Note that when running together with AXIS, all pyVCP widget HAL pins have names that start with "pyvcp.".
This is what the newly created pyVCP panel should look like in AXIS.
The sim/lathe
configuration is already configured this way.
This section describes how pyVCP panels can be displayed on their own with or without EMC’s machine controller.
To load a stand alone pyVCP panel with EMC use these commands:
loadusr -Wn mypanel pyvcp -g WxH+X+Y -c mypanel <path/>panel_file.xml
You would use this if you wanted a floating panel or a panel with a GUI other than AXIS.
"X Anchor"
"Y Anchor". You can set the size
or position or both. The anchor point is the upper left corner of the
panel. An example is -g 250x500+800+0 This sets the panel at 250 pixels
wide, 500 pixels tall, and anchors it at X800 Y0.
To load a "stand alone" pyVCP panel without EMC use this command:
loadusr -Wn mypanel pyvcp -g 250x500+800+0 -c mypanel mypanel.xml
The minimum command to load a pyvcp panel is:
loadusr pyvcp mypanel.xml
You would use this if you want a panel without EMC’s machine controller such as for testing or a standalone DRO.
The loadusr command is used when you also load a component that will stop HAL from closing until it’s done. If you loaded a panel and then loaded ClassicLadder using -w ClassicLadder CL would hold HAL open (and the panel) until you closed CL -Wn means wait for the component "panelname" to become ready. "panelname" can be any name. note the capital W and lowercase n. The -c tells pyVCP to build a panel with the name "panelname" using the info in "panel_file_name.xml" "panel_file_name.xml" can be any name but must end in .xml - it is the file that describes how to build the panel. You must add the path name if the panel is not in the directory that the HAL script is in.
An optional command to use if you want the panel to stop HAL from continuing commands / shutting down. After loading any other components you want the last HAL command to be:
waituser panelname
This tells HAL to wait for component "panelname" to close before continuing HAL commands This is usually set as the last command so that HAL shuts down when the panel is closed.
HAL signals come in two variants, bits and numbers. Bits are off/on signals. Numbers can be "float", "s32" or "u32". For more information on HAL data types see the [sec:Hal-Data] section. The pyVCP widget can either display the value of the signal with an indicator widget, or modify the signal value with a control widget. Thus there are four classes of pyVCP widgets that you can connect to a HAL signal. A fifth class of helper widgets allow you to organize and label your panel.
Each widget is described briefly, followed by the markup used, and a screen shot. All tags inside the main widget tag are optional.
At the present time, both a tag-based and an attribute-based syntax are supported. For instance, the following XML fragments are treated identically:
<led halpin="my-led"/>
and
<led><halpin>"my-led"</halpin></led>
When the attribute-based syntax is used, the following rules are used to turn the attributes value into a Python value:
{(["'
When the tag-based syntax is used, the text within the tag is always evaluated as a Python expression.
The examples below show a mix of formats.
Edit the XML file with a text editor. In most cases you can right click on the file and select "open with text editor" or similar.
Colors can be specified using the X11 rgb colors by name "gray75" or hex "#0000ff”. A complete list is located here http://sedition.com/perl/rgb.html.
Common Colors (colors with numbers indicate shades of that color)
HAL pins provide a means to "connect" the widget to something. Once you create a HAL pin for your widget you can "connect" it to another HAL pin with a "net" command in a .hal file. For more information on the "net" command see the HAL Commands section ( [sec:Hal-Commands]).
A label is a piece of text on your panel.
The label has an optional disable pin that is created when you add <disable_pin>True</disable_pin>.
<label>
The above code produced this example.
A LED is used to indicate the status of a "bit" halpin. The LED color will be on_color when the halpin is true, and off_color otherwise.
A button is used to control a BIT pin. The pin will be set True when the button is pressed and held down, and will be set False when the button is released. Buttons can use the following formatting options
A text button controls a "bit" halpin. The halpin is false until the button is pressed then it is true. The button is a momentary button.
The text button has an optional disable pin that is created when you add <disable_pin>True</disable_pin>.
<button>
The above code produced this example.
A checkbutton controls a "bit" halpin. The halpin will be set True when the button is checked, and false when the button is unchecked. The checkbutton is a toggle type button.
<checkbutton>
The above code produced this example. The coolant checkbutton is checked. Notice the extra spaces in the Chips text to keep the checkbuttons aligned.
A radiobutton will set one of the halpins true. The other pins are set false.
<radiobutton>
The above code produced this example.
Note that the HAL pins in the example above will me named my-radio.one, my-radio.two, and my-radio.three. In the image above, "one" is the selected value.
Number displays can use the following formatting options
The number widget displays the value of a float signal.
<number>
The above code produced this example.
<font> is a Tkinter font type and size specification. One font that will show up to at least size 200 is "courier 10 pitch", so for a really big Number widget you could specify:
<font>("courier 10 pitch",100)</font>
The s32 number widget displays the value of a s32 number. The syntax is the same as "number" except the name which is <s32>. Make sure the width is wide enough to cover the largest number you expect to use.
<s32>
The above code produced this example.
The u32 number widget displays the value of a u32 number. The syntax is the same as "number" except the name which is <u32>.
A bar widget displays the value of a FLOAT signal both graphically using a bar display and numerically.
<bar>
The above code produced this example.
Spinbox controls a FLOAT pin. You increase or decrease the value of the pin by either pressing on the arrows, or pointing at the spinbox and rolling your mouse-wheel.
<spinbox>
The above code produced this example.
Scale controls a float or a s32 pin. You increase or decrease the value of the pin be either dragging the slider, or pointing at the scale and rolling your mouse-wheel. The "halpin" will have both "-f" and "-i" added to it to form the float and s32 pins. Width is the width of the slider in vertical and the height of the slider in horizontal orientation.
<scale> <scale>
The above code produced this example.
The Dial outputs a HAL float and reacts to both mouse wheel and dragging. Double left click to increase the resolution and double right click to reduce the resolution by one digit. The output is capped by the min and max values. The <cpr> is how many tick marks are on the outside of the ring (beware of high numbers).
<dial>
The above code produced this example.
Image displays use only .gif image format. All of the images must be the same size. The images must be in the same directory as your ini file (or in the current directory if running from the command line with halrun/halcmd).
The "image_bit" toggles between two images by setting the halpin to true or false.
<image name='fwd' file='fwd.gif'/>
This example was produced from the above code. Using the two image files fwd.gif and rev.gif. FWD is displayed when "selectimage" is false and REV is displayed when "selectimage" is true.
The "image_u32" is the same as "image_bit" except you have essentially an unlimited number of images and you "select" the image by setting the halpin to a integer value with 0 for the first image in the images list and 1 for the second image etc.
<image name='stb' file='stb.gif'/>
The above code produced the following example by adding the stb.gif image.
Notice that the default is the min even though it is set higher than max unless there is a negative min.
Containers are widgets that contain other widgets. Containers are used to group other widgets.
Container borders are specified with two tags used together. The <relief> tag specifies the type of border and the <bd> specifies the width of the border.
Where "n" is the width of the border.
<hbox> <button> <button> <button> <button>
The above code produced this example.
Use a Hbox when you want to stack widgets horizontally next to each other.
<hbox>
The above code produced this example.
Inside a Hbox, you can use the <boxfill fill=""/>
, <boxanchor
anchor=""/>
, and <boxexpand expand=""/>
tags to choose how items in
the box behave when the window is
re-sized. For details of how fill, anchor, and expand behave, refer to
the Tk pack
manual page, pack(3tk)
. By default, fill="y"
,
anchor="center"
, expand="yes"
.
Use a Vbox when you want to stack widgets vertically on top of each other.
<vbox>
The above code produced this example.
Inside a Hbox, you can use the <boxfill fill=""/>
, <boxanchor
anchor=""/>
, and <boxexpand expand=""/>
tags to choose how items in
the box behave when the window is
re-sized. For details of how fill, anchor, and expand behave, refer to
the Tk pack
manual page, pack(3tk)
. By default, fill="x"
,
anchor="center"
, expand="yes"
.
A labelframe is a frame with a groove and a label at the upper-left corner.
<labelframe text="Group Title">
The above code produced this example.
A table is a container that allows layout in a grid of rows and
columns. Each row is started by a <tablerow/>
tag. A contained
widget may span rows or columns through the use of
the <tablespan rows= cols=/>
tag. The sides of the cells to which
the contained widgets “stick”
may be set through the use of the <tablesticky sticky=/>
tag. A
table expands on its flexible rows and columns.
Example:
<table flexible_rows="[2]" flexible_columns="[1,4]">
The above code produced this example.
To create a pyVCP panel to use with the AXIS interface that is attached to the right of AXIS you need to do the following basic things.
To create floating pyVCP panels that can be used with any interface you need to do the following basic things.
The following is an example of a loadusr command to load two pyVCP panels and name each one so the connection names in HAL will be known.
loadusr -Wn btnpanel pyvcp -c btnpanel panel1.xml + loadusr -Wn sppanel pyvcp -c sppanel panel2.xml
The -Wn makes hal "Wait for name" to be loaded before proceeding. The pyvcp -c makes pyVCP name the panel.
The HAL pins from panel1.xml will be named btnpanel.<pin name>
The HAL pins from panel2.xml will be named sppanel.<pin name>
Make sure the loadusr line is before any net’s that make use of the pyVCP pins.
In this example we will create a pyVCP panel with jog buttons for X, Y, and Z. This configuration will be built upon a Stepconf Wizard generated configuration. First we run the Stepconf Wizard and configure our machine, then on the Advanced Configuration Options page we make a couple of selections to add a blank pyVCP panel as shown in the following figure. For this example we named the configuration "pyvcp_xyz" on the Basic Machine Information page of the Stepconf Wizard.
The Stepconf Wizard will create several files and place them in the /emc/configs/pyvcp_xyz directory. If you left the create link checked you will have a link to those files on your desktop.
Open up the custompanel.xml file by right clicking on it and selecting "open with text editor". Between the <pyvcp></pyvcp> tags we will add the widgets for our panel.
Look in the pyVCP Widgets Reference section of the manual for more detailed information on each widget.
In your custompanel.xml file we will add the description of the widgets.
<pyvcp>
<labelframe text="Jog Buttons"> + <font>("Helvetica",16)</font>
<!--- the X jog buttons ---> + <hbox> + <relief>RAISED</relief> + <bd>3</bd> + <button> + <font>("Helvetica",20)</font> + <width>3</width> + <halpin>"x-plus"</halpin> + <text>"X+"</text> + </button> + <button> + <font>("Helvetica",20)</font> + <width>3</width> + <halpin>"x-minus"</halpin> + <text>"X-"</text> + </button> + </hbox>
<!--- the Y jog buttons ---> + <hbox> + <relief>RAISED</relief> + <bd>3</bd> + <button> + <font>("Helvetica",20)</font> + <width>3</width> + <halpin>"y-plus"</halpin> + <text>"Y+"</text> + </button> + <button> + <font>("Helvetica",20)</font> + <width>3</width> + <halpin>"y-minus"</halpin> + <text>"Y-"</text> + </button> + </hbox>
<!--- the Z jog buttons ---> + <hbox> + <relief>RAISED</relief> + <bd>3</bd> + <button> + <font>("Helvetica",20)</font> + <width>3</width> + <halpin>"z-plus"</halpin> + <text>"Z+"</text> + </button> + <button> + <font>("Helvetica",20)</font> + <width>3</width> + <halpin>"z-minus"</halpin> + <text>"Z-"</text> + </button> + </hbox>
<!--- the jog speed slider ---> + <vbox> + <relief>RAISED</relief> + <bd>3</bd> + <label> + <text>"Jog Speed"</text> + <font>("Helvetica",16)</font> + </label> + <scale> + <font>("Helvetica",14)</font> + <halpin>"jog-speed"</halpin> + <resolution>1</resolution> + <orient>HORIZONTAL</orient> + <min_>0</min_> + <max_>80</max_> + </scale> + </vbox>
</labelframe>
</pyvcp>
After adding the above you now will have a pyVCP panel that looks like the following attached to the right side of AXIS. It looks nice but it does not do anything until you "connect" the buttons to halui. If you get an error when you try and run scroll down to the bottom of the pop up window and usually the error is a spelling or syntax error and it will be there.
.Jog Buttons[[cap:Jog-Buttons]]
image::images/xyz_buttons.png[]
To make the connections needed open up your custom_postgui.hal file and add the following.
# connect the X pyVCP buttons + net my-jogxminus halui.jog.0.minus <= pyvcp.x-minus + net my-jogxplus halui.jog.0.plus <= pyvcp.x-plus
# connect the Y pyVCP buttons + net my-jogyminus halui.jog.1.minus <= pyvcp.y-minus + net my-jogyplus halui.jog.1.plus <= pyvcp.y-plus
# connect the Z pyVCP buttons + net my-jogzminus halui.jog.2.minus <= pyvcp.z-minus + net my-jogzplus halui.jog.2.plus <= pyvcp.z-plus
# connect the pyVCP jog speed slider + net my-jogspeed halui.jog-speed <= pyvcp.jog-speed-f
After resetting the E-Stop and putting it into jog mode and moving the jog speed slider in the pyVCP panel to a value greater than zero the pyVCP jog buttons should work. You can not jog when running a g code file or while paused or while the MDI tab is selected.
This example shows you how to make a simple parallel port tester using pyVCP and HAL.
First create the ptest.xml file with the following code to create the panel description.
<!-- Test panel for the parallel port cfg for out -->
This will create the following floating panel which contains a couple of in pins and a couple of out pins.
To run the HAL commands that we need to get everything up and running we put the following in our ptest.hal file.
loadrt hal_parport cfg="0x378 out"
To run the HAL file we use the following command from a terminal window.
~$
The following figure shows what a complete panel might look like.
To add the rest of the parallel port pins just modify the .xml and .hal files.
To show the pins after running the HAL script use the following command at the halcmd prompt:
halcmd:
This will show you what pins are IN and what pins are OUT as well as any connections.
The following example uses the Automation Direct GS2 VDF driver and displays the RPM and other info in a pyVCP panel. This example is based on the GS2 example in the Hardware Examples section this manual.
To create the panel we add the following to the .xml file.
<pyvcp>
<!-- the RPM meter --> + <hbox> + <relief>RAISED</relief> + <bd>3</bd> + <meter> + <halpin>"spindle_rpm"</halpin> + <text>"Spindle"</text> + <subtext>"RPM"</subtext> + <size>200</size> + <min_>0</min_> + <max_>3000</max_> + <majorscale>500</majorscale> + <minorscale>100</minorscale> + <region1>0,10,"yellow"</region1> + </meter> + </hbox>
<!-- the On Led --> + <hbox> + <relief>RAISED</relief> + <bd>3</bd> + <vbox> + <relief>RAISED</relief> + <bd>2</bd> + <label> + <text>"On"</text> + <font>("Helvetica",18)</font> + </label> + <width>5</width> + <hbox> + <label width=”2”/> <!-- used to center the led --> + <rectled> + <halpin>"on-led"</halpin> + <height>"30"</height> + <width>"30"</width> + <on_color>"green"</on_color> + <off_color>"red"</off_color> + </rectled> + </hbox> + </vbox>
<!-- the FWD Led --> + <vbox> + <relief>RAISED</relief> + <bd>2</bd> + <label> + <text>"FWD"</text> + <font>("Helvetica",18)</font> + <width>5</width> + </label> + <label width=”2”/> + <rectled> + <halpin>"fwd-led"</halpin> + <height>"30"</height> + <width>"30"</width> + <on_color>"green"</on_color> + <off_color>"red"</off_color> + </rectled> + </vbox>
<!-- the REV Led --> + <vbox> + <relief>RAISED</relief> + <bd>2</bd> + <label> + <text>"REV"</text> + <font>("Helvetica",18)</font> + <width>5</width> + </label> + <label width=”2”/> + <rectled> + <halpin>"rev-led"</halpin> + <height>"30"</height> + <width>"30"</width> + <on_color>"red"</on_color> + <off_color>"green"</off_color> + </rectled> + </vbox> + </hbox> + </pyvcp>
The above gives us a pyVCP panel that looks like the following.
To make it work we add the following code to the custom_postgui.hal file.
# display the rpm based on freq * rpm per hz + loadrt mult2 + addf mult2.0 servo-thread + setp mult2.0.in1 28.75 + net cypher_speed mult2.0.in0 <= spindle-vfd.frequency-out + net speed_out pyvcp.spindle_rpm <= mult2.0.out
# run led + net gs2-run => pyvcp.on-led
# fwd led + net gs2-fwd => pyvcp.fwd-led
# rev led + net running-rev spindle-vfd.spindle-rev => pyvcp.rev-led
Some of the lines might need some explanations. The fwd led line uses the signal created in the custom.hal file where as the rev led needs to use the spindle-rev bit. You can’t link the spindle-fwd bit twice so you use the signal that it was linked to.
The Axiom Measurement & Control AX5214H is a 48 channel digital I/O board. It plugs into an ISA bus, and resembles a pair of 8255 chips. In fact it may be a pair of 8255 chips, but I’m not sure. If/when someone starts a driver for an 8255 they should look at the ax5214 code, much of the work is already done.
loadrt hal_ax5214h cfg="<config-string>"
The config string consists of a hex port address, followed by an 8 character string of "I" and "O" which sets groups of pins as inputs and outputs. The first two character set the direction of the first two 8 bit blocks of pins (0-7 and 8-15). The next two set blocks of 4 pins (16-19 and 20-23). The pattern then repeats, two more blocks of 8 bits (24-31 and 32-39) and two blocks of 4 bits (40-43 and 44-47). If more than one board is installed, the data for the second board follows the first. As an example, the string "0x220 IIIOIIOO 0x300 OIOOIOIO" installs drivers for two boards. The first board is at address 0x220, and has 36 inputs (0-19 and 24-39) and 12 outputs (20-23 and 40-47). The second board is at address 0x300, and has 20 inputs (8-15, 24-31, and 40-43) and 28 outputs (0-7. 16-23, 32-39, and 44-47). Up to 8 boards may be used in one system.
For each pin, <boardnum> is the board number (starts at zero), and <pinnum> is the I/O channel number (0 to 47).
Note that the driver assumes active LOW signals. This is so that modules such as OPTO-22 will work correctly (TRUE means output ON, or input energized). If the signals are being used directly without buffering or isolation the inversion needs to be accounted for. The in- HAL pin is TRUE if the physical pin is low (OPTO-22 module energized), and FALSE if the physical pin is high (OPTO-22 module off). The in-<pinnum>-not HAL pin is inverted — it is FALSE if the physical pin is low (OPTO-22 module energized). By connecting a signal to one or the other, the user can determine the state of the input.
The -invert parameter determines whether an output pin is active high or active low. If -invert is FALSE, setting the HAL out- pin TRUE drives the physical pin low, turning ON an attached OPTO-22 module, and FALSE drives it high, turning OFF the OPTO-22 module. If -invert is TRUE, then setting the HAL out- pin TRUE will drive the physical pin high and turn the module OFF.
This is a userspace HAL program for the GS2 series of VFD’s at Automation Direct.
This component is loaded using the halcmd "loadusr" command:
loadusr -Wn spindle-vfd gs2_vfd -n spindle-vfd + (loadusr, wait for named to load, component gs2_vfd, named spindle-vfd)
The command-line options are:
Pins where <name> is the name given during loading with the -n option.
Parameters where <name> is the name given during loading with the -n option.
An example of using this component to drive a spindle is in the Hardware Examples section of this manual.
HostMot2 is an FPGA configuration developed by Mesa Electronics for their line of "Anything I/O" motion control cards. The firmware is open source, portable and flexible. It can be configured (at compile-time) with zero or more instances (an object created at runtime) of each of several Modules: encoders (quadrature counters), PWM generators, and step/dir generators. The firmware can be configured (at run-time) to connect each of these instances to pins on the I/O headers. I/O pins not driven by a Module instance revert to general-purpose bi-directional digital I/O.
Several pre-compiled HostMot2 firmware binaries are available for the different Anything I/O boards. (This list is incomplete, check the hostmot2-firmware distribution for up-to-date firmware lists.)
5i20, 5i23, 4i65, 4i68 (72 I/O pins): using hm2_pci module
5i22 (96 I/O pins): using hm2_pci module
3x20 (144 I/O pins): using hm2_pci module
7i43 (48 I/O pins): using hm2_7i43 module
Depending on how you installed EMC2 you may have to open the Synaptic Package Manager from the System menu and install the package for your Mesa card. The quickest way to find them is to do a search for "hostmot2" in the Synaptic Package Manager. Mark the firmware for installation then apply.
The EMC support for the HostMot2 firmware is split into a generic driver called "hostmot2" and two low-level I/O drivers for the Anything I/O boards. The low-level I/O drivers are "hm2_7i43" and "hm2_pci" (for all the PCI- and PC-104/Plus-based AnyIO boards). The hostmot2 driver must be loaded first, using a HAL command like this:
loadrt hostmot2
See the hostmot2(9) man page for details.
The hostmot2 driver by itself does nothing, it needs access to actual boards running the HostMot2 firmware. The low-level I/O drivers provide this access. The low-level I/O drivers are loaded with commands like this:
loadrt hm2_pci config="firmware=hm2/5i20/SVST8_4.BIT num_encoders=3 num_pwmgens=3 num_stepgens=1"
The config parameters are described in the hostmot2 man page.
The HostMot2 firmware may include a watchdog Module; if it does, the hostmot2 driver will use it.
The watchdog must be petted by EMC2 periodically or it will bite.
When the watchdog bites, all the board’s I/O pins are disconnected from their Module instances and become high-impedance inputs (pulled high), and all communication with the board stops. The state of the HostMot2 firwmare modules is not disturbed (except for the configuration of the IO Pins). Encoder instances keep counting quadrature pulses, and pwm- and step-generators keep generating signals (which are not relayed to the motors, because the IO Pins have become inputs).
Resetting the watchdog resumes communication and resets the I/O pins to the configuration chosen at load-time.
If the firmware includes a watchdog, the following HAL objects will be exported:
has_bit:::
(bit i/o) True if the watchdog has bit, False if the watchdog has not bit. If the watchdog has bit and the has_bit bit is True, the user can reset it to False to resume operation.
timeout_ns:::
(u32 read/write) Watchdog timeout, in nanoseconds. This is initialized to 1,000,000,000 (1 second) at module load time. If more than this amount of time passes between calls to the pet_watchdog() function, the watchdog will bite.
The hostmot2 driver does not have a particular pinout. The pinout comes from the firmware that the hostmot2 driver sends to the AnyIO board. Each firmware has different pinout, and the pinout depends on how many of the available encoders, pwmgens, and stepgens are used. To get a pinout list for your configuration after loading EMC2 in the terminal window type:
dmesg > hm2.txt
The resulting text file will contain lots of information as well as the pinout for the HostMot2 and any error and warning messages.
To reduce the clutter by clearing the message buffer before loading EMC type the following in the terminal window:
sudo dmesg -c
Now when you run EMC and then do a "dmesg > hm2.txt" in the terminal only the info from the time you loaded EMC will be in your file along with your pinout. The file will be in the current directory of the terminal window. Each line will contain the card name, the card number, the I/O Pin number, the connector and pin, and the usage. From this printout you will know the physical connections to your card based on your configuration.
An example of a 5i20 configuration:
[HOSTMOT2] + DRIVER=hm2_pci + BOARD=5i20 + CONFIG="firmware=hm2/5i20/SVST8_4.BIT num_encoders=1 num_pwmgens=1 num_stepgens=3"
The above configuration produced this printout .
[ 1141.053386] hm2/hm2_5i20.0: 72 I/O Pins used: + [ 1141.053394] hm2/hm2_5i20.0: IO Pin 000 (P2-01): IOPort + [ 1141.053397] hm2/hm2_5i20.0: IO Pin 001 (P2-03): IOPort + [ 1141.053401] hm2/hm2_5i20.0: IO Pin 002 (P2-05): Encoder #0, pin B (Input) + [ 1141.053405] hm2/hm2_5i20.0: IO Pin 003 (P2-07): Encoder #0, pin A (Input) + [ 1141.053408] hm2/hm2_5i20.0: IO Pin 004 (P2-09): IOPort + [ 1141.053411] hm2/hm2_5i20.0: IO Pin 005 (P2-11): Encoder #0, pin Index (Input) + [ 1141.053415] hm2/hm2_5i20.0: IO Pin 006 (P2-13): IOPort + [ 1141.053418] hm2/hm2_5i20.0: IO Pin 007 (P2-15): PWMGen #0, pin Out0 (PWM or Up) (Output) + [ 1141.053422] hm2/hm2_5i20.0: IO Pin 008 (P2-17): IOPort + [ 1141.053425] hm2/hm2_5i20.0: IO Pin 009 (P2-19): PWMGen #0, pin Out1 (Dir or Down) (Output) + [ 1141.053429] hm2/hm2_5i20.0: IO Pin 010 (P2-21): IOPort + [ 1141.053432] hm2/hm2_5i20.0: IO Pin 011 (P2-23): PWMGen #0, pin Not-Enable (Output) + <snip>... + [ 1141.053589] hm2/hm2_5i20.0: IO Pin 060 (P4-25): StepGen #2, pin Step (Output) + [ 1141.053593] hm2/hm2_5i20.0: IO Pin 061 (P4-27): StepGen #2, pin Direction (Output) + [ 1141.053597] hm2/hm2_5i20.0: IO Pin 062 (P4-29): StepGen #2, pin (unused) (Output) + [ 1141.053601] hm2/hm2_5i20.0: IO Pin 063 (P4-31): StepGen #2, pin (unused) (Output) + [ 1141.053605] hm2/hm2_5i20.0: IO Pin 064 (P4-33): StepGen #2, pin (unused) (Output) + [ 1141.053609] hm2/hm2_5i20.0: IO Pin 065 (P4-35): StepGen #2, pin (unused) (Output) + [ 1141.053613] hm2/hm2_5i20.0: IO Pin 066 (P4-37): IOPort + [ 1141.053616] hm2/hm2_5i20.0: IO Pin 067 (P4-39): IOPort + [ 1141.053619] hm2/hm2_5i20.0: IO Pin 068 (P4-41): IOPort + [ 1141.053621] hm2/hm2_5i20.0: IO Pin 069 (P4-43): IOPort + [ 1141.053624] hm2/hm2_5i20.0: IO Pin 070 (P4-45): IOPort + [ 1141.053627] hm2/hm2_5i20.0: IO Pin 071 (P4-47): IOPort + [ 1141.053811] hm2/hm2_5i20.0: registered + [ 1141.053815] hm2_5i20.0: initialized AnyIO board at 0000:02:02.0
Note that the IO Pin nnn will correspond to the pin number shown on the HAL Configuration screen for GPIO’s. Some of the Stepgen, Encoder and PWMGen will also show up as GPIO’s in the HAL Configuration screen.
The default pinout is described in a .PIN file. When you install a firmware package .deb, the .PIN file is installed in
/usr/share/doc/hostmot2-firmware-mesa-<board>-hostmot2
If you are using Run In Place, you must still install a hostmot2-firmware package.[14]
The HAL pins for each configuration can be seen by opening up "Show HAL Configuration" from the Machine menu. All the HAL pins and parameters can be found there. The following figure is of the 5i20 configuration used above.
5i20 & 5i23 Default Configurations
Firmware | 5i20 | 5i23 | Encoder | PWM | StepGen | GPIO |
---|---|---|---|---|---|---|
SV12 | X | X | 12 | 12 | 0 | 0 |
SVST2_8 | X | 2 | 2 | 8 | 12 | |
SVST2_4_7I47 | X | X | 4 | 2 | 4 | 48 |
SVST4_8 | X | 4 | 4 | 8 | 0 | |
SVST8_4 | X | X | 8 | 8 | 4 | 0 |
SVST8_4IM2 | X | X | 8 | 8 | 4 | 8 |
SVST8_8IM2 | X | 8 | 8 | 8 | 0 |
4i65 & 4i68 Default Configurations
Firmware | 4i65 | 4i68 | Encoder | PWM | StepGen | GPIO |
---|---|---|---|---|---|---|
SV12 | X | X | 12 | 12 | 0 | 0 |
SVST4_8 | X | 4 | 4 | 8 | 0 | |
SVST8_4 | X | X | 8 | 8 | 4 | 0 |
SVST8_4IM2 | X | X | 8 | 8 | 4 | 8 |
SVST8_8IM2 | X | 8 | 8 | 8 | 0 |
7i43 Default Configurations
Firmware | 200K | 400K | Encoder | PWM | StepGen | GPIO |
---|---|---|---|---|---|---|
SV8B | X | 8 | 8 | 0 | 0 | |
SV8S | X | 8 | 8 | 0 | 0 | |
SVST2_4_7I47 | X | X | 4 | 2 | 4 | |
SVST4_4B | X | 4 | 4 | 4 | 0 | |
SVST4_4S | X | 4 | 4 | 4 | 0 | |
SVST4_6B | X | 4 | 4 | 6 | 0 | |
SVST4_6S | X | 4 | 4 | 6 | 0 | |
SVST4_12B | X | 4 | 4 | 12 | 0 |
The 7i43 comes in 200K and 400K gate versions.
Even though several cards may have the same .BIT file you can not use a .BIT file that is not for that card. Different cards have different clock frequencies so make sure you load the proper .BIT file for your card. Custom hm2 firmwares can be created for special applications and you may see some custom hm2 firmwares in the directories with the default ones.
When you load the board-driver (hm2_pci or hm2_7i43), you can tell it to disable instances of the three primary modules (pwmgen, stepgen, and encoder) by setting the count lower. Any I/O pins belonging to disabled module instances become gpios.
General Purpose I/O pins on the board which are not used by a module instance are exported to HAL as "full" GPIO pins. Full GPIO pins can be configured at run-time to be inputs, outputs, or open drains, and have a HAL interface that exposes this flexibility. IO pins that are owned by an active module instance are constrained by the requirements of the owning module, and have a restricted HAL interface.
GPIOs have names like "hm2_<BoardType>.<BoardNum>.gpio.<IONum>." IONum. is a three-digit number. The mapping from IONum to connector and pin-on-that-connector is written to the syslog when the driver loads, and it’s documented in Mesa’s manual for the Anything I/O boards.
The hm2 GPIO representation is modeled after the Digital Inputs and Digital Outputs described in the Canonical Device Interface (part of the HAL General Reference document).
GPIO pins default to input.
in::
(Bit, Out) Normal state of the hardware input pin. Both full GPIO pins and IO pins used as inputs by active module instances have this pin.
in_not::
(Bit, Out) Inverted state of the hardware input pin. Both full GPIO pins and IO pins used as inputs by active module instances have this pin.
out::
(Bit, In) Value to be written (possibly inverted) to the hardware output pin. Only full GPIO pins have this pin.
invert_output::
(Bit, RW) This parameter only has an effect if the "is_output" parameter is true. If this parameter is true, the output value of the GPIO will be the inverse of the value on the "out" HAL pin. Only full GPIO pins and IO pins used as outputs by active module instances have this parameter. To invert an active module pin you have to invert the GPIO pin not the module pin.
is_opendrain::
(Bit, RW) This parameter only has an effect if the "is_output" parameter is true. If this parameter is false, the GPIO behaves as a normal output pin: the IO pin on the connector is driven to the value specified by the "out" HAL pin (possibly inverted), and the value of the "in" and "in_not" HAL pins is undefined. If this parameter is true, the GPIO behaves as an open-drain pin. Writing 0 to the "out" HAL pin drives the IO pin low, writing 1 to the "out" HAL pin puts the IO pin in a high-impedance state. In this high-impedance state the IO pin floats (weakly pulled high), and other devices can drive the value; the resulting value on the IO pin is available on the "in" and "in_not" pins. Only full GPIO pins and IO pins used as outputs by active module instances have this parameter.
is_output::
(Bit, RW) If set to 0, the GPIO is an input. The IO pin is put in a high-impedance state (weakly pulled high), to be driven by other devices. The logic value on the IO pin is available in the "in" and "in_not" HAL pins. Writes to the "out" HAL pin have no effect. If this parameter is set to 1, the GPIO is an output; its behavior then depends on the "is_opendrain" parameter. Only full GPIO pins have this parameter.
Stepgens have names like "hm2_<BoardType>.<BoardNum>.stepgen.<Instance>.". "Instance" is a two-digit number that corresponds to the HostMot2 stepgen instance number. There are "num_stepgens" instances, starting with 00.
Each stepgen allocates 2-6 IO pins (selected at firmware compile time), but currently only uses two: Step and Direction outputs.
The stepgen representation is modeled on the stepgen software component. Stepgen default is active high step output (high during step time low during step space). To invert a StepGen output pin you invert the corresponding GPIO pin that is being used by StepGen. To find the GPIO pin being used for the StepGen output run dmesg as shown above.
Each stepgen instance has the following pins and parameters:
control-type::
(Bit, In) Switches between position control mode (0) and velocity control mode (1). Defaults to position control (0).
counts::
(s32, Out) Feedback position in counts (number of steps).
enable::
(Bit, In) Enables output steps. When false, no steps are generated.
position-cmd::
(Float, In) Target position of stepper motion, in user-defined position units.
position-fb::
(Float, Out) Feedback position in user-defined position units (counts / position_scale).
velocity-cmd::
(Float, In) Target velocity of stepper motion, in user-defined position units per second. This pin is only used when the stepgen is in velocity control mode (control-type=1).
velocity-fb::
(Float, Out) Feedback velocity in user-defined position units per second.
dirhold::
(u32, RW) Minimum duration of stable Direction signal after a step ends, in nanoseconds.
dirsetup::
(u32, RW) Minimum duration of stable Direction signal before a step begins, in nanoseconds.
maxaccel::
(Float, RW) Maximum acceleration, in position units per second per second. If set to 0, the driver will not limit its acceleration.
maxvel::
(Float, RW) Maximum speed, in position units per second. If set to 0, the driver will choose the maximum velocity based on the values of steplen and stepspace (at the time that maxvel was set to 0).
position-scale::
(Float, RW) Converts from counts to position units. position = counts / position_scale
step_type::
(u32, RW) Output format, like the step_type modparam to the software stegen(9) component. 0 = Step/Dir, 1 = Up/Down, 2 = Quadrature. In Quadrature mode (step_type=2), the stepgen outputs one complete Gray cycle (00 -> 01 -> 11 -> 10 -> 00) for each "step" it takes.
steplen::
(u32, RW) Duration of the step signal, in nanoseconds.
stepspace::
(u32, RW) Minimum interval between step signals, in nanoseconds.
The Step and Direction pins of each StepGen have two additional parameters. To find which I/O pin belongs to which step and direction output run dmesg as described above.
invert_output::
(Bit, RW) This parameter only has an effect if the "is_output" parameter is true. If this parameter is true, the output value of the GPIO will be the inverse of the value on the "out" HAL pin.
is_opendrain::
(Bit, RW) If this parameter is false, the GPIO behaves as a normal output pin: the IO pin on the connector is driven to the value specified by the "out" HAL pin (possibly inverted). If this parameter is true, the GPIO behaves as an open-drain pin. Writing 0 to the "out" HAL pin drives the IO pin low, writing 1 to the "out" HAL pin puts the IO pin in a high-impedance state. In this high-impedance state the IO pin floats (weakly pulled high), and other devices can drive the value; the resulting value on the IO pin is available on the "in" and "in_not" pins. Only full GPIO pins and IO pins used as outputs by active module instances have this parameter.
PWMgens have names like "hm2_<BoardType>.<BoardNum>.pwmgen.<Instance>.". "Instance" is a two-digit number that corresponds to the HostMot2 pwmgen instance number. There are "num_pwmgens" instances, starting with 00.
In HM2, each pwmgen uses three output IO pins: Not-Enable, Out0, and Out1. To invert a PWMGen output pin you invert the corresponding GPIO pin that is being used by PWMGen. To find the GPIO pin being used for the PWMGen output run dmesg as shown above.
The function of the Out0 and Out1 IO pins varies with output-type parameter (see below).
The hm2 pwmgen representation is similar to the software pwmgen component. Each pwmgen instance has the following pins and parameters:
enable::
(Bit, In) If true, the pwmgen will set its Not-Enable pin false and output its pulses. If "enable" is false, pwmgen will set its Not-Enable pin true and not output any signals.
value::
(Float, In) The current pwmgen command value, in arbitrary units.
output-type::
(s32, RW) This emulates the output_type load-time argument to the software pwmgen component. This parameter may be changed at runtime, but most of the time you probably want to set it at startup and then leave it alone. Accepted values are 1 (PWM on Out0 and Direction on Out1), 2 (Up on Out0 and Down on Out1), 3 (PDM mode, PDM on Out0 and Dir on Out1), and 4 (Direction on Out0 and PWM on Out1, "for locked antiphase").
scale::
(Float, RW) Scaling factor to convert "value" from arbitrary units to duty cycle: dc = value / scale. Duty cycle has an effective range of -1.0 to +1.0 inclusive, anything outside that range gets clipped.
pdm_frequency::
(u32, RW) This specifies the PDM frequency, in Hz, of all the pwmgen instances running in PDM mode (mode 3). This is the "pulse slot frequency"; the frequency at which the pdm generator in the AnyIO board chooses whether to emit a pulse or a space. Each pulse (and space) in the PDM pulse train has a duration of 1/pdm_frequency seconds. For example, setting the pdm_frequency to 2e6 (2 MHz) and the duty cycle to 50% results in a 1 MHz square wave, identical to a 1 MHz PWM signal with 50% duty cycle. The effective range of this parameter is from about 1525 Hz up to just under 100 MHz. Note that the max frequency is determined by the ClockHigh frequency of the Anything IO board; the 5i20 and 7i43 both have a 100 MHz clock, resulting in a 100 Mhz max PDM frequency. Other boards may have different clocks, resulting in different max PDM frequencies. If the user attempts to set the frequency too high, it will be clipped to the max supported frequency of the board.
pwm_frequency::
(u32, RW) This specifies the PWM frequency, in Hz, of all the pwmgen instances running in the PWM modes (modes 1 and 2). This is the frequency of the variable-duty-cycle wave. Its effective range is from 1 Hz up to 193 KHz. Note that the max frequency is determined by the ClockHigh frequency of the Anything IO board; the 5i20 and 7i43 both have a 100 MHz clock, resulting in a 193 KHz max PWM frequency. Other boards may have different clocks, resulting in different max PWM frequencies. If the user attempts to set the frequency too high, it will be clipped to the max supported frequency of the board. Frequencies below about 5 Hz are not terribly accurate, but above 5 Hz they're pretty close.
The output pins of each PWMGen have two additional parameters. To find which I/O pin belongs to which output run dmesg as described above.
invert_output::
(Bit, RW) This parameter only has an effect if the "is_output" parameter is true. If this parameter is true, the output value of the GPIO will be the inverse of the value on the "out" HAL pin.
is_opendrain::
(Bit, RW) If this parameter is false, the GPIO behaves as a normal output pin: the IO pin on the connector is driven to the value specified by the "out" HAL pin (possibly inverted). If this parameter is true, the GPIO behaves as an open-drain pin. Writing 0 to the "out" HAL pin drives the IO pin low, writing 1 to the "out" HAL pin puts the IO pin in a high-impedance state. In this high-impedance state the IO pin floats (weakly pulled high), and other devices can drive the value; the resulting value on the IO pin is available on the "in" and "in_not" pins. Only full GPIO pins and IO pins used as outputs by active module instances have this parameter.
Encoders have names like "hm2_<BoardType>.<BoardNum>.encoder.<Instance>.". "Instance" is a two-digit number that corresponds to the HostMot2 encoder instance number. There are "num_encoders" instances, starting with 00.
Each encoder uses three or four input IO pins, depending on how the firmware was compiled. Three-pin encoders use A, B, and Index (sometimes also known as Z). Four-pin encoders use A, B, Index, and Index-mask.
The hm2 encoder representation is similar to the one described by the Canonical Device Interface (in the HAL General Reference document), and to the software encoder component. Each encoder instance has the following pins and parameters:
count::
(s32, Out) Number of encoder counts since the previous reset.
index-enable::
(Bit, I/O) When this pin is set to True, the count (and therefore also position) are reset to zero on the next Index (Phase-Z) pulse. At the same time, index-enable is reset to zero to indicate that the pulse has occurred.
position::
(Float, Out) Encoder position in position units (count / scale).
rawcounts::
(s32, Out) Total number of encoder counts since the start, not adjusted for index or reset.
reset::
(Bit, In) When this pin is TRUE, the count and position pins are set to 0. (The value of the velocity pin is not affected by this.) The driver does not reset this pin to FALSE after resetting the count to 0, that is the user's job.
velocity::
(Float, Out) Estimated encoder velocity in position units per second.
counter-mode::
(Bit, RW) Set to False (the default) for Quadrature. Set to True for Up/Down or for single input on Phase A. Can be used for a frequency to velocity converter with a single input on Phase A when set to true.
filter::
(Bit, RW) If set to True (the default), the quadrature counter needs 15 clocks to register a change on any of the three input lines (any pulse shorter than this is rejected as noise). If set to False, the quadrature counter needs only 3 clocks to register a change. The encoder sample clock runs at 33 MHz on the PCI AnyIO cards and 50 MHz on the 7i43.
index-invert::
(Bit, RW) If set to True, the rising edge of the Index input pin triggers the Index event (if index-enable is True). If set to False, the falling edge triggers.
index-mask::
(Bit, RW) If set to True, the Index input pin only has an effect if the Index-Mask input pin is True (or False, depending on the index-mask-invert pin below).
index-mask-invert::
(Bit, RW) If set to True, Index-Mask must be False for Index to have an effect. If set to False, the Index-Mask pin must be True.
scale::
(Float, RW) Converts from "count" units to "position" units. A quadrature encoder will normally have 4 counts per pulse so a 100 PPR encoder would be 400 counts per revolution. In ".counter-mode" a 100 PPR encoder would have 100 counts per revelution as it only uses the rising edge of A and direction is B.
vel-timeout::
(Float, RW) When the encoder is moving slower than one pulse for each time that the driver reads the count from the FPGA (in the hm2_read() function), the velocity is harder to estimate. The driver can wait several iterations for the next pulse to arrive, all the while reporting the upper bound of the encoder velocity, which can be accurately guessed. This parameter specifies how long to wait for the next pulse, before reporting the encoder stopped. This parameter is in seconds.
Several example configurations are included with EMC for both stepper and servo applications. The configurations are located in the hm2-servo and hm2-stepper sections of the EMC2 Configuration Selector window. You will need the same board installed for the configuration you pick to load. The examples are a good place to start and will save you time. Just pick the proper example from the EMC2 Configuration Selector and save a copy to your computer so you can edit it. To see the exact pins and parameters that your configuration gave you open the Show HAL Configuration window from the Machine menu or do dmesg as outlined above.
Vital Systems Motenc-100 and Motenc-LITE
The Vital Systems Motenc-100 and Motenc-LITE are 8- and 4-channel servo control boards. The Motenc-100 provides 8 quadrature encoder counters, 8 analog inputs, 8 analog outputs, 64 (68?) digital inputs, and 32 digital outputs. The Motenc-LITE has only 4 encoder counters, 32 digital inputs and 16 digital outputs, but it still has 8 analog inputs and 8 analog outputs. The driver automatically identifies the installed board and exports the appropriate HAL objects.
Installing:
loadrt hal_motenc
During loading (or attempted loading) the driver prints some usefull debugging message to the kernel log, which can be viewed with dmesg.
Up to 4 boards may be used in one system.
In the following pins, parameters, and functions, <board> is the board ID. According to the naming conventions the first board should always have an ID of zero. However this driver sets the ID based on a pair of jumpers on the baord, so it may be non-zero even if there is only one board.
(u32) motenc.<board>.watchdog-control — Configures the watchdog. The value may be a bitwise OR of the following values:
[width="90%", options="header"] |======================================== |Bit # | Value | Meaning |0 | 1 | Timeout is 16ms if set, 8ms if unset |2 | 4 | Watchdog is enabled |4 | 16 | Watchdog is automatically reset by DAC writes (the HAL dac-write function) |======================================== + Typically, the useful values are 0 (watchdog disabled) or 20 (8ms watchdog enabled, cleared by dac-write). - (u32) motenc.<board>.led-view -- Maps some of the I/O to onboard LEDs?
PCI AC5 ADAPTER CARD / HAL DRIVER This page is considered current as of November 2008.
This is a card made by OPTO22 for adapting the PCI port to solid state relay racks such as their standard or G4 series. It has 2 ports that can control up to 24 points each and has 4 on board LEDS. The ports use 50 pin connectors the same as Mesa boards. Any relay racks/breakout boards thats work with Mesa Cards should work with this card with the understanding any encoder counters, pwm etc would have to be done in software- The AC5 does not have any smart logic on board it just an adapter.
See the manufacturer’s website for more info:
http://www.opto22.com/site/pr_details.aspx?item=PCI-AC5&qs=100110021011,,,1,3
I would like to thank OPTO22 for releasing info in their manual, easing the writing of this driver!
This driver is for the PCI ac5 card and will not work with the ISA ac5 card. The HAL driver is a realtime module. It will support 4 cards as is (more cards are possible with a change in the source code). Load the basic driver like so:
loadrt opto_ac5
This will load the driver which will search for max 4 boards.It will set i/o of each board’s 2 ports to a default setting. The default configuration is for 12 inputs then 12 outputs. The pin name numbers correspond to the position on the relay rack. For example the pin names for the default i/o setting of port 0 would be:
opto_ac5.0.port0.in-00 They would be numbered from 00 to 11
opto_ac5.0.port0.out-12 They would be numbered 12 to 23 port 1 would be the same.
opto_ac5.[BOARDNUMBER].port[PORTNUMBER].in-[PINNUMBER] OUT bit
opto_ac5.[BOARDNUMBER].port[PORTNUMBER].in-[PINNUMBER]-not OUT bit
Connect a hal bit signal to this pin to read an I/O point from the card. The PINNUMBER represents the position in the relay rack. Eg. PINNUMBER 0 is position 0 in a opto22 relay rack and would be pin 47 on the 50 pin header connector. The -not pin is inverted so that LOW gives TRUE and HIGH gives FALSE.
opto_ac5.[BOARDNUMBER].port[PORTNUMBER].out-[PINNUMBER] IN bit
Connect a hal bit signal to this pin to write to an I/O point of the card. The PINNUMBER represents the position in the relay rack.Eg. PINNUMBER 23 is position 23 in a opto22 relay rack and would be pin 1 on the 50 pin header connector.
opto_ac5.[BOARDNUMBER].led[NUMBER] OUT bit
Turns one of the 4 onboard LEDS on/off. LEDS are numbered 0 to 3.
BOARDNUMBER can be 0-3 PORTNUMBER can be 0 or 1. Port 0 is closest to the card bracket.
opto_ac5.[BOARDNUMBER].port[PORTNUMBER].out-[PINNUMBER]-invert W bit
When TRUE, invert the meaning of the corresponding -out pin so that TRUE gives LOW and FALSE gives HIGH.
opto_ac5.0.digital-read Add this to a thread to read all the input points.
opto_ac5.0.digital-write Add this to a thread to write all the output points and LEDS.
For example the pin names for the default I/O setting of port 0 would be:
opto_ac5.0.port0.in-00
They would be numbered from 00 to 11
opto_ac5.0.port0.out-12
They would be numbered 12 to 23 port 1 would be the same.
To change the default setting load the driver something like so:
loadrt opto_ac5 portconfig0=0xffff portconfig1=0xff0000
Of course changing the numbers to match the i/o you would like. Each port can be set up different.
Heres how to figure out the number: The configuration number represents a 32 bit long code to tell the card which i/o points are output vrs input. The lower 24 bits are the i/o points of one port. The 2 highest bits are for 2 of the on board LEDS. A one in any bit position makes the i/o point an output. The two highest bits must be output for the LEDS to work. The driver will automatically set the two highest bits for you, we won’t talk about them.
The easiest way to do this is to fire up the calculator under APPLICATIONS/ACCESSORIES. Set it to scientific (click view). Set it BINARY (radio button Bin). Press 1 for every output you want and/or zero for every input. Remember that HAL pin 00 corresponds to the rightmost bit. 24 numbers represent the 24 i/o points of one port. So for the default setting (12 inputs then 12 outputs) you would push 1 twelve times (thats the outputs) then 0 twelve times (thats the inputs). Notice the first i/0 point is the lowest (right most) bit. (that bit corresponds to HAL pin 00 .looks backwards) You should have 24 digits on the screen. Now push the Hex radio button. The displayed number (fff000) is the configport number ( put a 0x in front of it designating it a HEX number).
Another example : to set the port for 8 outputs and 16 inputs (the same as a Mesa card). Here is the 24 bits represented in a BINARY number. Bit 1 is the rightmost number.
000000000000000011111111
16 zeros for the 16 inputs 8 ones for the 8 outputs
Which converts to FF on the calculator so 0xff is the number to use for portconfig0 and/or portconfig1 when loading the driver.
HAL pin 00 corresponds to bit 1 (the rightmost) which represents position 0 on an opto22 relay rack. HAL pin 01 corresponds to bit 2 (one spot to the left of the rightmost) which represents position 1 on an opto22 relay rack. etc. HAL pin 23 corresponds to bit 24 (the leftmost) which represents position 23 on an opto22 relay rack.
Hal pin 00 connects to pin 47 on the 50 pin connector of each port. Hal pin 01 connects to pin 45 on the 50 pin connector of each port. etc. Hal pin 23 connects to pin 1 on the 50 pin connector of each port.
Note that opto22 and Mesa use opposite numbering systems: opto22 position 23 = connector pin 1. and the position goes down as the connector pin number goes up Mesa Hostmot4 position 1 = connector pin 1. and the position number goes up as the connector pin number goes up
Pico Systems has a family of boards for doing analog servo, stepper, and pwm (digital) servo control. The boards connect to the PC through a parallel port working in EPP mode. Although most users connect one board to a parallel port, in theory any mix of up to 8 or 16 boards can be used on a single parport. One driver serves all types of boards. The final mix of I/O depends on the connected board(s). The driver doesn’t distinguish between boards, it simply numbers I/O channels (encoders, etc) starting from 0 on the first card.
Installing:
loadrt hal_ppmc port_addr=<addr1>[,<addr2>[,<addr3>...]]
The port_addr
parameter tells the driver what parallel port(s) to
check. By default, <addr1>
is 0x0378, and <addr2>
and following
are not used. The driver searches the entire address
space of the enhanced parallel port(s) at port_addr
, looking for
any board(s) in the PPMC family. It then exports HAL
pins for whatever it finds. During loading (or attempted loading) the
driver prints some usefull debugging message to the kernel log, which
can be viewed with dmesg
.
Up to 3 parport busses may be used, and each bus may have up to 8 devices on it.
In the following pins, parameters, and functions, <board> is the board ID. According to the naming conventions the first board should always have an ID of zero. However this driver sets the ID based on switches on the board, so it may be non-zero even if there is only one board.
(All s32
` output) ppmc.<port>.encoder.<channel>.count` — Encoder
position, in counts.
(All s32
` output) ppmc.<port>.encoder.<channel>.delta` — Change in
counts since last read, in raw encoder count units.
ppmc.<port>.encoder.<channel>.velocity
— Velocity scaled in user units per second. On PPMC and USC this is
derived from raw encoder counts per servo period, and hence is affected
by encoder granularity. On UPC boards with the 8/21/09 and later
firmware, velocity estimation by timestamping encoder counts can be
used to improve the smoothness of this velocity output. This can be fed
to the pid hal component to produce a more stable servo response. This
function has to be enabled in the hal command line that starts the ppmc
driver, with the timestamp=0x00 option.
(
All float
` output) ppmc.<port>.encoder.<channel>.position` — Encoder position, in user units.
(All bit ``bidir
) ``ppmc.<port>.encoder.<channel>.index-enable
— Connect to axis.#.index-enable for home-to-index. This is a
bidirectional hal signal. Setting it to true causes the encoder
hardware to reset the count to zero on the next encoder index pulse.
The driver will detect this and set the signal back to false.
(UPC bit ``input
) ``ppmc.<port>.pwm.<channel>.enable
— Enables a
PWM generator.
(UPC float ``input
) ``ppmc.<port>.pwm.<channel>.value
— Value
which determines the duty cycle of the PWM waveforms. The
value is divided by pwm.<channel>.scale
, and if the result is 0.6
the duty cycle will be 60%, and so on.
Negative values result in the duty cycle being based on the absolute
value, and the direction pin is set to indicate negative.
(USC bit ``input
) ``ppmc.<port>.stepgen.<channel>.enable
— Enables a step pulse generator.
(USC float ``input
) ``ppmc.<port>.stepgen.<channel>.velocity
— Value which determines the step frequency. The value is multiplied
by stepgen.<channel>.scale
, and the result is the frequency in
steps per second. Negative values
result in the frequency being based on the absolute value, and the
direction pin is set to indicate negative.
(All bit ``output
) ``ppmc.<port>.in-<channel>
— State of digital
input pin, see canonical digital input.
(All bit ``output
) ``ppmc.<port>.in.<channel>-not
— Inverted
state of digital input pin, see canonical digital input.
(All bit ``input
) ``ppmc.<port>.out-<channel>
— Value to be
written to digital output, seen canonical digital
output.
ppmc.<port>.DAC8-<channel>.value
— Value to
be written to analog output, range from 0 to 255. This
sends 8 output bits to J8, which should have a Spindle DAC board
connected to it. 0 correponds to zero Volts, 255 corresponds to 10
Volts. The polarity of the output can be set for always minus, always
plus, or can be controlled by the state of SSR1 (plus when on) and SSR2
(minus when on). You must specify extradac = 0x00 on the hal command
line that loads the ppmc driver to enable this function on the first
USC ur UPC board.
ppmc.<port>.dout-<channel>.out
— Value to be
written to one of the 8 extra digital output pins on
J8. You must specify extradout = 0x00 on the hal command line that
loads the ppmc driver to enable this function on the first USC ur UPC
board. extradac and extradout are mutually exclusive features as they
use the same signal lines for different purposes.
(All float) ``ppmc.<port>.enc.<channel>.scale
— The number of
counts / user unit (to convert from counts to units).
(UPC float) ``ppmc.<port>.pwm.<channel-range>.freq
— The PWM
carrier frequency, in Hz. Applies to a group of four
consecutive PWM generators, as indicated by <channel-range>
. Minimum
is 610Hz, maximum is 500KHz.
(
UPC float
) ppmc.<port>.pwm.<channel>.scale
— Scaling for PWM
generator. If scale
is X, then the duty cycle will be 100% when the
value
pin is X (or -X).
(
UPC float
) ppmc.<port>.pwm.<channel>.max-dc
— Maximum duty
cycle, from 0.0 to 1.0.
(
UPC float
) ppmc.<port>.pwm.<channel>.min-dc
— Minimum duty
cycle, from 0.0 to 1.0.
(
UPC float
) ppmc.<port>.pwm.<channel>.duty-cycle
— Actual duty
cycle (used mostly for troubleshooting.)
(
UPC bit
) ppmc.<port>.pwm.<channel>.bootstrap
— If true, the
PWM generator will generate a short sequence of
pulses of both polarities when E-stop goes false, to charge the
bootstrap capacitors used on some MOSFET gate drivers.
(
USC u32
) ppmc.<port>.stepgen.<channel-range>.setup-time
— Sets minimum time between direction change and step pulse, in
units of 100nS. Applies to a group of four consecutive PWM generators,
as indicated by <channel-range>
.
(
USC u32
) ppmc.<port>.stepgen.<channel-range>.pulse-width
— Sets width of step pulses, in units of 100nS. Applies to a group
of four consecutive PWM generators, as indicated by <channel-range>
.
(
USC u32
) ppmc.<port>.stepgen.<channel-range>.pulse-space-min
— Sets minimum time between pulses, in units of 100nS. The maximum
step rate is 1/( 100nS * ( pulse-width
+ pulse-space-min
)).
Applies to a group of four consecutive PWM generators, as
indicated by <channel-range>
.
(
USC float
) ppmc.<port>.stepgen.<channel>.scale
— Scaling for
step pulse generator. The step frequency in Hz is the
absolute value of velocity
* scale
.
(
USC float
) ppmc.<port>.stepgen.<channel>.max-vel
— The maximum
value for velocity
. Commands greater than max-vel
will be clamped.
Also applies to negative values. (The absolute value
is clamped.)
(
USC float
) ppmc.<port>.stepgen.<channel>.frequency
— Actual
step pulse frequency in Hz (used mostly for troubleshooting.)
(
Option float
) ppmc.<port>.DAC8.<channel>.scale
— Sets scale
of extra DAC output such that an output velue equal to
scale gives a magnitude of 10.0 V output. (The sign of the output is
set by jumpers and/or other digital outputs.)
(
Option bit
) ppmc.<port>.out.<channel>-invert
— Inverts a
digital output, see canonical digital output.
(
Option bit
) ppmc.<port>.dout.<channel>-invert
— Inverts a
digital output pin of J8, see canonical digital output.
(All funct) ``ppmc.<port>.read
— Reads all inputs (digital inputs
and encoder counters) on one
port. These reads are organized into blocks of contiguous registers to
be read in a block to minimize CPU overhead.
(All funct) ``ppmc.<port>.write
— Writes all outputs (digital
outputs, stepgens, PWMs) on one port.
These reads are organized into blocks of contiguous registers to be
read in a block to minimize CPU overhead.
The Pluto-P is an inexpensive ($60) FPGA board featuring the ACEX1K chip from Altera.
The src/hal/drivers/pluto_servo_firmware/
and
src/hal/drivers/pluto_step_firmware/
subdirectories contain the
Verilog source code plus additional files
used by Quartus for the FPGA firmwares. Altera’s Quartus II software is
required to rebuild the FPGA firmware. To rebuild the firmware from the
.hdl and other source files, open the .qpf
file and press CTRL-L.
Then, recompile emc2.
Like the HAL hardware driver, the FPGA firmware is licensed under the terms of the GNU General Public License.
The gratis version of Quartus II runs only on Microsoft Windows, although there is apparently a paid version that runs on Linux.
Some additional information about it is available from http://www.fpga4fun.com/board_pluto-P.html and from the developer’s blog.
The pluto_servo system is suitable for control of a 4-axis CNC mill with servo motors, a 3-axis mill with PWM spindle control, a lathe with spindle encoder, etc. The large number of inputs allows a full set of limit switches.
This driver features:
Primary function | Alternate Function | Behavior if both functions used |
---|---|---|
UP0 | PWM0 | When pwm-0-pwmdir is TRUE, this pin is the PWM output |
OUT10 | XOR’d with UP0 or PWM0 | |
UP1 | PWM1 | When pwm-1-pwmdir is TRUE, this pin is the PWM output |
OUT12 | XOR’d with UP1 or PWM1 | |
UP2 | PWM2 | When pwm-2-pwmdir is TRUE, this pin is the PWM output |
OUT14 | XOR’d with UP2 or PWM2 | |
UP3 | PWM3 | When pwm-3-pwmdir is TRUE, this pin is the PWM output |
OUT16 | XOR’d with UP3 or PWM3 | |
DN0 | DIR0 | When pwm-0-pwmdir is TRUE, this pin is the DIR output |
OUT11 | XOR’d with DN0 or DIR0 | |
DN1 | DIR1 | When pwm-1-pwmdir is TRUE, this pin is the DIR output |
OUT13 | XOR’d with DN1 or DIR1 | |
DN2 | DIR2 | When pwm-2-pwmdir is TRUE, this pin is the DIR output |
OUT15 | XOR’d with DN2 or DIR2 | |
DN3 | DIR3 | When pwm-3-pwmdir is TRUE, this pin is the DIR output |
OUT17 | XOR’d with DN3 or DIR3 | |
QZ0 | IN8 | Read same value |
QZ1 | IN9 | Read same value |
QZ2 | IN10 | Read same value |
QZ3 | IN11 | Read same value |
QA0 | IN12 | Read same value |
QA1 | IN13 | Read same value |
QA2 | IN14 | Read same value |
QA3 | IN15 | Read same value |
QB0 | IN16 | Read same value |
QB1 | IN17 | Read same value |
QB2 | IN18 | Read same value |
QB3 | IN19 | Read same value |
A list of all loadrt arguments, HAL function names, pin names and parameter names is in the manual page, pluto_servo.9.
A schematic for a 2A, 2-axis PWM servo amplifier board is available (http://emergent.unpy.net/projects/01148303608). The L298 H-Bridge (L298 H-bridge) is inexpensive and can easily be used for motors up to 4A (one motor per L298) or up to 2A (two motors per L298) with the supply voltage up to 46V. However, the L298 does not have built-in current limiting, a problem for motors with high stall currents. For higher currents and voltages, some users have reported success with International Rectifier’s integrated high-side/low-side drivers. (http://www.cnczone.com/forums/showthread.php?t=25929)
Pluto-step is suitable for control of a 3- or 4-axis CNC mill with stepper motors. The large number of inputs allows for a full set of limit switches.
The board features:
While the “extended main connector” has a superset of signals usually found on a Step & Direction DB25 connector—4 step generators, 9 inputs, and 6 general-purpose outputs—the layout on this header is different than the layout of a standard 26-pin ribbon cable to DB25 connector.
The firmware and driver enforce step length, space, and direction change times. Timings are rounded up to the next multiple of , with a maximum of . The timings are the same as for the software stepgen component, except that “dirhold” and “dirsetup” have been merged into a single parameter “dirtime” which should be the maximum of the two, and that the same step timings are always applied to all channels.
The Servo-To-Go is one of the first PC motion control cards supported by EMC. It is an ISA card and it exists in different flavours (all supported by this driver). The board includes up to 8 channels of quadrature encoder input, 8 channels of analog input and output, 32 bits digital I/O, an interval timer with interrupt and a watchdog.
loadrt hal_stg [base=<address>] [num_chan=<nr>] [dio="<dio-string>"] [model=<model>]
The base address field is optional; if it’s not provided the driver attempts to autodetect the board. The num_chan field is used to specify the number of channels available on the card, if not used the 8 axis version is assumed. The digital inputs/outputs configuration is determined by a config string passed to insmod when loading the module. The format consists of a four character string that sets the direction of each group of pins. Each character of the direction string is either "I" or "O". The first character sets the direction of port A (Port A - DIO.0-7), the next sets port B (Port B - DIO.8-15), the next sets port C (Port C - DIO.16-23), and the fourth sets port D (Port D - DIO.24-31). The model field can be used in case the driver doesn’t autodetect the right card version.
hint: after starting up the driver, dmesg can be consulted for messages relevant to the driver (e.g. autodetected version number and base address). For example:
loadrt hal_stg base=0x300 num_chan=4 dio="IOIO"
This example installs the stg driver for a card found at the base address of 0x300, 4 channels of encoder feedback, DAC’s and ADC’s, along with 32 bits of I/O configured like this: the first 8 (Port A) configured as Input, the next 8 (Port B) configured as Output, the next 8 (Port C) configured as Input, and the last 8 (Port D) configured as Output
loadrt hal_stg
This example installs the driver and attempts to autodetect the board address and board model, it installs 8 axes by default along with a standard I/O setup: Port A & B configured as Input, Port C & D configured as Output.
For each pin, <channel> is the axis number, and <pinnum> is the logic pin number of the STGif IIOO is defined, there are 16 input pins (in-00 .. in-15) and 16 output pins (out-00 .. out-15), and they correspond to PORTs ABCD (in-00 is PORTA.0, out-15 is PORTD.7).
The in- HAL pin is TRUE if the physical pin is high, and FALSE if the physical pin is low. The in-<pinnum>-not HAL pin is inverted — it is FALSE if the physical pin is high. By connecting a signal to one or the other, the user can determine the state of the input.
The -invert parameter determines whether an output pin is active high or active low. If -invert is FALSE, setting the HAL out- pin TRUE drives the physical pin high, and FALSE drives it low. If -invert is TRUE, then setting the HAL out- pin TRUE will drive the physical pin low.
When we talk about CNC machines, we usually think about machines that are commanded to move to certain locations and perform various tasks. In order to have an unified view of the machine space, and to make it fit the human point of view over 3D space, most of the machines (if not all) use a common coordinate system called the Cartesian Coordinate System.
The Cartesian Coordinate system is composed of 3 axes (X, Y, Z) each perpendicular to the other 2. [15]
When we talk about a G-code program (RS274NGC) we talk about a number of commands (G0, G1, etc.) which have positions as parameters (X- Y- Z-). These positions refer exactly to Cartesian positions. Part of the EMC2 motion controller is responsible for translating those positions into positions which correspond to the machine kinematics[16].
A joint of a CNC machine is a one of the physical degrees of freedom of the machine. This might be linear (leadscrews) or rotary (rotary tables, robot arm joints). There can be any number of joints on a certain machine. For example a typical robot has 6 joints, and a typical simple milling machine has only 3.
There are certain machines where the joints are layed out to match kinematics axes (joint 0 along axis X, joint 1 along axis Y, joint 2 along axis Z), and these machines are called Cartesian machines (or machines with Trivial Kinematics). These are the most common machines used in milling, but are not very common in other domains of machine control (e.g. welding: puma-typed robots).
As we said there is a group of machines in which each joint is placed along one of the Cartesian axes. On these machines the mapping from Cartesian space (the G-code program) to the joint space (the actual actuators of the machine) is trivial. It is a simple 1:1 mapping:
pos->tran.x = joints[0]; + pos->tran.y = joints[1]; + pos->tran.z = joints[2]; + pos->a = joints[3]; + pos->b = joints[4]; + pos->c = joints[5];
In the above code snippet one can see how the mapping is done: the X position is identical with the joint 0, Y with joint 1 etc. The above refers to the direct kinematics (one way of the transformation) whereas the next code part refers to the inverse kinematics (or the inverse way of the transformation):
joints[0] = pos->tran.x; + joints[1] = pos->tran.y; + joints[2] = pos->tran.z; + joints[3] = pos->a; + joints[4] = pos->b; + joints[5] = pos->c;
As one can see, it’s pretty straightforward to do the transformation for a trivial kins (or Cartesian) machine. It gets a bit more complicated if the machine is missing one of the axes.[17][18]
There can be quite a few types of machine setups (robots: puma, scara; hexapods etc.). Each of them is set up using linear and rotary joints. These joints don’t usually match with the Cartesian coordinates, therefor there needs to be a kinematics function which does the conversion (actually 2 functions: forward and inverse kinematics function).
To illustrate the above, we will analyze a simple kinematics called bipod (a simplified version of the tripod, which is a simplified version of the hexapod).
The Bipod we are talking about is a device that consists of 2 motors placed on a wall, from which a device is hanged using some wire. The joints in this case are the distances from the motors to the device (named AD and BD in figure [cap:Bipod-setup]).
The position of the motors is fixed by convention. Motor A is in (0,0), which means that its X coordinate is 0, and its Y coordinate is also 0. Motor B is placed in (Bx, 0), which means that its X coordinate is Bx.
Our tooltip will be in point D which gets defined by the distances AD and BD, and by the Cartesian coordinates Dx, Dy.
The job of the kinematics is to transform from joint lengths (AD, BD) to Cartesian coordinates (Dx, Dy) and vice-versa.
To transform from joint space into Cartesian space we will use some trigonometry rules (the right triangles determined by the points (0,0), (Dx,0), (Dx,Dy) and the triangle (Dx,0), (Bx,0) and (Dx,Dy).
we can easily see that , likewise .
If we subtract one from the other we will get:
and therefore:
From there we calculate:
Note that the calculation for y involves the square root of a difference, which may not result in a real number. If there is no single Cartesian coordinate for this joint position, then the position is said to be a singularity. In this case, the forward kinematics return -1.
Translated to actual code:
double AD2 = joints[0] * joints[0]; + double BD2 = joints[1] * joints[1]; + double x = (AD2 - BD2 + Bx * Bx) / (2 * Bx); + double y2 = AD2 - x * x; + if(y2 < 0) return -1; + pos->tran.x = x; + pos->tran.y = sqrt(y2); + return 0;
The inverse kinematics is lots easier in our example, as we can write it directly:
or translated to actual code:
double x2 = pos->tran.x * pos->tran.x; + double y2 = pos->tran.y * pos->tran.y; + joints[0] = sqrt(x2 + y2); + joints[1] = sqrt((Bx - pos->tran.x)*(Bx - pos->tran.x) + y2); + return 0;
A kinematics module is implemented as a HAL component, and is permitted to export pins and parameters. It consists of several “C” functions (as opposed to HAL functions):
KINEMATICS_TYPE kinematicsType(void)
Returns the kinematics type identifier, typically KINEMATICS_BOTH
.
int rtapi_app_main(void)
void rtapi_app_exit(void)
These are the standard setup and tear-down functions of RTAPI modules.
When they are contained in a single source file, kinematics modules
may be compiled and installed by comp
. See the comp(1)
manpage or
the HAL manual for more information.
[15] The word “axes” is also commonly (and wrongly) used when talking about CNC machines, and referring to the moving directions of the machine.
[16] Kinematics: a two way function to transform from Cartesian space to joint space
[17] If a machine (e.g. a lathe) is set up with only the axes X,Z & A, and the EMC2 inifile holds only these 3 joints defined, then the above matching will be faulty. That is because we actually have (joint0=x, joint1=Z, joint2=A) whereas the above assumes joint1=Y. To make it easily work in EMC2 one needs to define all axes (XYZA), then use a simple loopback in HAL for the unused Y axis.
[18] One other way of making it work, is by changing the matching code and recompiling the software.
Generating step pulses in software has one very big advantage - it’s free. Just about every PC has a parallel port that is capable of outputting step pulses that are generated by the software. However, software step pulses also have some disadvantages:
This chapter has some steps that can help you get the best results from software generated steps.
The new easy way to run a latency test is described in the Getting Started Guide.
Latency is how long it takes the PC to stop what it is doing and respond to an external request. In our case, the request is the periodic "heartbeat" that serves as a timing reference for the step pulses. The lower the latency, the faster you can run the heartbeat, and the faster and smoother the step pulses will be.
Latency is far more important than CPU speed. A lowly Pentium II that responds to interrupts within 10 microseconds each and every time can give better results than the latest and fastest P4 Hyperthreading beast.
The CPU isn’t the only factor in determining latency. Motherboards, video cards, USB ports, and a number of other things can hurt the latency. The best way to find out what you are dealing with is to run the RTAI latency test.
DO NOT TRY TO RUN EMC2 WHILE THE TEST IS RUNNING
On Ubuntu Dapper, you can run the test by opening a shell and doing:
sudo mkdir /dev/rtf; + sudo mknod /dev/rtf/3 c 150 3; + sudo mknod /dev/rtf3 c 150 3; + cd /usr/realtime*/testsuite/kern/latency; ./run
and then you should see something like this:
ubuntu:/usr/realtime-2.6.12-magma/testsuite/kern/latency$ ./run + * + * + * Type ^C to stop this application. + * + *
## RTAI latency calibration tool ## + # period = 100000 (ns) + # avrgtime = 1 (s) + # do not use the FPU + # start the timer + # timer_mode is oneshot
RTAI Testsuite - KERNEL latency (all data in nanoseconds) + RTH| lat min| ovl min| lat avg| lat max| ovl max| overruns + RTD| -1571| -1571| 1622| 8446| 8446| 0 + RTD| -1558| -1571| 1607| 7704| 8446| 0 + RTD| -1568| -1571| 1640| 7359| 8446| 0 + RTD| -1568| -1571| 1653| 7594| 8446| 0 + RTD| -1568| -1571| 1640| 10636| 10636| 0 + RTD| -1568| -1571| 1640| 10636| 10636| 0
While the test is running, you should "abuse" the computer. Move windows around on the screen. Surf the web. Copy some large files around on the disk. Play some music. Run an OpenGL program such as glxgears. The idea is to put the PC through its paces while the latency test checks to see what the worst case numbers are.
The last number in the column labeled "ovl max" is the most important. Write it down - you will need it later. It contains the worst latency measurement during the entire run of the test. In the example above, that is 10636 nano-seconds, or 10.6 micro-seconds, which is excellent. However the example only ran for a few seconds (it prints one line every second). You should run the test for at least several minutes; sometimes the worst case latency doesn’t happen very often, or only happens when you do some particular action. I had one Intel motherboard that worked pretty well most of the time, but every 64 seconds it had a very bad 300uS latency. Fortunately that is fixable, see FixingDapperSMIIssues in the wiki found at wiki.linuxcnc.org.
So, what do the results mean? If your "ovl max" number is less than about 15-20 microseconds (15000-20000 nanoseconds), the computer should give very nice results with software stepping. If the max latency is more like 30-50 microseconds, you can still get good results, but your maximum step rate might be a little dissapointing, especially if you use microstepping or have very fine pitch leadscrews. If the numbers are 100uS or more (100,000 nanoseconds), then the PC is not a good candidate for software stepping. Numbers over 1 millisecond (1,000,000 nanoseconds) mean the PC is not a good candidate for EMC, regardless of whether you use software stepping or not.
Note that if you get high numbers, there may be ways to improve them. For example, one PC had very bad latency (several milliseconds) when using the onboard video. But a $5 used Matrox video card solved the problem - EMC does not require bleeding edge hardware.
Different brands of stepper drives have different timing requirements on their step and direction inputs. So you need to dig out (or Google for) the data sheet that has your drive’s specs.
For example, the Gecko G202 manual says this:
Step Frequency: 0 to 200 kHz
Step Pulse “0” Time: 0.5 uS min (Step on falling edge)
Step Pulse “1” Time: 4.5 uS min
Direction Setup: 1 uS min (20 uS min hold time after Step edge)
The Gecko G203V specifications are:
Step Frequency: 0 to 333 kHz
Step Pulse “0” Time: 2.0 uS min (Step on rising edge)
Step Pulse “1” Time: 1.0 uS min
Direction Setup:
200 nS (0.2uS) before step pulse rising edge
200 nS (0.2uS) hold after step pulse rising edge
A Xylotex drive datasheet has a nice drawing of the timing requirements, which are:
Minimum DIR setup time before rising edge of STEP Pulse 200nS Minimum
DIR hold time after rising edge of STEP pulse 200nS
Minimum STEP pulse high time 2.0uS
Minimum STEP pulse low time 1.0uS
Step happens on rising edge
Once you find the numbers, write them down too - you need them in the next step.
BASE_PERIOD is the "heartbeat" of your EMC computer. Every period, the software step generator decides if it is time for another step pulse. A shorter period will allow you to generate more pulses per second, within limits. But if you go too short, your computer will spend so much time generating step pulses that everything else will slow to a crawl, or maybe even lock up. Latency and stepper drive requirements affect the shortest period you can use, as we will see in a minute.
Let’s look at the Gecko example first. The G202 can handle step pulses that go low for 0.5uS and high for 4.5uS, it needs the direction pin to be stable 1uS before the falling edge, and remain stable for 20uS after the falling edge. The longest timing requirement is the 20uS hold time. A simple approach would be to set the period at 20uS. That means that all changes on the STEP and DIR lines are separated by 20uS. All is good, right?
Wrong! If there was ZERO latency, then all edges would be separated by 20uS, and everything would be fine. But all computers have some latency. Latency means lateness. If the computer has 11uS of latency, that means sometimes the software runs as much as 11uS later than it was supposed to. If one run of the software is 11uS late, and the next one is on time, the delay from the first to the second is only 9uS. If the first one generated a step pulse, and the second one changed the direction bit, you just violated the 20uS G202 hold time requirement. That means your drive might have taken a step in the wrong direction, and your part will be the wrong size.
The really nasty part about this problem is that it can be very very rare. Worst case latencies might only happen a few times a minute, and the odds of bad latency happening just as the motor is changing direction are low. So you get very rare errors that ruin a part every once in a while and are impossible to troubleshoot.
The simplest way to avoid this problem is to choose a BASE_PERIOD that is the sum of the longest timing requirement of your drive, and the worst case latency of your computer. If you are running a Gecko with a 20uS hold time requirement, and your latency test said you have a maximum latency of 11uS, then if you set the BASE_PERIOD to 20+11 = 31uS (31000 nano-seconds in the ini file), you are guaranteed to meet the drive’s timing requirements.
But there is a tradeoff. Making a step pulse requires at least two periods. One to start the pulse, and one to end it. Since the period is 31uS, it takes 2x31 = 62uS to create a step pulse. That means the maximum step rate is only 16,129 steps per second. Not so good. (But don’t give up yet, we still have some tweaking to do in the next section.)
For the Xylotex, the setup and hold times are very short, 200nS each (0.2uS). The longest time is the 2uS high time. If you have 11uS latency, then you can set the BASE_PERIOD as low as 11+2=13uS. Getting rid of the long 20uS hold time really helps! With a period of 13uS, a complete step takes 2x13 = 26uS, and the maximum step rate is 38,461 steps per second!
But you can’t start celebrating yet. Note that 13uS is a very short period. If you try to run the step generator every 13uS, there might not be enough time left to run anything else, and your computer will lock up. If you are aiming for periods of less than 25uS, you should start at 25uS or more, run EMC, and see how things respond. If all is well, you can gradually decrease the period. If the mouse pointer starts getting sluggish, and everything else on the PC slows down, your period is a little too short. Go back to the previous value that let the computer run smoothly.
In this case, sppose you started at 25uS, trying to get to 13uS, but you find that around 16uS is the limit - any less and the computer doesn’t respond very well. So you use 16uS. With a 16uS period and 11uS latency, the shortest output time will be 16-11 = 5uS. The drive only needs 2uS, so you have some margin. Margin is good - you don’t want to lose steps because you cut the timing too close.
What is the maximum step rate? Remember, two periods to make a step. You settled on 16uS for the period, so a step takes 32uS. That works out to a not bad 31,250 steps per second.
In the last section, we got the Xylotex drive to a 16uS period and a 31,250 step per second maximum speed. But the Gecko was stuck at 31uS and a not-so-nice 16,129 steps per second. The Xylotex example is as good as we can make it. But the Gecko can be improved.
The problem with the G202 is the 20uS hold time requirement. That plus the 11uS latency is what forces us to use a slow 31uS period. But the EMC2 software step generator has some parameters that let you increase the various time from one period to several. For example, if steplen is changed from 1 to 2, then it there will be two periods between the beginning and end of the step pulse. Likewise, if dirhold is changed from 1 to 3, there will be at least three periods between the step pulse and a change of the direction pin.
If we can use dirhold to meet the 20uS hold time requirement, then the next longest time is the 4.5uS high time. Add the 11uS latency to the 4.5uS high time, and you get a minimum period of 15.5uS. When you try 15.5uS, you find that the computer is sluggish, so you settle on 16uS. If we leave dirhold at 1 (the default), then the minimum time between step and direction is the 16uS period minus the 11uS latency = 5uS, which is not enough. We need another 15uS. Since the period is 16uS, we need one more period. So we change dirhold from 1 to 2. Now the minimum time from the end of the step pulse to the changing direction pin is 5+16=21uS, and we don’t have to worry about the Gecko stepping the wrong direction because of latency.
If the computer has a latency of 11uS, then a combination of a 16uS base period, and a dirhold value of 2 ensures that we will always meet the timing requirements of the Gecko. For normal stepping (no direction change), the increased dirhold value has no effect. It takes two periods totalling 32uS to make each step, and we have the same 31,250 step per second rate that we got with the Xylotex.
The 11uS latency number used in this example is very good. If you work through these examples with larger latency, like 20 or 25uS, the top step rate for both the Xylotex and the Gecko will be lower. But the same formulas apply for calculating the optimum BASE_PERIOD, and for tweaking dirhold or other step generator parameters.
For a fast AND reliable software based stepper system, you cannot just guess at periods and other configuration paremeters. You need to make measurements on your computer, and do the math to ensure that your drives get the signals they need.
To make the math easier, I’ve created an Open Office spreadsheet http://wiki.linuxcnc.org/uploads/StepTimingCalculator.ods You enter your latency test result and your stepper drive timing requirements and the spreadsheet calculates the optimum BASE_PERIOD. Next, you test the period to make sure it won’t slow down or lock up your PC. Finally, you enter the actual period, and the spreadsheet will tell you the stepgen parameter settings that are needed to meet your drive’s timing requirements. It also calculates the maximum step rate that you will be able to generate.
I’ve added a few things to the spreadsheet to calculate max speed and stepper electrical calculations.
A proportional-integral-derivative controller (PID controller) is a common feedback loop component in industrial control systems.[19]
The Controller compares a measured value from a process (typically an industrial process) with a reference set point value. The difference (or "error" signal) is then used to calculate a new value for a manipulable input to the process that brings the process measured value back to its desired set point.
Unlike simpler control algorithms, the PID controller can adjust process outputs based on the history and rate of change of the error signal, which gives more accurate and stable control. (It can be shown mathematically that a PID loop will produce accurate, stable control in cases where a simple proportional control would either have a steady-state error or would cause the process to oscillate).
Intuitively, the PID loop tries to automate what an intelligent operator with a gauge and a control knob would do. The operator would read a gauge showing the output measurement of a process, and use the knob to adjust the input of the process (the "action") until the process’s output measurement stabilizes at the desired value on the gauge.
In older control literature this adjustment process is called a "reset" action. The position of the needle on the gauge is a "measurement", "process value" or "process variable". The desired value on the gauge is called a "set point" (also called "set value"). The difference between the gauge’s needle and the set point is the "error".
A control loop consists of three parts:
As the controller reads a sensor, it subtracts this measurement from the "set point" to determine the "error". It then uses the error to calculate a correction to the process’s input variable (the "action") so that this correction will remove the error from the process’s output measurement.
In a PID loop, correction is calculated from the error in three ways: cancel out the current error directly (Proportional), the amount of time the error has continued uncorrected (Integral), and anticipate the future error from the rate of change of the error over time (Derivative).
A PID controller can be used to control any measurable variable which can be affected by manipulating some other process variable. For example, it can be used to control temperature, pressure, flow rate, chemical composition, speed, or other variables. Automobile cruise control is an example of a process outside of industry which utilizes crude PID control.
Some control systems arrange PID controllers in cascades or networks. That is, a "master" control produces signals used by "slave" controllers. One common situation is motor controls: one often wants the motor to have a controlled speed, with the "slave" controller (often built into a variable frequency drive) directly managing the speed based on a proportional input. This "slave" input is fed by the "master" controller’s output, which is controlling based upon a related variable.
"PID" is named after its three correcting calculations, which all add to and adjust the controlled quantity. These additions are actually "subtractions" of error, because the proportions are usually negative:
To handle the present, the error is multiplied by a (negative) constant P (for "proportional"), and added to (subtracting error from) the controlled quantity. P is only valid in the band over which a controller’s output is proportional to the error of the system. Note that when the error is zero, a proportional controller’s output is zero.
To learn from the past, the error is integrated (added up) over a period of time, and then multiplied by a (negative) constant I (making an average), and added to (subtracting error from) the controlled quantity. I averages the measured error to find the process output’s average error from the set point. A simple proportional system either oscillates, moving back and forth around the set point because there’s nothing to remove the error when it overshoots, or oscillates and/or stabilizes at a too low or too high value. By adding a negative proportion of (i.e. subtracting part of) the average error from the process input, the average difference between the process output and the set point is always being reduced. Therefore, eventually, a well-tuned PID loop’s process output will settle down at the set point.
To handle the future, the first derivative (the slope of the error) over time is calculated, and multiplied by another (negative) constant D, and also added to (subtracting error from) the controlled quantity. The derivative term controls the response to a change in the system. The larger the derivative term, the more rapidly the controller responds to changes in the process’s output.
More technically, a PID loop can be characterized as a filter applied to a complex frequency-domain system. This is useful in order to calculate whether it will actually reach a stable value. If the values are chosen incorrectly, the controlled process input can oscillate, and the process output may never stay at the set point.
"Tuning" a control loop is the adjustment of its control parameters (gain/proportional band, integral gain/reset, derivative gain/rate) to the optimum values for the desired control response. The optimum behavior on a process change or set point change varies depending on the application. Some processes must not allow an overshoot of the process variable from the set point. Other processes must minimize the energy expended in reaching a new set point. Generally stability of response is required and the process must not oscillate for any combination of process conditions and set points.
Tuning of loops is made more complicated by the response time of the process; it may take minutes or hours for a set point change to produce a stable effect. Some processes have a degree of non-linearity and so parameters that work well at full-load conditions don’t work when the process is starting up from no-load. This section describes some traditional manual methods for loop tuning.
There are several methods for tuning a PID loop. The choice of method will depend largely on whether or not the loop can be taken "offline" for tuning, and the response speed of the system. If the system can be taken offline, the best tuning method often involves subjecting the system to a step change in input, measuring the output as a function of time, and using this response to determine the control parameters.
If the system must remain on line, one tuning method is to first set the I and D values to zero. Increase the P until the output of the loop oscillates. Then increase I until oscillation stops. Finally, increase D until the loop is acceptably quick to reach its reference. A fast PID loop tuning usually overshoots slightly to reach the set point more quickly; however, some systems cannot accept overshoot.
Parameter | Rise Time | Overshoot | Settling Time | S.S. Error |
---|---|---|---|---|
P | Decrease | Increase | Small Change | Decrease |
I | Decrease | Increase | Increase | Eliminate |
D | Small Change | Decrease | Decrease | Small Change |
Effects of increasing parameters
Another tuning method is formally known as the "Ziegler-Nichols method", introduced by John G. Ziegler and Nathaniel B. Nichols. It starts in the same way as the method described before: first set the I and D gains to zero and then increase the P gain and expose the loop to external interference for example knocking the motor axis to cause it to move out of equilibrium in order to determine critical gain and period of oscillation until the output of the loop starts to oscillate. Write down the critical gain () and the oscillation period of the output (). Then adjust the P, I and D controls as the table shows:
Control type | P | I | D |
---|---|---|---|
P | |||
PI | |||
PID |
After tuning the axis check the following error with Hal Scope to make sure it is within your machine requirements. More information on Hal Scope is in the HAL User manual.
[19] This Subsection is taken from an much more extensive article found at http://en.wikipedia.org/wiki/PID_controller
Ladder logic or the Ladder programming language is a method of drawing electrical logic schematics. It is now a graphical language very popular for programming Programmable Logic Controllers (PLCs). It was originally invented to describe logic made from relays. The name is based on the observation that programs in this language resemble ladders, with two vertical "rails" and a series of "rungs" between them. In Germany and elsewhere in Europe, the style is to draw the rails horizontal along the top and bottom of the page while the rungs are drawn sequentially from left to right.
A program in ladder logic, also called a ladder diagram, is similar to a schematic for a set of relay circuits. Ladder logic is useful because a wide variety of engineers and technicians can understand and use it without much additional training because of the resemblance.
Ladder logic is widely used to program PLCs, where sequential control of a process or manufacturing operation is required. Ladder logic is useful for simple but critical control systems, or for reworking old hardwired relay circuits. As programmable logic controllers became more sophisticated it has also been used in very complex automation systems.
Ladder logic can be thought of as a rule-based language, rather than a procedural language. A "rung" in the ladder represents a rule. When implemented with relays and other electromechanical devices, the various rules "execute" simultaneously and immediately. When implemented in a programmable logic controller, the rules are typically executed sequentially by software, in a loop. By executing the loop fast enough, typically many times per second, the effect of simultaneous and immediate execution is obtained.
The most common components of ladder are contacts (inputs), these usually are either NC (normally closed) or NO (normally open), and coils (outputs).
Of course there are way more components to a full ladder language, but understanding these will help grasp the overall concept.
The ladder consists of one or more rungs. These rungs are horizontal traces, with components on them (inputs, outputs and other), which get evaluated left to right.
This example is the simplest rung:
The input on the left, a normal open contact is connected to the output on the right Q0. Now imagine a voltage gets applied to the leftmost end, as soon as the B0 turns true (e.g. the input is activated, or the user pushed the NO contact), the voltage reaches the right part Q0. As a consequence, the Q0 output will turn from 0 to 1.
Classic Ladder is a free implementation of a ladder interpreter, released under the LGPL. It has been written by Marc Le Douarain.
He describes the beginning of the project on his website Original project homepage:
“I decided to program a ladder language only for test purposes at the start, in February 2001. It was planned, that I would have to participate to a new product after leaving the enterprise in which I was working at that time. And I was thinking that to have a ladder language in those products could be a nice option to considerate. And so I started to code the first lines for calculating a rung with minimal elements and displaying dynamically it under Gtk, to see if my first idea to realize all this works.
And as quickly I've found that it advanced quite well, I've continued with more complex elements : timer, multiples rungs, etc...
Voila, here is this work... and more : I've continued to add features since then.”
Classic Ladder has been adapted to work with EMC2’s HAL, and is currently being distributed along with EMC2. If there are issues/problems/bugs please report them to the Enhanced Machine Controller project.
Classic Ladder is a type of programming language originally implemented on industrial PLC’s (it’s called Ladder Programming). It is based on the concept of relay contacts and coils, and can be used to construct logic checks and functions in a manner that is familiar to many systems integrators. It is important to know how ladder programs are evaluated when running:
It seems natural that each line would be evaluated left to right then the next line down etc-but it doesn’t work this way. ALL the inputs are read, ALL the logic is figured out, then ALL the outputs are set. This can presents a problem in certain circumstance if the output of one line feeds the input of another. Another gotcha with ladder programming is the "Last One Wins" rule. If you have the same output in different locations of your ladder the state of the last one will be what the output is set to.
Classic Ladder version 7.124 has been adapted for EMC 2.3 This document describes that version.
The most common language used when working with Classic Ladder is ladder. Classic Ladder also supports Sequential Function Chart (Grafcet).
There are 2 components to Classic Ladder.
Typically classic ladder components are placed in the custom.hal file if your working from a Stepconf generated configuration. These must not be placed in the custom_postgui.hal file or the Ladder Editor menu will be grayed out.
Ladder files (.clp) must not contain any blank spaces in the name.
Loading the Classic Ladder real time module (classicladder_rt) is possible from a hal file, or directly using a halcmd instruction. The first line loads real time the Classic Ladder module. The second line adds the function classicladder.0.refresh to the servo thread. This line makes Classic Ladder update at the servo thread rate.
loadrt classicladder_rt + addf classicladder.0.refresh servo-thread
The speed of the thread that Classic Ladder is running in directly effects the responsiveness to inputs and outputs. If you can turn a switch on and off faster than Classic Ladder can notice it then you may need to speed up the thread. The fastest that Classic Ladder can update the rungs is one millisecond. You can put it in a faster thread but it will not update any faster. If you put it in a slower then one microsecond thread then Classic Ladder will update the rungs slower. The current scan time will be displayed on the section display, it is rounded to microseconds. If the scan time is longer than one millisecond you may want to shorten the ladder or put it in a slower thread.
It is possible to configure the number of each type of ladder object while loading the Classic Ladder real time module. If you do not configure the number of ladder objects Classic Ladder will use the default values.
Table 27.1. Default Variable Count
Object Name | Variable Name | Default Value |
---|---|---|
Number of rungs | (numRungs) | 100 |
Number of bits | (numBits) | 20 |
Number of word variables | (numWords) | 20 |
Number of timers | (numTimers) | 10 |
Number of timers IEC | (numTimersIec) | 10 |
Number of monostables | (numMonostables) | 10 |
Number of counters | (numCounters) | 10 |
Number of hal inputs bit pins | (numPhysInputs) | 15 |
Number of hal output bit pins | (numPhysOutputs) | 15 |
Number of arithmetic expressions | (numArithmExpr) | 50 |
Number of Sections | (numSections) | 10 |
Number of Symbols | (numSymbols) | Auto |
Number of S32 inputs | (numS32in) | 10 |
Number of S32 outputs | (numS32out) | 10 |
Number of Float inputs | (numFloatIn) | 10 |
Number of Float outputs | (numFloatOut) | 10 |
Objects of most interest are numPhysInputs, numPhysOutputs, numS32in, and numS32out.
Changing these numbers will change the number of HAL bit pins available. numPhysInputs and numPhysOutputs control how many HAL bit (on/off) pins are available. numS32in and numS32out control how many HAL signed integers (+- integer range) pins are available.
For example (you don’t need all of these to change just a few):
loadrt classicladder_rt numRungs=12 numBits=100 numWords=10 numTimers=10 numMonostables=10 numCounters=10 numPhysInputs=10 numPhysOutputs=10 numArithmExpr=100 numSections=4 numSymbols=200 numS32in=5 numS32out=5
To load the default number of objects:
loadrt classicladder_rt
Classic Ladder hal commands must executed before the GUI loads or the menu item Ladder Editor will not function. If you used the Stepper Config Wizard place any Classic Ladder hal commands in the custom.hal file.
To load the user module:
loadusr classicladder
To load a ladder file:
loadusr classicladder myladder.clp
Classic Ladder Loading Options
--
nogui (loads with out the ladder editor) normally used after
debugging
is finished.
--
modmaster (initializes MODBUS master) should load the ladder
program
at the same time or the TCP is default port.
To use Classic Ladder with HAL without EMC. The -w tells HAL not to close down the HAL environment until Classic Ladder is finished.
loadusr -w classicladder
If you first load ladder program with the --
nogui option then load
Classic Ladder again with no options the GUI
will display the last loaded ladder program.
In AXIS you can load the GUI from File/Ladder Editor…
If you load Classic Ladder with the GUI it will display two windows: section display, and section manager.
When you first start up Classic Ladder you get an empty Sections Manager window.
This window allows you to name, create or delete sections and choose what language that section uses. This is also how you name a subroutine for call coils.
When you first start up Classic Ladder you get an empty Section Display window.
Most of the buttons are self explanatory:
The Vars button is for looking at variables, toggle it to display one, the other, both, then none of the windows.
The Config button is used for modbus and shows the max number of ladder elements that was loaded with the real time module.
The Symbols button will display an editable list of symbols for the variables (hint you can name the inputs, outputs, coils etc).
The Quit button will shut down the user program meaning Modbus and the display- the real time ladder program will still run in the back ground.
The check box at the top right allows you to select whether variable names or symbol names are displayed
You might notice that there is a line under the ladder program display that reads "Project failed to load…" That is the status bar that gives you info about elements of the ladder program that you click on in the display window. This status line will now display HAL signal names for variables %I, %Q and the first %W (in an equation) You might see some funny labels, such as (103) in the rungs. This is displayed (on purpose) because of an old bug- when erasing elements older versions sometimes didn’t erase the object with the right code. You might have noticed that the long horizontal connection button sometimes didn’t work in the older versions. This was because it looked for the free code but found something else. The number in the brackets is the unrecognized code. The ladder program will still work properly, to fix it erase the codes with the editor and save the program.
This are two variable windows: bool and signed integer. the vars button is in the section display window, toggle the Vars button to display one, the other, both, then none of the windows.
The Bool window displays some of all the bool (on/off) variable data . Notice all variable start with the % sign. The %I variable represents HAL input bit pins. The %Q represents the relay coil and HAL output bit pins. The %B represents an internal relay coil or internal contact The three edit areas at the top allow you to select what 15 variable will be displayed in each column. For instance if there were 30 %B variable and you entered 5 at the top of the column, variable %B5 to %B19 would be displayed. The check boxes allow you to set and unset %B variables manually as long as the ladder program isn’t setting them as outputs. Any Bits that are set as outputs by the program when Classic Ladder is running can not be changed and will be displayed as checked if on and unchecked if off.
The Watch Window displays variable status. The edit box beside it is the number stored in the variable and the drop-down box beside that allow you to choose whether the number to be displayed in hex, decimal or binary. If there are symbol names defined in the symbols window for the word variables showing and the display symbols checkbox is checked the the section display window, symbol names will be displayed. To change the variable displayed type the variable number eg. %W2 (if display symbols check box is not checked) or symbol name (if the display symbols checkbox is checked) over an existing variable number/name and press the Enter Key.
This is a list of symbol names to use instead of variable names to be displayed in the section window when the display symbols check box is checked. You add the variable name (remember the % symbol and capital letters), symbol name . If the variable can have a HAL signal connected to it (%I, %Q, and %W-if you have loaded s32 pin with the real time module) then the comment section will show the current HAL signal name or lack there of. symbol names should be kept short to display better. keep in mind that you can display the longer HAL signal name of %I, %Q and %W variable by clicking on them in the section window. Between the two on should be able to keep track of what the ladder program is connected to!
Starting from the top left image:
A short description of each of the buttons:
Represent switches or relay contacts. They are controlled by the variable letter and number assigned to them.
The variable letter can be B, I, or Q and the number can be up to a three digit number eg. %I2, %Q3, or %B123. Variable I is controlled by a Hal input pin with a corresponding number . Variable B is for internal contacts, controlled by a B coil with a corresponding number. Variable Q is controlled by a Q coil with a corresponding number. (like a relay with multiple contacts). Eg. if HAL pin classicladder.0.in-00 is true then %I0 N.O. contact would be on (closed, true, whatever you like to call it). If %B7 coil is energized (on, true, etc) then %B7 N.O. contact would be on. If %Q1 coil is energized then %Q1 N.O. contact would be on (and HAL pin classicladder.0.out-01 would be true.)
Represent new count down timers! IEC Timers replace Timers and Monostables.
IEC Timers have 2 contacts.
There are three modes - TON, TOF, TP.
The time intervals can be set in multiples of 100ms, seconds, or minutes.
There are also Variables for IEC timers that can be read and/or written to in compare or operate blocks.
Represent count down timers. This is deprecated and replaced by IEC Timers.
Timers have 4 contacts.
The timer base can be multiples of milliseconds, seconds, or minutes.
There are also Variables for timers that can be read and/or written to in compare or operate blocks.
Represent are one-shot timers. This is deprecated and replaced by IEC Timers.
Monostables have 2 contacts I and R.
The I contact is rising edge sensitive meaning it starts the timer only when changing from false to true (or off to on). While the timer is running the I contact can change with no effect to the running timer. R will be true and stay true till the timer finishes counting to zero. The timer base can be multiples of milliseconds, seconds, or minutes.
There are also Variables for monostables that can be read and/or written to in compare or operate blocks.
The up and down count contacts are edge sensitive meaning they only count when the contact changes from false to true (or off to on if you rather).
The range is 0 to 9999.
There are also Variables for counters that can be read and/or written to in compare or operate blocks.
For arithmetic comparison. Is variable %XXX = to this number (or evaluated number)
The compare block will be true when comparison is true. you can use most math symbols:
For example ABS(%W2)=1, MOY(%W1,%W2)<3.
No spaces are allowed in the comparison equation. For example %C0.V>%C0.P is a valid comparison expression while %C0.V > %CO.P is not a valid expression.
There is a list of Variables down the page that can be used for reading writing to ladder objects. When a new compare block is opened be sure and delete the # symbol when you enter a compare.
To find out if word variable #1 is less than 2 times the current value of counter #0 the syntax would be:
%W1<2*%C0.V
To find out if S32in bit 2 is equal to 10 the syntax would be:
%IW2=10
Note: Compare uses the arithmetic equals not the double equals that programmers are use to.
For variable assignment. eg assign this number (or evaluated number) to this variable %xxx there are two math functions MINI and MAXI that check a variable for maximum (0x80000000) and minimum values (0x07FFFFFFF) (think signed values) and keeps them from going beyond.
When a new variable assignment block is opened be sure and delete the # symbol when you enter an assignment.
To assign a value of 10 to the timer preset of IEC Timer 0 the syntax would be:
%TM0.P=10
To assign the value of 12 to S32out bit 3 the syntax would be:
%QW3=12
The following figure shows an Assignment and a Comparison Example. %QW0 is a S32out bit and %IW0 is a S32in bit. In this case the HAL pin classicladder.0.s32out-00 will be set to a value of 5 and when the HAL pin classicladder.0.s32in-00 is 0 the HAL pin classicladder.0.out-00 will be set to True.
Coils represent relay coils. They are controlled by the variable letter and number assigned to them.
The variable letter can be B or Q and the number can be up to a three digit number eg. %Q3, or %B123. Q coils control HAL out pins. eg if &Q15 is energized then HAL pin classicladder.0.out-15 will be true. B coils are internal coils used to control program flow.
WARNING if you use a N.C. contact with a N.C. coil the logic will work (when the coil is energized the contact will be closed) but that is really hard to follow!
A JUMP COIL is used to JUMP to another section-like a goto in BASIC programming language.
If you look at the top left of the sections display window you will see a small label box and a longer comment box beside it. Now go to Editor→Modify then go back to the little box, type in a name.
Go ahead and add a comment in the comment section. This label name is the name of this rung only and is used by the JUMP COIL to identify where to go.
When placing a JUMP COIL add it in the right most position and change the label to the rung you want to JUMP to.
A CALL COIL is used to go to a subroutine section then return-like a gosub in BASIC programming language.
If you go to the sections manager window hit the add section button. You can name this section, select what language it will use (ladder or sequential), and select what type (main or subroutine).
Select a subroutine number (SR0 for example). An empty section will be displayed and you can build your subroutine.
When you’ve done that, go back to the section manager and click on the your main section (default name prog1).
Now you can add a CALL COIL to your program. CALL COILs are to be placed at the right most position in the rung.
Remember to change the label to the subroutine number you choose before.
These Variables are used in COMPARE or OPERATE to get information about, or change specs of, ladder objects Such as changing a counter preset, or seeing if the a timer is done running.
List of variables :
WARNING -These is probably the least used/known about feature of Classic Ladder. Sequential programming is used to make sure a series of ladder events always happen in a prescribed order. Sequential programs do not work alone-there is always a ladder program as well that controls the variables. Here are the basic rules governing sequential programs:
This is the SEQUENTIAL editor window Starting from the top left image: Selector arrow , Eraser Ordinary step , Initial (Starting) step Transition , Step and Transition Transition Link-Downside , Transition Link-Upside Pass-through Link-Downside , Pass-through Link-Upside Jump Link Comment Box [show sequential program]
To use links you must have steps already placed , select the type of link , then select the two steps or transactions one at a time- It takes practice!
With sequential programming: The variable %Xxxx (eg. %X5) is used to see if a step is active. The variable %Xxxx.V (eg. %X5.V) is used to see how long the step has been active. The %X and %X.v variables are use in LADDER logic. The variables assigned to the transitions (eg. %B) control whether the logic will pass to the next step. After a step has become active the transition variable that caused it to become active has no control of it anymore. The last step has to JUMP LINK back (only to the beginning step?)
Things to consider:
To get MODBUS to initialize you must specify that when loading the
Classic Ladder userspace program eg. loadusr -w classicladder --
modmaster myprogram.clp (assuming myprogram.clp is present -w makes
HAL wait till you close Classic Ladder before closing realtime session)
my idea behind this is to get a working modbus solution out there then
we can decide how it should be done in the best way. As it stands now
Classic Ladder also loads a TCP modbus slave (if you add --
modserver on command line) - I have not tested this nor have I tested
the TCP modbus master. I have done some testing with the serial port
and had to add some functions to get it to talk to my VFD -but it does
work. Modbus function 1,2,3,4,5,6,8,15,16 (read coils,read inputs, read
holding registers, read input registers, write single coils, write
single register, echo test, write multiple coils, write multiply
registers) are currently available. If you do not specify a --
modmaster when loading the Classic Ladder user program this (next)
page will not be displayed.
In the example above: Port number- for my computer /dev/ttyS0 was my serial port
The serial speed is set to 9600 baud.
Slave address is set to 12 ( on my VFD I can set this from 1-31, meaning I can talk to 31 VFDs maximum on one system)
The first line is set up for 8 input bits starting at the first register number (register 1) so register numbers 1-8 and maps them on to Classic Ladder’s %B variables starting at %B1 ending at %B8.
The second line is set for 2 output bits starting at the ninth register number (register 9) so register numbers 9-10 and maps them on to Classic Ladder’s %Q variables starting at %Q9 ending at %Q10.
The third line is set to write 2 registers (16 bit each) starting at the 0th register number (register 0) so register numbers 0-1 and maps them on to Classic Ladder’s %W variables starting at %W0 ending at %W1
It’s easy to make an off-by-one error as sometimes the modbus elements are referenced starting at one rather then 0 (actually by the standard that is the way it’s supposed to be!) You can use the modbus element offset radio button to help with this.
The documents for your modbus slave device will tell you how the registers are set up- there is no standard way.
The SERIAL PORT, PORT SPEED, PAUSE, and DEBUG level are editable for changes (when you close the config window values are applied though Radio buttons apply immediately)
To use the echo function select the echo function and add the slave number you wish to test. You don’t need to specify any variables.
The number 257 will be sent to the slave number you specified and the slave should send it back. you will need to have Classic Ladder running in a terminal to see the message.
Serial:
Classic Ladder has a Modbus/TCP server integrated. Default port is
Info on modbus protocol are available here:
If there is a communication error, a warning window will pop up (if the GUI is running) and %E0 will be true. Modbus will continue to try and communicate. The %E0 could be used to make a decision based on the error. A timer could be used to stop the machine if timed out etc.
--
modmaster you must load the ladder program at the same
time or else
only TCP will work.
In this section we will cover the steps needed to add Classic Ladder to a Stepconf Wizard generated config. On the advanced Configuration Options page of Stepconf Wizard check off "Include Classic Ladder PLC"
If you used the Stepconf Wizard to add Classic Ladder you can skip this step.
To manually add Classic Ladder you must first add the modules. This is done by adding a couple of lines to the custom.hal file.
This line loads the real time module:
loadrt classicladder_rt
This line adds the Classic Ladder function to the servo thread:
addf classicladder.0.refresh servo-thread
Now start up your config and select File/Ladder Editor… to open up the Classic Ladder GUI. You should see a blank Section Display and Sections Manager window as shown above. In the Section Display window open the Editor. In the Editor window select Modify. Now a Properties window pops up and the Section Display shows a grid. The grid is one rung of ladder. The rung can contain branches. A simple rung has one input, a connector line and one output.
Now click on the N.O. Input in the Editor Window.
Now click in the upper left grid to place the N.O. Input into the ladder.
Repeat the above steps to add a N.O. Output to the upper right grid and use the Horizontal Connection to connect the two. It should look like the following. If not use the Eraser to remove unwanted sections.
Now click on the OK button in the Editor window. Now your Section Display should look like this.
To save the new file select Save As and give it a name. The .clp extension will be added automatically. It should default to the running config directory as the place to save it.
Again if you used the Stepconf Wizard to add Classic Ladder you can skip this step.
To manually add a ladder you need to add add a line to your custom.hal file that will load your ladder file. Close your EMC2 session and add this line to your custom.hal file.
loadusr -w classicladder `--`nogui MyLadder.clp
Now if you start up your EMC2 config your ladder program will be running as well. If you select File/Ladder Editor… the program you created will show up in the Section Display window.
To have a counter that "wraps around" you have to use the preset pin and the reset pin. When you create the counter set the preset at the number you wish to reach before wrapping around to 0. The logic is if the counter value is over the preset then reset the counter and if the underflow is on then set the counter value to the preset value. As you can see in the example when the counter value is greater than the counter preset the counter reset is triggered and the value is now 0. The underflow output %Q2 will set the counter value at the preset when counting backwards.
This example shows you how to reject extra pulses from an input. Suppose the input pulse %I0 has an annoying habit of giving an extra pulse that spoils our logic. The TOF (Timer Off Delay) prevents the extra pulse from reaching our cleaned up output %Q0. How this works is when the timer gets an input the output of the timer is on for the duration of the time setting. Using a normally closed contact %TM0.Q the output of the timer blocks any further inputs from reaching our output until it times out.
The External E-Stop example is in the /config/classicladder/cl-estop folder. It uses a pyVCP panel to simulate the external components.
To interface an external E-Stop to EMC and have the external E-Stop work together with the internal E-Stop requires a couple of connections through ClassicLadder.
First we have to open the E-Stop loop in the main hal file by commenting out by adding the pound sign as shown or removing the following lines.
# net estop-out <= iocontrol.0.user-enable-out + # net estop-out => iocontrol.0.emc-enable-in
Next we add ClassicLadder to our custom.hal file by adding these two lines:
loadrt classicladder_rt + addf classicladder.0.refresh servo-thread
Next we run our config and build the ladder as shown here.
After building the ladder select Save As and save the ladder as estop.clp
Now add the following line to your custom.hal file.
# Load the ladder + loadusr classicladder --nogui estop.clp
I/O assignments
Next we add the following lines to the custom_postgui.hal file
# E-Stop example using pyVCP buttons to simulate external components
# The pyVCP checkbutton simulates a normally closed external E-Stop + net ext-estop classicladder.0.in-00 <= pyvcp.py-estop
# Request E-Stop Enable from EMC + net estop-all-ok iocontrol.0.emc-enable-in <= classicladder.0.out-00
# Request E-Stop Enable from pyVCP or external source + net ext-estop-reset classicladder.0.in-03 <= pyvcp.py-reset
# This line resets the E-Stop from EMC + net emc-reset-estop iocontrol.0.user-request-enable => classicladder.0.in-02
# This line enables EMC to unlatch the E-Stop in classicladder + net emc-estop iocontrol.0.user-enable-out => classicladder.0.in-01
# This line turns on the green indicator when out of E-Stop + net estop-all-ok => pyvcp.py-es-status
Next we add the following lines to the panel.xml file. Note you have to open it with the text editor not the default html viewer.
<pyvcp> + <vbox> + <label><text>"E-Stop Demo"</text></label> + <led> + <halpin>"py-es-status"</halpin> + <size>50</size> + <on_color>"green"</on_color> + <off_color>"red"</off_color> + </led> + <checkbutton> + <halpin>"py-estop"</halpin> + <text>"E-Stop"</text> + </checkbutton> + </vbox> + <button> + <halpin>"py-reset"</halpin> + <text>"Reset"</text> + </button> + </pyvcp>
Now start up your config and it should look like this.
Note that in this example like in real life you must clear the remote E-Stop (simulated by the checkbox) before the AXIS E-Stop or the external Reset will put you in OFF mode. If the E-Stop in the AXIS screen was pressed you must press it again to clear it. You cannot reset from the external after you do an E-Stop in AXIS.
In this example we are using the Operate block to assign a value to the timer preset based on if an input is on or off.
In this case %I0 is true so the timer preset value is 10. If %I0 was false the timer preset would be 5.
This is a program for one type of tool turret. The turret has a home switch at tool position 1 and another switch to tell you when the turret is in a lockable position. To keep track of the actual tool number one must count how many positions past home you are. We will use ClassicLadder’s counter block $CO.The counter is preset to 1 when RESET is true. The counter is increased by one on the rising edge of INDEX. We then COMPARE the counter value (%C0.V) to the tool number we want (in the example only checks for tool 1 and 2 are shown). We also OPERATE the counter value to a word variable (%W0) that (you can assume) is mapped on to a S32 out HAL pin so you can let some other HAL component know what the current tool number is. In the real world another S32 (in) pin would be used to get the requested tool number from EMC.You would have to load ClassicLadder’s real time module specifying that you want S32 in and out pins. See loading options above. [display turret sample]
This is a sequential program when the program is first started step one is active then when %B0 is true then steps 2 and 3 are then active and step one is inactive then when %B1 and/or %B2 are true, step 4 and/or 5 are active and step 2 and/or 3 are inactive Then when either %B3 OR %B4 are true, step 6 is true and steps 4 and 5 are inactive then when %B5 is true step 1 is active and step 6 is inactive and it all starts again As shown the sequence has been: %B0 was true making step 2 and 3 active then %B1 became true (and still is-see the pink line through %B1) making step 4 active and step 2 inactive step 3 is active and waiting for %B2 to be true step 4 is active and is waiting for %B3 to be true WOW that was quite a mouth full!! [display sequential program]
When you add a second parallel port to your PCI bus you have to find out the address before you can use it with EMC.
To find the address of your parallel port card open a terminal window and type
lspci -v
You will see something similar to this as well as info on everything else on the PCI bus:
0000:00:10.0 Communication controller: NetMos Technology PCI 1 port parallel adapter (rev 01) + Subsystem: LSI Logic / Symbios Logic: Unknown device 0010 + Flags: medium devsel, IRQ 11 + I/O ports at a800 [size=8] + I/O ports at ac00 [size=8] + I/O ports at b000 [size=8] + I/O ports at b400 [size=8] + I/O ports at b800 [size=8] + I/O ports at bc00 [size=16]
In my case the address was the first one so I changed my .hal file from
loadrt hal_parport cfg=0x378
to
loadrt hal_parport cfg="0x378 0xa800 in"
Note the double quotes surrounding the addresses.
and then added the following lines so the parport will get read and written to.
addf parport.1.read base-thread + addf parport.1.write base-thread
After doing the above then run your config and verify that the parallel port got loaded in Machine/Show Hal Configuration window.
If your spindle is controlled by a VFD with a 0 to 10 volt signal and your using a DAC card like the m5i20 to output the control signal.
First you need to figure the scale of spindle speed to control signal. For this example the spindle top speed of 5000 RPM is equal to 10 volts. so our scale factor is 0.002
We have to add a scale component to the hal file to scale the motion.spindle-speed-out to the 0 to 10 needed by the VFD if your DAC card does not do scaling.
loadrt scale count=1
If your spindle can be controlled by a PWM signal, use the pwmgen component to create the signal:
loadrt pwmgen output_type=0
This assumes that the spindle controller’s response to PWM is simple: 0% PWM gives 0RPM, 10% PWM gives 180 RPM, etc. If there is a minimum PWM required to get the spindle to turn, follow the example in the nist-lathe sample configuration to use a scale component.
If you need a spindle enable signal link your output pin to motion.spindle-on. To link these pins to a parallel port pin put something like the following in your .hal file making sure you pick the pin that is connected to your control device.
net spindle-enable motion.spindle-on => parport.0.pin-14-out
If you have direction control of your spindle the hal pins motion.spindle-forward and motion.spindle-reverse are controlled by M3 and M4. Spindle speed "Sn" must be set to a positive non zero value for M3/4 to turn on spindle motion.
To link these pins to a parallel port pin put something like the following in your .hal file making sure you pick the pin that is connected to your control device.
net spindle-fwd motion.spindle-forward => parport.0.pin-16-out
If you need to ramp your spindle speed command and your control does not have that feature it can be done in HAL. Basically you need to hijack the output of motion.spindle-speed-out and run it through a limit2 component with the scale set so it will ramp the rpm from motion.spindle-speed-out to your device that receives the rpm. The second part is to let EMC know when the spindle is at speed so motion can begin.
In the 0-10 volt example the line "net spindle-speed-scale motion.spindle-speed-out ⇒ scale.0.in" is changed as shown in the following example.
# load real time a limit2 and a near with names so it is easier to follow # add the functions to a thread # set the parameter for max velocity # hijack the spindle speed out and send it to spindle ramp in # the output of spindle ramp is sent to the scale in # to know when to start the motion we send the near component # the output from spindle-at-speed is sent to motion.spindle-at-speed
Spindle feedback is needed by EMC to perform any spindle coordinated motions like threading and constant surface speed. The StepConf Wizard can perform the connections for you if you select Encoder Phase A and Encoder Index as inputs.
Hardware assumptions:
Basic Steps to add the components and configure them:
loadrt encoder num_chan=1
To enable EMC to wait for the spindle to be at speed before executing a series of moves you need to set motion.spindle-at-speed to true when the spindle is at the commanded speed. To do this you need spindle feedback from an encoder. Since the feedback and the commanded speed are not usually exactly the same you need to use the "near" component to say that the two numbers are close enough. The connections needed are from the spindle velocity command signal to near.n.in1 and from the spindle velocity from the encoder to near.n.in2. Then the near.n.out is connected to motion.spindle-at-speed. The near.n.scale needs to be set to say how close the two numbers must be before turning on the output. Depending on your setup you may need to adjust the scale to work with your hardware. The following is typical of the additions needed to your hal file to enable Spindle At Speed. If you already have near in your hal file then increase the count and adjust code to suit. Check to make sure the signal names are the same in your hal file.
loadrt near addf near.0 servo-thread net spindle-cmd near.0.in1 net spindle-velocity near.0.in2 net spindle-at-speed motion.spindle-at-speed <= near.0.out setp near.0.scale 1.01
This example is to explain how to hook up the common MPG pendants found on the market place today. This example uses a MPG3 pendant and a C22 pendant interface card from CNC4PC connected to a second parallel port plugged into the PCI slot. This example gives you 3 axis with 3 step increments of 0.1, 0.01, 0.001
In your custom.hal file or other .hal file add the following making sure you don’t have mux4 or an encoder already in use. If you do just increase the counts and change the reference number. More information about mux4 and encoder can be found here [cha:HAL-Components] and here [sec:Encoder].
# Jog Pendant + loadrt encoder num_chan=1 + loadrt mux4 count=1 + addf encoder.capture-position servo-thread + addf encoder.update-counters base-thread + addf mux4.0 servo-thread
# If your MPG outputs a quadture signal per click set x4 to 1 + # If your MPG puts out 1 pulse per click set x4 to 0 + setp encoder.0.x4-mode 0
# For velocity mode, set n to 1 + # In velocity mode the axis stops when dial is stopped even if that means + # the commanded motion is not completed, + # For position mode (the default), set n to 0 + # In position mode the axis will move exactly jog-scale + # units for each count, regardless of how long that might take, + # This must be set for each axis you want to behave other than default + setp axis.N.jog-vel-mode n
setp mux4.0.in0 0.1 + setp mux4.0.in1 0.01 + setp mux4.0.in2 0.001 + net scale1 mux4.0.sel0 <= parport.1.pin-09-in + net scale2 mux4.0.sel1 <= parport.1.pin-10-in + net pend-scale axis.0.jog-scale <= mux4.0.out + net pend-scale axis.1.jog-scale + net pend-scale axis.2.jog-scale + net mpg-a encoder.0.phase-A <= parport.1.pin-02-in + net mpg-b encoder.0.phase-B <= parport.1.pin-03-in + net mpg-x axis.0.jog-enable <= parport.1.pin-04-in + net mpg-y axis.1.jog-enable <= parport.1.pin-05-in + net mpg-z axis.2.jog-enable <= parport.1.pin-06-in + net pend-counts axis.0.jog-counts <= encoder.0.counts + net pend-counts axis.1.jog-counts + net pend-counts axis.2.jog-counts
This example shows the connections needed to use an Automation Direct GS2 VFD to drive a spindle. The spindle speed and direction is controlled by EMC.
Using the GS2 component involves very little to set up. We start with a Stepconf Wizard generated config. Make sure the pins with "Spindle CW" and "Spindle PWM" are set to unused in the parallel port setup screen.
In the custom.hal file we place the following to connect EMC to the GS2 and have EMC control the drive.
# load the user space component for the Automation Direct GS2 VFD's + loadusr -Wn spindle-vfd gs2_vfd -n spindle-vfd
# connect the spindle direction pin to the GS2 + net gs2-fwd spindle-vfd.spindle-fwd <= motion.spindle-forward
# connect the spindle on pin to the GS2 + net gs2-run spindle-vfd.spindle-on <= motion.spindle-on
# connect the GS2 at speed to the motion at speed + net gs2-at-speed motion.spindle-at-speed <= spindle-vfd.at-speed
# connect the spindle RPM to the GS2 + net gs2-RPM spindle-vfd.speed-command <= motion.spindle-speed-out
On the GS2 drive itself you need to set a couple of things before the modbus communications will work. Other parameters might need to be set based on your physical requirements but is beyond the scope of this manual. Refer to the GS2 manual that came with the drive for more information on parameters.
A pyVCP panel based on this example is in the pyVCP Examples section of this manual.
If what you get is not what you expect many times you just got some experience. Learning from the experience increases your understanding of the whole. Diagnosing problems is best done by divide and conquer. By this I mean if you can remove 1/2 of the variables from the equation each time you will find the problem the fastest. In the real world this is not always the case but a good place to start usually.
The most common reason in a new installation for the stepper not to move is the step and direction signals are backwards. If you press the jog foward and backward key and the stepper moves one step each time in the same direction there is your sign.
The concept of a following error is funny when talking about stepper motors. Since they are an open loop system, there is no position feedback to let you know if you actually are out of range. EMC calculates if it can keep up with the motion called for and if not then it gives a following error. Following errors usually are the result of one of the following on stepper systems.
Any of the above can cause the RT pulsing to not be able to keep up the requested step rate. This can happen if you didn’t run the latency test long enough to get a good number to plug into the Stepconf Wizard or if you set the Maximum Velocity or Maximum Acceleration too high.
If you added backlash you need to increase the STEPGEN_MAXACCEL up to double the MAX_ACCELERATION in the AXIS section of the INI file for each axis you added backlash to. EMC uses "extra acceleration" at a reversal to take up the backlash. Without backlash correction step generator acceleration can be just a few percent above the motion planner acceleration.
When you get this error:
RTAPI: ERROR: Unexpected realtime delay on task n
This error is generated by rtapi based on an indication from rtai that a deadline was missed. It is usually an indication that the BASE_PERIOD in the [EMCMOT] section of the ini file is set too low. You should run the Latency Test for an extended period of time to see if you have any delays that would cause this problem. If you used the Stepconf Wizard run it again and test the Base Period Jitter again and adjust the Base Period Maximum Jitter on the Basic Machine Information page. You might have to leave the test running for an extended period of time to find out if some hardware causes intermittent problems.
EMC2 tracks the number of CPU cycles between invocations of the real-time thread. If some element of your hardware is causing delays or your realtime threads are set too fast you will get this error.
This error is only displayed once per session. If you had your BASE_PERIOD too low you could get hundreds of thousands of error messages per second if more than one was displayed.
If you are seeing an axis ending up in the wrong location over multiple moves, it is likely that you do not have the correct direction hold times or step timing for your stepper drivers. Each direction change may be losing a step or more. If the motors are stalling, it is also possible you have either the MAX_ACCELERATION or MAX_VELOCITY set too high for that axis.
The following program will test the Z axis configuration for proper setup. Copy the program to your emc2/nc_files directory and name it TestZ.ngc or similar. Zero your machine with Z = 0.000 at the table top. Load and run the program. It will make 200 moves back and forth from 0.5 to 1". If you have a configuration issue, you will find that the final position will not end up 0.500" that the axis window is showing. To test another axis just replace the Z with your axis in the G0 lines.
( test program to see if Z axis loses position ) + ( msg, test 1 of Z axis configuration ) + G20 #1000=100 ( loop 100 times ) + ( this loop has delays after moves ) + ( tests acc and velocity settings ) + o100 while [#1000] + G0 Z1.000 + G4 P0.250 + G0 Z0.500 + G4 P0.250 + #1000 = [#1000 - 1] + o100 endwhile + ( msg, test 2 of Z axis configuration S to continue) + M1 (stop here) + #1000=100 ( loop 100 times ) + ( the next loop has no delays after moves ) + ( tests direction hold times on driver config and also max accel setting ) + o101 while [#1000] + G0 Z1.000 + G0 Z0.500 + #1000 = [#1000 - 1] + o101 endwhile + ( msg, Done...Z should be exactly .5" above table ) + M2
These are some basic Linux commands and techniques for new to Linux users. More complete information can be found on the web or by using the man pages.
When you install EMC2 with the Ubuntu LiveCD the default is to have to log in each time you turn the computer on. To enable automatic login go to System/Administration/Login Window. If it is a fresh install the Login Window might take a second or three to pop up. You will have to have your password that you used for the install to gain access to the Login Window Preferences window. In the Security tab check off Enable Automatic Login and pick a user name from the list (that would be you).
To have EMC start automatically with your config after turning on the computer go to System/Preferences/Sessions Startup Programs , add new. Navigate to your config and select the .ini file. When the file picker dialog closes add emc and a space in front of the path to your .ini file.
Example:
emc /home/mill/emc2/config/mill/mill.ini
Man pages are automatically generated manual pages in most cases. Man pages are usually available for most programs and commands in Linux.
To view a man page open up a terminal window by going to Applications, Accessories, Terminal. For example if you wanted to find out something about the find command in the terminal window type:
man find
Use the Page Up and Page Down keys to view the man page and the Q key to quit viewing.
Sometimes when troubleshooting you need to get a list of modules that are loaded. In a terminal window type:
lsmod
If you want to send the output from lsmod to a text file in a terminal window type:
lsmod > mymod.txt
The resulting text file will be located in the home directory if you did not change directories when you opened up the terminal window and it will be named mymod.txt or what ever you named it.
When you open the file browser and you see the Owner of the file is root you must do extra steps to edit that file. Editing some root files can have bad results. Be careful when editing root files. You can open and view most root files normally but they will open in “read only” mode.
Open up Applications, Accessories, Terminal.
In the terminal window type
sudo gedit
Open the file with File, Open then edit
gksudo "gnome-open %u"
as the command and save the launcher to
your desktop
To find out the path to the present working directory in the terminal window type:
pwd
To move up one level in the terminal window type:
cd ..
To move up two levels in the terminal window type:
cd ../..
To move down to the emc2/configs subdirectory in the terminal window type:
cd emc2/configs
To view a list of all the files and subdirectories in the terminal window type:
dir
or
ls
The find command can be a bit confusing to a new Linux user. The basic syntax is:
find starting-directory parameters actions
For example to find all the .ini files in your emc2 directory you first need to use the pwd command to find out the directory. Open a new terminal window and type:
pwd
might return the following result
/home/joe
With this information put the command together like this:
find /home/joe/emc2 -name *.ini -print
The -name is the name of the file your looking for and the -print tells it to print out the result to the terminal window. The *.ini tells find to return all files that have the .ini extension.
To find all the files in the directory named and all the subdirectories under that add the -L option to the find command like this:
find -L /home/joe/emc2 -name *.ini -print
grep -i -r 'text to search for' *
To find all the files that contain the text to search for in the current directory and all the subdirectories below the current while ignoring the case. The -i is for ignore case and the -r is for recursive (include all subdirectories in the search). The * is a wild card for search all files.
To view the bootup messages use "dmesg" from the command window. To save the bootup messages to a file use the redirection operator like this:
dmesg > bootmsg.txt
The contents of this file can be copied and pasted on line to share with people trying to help you diagnose your problem.
To clear the message buffer type this:
sudo dmesg -c
This can be useful to do just before you launch EMC to only show the infomation related to the start up of EMC.
To find out what hardware is connected to your motherboard in a terminal window type:
lspci -v
During installation Ubuntu attempts to detect the monitor settings. If this fails you are left with a generic monitor with a maximum resolution of 800x600.
Instructions for fixing this are located here:
A listing of terms and what they mean. Some terms have a general meaning and several additional meanings for users, installers, and developers.
Copyright (c) 2000 LinuxCNC.org
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and one Back-Cover Text: "This EMC Handbook is the product of several authors writing for linuxCNC.org. As you find it to be of value in your work, we invite you to contribute to its revision and growth." A copy of the license is included in the section entitled "GNU Free Documentation License". If you do not find the license you may order a copy from Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307
GNU Free Documentation License Version 1.1, March 2000
Copyright © 2000 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
The purpose of this License is to make a manual, textbook, or other written document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
1. APPLICABILITY AND DEFINITIONS
This License applies to any manual or other work that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you".
A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document’s overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (For example, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License.
The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License.
A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, whose contents can be viewed and edited directly and straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup has been designed to thwart or discourage subsequent modification by readers is not Transparent. A copy that is not "Transparent" is called "Opaque".
Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML designed for human modification. Opaque formats include PostScript, PDF, proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML produced by some word processors for output purposes only.
The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work’s title, preceding the beginning of the body of the text.
2. VERBATIM COPYING
You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
You may also lend copies, under the same conditions stated above, and you may publicly display copies.
3. COPYING IN QUANTITY
If you publish printed copies of the Document numbering more than 100, and the Document’s license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a publicly-accessible computer-network location containing a complete Transparent copy of the Document, free of added material, which the general network-using public has access to download anonymously at no charge using public-standard network protocols. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
4. MODIFICATIONS
You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version’s license notice. These titles must be distinct from any other section titles.
You may add a section entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties—for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
5. COMBINING DOCUMENTS
You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice.
The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
In the combination, you must combine any sections entitled "History" in the various original documents, forming one section entitled "History"; likewise combine any sections entitled "Acknowledgements", and any sections entitled "Dedications". You must delete all sections entitled "Endorsements."
6. COLLECTIONS OF DOCUMENTS
You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
7. AGGREGATION WITH INDEPENDENT WORKS
A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, does not as a whole count as a Modified Version of the Document, provided no compilation copyright is claimed for the compilation. Such a compilation is called an "aggregate", and this License does not apply to the other self-contained works thus compiled with the Document, on account of their being thus compiled, if they are not themselves derivative works of the Document.
If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one quarter of the entire aggregate, the Document’s Cover Texts may be placed on covers that surround only the Document within the aggregate. Otherwise they must appear on covers around the whole aggregate.
8. TRANSLATION
Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License provided that you also include the original English version of this License. In case of a disagreement between the translation and the original English version of this License, the original English version will prevail.
9. TERMINATION
You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
10. FUTURE REVISIONS OF THIS LICENSE
The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http:///www.gnu.org/copyleft/.
Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.
ADDENDUM: How to use this License for your documents
To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. A copy of the license is included in the section entitled "GNU Free Documentation License".
If you have no Invariant Sections, write "with no Invariant Sections" instead of saying which ones are invariant. If you have no Front-Cover Texts, write "no Front-Cover Texts" instead of "Front-Cover Texts being LIST"; likewise for Back-Cover Texts.
If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.