Hardware Abstraction Layer - Rev 0.01
- Subject: Hardware Abstraction Layer - Rev 0.01
- From: John Kasunich <jmkasunich-at-worldnet.att.net>
- Date: Mon, 05 May 2003 00:51:30 -0400
- Content-Type: text/plain; charset="us-ascii"; format=flowed
The following is a description of a possible hardware abstraction
layer for EMC that I put together since our discussions at NAMES.
This probably should be posted to the developers list at Sourceforge,
but I don't know if all the right people are subscribed there yet.
To those who care about this stuff: When should we make the
move to the other list? And what do you think about the HAL?
To those who don't care about this stuff: Sorry for the long message.
John Kasunich
---------------------------------------
Background
The hardware abstraction layer (HAL) is an attempt to
make EMC work with a wider variety of I/O hardware, and
to make configuring a machine easier.
There are actually two levels of hardware that we need
to consider. The lowest level is the computer's I/O
hardware. This can be as simple as a parallel printer
port, or as complex as a Gecko G2002. (The G2002 has
up to 6 axis cards. Each card has either a step pulse
generator or a D/A converter and an encoder counter.
Each axis card also has a couple bits of generic digital
I/O. All the axis cards are driven by a dedicated micro,
which communicates with the main PC using USB.)
Even though the printer port and the G2002 are totally
different in capabilities, they can both be viewed as
simple I/O. Each provides some mix of digital inputs,
digital outputs, analog inputs, and analog outputs.
(I am using the term digital to refer to single bit,
on/off signals, and analog to refer to signals with a
range of values. The A and B signals of an encoder
are digital signals, but if they are connected to
a counter, the counter output is an "analog" signal.
Likewise, a PWM or step frequency signal is an "analog"
signal. Of course, DAC outputs are analog signals as
well.)
Since both the parallel port and the G2002 consist of
a mix of analog and digital I/O, both can be modeled
as a set of variables in shared memory space. Then
all that needs to be done is to allow any of those
variables to be linked to the appropriate places in
the rest of the EMC code.
The second level of hardware is more difficult to
address. It includes things like spindle drives, tool
changers, lube systems, and so on. These things are
not electronic, they are electrical and mechanical.
In many cases, they will require non-trivial control
algorithms involving timing, sequencing, and perhaps
even operator interaction. Currently that control
is done in the I/O task. The HAL does not address
that control.
An example may be the best way to illustrate what
the HAL does and does not do.
Assume you have a toolchanger. It has a motor that
can rotate the turret either direction, and a switch
that closes momentarily each time a turret slot passes
the loading station. It also has a pneumatically
actuated loading arm and a drawbar release. The code
to control that toolchanger needs to sequence the
drawbar and loading arm, then rotate the turret while
counting slots, until the right slot is aligned with
the loading station. Then it has to activate the arm
and drawbar again to load the new tool. The HAL does
_not_ do any of that logic. Today, that control would
be done in a C program. In the future, it might be
possible to do it in a ladder logic program or some
other way.
What the HAL _does_ do is control the routing of the
control signals from the C (or PLC) program through
the I/O to the toolchanger. The C program manipulates
variables like "drawbar" and "turret_ccw". The HAL
allows you to connect "drawbar" to pin 7 of a parallel
port, and "turret_ccw" to pin 23 of a Servo-to-Go card,
or whatever other configuration is required. That
means that the control program for that particular
type of toolchanger does not have to be re-written to
support different I/O devices.
Terminology:
HAL runtime data: Memory mapped data, representing both
EMC side variables such as "axis[0].hi_limit" and hardware
(HW) side variables such as "printerport.pin_12". These
variables are updated in realtime as the program runs.
HAL init data: Memory mapped data, representing init
parameters that do not change while the machine runs. This
includes info about which drivers are loaded, what variables
they export, and how EMC side and HW side variables are
interconnected.
HAL runtime code: Core code that runs once per servo loop.
It implements linking of EMC side and HW side variables,
and calls each driver's runtime code.
Driver: A driver supports one particular type of I/O
hardware, such as a parallel port, STG card, etc. Drivers
are loadable modules, and are insmod'ed based on the ini
file. Basic drivers do not create realtime threads of
their own, instead they are called by the HAL runtime code.
However, some drivers (like software step pulse generators)
will create realtime threads that run much faster than the
servo loop.
Driver runtime code: Code that is called by the HAL
runtime code once per servo cycle to update the hardware.
It takes data from HW side variables like "printerport.
pin12" packs the bits into bytes, and writes them to
the hardware. Likewise it reads bytes from the hardware
and unpacks them to individual HW side variables. A
driver that implements software step and direction would
convert its floating point input to an int at this point,
so that the faster realtime thread could work in fixed
point only. Ethernet or firewire drivers would assemble
and dissassemble packets at this point.
Driver init code: Code that runs when the driver is
insmod'ed. It adds the driver's specific list of HW
side variables to the HAL runtime data, and adds their
names and linkage information to the HAL init data.
Design
The proposed HAL architecture makes some assumptions
about the system:
1) There is a single master servo rate. (This is how
EMC works today - it is possible to request different
rates for each axis in the ini file, but the fastest
one is used.) The proposed design does not require
all servo loops to run at the same rate, but it does
require that the HAL update all the I/O at the rate
of the fastest servo loop. This should not be a
problem, but the I/O update code needs to be simple
and FAST to allow fast servo loops.
2) There are only two types of I/O, analog and digital.
Digital values are either TRUE or FALSE. A "bit" data
type is typedef'ed for digital data:
typedef enum { FALSE = 0, TRUE = 1 } bit;
(C++ defines a "bool" type, but I'm not sure it is
supported in C. If it is, it would be better than
creating a custom type.) "bit" values occupy at
least one byte - no packing is done at the HAL level.
This prevents problems when two threads try to modify
different bits in the same byte.
Analog values are represented as "doubles". Most of the
code that manipulates these values will be running in a
thread that already uses floating point, so there will
be no performance hit for saving/restoring the FP state.
Drivers that use a faster thread (for example software
step/direction) could convert from float to int in the
slow thread, and run the fast thread entirely with
fixed point. (But they don't have to, the decision is
made by the driver implementer.)
Data Structures:
HAL Runtime Data:
EMC Side Data:
/* structure containing all EMC side variables for an abstracted
motion axis */
typedef struct
{
double PosFb; /* input: position feedback, in
counts */
double VelCmd; /* output: velocity command, in
counts/second */
bit Home; /* input: true means home switch
tripped*/
bit LoLim; /* input: true means low limit
switch tripped */
bit HiLim; /* input: true means high limit
switch tripped */
bit Fault; /* input: true means amp has faulted */
bit Clamp; /* output: true means axis should be
clamped */
bit Enable; /* output: true means amp should be
enabled */
/* Anything else? */
} HAL_AXIS;
There would be one of these for each axis. There would also be
structures for
the spindle(s), and variables or structures for other I/O such as coolant,
lube, toolchangers, etc.
HW Side Data:
/* structure containing all HW side variables for a printer port */
/* defined in the printerport driver and present only when that driver
is loaded */
typedef struct
{
bit Pin1; /* output: true drives pin 1 hi */
bit Pin14; /* output: true drives pin 14 hi */
bit Pin16; /* output: true drives pin 16 hi */
bit Pin17; /* output: true drives pin 17 hi */
bit Pin2; /* output: true drives pin 2 hi */
bit Pin3; /* output: true drives pin 3 hi */
bit Pin4; /* output: true drives pin 4 hi */
bit Pin5; /* output: true drives pin 5 hi */
bit Pin6; /* output: true drives pin 6 hi */
bit Pin7; /* output: true drives pin 7 hi */
bit Pin8; /* output: true drives pin 8 hi */
bit Pin9; /* output: true drives pin 9 hi */
bit Pin10; /* input: true when pin 10 is hi */
bit Pin11; /* input: true when pin 11 is hi */
bit Pin12; /* input: true when pin 12 is hi */
bit Pin13; /* input: true when pin 13 is hi */
bit Pin15; /* input: true when pin 15 is hi */
} DRV_PPBITS;
There would be a data structure for each driver loaded. The structure
would
vary depending on the I/O mix provided by the driver.
HAL Init Data:
Link Data:
/* structures used to establish a link between an EMC side variable
and a HW side variable */
typedef struct
{
double *src; /* pointer to data source */
double *dst; /* pointer to data destination */
} HAL_LINK_DBL;
typedef struct
{
bit *src; /* pointer to data source */
bit *dst; /* pointer to data destination */
bit invert; /* true means an inverting link */
} HAL_LINK_bit;
/* arrays of links, filled in from ini file */
HAL_LINK_DBL hal_output_links_dbl[MAX_LINKS_DBL];
HAL_LINK_BIT hal_output_links_bit[MAX_LINKS_BIT];
HAL_LINK_DBL hal_input_links_dbl[MAX_LINKS_DBL];
HAL_LINK_BIT hal_input_links_bit[MAX_LINKS_BIT];
Driver Function pointers:
An array of pointers to each driver's runtime code
Sqeuence of operation:
1) Hardware interrupt occurs (either system timer, or hardware device)
2) Run through both output link arrays, copying data from EMC side to HW side.
3) Run through driver function pointer array, calling driver runtime code.
4) Driver runtime code copies output data from HW side structs to the
actual hardware.
5) Driver runtime code copies input data from the actual hardware to the
HW side structs.
6) Driver runtime code returns.
7) Run through both input link arrays, copying data from HW side to EMC side.
8) End of HAL code, begin executing servo loop code (emcmot).
MAJOR ISSUES:
The runtime stuff is pretty simple, in fact I coded it in less than an hour.
Init is the hard part. The drivers must export the names and locations
of their runtime data in a way that allows the ini file to specify links.
Links might look like this in the ini file:
[INLINKS]
axis[0].pos_fb = stg[1].chan[2].encoder
axis[0].hi_limit = parallelport[0].Pin11
axis[0].lo_limit = parallelport[0].Pin11
axis[0].home = !stg[1].digio[20]
[OUTLINKS]
stg[1].chan[2].dac = axis[0].vel_cmd
parallelport[0].Pin3 = axis[0].enable
Note in the INLINKS section that both the high limit and
low limit are linked to the same input, and the home input
is low true, which generates an inverting link.
There is also no reason why a link has to go from the HW
side to the EMC side, or vice-versa. A link from one EMC
variable to another would be perfectly legal, as would a link
from one HW side variable to another.
In fact, a PLC could be loaded just like a driver. It would
export a number of bits that would be coils and contacts
in it's ladder logic. Links would connect the coils and
contacts to EMC side and/or HW side variables as needed.
I'm learning more about the ini file and the functions that
manipulate it, but I'm not ready to propose a detailed
mechanism to get from the list of links above to a
completely filled in set of link arrays. I just wanted to
post this to get other people looking at it. Please
review and let me know what you think.
John Kasunich
Date Index |
Thread Index |
Back to archive index |
Back to Mailing List Page
Problems or questions? Contact