Version 4-1

This commit is contained in:
Ron Sluiter
2000-02-08 22:19:43 +00:00
parent adca30caa2
commit a3ea18fc83
83 changed files with 23659 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
#CONFIG
# Add any changes to make rules here
CROSS_COMPILER_TARGET_ARCHS = mv167
+20
View File
@@ -0,0 +1,20 @@
#CONFIG_APP
include $(TOP)/config/RELEASE
-include $(TOP)/config/RELEASE.$(HOST_ARCH)
include $(EPICS_BASE)/config/CONFIG
INSTALL_LOCATION = $(TOP)
ifdef INSTALL_LOCATION_APP
INSTALL_LOCATION = $(INSTALL_LOCATION_APP)
endif
ifdef T_A
-include $(TOP)/config/O.$(T_A)/CONFIG_APP_INCLUDE
endif
# Where to install databases
INSTALL_DB = $(INSTALL_LOCATION)/db
# dbst based database optimization (default: NO)
DB_OPT = NO
# May be overridden if not all databases should be installed
INSTALLDB = $(DB)
include $(TOP)/config/CONFIG
+9
View File
@@ -0,0 +1,9 @@
#
# $Id: Makefile,v 1.1 2000-02-08 22:17:42 sluiter Exp $
#
TOP=..
include $(TOP)/config/CONFIG_APP
include $(TOP)/config/RULES_ARCHS
+20
View File
@@ -0,0 +1,20 @@
#
# $Id: Makefile.Host,v 1.1 2000-02-08 22:17:42 sluiter Exp $
#
TOP=../..
include $(TOP)/config/CONFIG_APP
TARGETS = CONFIG_APP_INCLUDE
include $(TOP)/config/RULES.Host
inc:: $(TARGETS)
ifeq ($(wildcard $(TOP)/config/RELEASE.$(HOST_ARCH)),$(TOP)/config/RELEASE.$(HOST_ARCH))
CONFIG_APP_INCLUDE: $(TOP)/config/RELEASE.$(HOST_ARCH)
endif
CONFIG_APP_INCLUDE: $(TOP)/config/RELEASE $(TOP)/config/CONFIG_APP
$(RM) $@
@$(PERL) $(TOP)/config/makeConfigAppInclude.pl $(T_A) $@ $(TOP)
+20
View File
@@ -0,0 +1,20 @@
#
# $Id: Makefile.Vx,v 1.1 2000-02-08 22:17:43 sluiter Exp $
#
TOP=../..
include $(TOP)/config/CONFIG_APP
TARGETS = CONFIG_APP_INCLUDE
include $(TOP)/config/RULES.Vx
inc:: $(TARGETS)
ifeq ($(wildcard $(TOP)/config/RELEASE.$(HOST_ARCH)),$(TOP)/config/RELEASE.$(HOST_ARCH))
CONFIG_APP_INCLUDE: $(TOP)/config/RELEASE.$(HOST_ARCH)
endif
CONFIG_APP_INCLUDE: $(TOP)/config/RELEASE $(TOP)/config/CONFIG_APP
$(RM) $@
@$(PERL) $(TOP)/config/makeConfigAppInclude.pl $(T_A) $@ $(TOP)
+14
View File
@@ -0,0 +1,14 @@
# RELEASE - Location of external products
#
# (Architecture dependent declarations should go into RELEASE.<arch>)
#
# Note: This file will be scanned to automatically generate
# include path definitions etc.
# The order of the following declarations will be retained
# within those definitions.
#
# Add your declarations here
SUPPORT=/usr/local/iocapps/R3.13.2/support
EPICS_BASE=$(SUPPORT)/base/3-13-2
HIDEOS=/usr/local/hideos/v3.1/install
+200
View File
@@ -0,0 +1,200 @@
# $Id: RULES.Db,v 1.1 2000-02-08 22:17:43 sluiter Exp $
#
# Rules for making things related to databases
#
MAKEBPT = $(EPICS_BASE_HOST_BIN)/makeBpt$(EXE)
DBLOADTEMPLATE = $(EPICS_BASE_HOST_BIN)/dbLoadTemplate$(EXE)
DBEXPAND = $(EPICS_BASE_HOST_BIN)/dbExpand$(EXE)
DBST = dbst
MAKEDBDEPENDS = $(PERL) $(TOP)/config/makeDbDepends.pl
REPLACEVAR = $(PERL) $(TOP)/config/replaceVAR.pl
ifndef WIN32
TOUCH = touch
else
TOUCH = type NUL >>
endif
#-----------------------------------------------------------------
# if we are not building base add base dbd dirs
ifneq ($(EPICS_BASE),$(TOP))
ifneq ($(EPICS_BASE),$(INSTALL_LOCATION))
EPICS_DBDFLAGS += -I $(EPICS_BASE)/dbd
endif
endif
#---------------------------------------------------------------
# ----------------------------------------------------
# create names (lists) for installed things
# ----------------------------------------------------
INSTALL_BPTS = $(BPTS:%= $(INSTALL_DBD)/%)
INSTALL_DBDS = $(DBDINSTALL:%= $(INSTALL_DBD)/%)
INSTALL_DBDNAME = $(DBDNAME:%= $(INSTALL_DBD)/%)
INSTALL_DATA = $(INSTALLDB:%=$(INSTALL_DB)/%)
INSTALL_TEMPLATES = $(filter %.template,$(INSTALL_DATA))
#---------------------------------------------------------------
# Main targets
all:: install
inc:: $(INSTALL_DBDS) $(INSTALL_BPTS) $(INSTALL_TEMPLATES)
rebuild:: clean install
install:: inc buildInstall
buildInstall:: build $(INSTALL_DATA)
clean::
@echo "Cleaning"
@$(RM) $(DB) $(DBDNAME) *.template *.substitutions *.db.raw \
*.db-stamp *.edf esiread.cnf
##################################################### "Foreign" templates
TEMPLATE_LINKS = $(filter-out $(notdir $(USES_TEMPLATE)), $(USES_TEMPLATE))
TEMPLATE_FILES = $(filter $(notdir $(USES_TEMPLATE)), $(USES_TEMPLATE))
DB_STAMP = $(patsubst %.db, %.db-stamp, $(DB))
DB_REALTARGET = $(patsubst %.db-stamp, %.db, $@)
ifneq '$(TEMPLATE_LINKS)' ''
build:: $(notdir $(TEMPLATE_LINKS))
endif
build:: $(INSTALL_DBDNAME) $(TEMPLATE_FILES) $(DB_STAMP)
$(notdir $(TEMPLATE_LINKS)): %.template:
ifndef WIN32
@$(RM) $(notdir $(TEMPLATE_LINKS))
ln -s $(TEMPLATE_LINKS) .
# Workaround for dbLoadTemplate bug: terminate here if link target doesn't exist
@cat $(TEMPLATE_LINKS) > /dev/null
else
@$(RM) $(notdir $(TEMPLATE_LINKS))
$(CP) $(TEMPLATE_LINKS) .
endif
##################################################### Inflated or plain databases
$(INSTALL_DB)/%.db: %.db-stamp
@echo "Installing database $@"
@$(INSTALL) -d -m 644 $(patsubst %.db-stamp, %.db, $<) $(@D)
# Must have DBDNAME defined to use dbst optimization
ifndef DBDNAME
DB_OPT = NO
endif
# dbst based database optimization
ifeq '$(DB_OPT)' 'YES'
.PRECIOUS: %.db.raw
%.db-stamp: %.db.raw $(INSTALL_DBD)/$(DBDNAME)
@echo "Optimizing database $@"
$(DBST) $(INSTALL_DBD)/$(DBDNAME) $< -d > $(DB_REALTARGET)
@$(TOUCH) $@
%.db-stamp: %.t.db.raw $(INSTALL_DBD)/$(DBDNAME)
@echo "Optimizing database $@"
$(DBST) $(INSTALL_DBD)/$(DBDNAME) $< -d > $(DB_REALTARGET)
@$(TOUCH) $@
else
# NO optimization => move it and keep a stamp
%.db-stamp: %.db.raw
@$(MV) $< $(DB_REALTARGET)
@$(TOUCH) $@
@$(TOUCH) $<
%.db-stamp: %.t.db.raw
@$(MV) $< $(DB_REALTARGET)
@$(TOUCH) $@
@$(TOUCH) $<
endif
%.t.db.raw: %.substitutions
@echo "Inflating database from $<"
@$(RM) $@
@$(DBLOADTEMPLATE) $< > $@
##################################################### CapFast filter
%.edf:: ../%.sch $(DEPSCHS)
@if [ ! -f cad.rc -a -r ../cad.rc ] ; then ln -s ../cad.rc ; fi
$(SCH2EDIF) $(SCH2EDIF_SYSFLAGS) $(SCH2EDIF_FLAGS) $<
##################################################### Substitution files
$(INSTALL_DB)/%.substitutions: %.substitutions
@echo "Installing $@"
@$(INSTALL) -d -m 644 $(@F) $(@D)
%.substitutions:: ../%.substitutions
@$(CP) $< $@
ifdef CREATESUBSTITUTIONS
%.substitutions:: $(word $(words $(CREATESUBSTITUTIONS)),$(CREATESUBSTITUTIONS))
@$(CREATESUBSTITUTIONS) $*
endif
# Better make it PRECIOUS (to get around make bug)
.PRECIOUS: %.substitutions
##################################################### Template databases
# Installed template files (dbLoadTemplate() on IOC side)
$(INSTALL_DB)/%.template: %.template
@echo "Installing $@"
@$(INSTALL) -d -m 644 $(@F) $(@D)
%.template:: ../%.template
@$(CP) $< $@
%.template: %.edf
$(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) -n $@.VAR $<
@$(REPLACEVAR) < $@.VAR > $@
@$(RM) $@.VAR
##################################################### Flat databases
%.db.raw:: ../%.db
$(CP) $< $@
%.db.raw: %.edf
$(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) -n $@.VAR $<
@$(REPLACEVAR) < $@.VAR > $@
@$(RM) $@.VAR
##################################################### DBD stuff
$(INSTALL_DBD)/%: %
@echo "Installing $@"
@$(INSTALL) -d -m 644 $< $(@D)
$(INSTALL_DBD)/%:: ../%
@echo "Installing $@"
@$(INSTALL) -d -m 644 $< $(@D)
bpt%.dbd: bpt%.data
$(RM) $@
$(MAKEBPT) $<
bpt%.dbd:: ../bpt%.data
$(RM) $@
$(MAKEBPT) $<
# Patch for old applications
ifdef USER_DBDFLAGS
DBDFLAGS = $(USER_DBDFLAGS)
endif
ifdef DBDEXPAND
$(DBDNAME): ../$(DBDEXPAND)
@echo "Expanding dbd"
@$(RM) $@
$(DBEXPAND) $(DBDFLAGS) $< > $@
endif
##################################################### Dependencies
DEPENDS: $(filter $(patsubst %.db, %.substitutions, $(DB)), $(wildcard *.substitutions))
@$(MAKEDBDEPENDS) $^
-include DEPENDS
+3
View File
@@ -0,0 +1,3 @@
#RULES.Host
include $(EPICS_BASE)/config/RULES.Host
+9
View File
@@ -0,0 +1,9 @@
#RULES.Vx
include $(EPICS_BASE)/config/RULES.Vx
inc:: $(INSTALL_INCREC)
#
# turn off implicit rules search for all binaries in base
#
#Jeff says this turned off all dependency checking
#????? What to do????
#$(EPICS_BASE_BIN)/* : ;
+9
View File
@@ -0,0 +1,9 @@
#RULES.ioc
include $(EPICS_BASE)/config/RULES_DIRS
buildInstall:: cdCommands
cdCommands: Makefile
$(PERL) $(TOP)/config/makeIocCdCommands.pl $(ARCH)
clean::
@$(RM) cdCommands
+4
View File
@@ -0,0 +1,4 @@
#RULES.iocBoot
DIRS += $(wildcard ioc*)
DIRS += $(wildcard as*)
include $(EPICS_BASE)/config/RULES_DIRS
+2
View File
@@ -0,0 +1,2 @@
#RULES_ARCHS
include $(EPICS_BASE)/config/RULES_ARCHS
+2
View File
@@ -0,0 +1,2 @@
#RULES_DIRS
include $(EPICS_BASE)/config/RULES_DIRS
+5
View File
@@ -0,0 +1,5 @@
#RULES_TOP
include $(EPICS_BASE)/config/RULES_TOP
uninstall::
@$(RMDIR) $(INSTALL_DB)
+62
View File
@@ -0,0 +1,62 @@
# $Id: makeConfigAppInclude.pl,v 1.1 2000-02-08 22:17:45 sluiter Exp $
eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*-
if $running_under_some_shell; # makeConfigAppInclude.pl
use Cwd;
$arch = $ARGV[0];
$outfile = $ARGV[1];
$top = $ARGV[2];
unlink("${outfile}");
open(OUT,">${outfile}") or die "$! opening ${outfile}";
print OUT "#Do not modify this file.\n";
print OUT "#This file is created during the build.\n";
@files =();
push(@files,"$top/config/RELEASE");
push(@files,"$top/config/RELEASE.${arch}");
foreach $file (@files) {
if (-r "$file") {
open(IN, "$file") or die "Cannot open $file\n";
while ($line = <IN>) {
next if ( $line =~ /\s*#/ );
chomp($line);
$_ = $line;
#the following looks for
# prefix = $(macro)post
($prefix,$macro,$post) = /(.*)\s*=\s*\$\((.*)\)(.*)/;
if ($macro eq "") { # true if no macro is present
# the following looks for
# prefix = post
($prefix,$post) = /(.*)\s*=\s*(.*)/;
} else {
$base = $applications{$macro};
if ($base eq "") {
#print "error: $macro was not previously defined\n";
} else {
$post = $base . $post;
}
}
$applications{$prefix} = $post;
if ( -d "$post") { #check that directory exists
print OUT "\n";
if ( -d "$post/bin/$arch") { #check that directory exists
print OUT "${prefix}_BIN = $post/bin/${arch}\n";
}
if ( -d "$post/lib/$arch") { #check that directory exists
print OUT "${prefix}_LIB = $post/lib/${arch}\n";
}
if ( -d "$post/include") { #check that directory exists
print OUT "EPICS_INCLUDES += -I$post/include\n";
}
if ( -d "$post/dbd") { #check that directory exists
print OUT "EPICS_DBDFLAGS += -I $post/dbd\n";
}
}
}
close IN;
}
}
close OUT;
+23
View File
@@ -0,0 +1,23 @@
eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*-
if $running_under_some_shell; # makeDbDepends.pl
# Called from within the object directory.
# Searches the .substitutions files (from the command line) for
# "file xxx {" entries to create a .DEPENDS file
open(OUT, ">.DEPENDS") or die "Cannot open .DEPENDS: $!";
foreach $file (@ARGV) {
open(IN, "<$file") or die "Cannot open $file: $!";
@substfile = <IN>;
close IN or die "Cannot close $file: $!";
@depends = grep { s/^\s*file\s*(.*)\s*\{.*$/\1/ } @substfile;
chomp @depends;
if (@depends) {
$file =~ s/\.substitutions/\.t.db.raw/;
print OUT "${file}:: @depends\n";
}
}
close OUT or die "Cannot close $file: $!";
+60
View File
@@ -0,0 +1,60 @@
eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*-
if $running_under_some_shell; # makeIocCdCommands.pl
use Cwd;
$cwd = cwd();
#hack for sun4
$cwd =~ s|/tmp_mnt||;
$arch = $ARGV[0];
unlink("cdCommands");
open(OUT,">cdCommands") or die "$! opening cdCommands";
print OUT "startup = \"$cwd\"\n";
#appbin is kept for compatibility with 3.13.1
$appbin = $cwd;
$appbin =~ s/iocBoot.*//;
$appbin = $appbin . "/bin/${arch}";
print OUT "appbin = \"$appbin\"\n";
$top = $cwd;
$top =~ s/\/iocBoot.*//;
print OUT "top = \"$top\"\n";
$topbin = "${top}/bin/${arch}";
#skip check that top/bin/${arch} exists; src may not have been builT
print OUT "topbin = \"$topbin\"\n";
$release = "$top/config/RELEASE";
if (-r "$release") {
open(IN, "$release") or die "Cannot open $release\n";
while ($line = <IN>) {
next if ( $line =~ /\s*#/ );
chomp($line);
$_ = $line;
#the following looks for
# prefix = $(macro)post
($prefix,$macro,$post) = /(.*)\s*=\s*\$\((.*)\)(.*)/;
if ($macro eq "") { # true if no macro is present
# the following looks for
# prefix = post
($prefix,$post) = /(.*)\s*=\s*(.*)/;
} else {
$base = $applications{$macro};
if ($base eq "") {
print "error: $macro was not previously defined\n";
} else {
$post = $base . $post;
}
}
$applications{$prefix} = $post;
$app = lc($prefix);
if ( -d "$post") { #check that directory exists
print OUT "$app = \"$post\"\n";
}
if ( -d "$post/bin/$arch") { #check that directory exists
print OUT "${app}bin = \"$post/bin/$arch\"\n";
}
}
close IN;
}
close OUT;
+12
View File
@@ -0,0 +1,12 @@
eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*-
if $running_under_some_shell; # replaceVAR.pl
# Called from within the object directory
# Replaces VAR(xxx) with $(xxx)
# and VAR_xxx_ with $(xxx)
while (<STDIN>) {
s/VAR\(/\$\(/g;
s/VAR_([^_]*)_/\$\($1\)/g;
print;
}
+3
View File
@@ -0,0 +1,3 @@
TOP=..
include $(TOP)/config/CONFIG_APP
include $(EPICS_BASE)/config/RULES_ARCHS
+6
View File
@@ -0,0 +1,6 @@
TOP = ../..
include $(TOP)/config/CONFIG_APP
HTMLS += motorRecord.html
include $(TOP)/config/RULES.Host
File diff suppressed because it is too large Load Diff
+61
View File
@@ -0,0 +1,61 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.51 [en] (X11; U; SunOS 5.6 sun4u) [Netscape]">
</head>
<body>
<center>
<h1>
Motor record source code file organization</h1></center>
<h4>
Record source files</h4>
<blockquote>motorRecord.dbd
<br>motor.h
<br>motorRecord.c</blockquote>
<h4>
Device driver source files</h4>
<blockquote><b><u>Common to all motor Record device drivers</u></b>
<br>motordevCom.h
<br>motordevCom.c
<br>motordrvCom.h
<br>motordrvComCode.h
<br>motordrvCom.c</blockquote>
<ul><b><u>Oregon Micro Systems</u></b>
<p><u>Common to all OMS device drivers</u>
<br>devOmsCom.h
<br>devOmsCom.c
<br>drvOmsCom.h
<p><u>VME8/44</u>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <u>VME58</u>
<br>devOms.c&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; devOms58.c
<br>drvOms.h&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; drvOms58.h
<br>drvOms.c&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; drvOms58.c
<p><b><u>Newport Motion Master</u></b>
<p><u>Common to all Motion Master device drivers</u>
<br>drvMMCom.h
<br>gpibIO.h
<br>gpibIO.c
<br>serialIO.h
<br>serialIO.cc
<p><u>MM3000</u>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<u>MM4000</u>
<br>devMM3000.c&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; devMM4000.c
<br>drvMM3000.c&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; drvMM4000.c
<p><b><u>Highland V544</u></b>
<br>devV544.c
<br>drvV544.h
<br>drvV544.c
<p><b><u>Soft Channel</u></b>
<br>devSoft.h
<br>devSoft.c
<br>devSoftAux.c</ul>
</body>
</html>
+531
View File
@@ -0,0 +1,531 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.7 [en] (X11; U; SunOS 5.6 sun4u) [Netscape]">
</head>
<body>
<center>
<h1>
<u>Motor Record Version 4.1 Release Notice</u></h1></center>
Although the motor record software in this release is compatible with EPICS
baseR3.13.1.1 and above, the directory structure, the "make" files and
the configuration files are intended for the "unbundled" architecture of
EPICS R3.13.2 and above.
<center>
<h3>
<u>Modifications to Existing Features</u></h3></center>
Three errors in the OMS VME58 driver were found.&nbsp; All the errors caused
the same problem.&nbsp; Namely, erroneous retries occurred intermittently
when multiple axes were commanded to move on the same controller.&nbsp;
This error occurred because old position data was being passed back from
the driver after Done was detected.&nbsp; The erroneous intermittent retries
occurred more often when the Oms setup parameters called for a high frequency
(e.g., 60 Hz) "polling" rate and Oms interrupts were enabled.
<br>&nbsp;
<center>
<h1>
<u>Motor Record Version 4.0 Release Notice</u></h1></center>
Release 4.0 of the motor record is <u>only</u> compatible with EPICS baseR3.13.1.1
and above.&nbsp; This document describes changes made to the motor record
and its' associated device driver support between versions 3.5 and 4.0.
<center>
<h3>
<u>Modifications to Existing Features</u></h3></center>
<h4>
<b><u>GAIN Field Removed</u></b></h4>
Since the GAIN field is redundant (i.e., PID parameters can be set individually)
it has been removed.
<br>&nbsp;
<h4>
<b><u>HOM[F/R] Bug Fix</u></b></h4>
Previous releases of the motor record had a potential problem associated
with the homing function.&nbsp; The motorx_all.adl V1.9 MEDM display sets
the HOM[F/R] fields on and off, corresponding to the user pressing and
releasing the respective home button.&nbsp; Depending on the <i>pollRate</i>
defined in the st.cmd <i>Setup</i> command and the speed at which the user
toggled the HOM[F/R] buttons; the record level software would turn off
the DMOV field when the HOM[F/R] button was turned off and a motor status
update had not yet been received.&nbsp; As a result, when the motor completed
it's homing function the command positions (i.e., VAL, DVAL, RVAL) were
not updated.
<p>All previous motor record releases contained the DMOV problem; the commanded
position update problem was limited to the previous release (V3.5).&nbsp;
With this release, a dbPutField to either HOMF or HOMR is valid on a FALSE
to TRUE transition (i.e., 0 to 1), but a TRUE to FALSE transition (i.e.,
1 to 0) will result in an error.&nbsp;&nbsp; <i>motorx_all.adl_V2.2</i>
(which is included with this distribution) sets the HOM[F/R] fields on
when the user presses the homing button, but is does <b>not</b> set it
off when the button is released.&nbsp; The motor record clears the HOM[F/R]
field when the homing operation is done (i.e., completed or terminated).
<br>&nbsp;
<h4>
<u>Initial Position</u></h4>
If both the auto restore and controller target position values are non-zero
at boot-up, then DVAL will be initialized from the controller's value.&nbsp;
In other words, the controller's target position takes precedence over
the autorestore value when both systems have non-zero DVAL values.&nbsp;
As before, it is assumed that a zero target position from autorestore or
the controller at boot-up are default values, and hence, they are ignored
in favor of a non-zero value.
<p>Previous releases of the motor record allowed the auto restore to take
precedence over the controller when initializing the target position (i.e.,
DVAL).
<br>&nbsp;
<h4>
<u>Access Security Level</u></h4>
In order to support the new VMAX/SMAX fields, the Access Security Level
for the following fields have been changed from one to zero:
<blockquote>
<ul>
<li>
MRES</li>
<li>
UREV</li>
<li>
VBAS</li>
<li>
SBAS</li>
</ul>
</blockquote>
<h4>
<u>MM4000/5 Device Driver</u></h4>
Although the previous motor record release (V3.5) had device driver support
for the MM4000, it is <b>not</b> recommended for use with either the MM4000
or the MM4005 controllers.&nbsp; The following changes were made to the
previous release to create, what is now, the MM4000/5 device driver support:
<ol>
<li>
The MM4000 device driver software supports both the MM4000 and MM4005 controllers.&nbsp;
Driver level software detects and saves which controller it is communicating
with at boot-up.&nbsp; Currently, there are two functional differences&nbsp;
between the two models.</li>
<ol>
<li>
The MM4005's position cannot be set by a host.&nbsp; This mean that, for
the MM4005 only, setting the motor record RVAL or DVAL fields has no effect.</li>
<li>
Since the MM4005's position cannot be set by EPICS, the initial position
of its' motors (see <i>Initial Position </i>above) will always be initialized
from the controller's value.</li>
</ol>
<li>
The MM4005 supports up to eight axes.&nbsp; User access to the controller's
front panel is required to scroll the front panel display through all eight
axes.&nbsp; Since <i>remote mode</i> locks out the user from using the
controller's front panel, the motor record no longer puts the MM4000/5
in <i>remote mode</i>.&nbsp; EPICS is unable to communicate with the MM4000/5
controller if it is in <i>local</i> <i>mode</i> and the front panel is
not at the top menu.&nbsp;&nbsp; A <i>Controller communication error</i>
bit was allocated in the MSTA field to help aid user's in diagnosing a
controller communication error.&nbsp; Currently, only the MM4000/5 device
driver sets this error indicator.&nbsp; When the communication error bit
is set True in the MSTA, the motor record SEVR field is set to INVALID_ALARM
and the STAT field is set to COMM_ALARM.&nbsp; User's who want their MM4000/5
in remote mode at boot-up can add the remote mode command ("MR") to&nbsp;
their INIT field.</li>
</ol>
<h4>
<u>Bug Fix for OMS VME58 running on PowerPC</u></h4>
Through an odd set of circumstances the Oms58 driver was not performing
status updates on PowerPC (PPC) platforms.&nbsp; All users of the OMS VME58
controller on a PPC platform must upgrade to Motor Record version 4.0 for
full functionality.
<center>
<h3>
<u>New Features</u></h3></center>
<b><u>Newport MM3000 Device Support</u></b>
<p>Device driver support for the MM3000 exist for this release of the motor
record.&nbsp; Note the following differences between the MM4000 and MM3000
device drivers:
<ul>
<li>
The MM3000 does not support a generic <i>Load Position </i>commands.&nbsp;
In other words, the user can only define the current controller position
as being the zero position.&nbsp; This limitation is reflected in the motor
record device support.&nbsp; When the SET field is true, the only valid
entry to either the DVAL or RVAL fields is zero.</li>
<li>
The only position units the MM3000 will communicate with the host in, are
either encoder ticks or stepper motor steps.</li>
</ul>
<h4>
<u>Uninitialized Oms/Oms58 Driver Error Check</u></h4>
With previous release, if the Oms or Oms58 driver was some how omitted
from the database, a "... card does not exist" error message would result.&nbsp;
With this release, an explicit error check and corresponding error message
(i.e., "Oms[58] driver uninitialized.") is issued at record initialization
if a required driver is not initialized. (Because of the particulars of
MM[3000/4000] initialization, this is not an issue with Newport controllers.)
<h4>
<u>Maximum Velocity Fields (VMAX/SMAX)</u></h4>
Maximum velocity fields have been added; VMAX in EGU/s units and SMAX in
RPS units.
<p>In order to support BURT, range checking is done in such a way that
any minimum (i.e., VBAS/SBAS) or maximum (i.e., VMAX/SMAX) value entered
is valid.&nbsp; For example, if the minimum is entered and it exceeds the
maximum, then the maximum is set to the new minimum value.&nbsp; Slew (VELO/S)
and backup velocity (BVEL/SBAK) fields are forced by the motor record to
be within the range set by VMAX/SMAX and VBAS/SBAS, inclusively.&nbsp;
For example, if VELO is entered and it is less than the minimum, then VELO
is set to VBAS.
<p>To facilitate software upgrades, a zero VMAX disables maximum velocity
error checking.&nbsp; Those who use both BURT and VMAX (i.e., nonzero VMAX)
should insure that VMAX and VBAS are placed before VELO and BVEL in their
BURT <i>request files.&nbsp; </i>VMAX/SMAX have Access Security Level zero.
<p><i>motorx_setup_1.7.adl</i> (which is included with this distribution)
includes support for the maximum velocity fields.
<br>&nbsp;
<center>
<h1>
<u>Motor Record Version 3.5 Release Notice</u></h1></center>
Release 3.5 of the motor record is only compatible with EPICS baseR3.13.1.1
and above.&nbsp; This document describes changes made to the motor record
and its' associated device driver support between versions 3.4 and 3.5.&nbsp;
Those changes are as follows:
<center>
<h3>
<u>Modifications to Existing Features</u></h3></center>
<h4>
<u>Command Primitives</u></h4>
<b>This feature is available only with OMS VME8/44/58 or Newport MM4000
device support (i.e., devOms, devOms58, devMM4000)</b>. Three motor record
fields are available to the user to send ASCII command primitives to the
servo control board at fixed, predefined, times.&nbsp; Each of the following
fields is defined as a character string:
<blockquote>&nbsp; 1. INIT - Sent at record initialization.
<br>&nbsp; 2. PREM - Sent before every command string that causes motion.
<br>&nbsp; 3. POST - Sent after a complete motion is finished <b>or when
an overtravel limit switch is detected</b>.</blockquote>
No error checking is done by the motor record or the device driver to insure
that the command strings are valid.&nbsp; Command primitives that result
in a response from the motion control board are valid, but the response
is not processed.
<h4>
<u>Servo related Fields</u></h4>
<ul>
<li>
The GAIN field "prompt" has been changed from "Response Gain" to "Combined
Gain".&nbsp; In addition, valid values for the GAIN field have been changed
to [0.0, for MM4000 - 0.00005, for OMS] &lt;= GAIN &lt;= 1.&nbsp; For more
on PID, see "PID Gain Parameters" below.</li>
<li>
The <i>status enable </i>field (i.e., STEN) has been eliminated; the <i>control
enable </i>field (i.e., CNEN) is used to both control the torque enable
and show its' status.</li>
</ul>
<h4>
<u>Unused Fields Removed</u></h4>
The following unused motor record fields were deleted: MODE, TRAK, MDEL,
ADEL, CVEL, POSM, ALST, MLST
<br>&nbsp;
<br>&nbsp;
<center>
<h3>
<u>New Features</u></h3></center>
<h4>
<u>Device Directives</u></h4>
Device directive definition and processing:
<ul>
<li>
Valid only in the INIT field.</li>
<li>
Must be identified by the following;</li>
<blockquote>
<li>
First character of INIT string must be a '@'.</li>
<li>
One or more characters followed by a terminating '@'; i.e., device directives
must have nonzero length.</li>
<li>
A valid device directive; currently, only "DPM_ON".</li>
</blockquote>
<li>
INIT strings are stripped of valid device directives (including @'s) and
tested for nonzero length before being sent to the controller.&nbsp; For
example, given the INIT string, "@DPM_ON@HE", the device directive @DPM_ON@
is stripped out before HE is sent to the controller.</li>
</ul>
<h4>
<u>Driver Power Monitoring</u></h4>
<ul>
<li>
This feature is only available with the OMS VME58 device support.</li>
<li>
The 8 User I/O signals are assigned to the 8 possible VME58 axes as follows:</li>
</ul>
<ul>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
User I/O #0 &lt;> X axis
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; " 1 &lt;> Y&nbsp; "
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
.....................
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; " 7 &lt;> S&nbsp; "
<li>
Drive-power monitoring defaults to disabled at boot-up.&nbsp; Request enabling
drive-power monitoring by entering the device directive "@DPM_ON@" command
into the motor record initialization field (i.e., INIT).&nbsp; The INIT
field is processed at record initialization (i.e., bootup), hence if there
are no errors, drive-power monitoring will be enabled after the next bootup.</li>
<li>
Whenever a request is made to enable drive-power status monitoring, an
error check is made (using the VME58 "RB" command) to verify that the User
I/O has been configured as an input.&nbsp; The following message will appear
in the error log if a configuration error is detected; "Invalid VME58 configuration;
RB = 0x####", where "####" is the VME58's response to the RB command.</li>
<li>
When drive-power status monitoring is enabled and a power failure is detected,
the device driver will respond by activating the the RA_OVERTRAVEL bit
in the MSTA.&nbsp; This results in either HLS or LLS being activated depending
on the DIR field. In addition, the following message will appear in the
error log; "Drive power failure at VME58 card#?? motor#??".</li>
</ul>
<h4>
<u>Soft Channel Device Support</u></h4>
The immediate goals for soft channel motor record device support were twofold.&nbsp;
First, to solve the problem of nonlinear position conversion in the data
base, rather than in the record.&nbsp; Second, to provide a more flexible
motor record interface for Table Records and SPEC.
<p>New fields have been added to the motor record to support the Soft Channel
device driver.&nbsp;&nbsp; The new fields are all database links associated
with existing motor record fields.&nbsp; The new links and their associated
fields are listed in the table below:
<br>&nbsp;
<br>&nbsp;
<center><table BORDER NOSAVE >
<tr ALIGN=CENTER NOSAVE>
<td NOSAVE><u>Link</u></td>
<td><u>Link Type</u></td>
<td>&nbsp;<u>Associated Field</u></td>
</tr>
<tr>
<td>OUT*</td>
<td>DBOUTPUT</td>
<td>
<center>DVAL</center>
</td>
</tr>
<tr ALIGN=CENTER NOSAVE>
<td NOSAVE>STOO</td>
<td>DBOUTPUT</td>
<td ALIGN=CENTER NOSAVE>STOP</td>
</tr>
<tr ALIGN=CENTER NOSAVE>
<td NOSAVE>DINP</td>
<td>DBINPUT</td>
<td>DMOV</td>
</tr>
<tr>
<td>RDBL*</td>
<td>DBINPUT</td>
<td>
<center>DRBV</center>
</td>
</tr>
<tr>
<td>RINP</td>
<td>DBINPUT</td>
<td>
<center>RMP</center>
</td>
</tr>
</table></center>
<center>
<p>* - Not a new field, but a new function provided only by soft channel
device support.</center>
<p>The Soft Channel database links are only processed when the Soft Channel
device driver is selected.&nbsp; These links are ignored when using any
other Motor Record device driver.&nbsp; At this time, the above links are
<b>not</b>
dynamically retargetable.
<p>The OUT, DINP and RDBL are monitored for value changes via a CA event
task.&nbsp; Users must choose either a dial input link (RDBL) or a raw
input link (RINP), but not both.
<br>&nbsp;
<br>&nbsp;
<h4>
<u>Newport MM4000 Device Support</u></h4>
This is <a href="http://cars1.uchicago.edu/software/dev_mm4000.html">Mark
Rivers MM4000</a> device support ported to the latest motor record release.&nbsp;
The following are the functional differences between Mark's version 1.01
and
<br>this release:
<blockquote>
<ol>
<li>
When either user or dial travel limit values are entered, a validity check
is made using the travel limit values from the MM4000 control.&nbsp; User
and dial travel limit values are valid if they are within the travel limits
set on the front panel of the MM4000 controller.&nbsp; Attempting to enter
a travel limit outside the MM4000 controller's range results in the travel
limit being reset to the MM4000's value.</li>
<li>
Servo support has been extended to the MM4000 controller.</li>
</ol>
</blockquote>
<h4>
<u>PID Gain Parameters</u></h4>
With this release there are two ways to set the motor controllers' PID
parameters; either through the <i>Combined Gain </i>field (i.e., GAIN)
or through the individual PID gain parameter fields (i.e., PCOF, ICOF,
DCOF).&nbsp; Let the motor controller PID parameters be represented by
CKP, CKI and CKD; then the relationship between these two methods is as
follows:
<blockquote><u>For the MM4000</u>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<u>For all OMS controllers</u></blockquote>
<ul>
<li>
CKP&nbsp; = GAIN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
CKP =&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1999.9 * GAIN</li>
<li>
CKI&nbsp;&nbsp; = 2 * GAIN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
CKI =&nbsp; 2 * 1999.9 * GAIN</li>
<li>
CKD = 3 * GAIN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
CKD = 3 * 1999.9 * GAIN</li>
</ul>
Note that setting any of the individual PID record fields is <b>not</b>
reflected in the value of the GAIN field.
<h4>
<u>Highland V544 Device Support</u></h4>
Device support for the Highland V544 is available with this release.&nbsp;
This version (i.e., version 1.3) is functionally the same as the earlier
release (i.e., version 1.2).&nbsp; No new features have been added.
<br>&nbsp;
<center>
<h1>
<u>Motor Record Version 3.4 Release Notice</u></h1></center>
This document describes changes made to the motor record and its' associated
device driver support between versions 3.3 and 3.4.&nbsp; Those changes
are as follows:
<h3>
<u><font size=+0>Device driver design error fix</font></u></h3>
A design error was discovered in the OMS device drivers (drvOms and drvOms58).&nbsp;
The EPICS device driver support task (i.e., tmotor) would query the OMS
motion controller for status information immediately after a motion command.&nbsp;
Since the state of the controller board was in the midst of changing, this
sometimes resulted in inconsistent or conflicting status information being
returned to the motor record.&nbsp; This problem was remedied by enforcing
a minimum time delay (16.67 ms) between a motion command and a status query.
<p>It is difficult to enumerate the symptoms associated with this problem.&nbsp;
Sometimes they exhibited themselves intermittently, other times bad data
stayed in the record.&nbsp;&nbsp; Several symptoms are as follows:
<blockquote>
<ul>
<li>
Erroneous motor stops being issued by the device support when changing
motor direction.</li>
<li>
Backlash occurring when the motor is moving in the same direction as the
sign of BDST.</li>
<li>
DVAL and RBV becoming out-of-synch.&nbsp; RBV would always be an old value
from the previous move.</li>
</ul>
</blockquote>
<h3>
<u><font size=+0>PID Parameter Support for VME58</font></u></h3>
The motor record classifies a motor as a servo if it has two features;
an adjustable closed loop position response gain and the ability to enable/disable
closed loop position control (i.e., motor torque).&nbsp; A motor lacking
either of the above two features is classified as a stepper and is supported
by the standard motor record features.&nbsp; The following three motor
record fields support servo motors:
<p>&nbsp; 1. GAIN - Closed loop position response gain of the motor.
<br>&nbsp; 2. CNEN - Enable/disable closed loop position control request.
<br>&nbsp; 3. STEN - Closed loop position control status (i.e.,
<br>&nbsp;&nbsp;&nbsp;&nbsp; enabled/disabled).
<p>Currently, servo support is only available with OMS's VME58 device support
(i.e., VME58-[4S/8S/2S2/2S6/4S4/6S2] model boards).&nbsp; At driver initialization,
the driver automatically detects whether or not the underlying device supports
the use of the GAIN field and thereby classifies the motor as a stepper
or a servo.&nbsp;&nbsp; Bit#11 of the MSTA field is set on/off based on
the results of this test.&nbsp; If the device supports the GAIN field,
then bit#11 of the MSTA is set on and all of the above servo fields are
enabled.&nbsp;&nbsp; Otherwise, they are disabled and bit#11 of the MSTA
is set off.&nbsp; When the servo fields are disabled, they can still be
read or written to without an error response.
<p>The VME58 device support allows the closed loop position gain of the
motor to be set directly through the GAIN field.&nbsp; For the VME58, setting
the GAIN field value results in the Combined Coefficient command (i.e.,
KK#) being executed with the GAIN field value as the argument to the command.&nbsp;
This command, in turn, sets the PID loop parameter values (with the OMS
VME58, gain changes do not take effect until the command velocity is zero).
<p>The user can request that the closed loop position control be enabled
or disabled by setting the CNEN field nonzero or zero, respectively.
<br>Likewise, the user can monitor the state of closed loop position control
(i.e., enabled/disabled) by reading the STEN field.&nbsp; See the OMS "VME58
Family User's Manual" for further details.
<h3>
<u><font size=+0>Command primitives feature</font></u></h3>
Three motor record fields are available to the user that can be used to
send ASCII command primitives to the servo control board at fixed, predefined,
times.&nbsp; Each field is defined as a character string.
<p>&nbsp; 1. INIT - Sent at record initialization.
<br>&nbsp; 2. PREM - Sent before every command string that causes motion.
<br>&nbsp; 3. POST - Sent after a complete motion is finished.
<p>No error checking is done by the motor record or the device driver to
insure that the command strings are valid.&nbsp; Command primitives that
result in a response from the motion control board are valid, but the response
is not processed.
<br>&nbsp;
<br>&nbsp;
</body>
</html>
+4
View File
@@ -0,0 +1,4 @@
TOP = ..
include $(TOP)/config/CONFIG_APP
DIRS += $(wildcard *ioc*)
include $(TOP)/config/RULES.iocBoot
+4
View File
@@ -0,0 +1,4 @@
TOP = ../..
include $(TOP)/config/CONFIG_APP
ARCH = mv167
include $(TOP)/config/RULES.ioc
+20
View File
@@ -0,0 +1,20 @@
# Example vxWorks startup file
#Following must be added for many board support packages
#cd <full path to target bin directory>
< cdCommands
#< ../nfsCommands
cd appbin
ld < iocCore
ld < seq
#ld < Lib
cd startup
#dbLoadDatabase("../../dbd/.dbd")
#dbLoadRecords("../../db/.db")
#dbLoadTemplate("../../db/.substitutions")
iocInit
#seq &<some snc program>
+28
View File
@@ -0,0 +1,28 @@
#Instructions for creating and using a real nfsCommands file
#
# in order to use nfs do the following:
# 1) Create hostAdd and nfsMountAll commands for each nfs server
# 2) In each st.cmd file add the following two commands BEFORE any load commands
# ../nfs.cmd
# cd "<iocname>
#
# The hostAdd and nfsMountAll commands have the form:
# hostAdd("<host>","xxx.xxx.xxx.xxx")
# nfsMountAll("<host>")
#
# You can also mount subdirectories as follows:
# nfsMount("<host>", "/xxx/xxx/xxx", "/xxx")
#
# For example assume
#
# host is mercury with inet address 155.77.2.56
# You want to mount the directory (which is a file system of mercury)
# /home/mercury5/iocinfo
# as
# /iocinfo
#
# The commands would be
#
# hostAdd("mercury","155.77.2.56")
# nfsMountAll("mercury")
# nfsMount("mercury","/home/mercury5/iocinfo","/iocinfo")
+3
View File
@@ -0,0 +1,3 @@
TOP=../..
include $(TOP)/config/CONFIG_APP
include $(TOP)/config/RULES_ARCHS
+30
View File
@@ -0,0 +1,30 @@
# Makefile.Host
TOP = ../../..
include $(TOP)/config/CONFIG_APP
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
#----------------------------------------------------
# Optimization of DB files using dbst (default: NO)
#DB_OPT = YES
#----------------------------------------------------
# Optimization needs the name of the appropriate
# dbd file (compare with ../src/Makefile.Vx)
#DBDNAME = motor.dbd
#----------------------------------------------------
# Add databases, templates, substitutions like this
#DB += motor.db
#----------------------------------------------------
# Declare template files which do not show up in DB
#USES_TEMPLATE += motor.template
#----------------------------------------------------
# The following lines are for local dbd expansion
#DBDEXPAND = motorInclude.dbd
include $(TOP)/config/RULES.Db
#----------------------------------------
# ADD RULES AFTER THIS LINE
+16
View File
@@ -0,0 +1,16 @@
TOP = ..
include $(TOP)/config/CONFIG_APP
# MotorSrc is required for all motor record configurations.
# It MUST appear 1st in this list of directories.
DIRS += MotorSrc
# Individual device/driver support modules may be commented
# out here.
DIRS += NewportSrc
DIRS += OmsSrc
DIRS += SoftMotorSrc
DIRS += V544Src
DIRS += Db
include $(TOP)/config/RULES_DIRS
+3
View File
@@ -0,0 +1,3 @@
TOP=../..
include $(TOP)/config/CONFIG_APP
include $(TOP)/config/RULES_ARCHS
+14
View File
@@ -0,0 +1,14 @@
# Makefile.Host
TOP = ../../..
include $(TOP)/config/CONFIG_APP
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
RECTYPES += motorRecord.h
INC += motor.h motordevCom.h motordrvCom.h
INC += motordrvComCode.h
include $(TOP)/config/RULES.Host
#----------------------------------------
# ADD RULES AFTER THIS LINE
+18
View File
@@ -0,0 +1,18 @@
# Makefile.Vx
TOP = ../../..
include $(TOP)/config/CONFIG_APP
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
# The following is required for all motor record configurations.
SRCS.c += ../motorRecord.c ../motordevCom.c ../motordrvCom.c
LIBOBJS = motorRecord.o motordevCom.o motordrvCom.o
LIBNAME = MotorLib
#Note that the command line that builds the
#library $(LIBNAME) may be HUGE (>3kB)
#
include $(TOP)/config/RULES.Vx
#----------------------------------------
# ADD RULES AFTER THIS LINE
+156
View File
@@ -0,0 +1,156 @@
/*
FILENAME... motor.h
USAGE... Definitions and structures common to all levels of motorRecord
support (i.e., record, device and driver).
Version: $Revision: 1.1 $
Modified By: $Author: sluiter $
Last Modified: $Date: 2000-02-08 22:18:42 $
*/
/*
* Current Author: Ron Sluiter
* Date: 12/22/98
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
*/
#ifndef INCmotorh
#define INCmotorh 1
#include <stdio.h>
#include <dbScan.h>
#include <devSup.h>
/* Maximum message size of all supported devices; see drv[device].h for maximum
message size for each device. */
#define MAX_MSG_SIZE 300
typedef enum BOOLEAN_VALUES {OFF = 0, ON = 1} BOOLEAN;
#define NINT(f) (long)((f)>0 ? (f)+0.5 : (f)-0.5) /* Nearest integer. */
/* Motor Record Command Set. !WARNING! this enumeration must match the following;
- "oms_table" in devOmsCom.c
- "MM4000_table" in devMM4000.c
*/
enum motor_cmnd {
MOVE_ABS, /* Absolute Move. */
MOVE_REL, /* Relative Move. */
HOME_FOR, /* Home Forward. */
HOME_REV, /* Home Reverse. */
LOAD_POS, /* Load Zero Position. */
SET_VEL_BASE, /* Set Minimum Velocity. */
SET_VELOCITY, /* Set Jog and Trajectory Velocity. */
SET_ACCEL, /* Set Acceleration. */
GO, /* Start previously programmed move. */
SET_ENC_RATIO, /* Set Encoder Ratio. */
GET_INFO, /* Update Motor Status. */
STOP_AXIS, /* Stop Axis Motion. */
JOG, /* Momentary Jog. */
SET_PGAIN, /* Set Proportional Gain. */
SET_IGAIN, /* Set Integral Gain. */
SET_DGAIN, /* Set Derivative Gain. */
ENABLE_TORQUE, /* Enable Servo Closed-Loop Control. */
DISABL_TORQUE, /* Disable Servo Closed-Loop Control. */
PRIMITIVE, /* Primitive Controller command. */
SET_HIGH_LIMIT, /* Set High Travel Limit. */
SET_LOW_LIMIT /* Set Low Travel Limit. */
};
#ifndef __cplusplus
typedef enum motor_cmnd motor_cmnd;
#endif
/* -------------------------------------------------- */
/* driver and device support parameters */
#define SCAN_RATE 6 /* 60=once a second */
#define MAX_COUNT 50000 /*19000*/ /* timeout value */
#define MAX_AXIS 10 /* max number of axis per board */
#define NOTHING_DONE 0
#define CALLBACK_DATA 1
#define NO 0
#define YES 1
/* -------------------------------------------------- */
/* axis and encoder status for return to requester */
#define RA_DIRECTION 0x01 /* (last) 0=Negative, 1=Positive */
#define RA_DONE 0x02 /* a motion is complete */
#define RA_OVERTRAVEL 0x04 /* a limit switch has been hit */
#define RA_HOME 0x08 /* The home signal is on */
#define EA_SLIP 0x10 /* encoder slip enabled */
#define EA_POSITION 0x20 /* position maintenence enabled */
#define EA_SLIP_STALL 0x40 /* slip/stall detected */
#define EA_HOME 0x80 /* encoder home signal on */
#define EA_PRESENT 0x100 /* encoder is present */
#define RA_PROBLEM 0x200 /* driver stopped polling */
#define RA_MOVING 0x400 /* non-zero velocity present */
#define GAIN_SUPPORT 0x800 /* Motor supports closed-loop position control. */
#define CNTRL_COMM_ERR 0x1000 /* Controller communication error. */
/*
The RA_PROBLEM status bit indicates that the driver has stopped the polling
because it believes that the motor is not really moving. Under normal
operation, the controller will tell the driver when the motor is done
moving. This is a safety precaution and should normally be treated like a
RA_DONE.
*/
#ifdef __cplusplus
struct local_dset
{
long number; /*number of support routines*/
long (*report) (FILE, int); /*print report*/
long (*init) (int); /*init support*/
long (*init_record) (void *); /*init support for particular record*/
long (*get_ioint_info)
(int, struct dbCommon *, IOSCANPVT *); /* get io interrupt information*/
};
#endif
/* device support entry table */
struct motor_dset
{
#ifdef __cplusplus
struct local_dset base;
long (*update_values) (struct motorRecord *);
long (*start_trans) (struct motorRecord *);
long (*build_trans) (motor_cmnd, double *, struct motorRecord *);
long (*end_trans) (struct motorRecord *);
#else
struct dset base;
DEVSUPFUN update_values;
DEVSUPFUN start_trans;
DEVSUPFUN build_trans;
DEVSUPFUN end_trans;
#endif
};
#endif /* INCmotorh */
File diff suppressed because it is too large Load Diff
+688
View File
@@ -0,0 +1,688 @@
# FILENAME... motorRecord.dbd
# Version: $Revision: 1.1 $
# Modified By: $Author: sluiter $
# Last Modified: $Date: 2000-02-08 22:18:43 $
# Experimental Physics and Industrial Control System (EPICS)
# Copyright 1991, the Regents of the University of California,
# and the University of Chicago Board of Governors.
# This software was produced under U.S. Government contracts:
# (W-7405-ENG-36) at the Los Alamos National Laboratory,
# and (W-31-109-ENG-38) at Argonne National Laboratory.
# Initial development by:
# The Controls and Automation Group (AT-8)
# Ground Test Accelerator
# Accelerator Technology Division
# Los Alamos National Laboratory
# Co-developed with
# The Controls and Computing Group
# Accelerator Systems Division
# Advanced Photon Source
# Argonne National Laboratory
#Modification Log:
#-----------------
menu(motorDIR) {
choice(motorDIR_Pos,"Pos")
choice(motorDIR_Neg,"Neg")
}
menu(motorSET) {
choice(motorSET_Use,"Use")
choice(motorSET_Set,"Set")
}
menu(motorMODE) {
choice(motorMODE_Position,"Position")
choice(motorMODE_Velocity,"Velocity")
}
menu(motorUEIP) {
choice(motorUEIP_No,"No")
choice(motorUEIP_Yes,"Yes")
}
menu(motorSPMG) {
choice(motorSPMG_Stop,"Stop")
choice(motorSPMG_Pause,"Pause")
choice(motorSPMG_Move,"Move")
choice(motorSPMG_Go,"Go")
}
menu(motorFOFF) {
choice(motorFOFF_Variable,"Variable")
choice(motorFOFF_Frozen,"Frozen")
}
menu(motorTORQ) {
choice(motorTORQ_Disable,"Disable")
choice(motorTORQ_Enable,"Enable")
}
recordtype(motor) {
include "dbCommon.dbd"
field(VERS,DBF_FLOAT) {
prompt("Code Version")
special(SPC_NOMOD)
initial("1")
}
field(OFF,DBF_DOUBLE) {
prompt("User Offset (EGU)")
special(SPC_MOD)
pp(TRUE)
}
field(FOFF,DBF_MENU) {
prompt("Offset-Freeze Switch")
promptgroup(GUI_COMMON)
special(SPC_MOD)
interest(1)
menu(motorFOFF)
}
field(FOF,DBF_SHORT) {
prompt("Freeze Offset")
asl(ASL0)
special(SPC_MOD)
interest(1)
}
field(VOF,DBF_SHORT) {
prompt("Variable Offset")
asl(ASL0)
special(SPC_MOD)
interest(1)
}
field(DIR,DBF_MENU) {
prompt("User Direction")
promptgroup(GUI_COMMON)
special(SPC_MOD)
pp(TRUE)
interest(1)
menu(motorDIR)
}
field(SET,DBF_MENU) {
prompt("Set/Use Switch")
interest(1)
menu(motorSET)
}
field(SSET,DBF_SHORT) {
prompt("Set SET Mode")
asl(ASL0)
special(SPC_MOD)
interest(1)
}
field(SUSE,DBF_SHORT) {
prompt("Set USE Mode")
asl(ASL0)
special(SPC_MOD)
interest(1)
}
field(VELO,DBF_FLOAT) {
prompt("Velocity (EGU/s)")
promptgroup(GUI_COMMON)
special(SPC_MOD)
interest(1)
}
field(VBAS,DBF_FLOAT) {
prompt("Base Velocity (EGU/s)")
promptgroup(GUI_COMMON)
asl(ASL0)
special(SPC_MOD)
interest(1)
}
field(VMAX,DBF_FLOAT) {
prompt("Max. Velocity (EGU/s)")
promptgroup(GUI_COMMON)
asl(ASL0)
special(SPC_MOD)
interest(1)
}
field(S,DBF_FLOAT) {
prompt("Speed (revolutions/sec)")
promptgroup(GUI_COMMON)
special(SPC_MOD)
interest(1)
}
field(SBAS,DBF_FLOAT) {
prompt("Base Speed (RPS)")
promptgroup(GUI_COMMON)
asl(ASL0)
special(SPC_MOD)
interest(1)
}
field(SMAX,DBF_FLOAT) {
prompt("Max. Speed (RPS)")
promptgroup(GUI_COMMON)
asl(ASL0)
special(SPC_MOD)
interest(1)
}
field(ACCL,DBF_FLOAT) {
prompt("Seconds to Velocity")
promptgroup(GUI_COMMON)
special(SPC_MOD)
interest(1)
initial("0.2")
}
field(BDST,DBF_FLOAT) {
prompt("BL Distance (EGU)")
promptgroup(GUI_COMMON)
special(SPC_MOD)
interest(1)
}
field(BVEL,DBF_FLOAT) {
prompt("BL Velocity (EGU/s)")
promptgroup(GUI_COMMON)
special(SPC_MOD)
interest(1)
}
field(SBAK,DBF_FLOAT) {
prompt("BL Speed (RPS)")
promptgroup(GUI_COMMON)
special(SPC_MOD)
interest(1)
}
field(BACC,DBF_FLOAT) {
prompt("BL Seconds to Velocity")
promptgroup(GUI_COMMON)
special(SPC_MOD)
interest(1)
initial("0.5")
}
field(FRAC,DBF_FLOAT) {
prompt("Move Fraction")
promptgroup(GUI_COMMON)
special(SPC_MOD)
interest(1)
initial("1")
}
field(OUT,DBF_OUTLINK) {
prompt("Output Specification")
promptgroup(GUI_COMMON)
special(SPC_MOD)
interest(1)
}
field(CARD,DBF_SHORT) {
prompt("Card Number")
special(SPC_NOMOD)
interest(3)
}
field(RDBL,DBF_INLINK) {
prompt("Readback Location")
promptgroup(GUI_COMMON)
special(SPC_MOD)
interest(1)
}
field(DOL,DBF_INLINK) {
prompt("Desired Output Loc")
promptgroup(GUI_COMMON)
special(SPC_MOD)
interest(1)
}
field(OMSL,DBF_MENU) {
prompt("Output Mode Select")
promptgroup(GUI_COMMON)
interest(1)
menu(menuOmsl)
}
field(RLNK,DBF_OUTLINK) {
prompt("Readback OutLink")
promptgroup(GUI_COMMON)
special(SPC_MOD)
interest(1)
}
field(SREV,DBF_LONG) {
prompt("Steps per Revolution")
promptgroup(GUI_COMMON)
special(SPC_MOD)
pp(TRUE)
interest(1)
initial("200")
}
field(UREV,DBF_FLOAT) {
prompt("EGU's per Revolution")
promptgroup(GUI_COMMON)
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
}
field(MRES,DBF_FLOAT) {
prompt("Motor Step Size (EGU)")
promptgroup(GUI_COMMON)
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
}
field(ERES,DBF_FLOAT) {
prompt("Encoder Step Size (EGU)")
promptgroup(GUI_COMMON)
special(SPC_MOD)
pp(TRUE)
interest(1)
}
field(RRES,DBF_FLOAT) {
prompt("Readback Step Size (EGU")
promptgroup(GUI_COMMON)
interest(1)
}
field(RES,DBF_FLOAT) {
prompt("Step Size (EGU)")
special(SPC_NOMOD)
interest(1)
}
field(UEIP,DBF_MENU) {
prompt("Use Encoder If Present")
promptgroup(GUI_COMMON)
special(SPC_MOD)
pp(TRUE)
interest(1)
menu(motorUEIP)
}
field(URIP,DBF_MENU) {
prompt("Use RDBL Link If Presen")
promptgroup(GUI_COMMON)
pp(TRUE)
interest(1)
menu(motorUEIP)
}
field(PREC,DBF_SHORT) {
prompt("Display Precision")
promptgroup(GUI_COMMON)
interest(1)
}
field(EGU,DBF_STRING) {
prompt("Engineering Units")
promptgroup(GUI_COMMON)
interest(1)
size(16)
}
field(HLM,DBF_FLOAT) {
prompt("User High Limit")
special(SPC_MOD)
pp(TRUE)
}
field(LLM,DBF_FLOAT) {
prompt("User Low Limit")
special(SPC_MOD)
pp(TRUE)
}
field(DHLM,DBF_FLOAT) {
prompt("Dial High Limit")
promptgroup(GUI_COMMON)
special(SPC_MOD)
pp(TRUE)
}
field(DLLM,DBF_FLOAT) {
prompt("Dial Low Limit")
promptgroup(GUI_COMMON)
special(SPC_MOD)
pp(TRUE)
}
field(HOPR,DBF_FLOAT) {
prompt("High Operating Range")
promptgroup(GUI_COMMON)
interest(1)
}
field(LOPR,DBF_FLOAT) {
prompt("Low Operating Range")
promptgroup(GUI_COMMON)
interest(1)
}
field(HLS,DBF_SHORT) {
prompt("User High Limit Switch")
special(SPC_NOMOD)
}
field(LLS,DBF_SHORT) {
prompt("User Low Limit Switch")
special(SPC_NOMOD)
}
field(RHLS,DBF_SHORT) {
prompt("Raw High Limit Switch")
special(SPC_NOMOD)
}
field(RLLS,DBF_SHORT) {
prompt("Raw Low Limit Switch")
special(SPC_NOMOD)
}
field(HIHI,DBF_FLOAT) {
prompt("Hihi Alarm Limit (EGU)")
promptgroup(GUI_COMMON)
pp(TRUE)
interest(2)
}
field(LOLO,DBF_FLOAT) {
prompt("Lolo Alarm Limit (EGU)")
promptgroup(GUI_COMMON)
pp(TRUE)
interest(2)
}
field(HIGH,DBF_FLOAT) {
prompt("High Alarm Limit (EGU)")
promptgroup(GUI_COMMON)
pp(TRUE)
interest(2)
}
field(LOW,DBF_FLOAT) {
prompt("Low Alarm Limit (EGU)")
promptgroup(GUI_COMMON)
pp(TRUE)
interest(2)
}
field(HHSV,DBF_MENU) {
prompt("Hihi Severity")
promptgroup(GUI_COMMON)
pp(TRUE)
interest(2)
menu(menuAlarmSevr)
}
field(LLSV,DBF_MENU) {
prompt("Lolo Severity")
promptgroup(GUI_COMMON)
pp(TRUE)
interest(2)
menu(menuAlarmSevr)
}
field(HSV,DBF_MENU) {
prompt("High Severity")
promptgroup(GUI_COMMON)
pp(TRUE)
interest(2)
menu(menuAlarmSevr)
}
field(LSV,DBF_MENU) {
prompt("Low Severity")
promptgroup(GUI_COMMON)
pp(TRUE)
interest(2)
menu(menuAlarmSevr)
}
field(HLSV,DBF_MENU) {
prompt("HW Limit Violation Svr")
promptgroup(GUI_COMMON)
pp(TRUE)
interest(2)
menu(menuAlarmSevr)
}
field(RDBD,DBF_FLOAT) {
prompt("Retry Deadband (EGU)")
promptgroup(GUI_COMMON)
special(SPC_MOD)
interest(1)
}
field(RCNT,DBF_SHORT) {
prompt("Retry count")
special(SPC_NOMOD)
interest(2)
}
field(RTRY,DBF_SHORT) {
prompt("Max retry count")
promptgroup(GUI_COMMON)
interest(1)
initial("10")
}
field(MISS,DBF_SHORT) {
prompt("Ran out of retries")
special(SPC_NOMOD)
interest(2)
}
field(SPMG,DBF_MENU) {
prompt("Stop/Pause/Move/Go")
pp(TRUE)
interest(1)
menu(motorSPMG)
initial("3")
}
field(LSPG,DBF_MENU) {
prompt("Last SPMG")
special(SPC_NOMOD)
interest(1)
menu(motorSPMG)
initial("3")
}
field(STOP,DBF_SHORT) {
prompt("Stop")
pp(TRUE)
interest(1)
}
field(HOMF,DBF_SHORT) {
prompt("Home Forward")
special(SPC_MOD)
pp(TRUE)
interest(1)
}
field(HOMR,DBF_SHORT) {
prompt("Home Reverse")
special(SPC_MOD)
pp(TRUE)
interest(1)
}
field(JOGF,DBF_SHORT) {
prompt("Jog motor Forward")
special(SPC_MOD)
pp(TRUE)
interest(1)
}
field(JOGR,DBF_SHORT) {
prompt("Jog motor Reverse")
special(SPC_MOD)
pp(TRUE)
interest(1)
}
field(TWF,DBF_SHORT) {
prompt("Tweak motor Forward")
pp(TRUE)
interest(1)
}
field(TWR,DBF_SHORT) {
prompt("Tweak motor Reverse")
pp(TRUE)
interest(1)
}
field(TWV,DBF_FLOAT) {
prompt("Tweak Step Size (EGU)")
promptgroup(GUI_COMMON)
interest(1)
}
field(VAL,DBF_DOUBLE) {
prompt("User Desired Value (EGU")
special(SPC_MOD)
pp(TRUE)
}
field(LVAL,DBF_DOUBLE) {
prompt("Last User Des Val (EGU)")
special(SPC_NOMOD)
interest(1)
}
field(DVAL,DBF_DOUBLE) {
prompt("Dial Desired Value (EGU")
special(SPC_MOD)
pp(TRUE)
}
field(LDVL,DBF_DOUBLE) {
prompt("Last Dial Des Val (EGU)")
special(SPC_NOMOD)
interest(1)
}
field(RVAL,DBF_LONG) {
prompt("Raw Desired Value (step")
special(SPC_MOD)
pp(TRUE)
}
field(LRVL,DBF_LONG) {
prompt("Last Raw Des Val (steps")
special(SPC_NOMOD)
interest(1)
}
field(RLV,DBF_DOUBLE) {
prompt("Relative Value (EGU)")
special(SPC_MOD)
pp(TRUE)
}
field(LRLV,DBF_DOUBLE) {
prompt("Last Rel Value (EGU)")
special(SPC_NOMOD)
interest(1)
}
field(RBV,DBF_DOUBLE) {
prompt("User Readback Value")
special(SPC_NOMOD)
}
field(DRBV,DBF_DOUBLE) {
prompt("Dial Readback Value")
special(SPC_NOMOD)
}
field(DIFF,DBF_DOUBLE) {
prompt("Difference dval-drbv")
special(SPC_NOMOD)
}
field(RDIF,DBF_LONG) {
prompt("Difference rval-rrbv")
special(SPC_NOMOD)
}
field(PDIF,DBF_LONG) {
prompt("Previous RDIF")
special(SPC_NOMOD)
}
field(RRBV,DBF_LONG) {
prompt("Raw Readback Value")
special(SPC_NOMOD)
}
field(RMP,DBF_LONG) {
prompt("Raw Motor Position")
special(SPC_NOMOD)
}
field(REP,DBF_LONG) {
prompt("Raw Encoder Position")
special(SPC_NOMOD)
}
field(RVEL,DBF_LONG) {
prompt("Raw Velocity")
promptgroup(GUI_COMMON)
special(SPC_NOMOD)
interest(1)
}
field(DMOV,DBF_SHORT) {
prompt("Done moving to value")
special(SPC_NOMOD)
initial("1")
}
field(MOVN,DBF_SHORT) {
prompt("Motor is moving")
special(SPC_NOMOD)
}
field(MSTA,DBF_ULONG) {
prompt("Motor Status")
special(SPC_NOMOD)
interest(3)
}
field(LVIO,DBF_SHORT) {
prompt("Limit violation")
special(SPC_NOMOD)
initial("1")
}
field(TDIR,DBF_SHORT) {
prompt("Direction of Travel")
special(SPC_NOMOD)
}
field(ATHM,DBF_SHORT) {
prompt("At HOME")
special(SPC_NOMOD)
}
field(PP,DBF_SHORT) {
prompt("Post process command")
special(SPC_NOMOD)
interest(2)
initial("1")
}
field(MIP,DBF_USHORT) {
prompt("Motion In Progress")
special(SPC_NOMOD)
interest(3)
}
field(MMAP,DBF_ULONG) {
prompt("Monitor Mask")
special(SPC_NOMOD)
interest(3)
}
field(NMAP,DBF_ULONG) {
prompt("Monitor Mask (more)")
special(SPC_NOMOD)
interest(3)
}
field(DLY,DBF_FLOAT) {
prompt("Readback settle time (s)")
promptgroup(GUI_COMMON)
special(SPC_MOD)
interest(1)
}
field(CBAK,DBF_NOACCESS) {
prompt("Callback structure")
special(SPC_NOMOD)
interest(4)
size(4)
extra("void *cbak")
}
field(PCOF,DBF_FLOAT) {
promptgroup(GUI_COMMON)
prompt("Proportional Gain")
special(SPC_MOD)
interest(1)
initial("0")
}
field(ICOF,DBF_FLOAT) {
promptgroup(GUI_COMMON)
prompt("Integral Gain")
special(SPC_MOD)
interest(1)
initial("0")
}
field(DCOF,DBF_FLOAT) {
promptgroup(GUI_COMMON)
prompt("Derivative Gain")
special(SPC_MOD)
interest(1)
initial("0")
}
field(CNEN,DBF_MENU) {
prompt("Enable control")
promptgroup(GUI_COMMON)
special(SPC_MOD)
menu(motorTORQ)
}
field(INIT,DBF_STRING) {
promptgroup(GUI_COMMON)
prompt("Startup commands")
size(40)
interest(1)
}
field(PREM,DBF_STRING) {
promptgroup(GUI_COMMON)
prompt("Pre-move commands")
size(40)
interest(1)
}
field(POST,DBF_STRING) {
promptgroup(GUI_COMMON)
prompt("Post-move commands")
size(40)
interest(1)
}
field(STOO,DBF_OUTLINK) {
prompt("STOP OutLink")
promptgroup(GUI_COMMON)
special(SPC_MOD)
interest(1)
}
field(DINP,DBF_INLINK) {
prompt("DMOV Input Link")
promptgroup(GUI_COMMON)
special(SPC_MOD)
interest(1)
}
field(RINP,DBF_INLINK) {
prompt("RMP Input Link")
promptgroup(GUI_COMMON)
special(SPC_MOD)
interest(1)
}
}
+521
View File
@@ -0,0 +1,521 @@
/*
FILENAME: motordevCom.c
USAGE... This file contains device functions that are common to all motor
record device support modules.
Version: $Revision: 1.1 $
Modified By: $Author: sluiter $
Last Modified: $Date: 2000-02-08 22:18:44 $
*/
/*
* Original Author: Jim Kowalkowski
* Current Author: Joe Sullivan
* Date: 01/18/93
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 01-18-93 jbk initialized
* ...
* .03 03-19-96 tmm v1.10: modified encoder-ratio calculation
* .04 11-26-96 jps allow for bumpless-reboot on position
* .04a 02-19-97 tmm fixed for EPICS 3.13
*/
#include <vxWorks.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <callback.h>
#include <fast_lock.h>
#ifdef __cplusplus
extern "C" {
#include <dbAccess.h>
#include <recSup.h>
}
#else
#include <dbAccess.h>
#include <recSup.h>
#endif
#include <devSup.h>
#include "motorRecord.h"
#include "motor.h"
#include "motordevCom.h"
#include "motordrvCom.h"
static void motor_callback(struct mess_node * motor_return);
static void motor_init_callback(struct mess_node * motor_return);
/* Command set used by record support. WARNING! this must match
"motor_cmnds" in motor.h .
*/
long motor_init_com(int after, int brdcnt, struct driver_table *tabptr,
struct board_stat ***sptr) /* Initialize motor record device support. */
{
MOTOR_CARD_QUERY card_query;
int card, motor;
if (after == 0)
{
/* allocate space for maximum possible cards in system */
*sptr = (struct board_stat **) malloc(brdcnt * sizeof(struct board_stat *));
/* for each card ask driver for num of axis supported and name */
for (card = 0; card < brdcnt; card++)
{
struct board_stat *brdptr;
brdptr = (*sptr)[card] = (struct board_stat *) malloc(brdcnt * sizeof(struct board_stat));
(tabptr->get_card_info) (card, &card_query, tabptr);
if (card_query.total_axis == 0)
brdptr->exists = NO;
else
{
brdptr->exists = YES;
brdptr->name = card_query.card_name;
brdptr->total_axis = card_query.total_axis;
brdptr->axis_stat = (struct axis_stat *) malloc(
card_query.total_axis * sizeof(struct axis_stat));
for (motor = 0; motor < card_query.total_axis; motor++)
{
brdptr->axis_stat[motor].in_use = OFF;
brdptr->axis_stat[motor].name = card_query.axis_names[motor];
}
}
}
}
return (0);
}
/*
FUNCTION... motor_init_record_com - initialize a record instance.
SYNOPSIS... motor_init_record_com( mr - motor record pointer,
brdcnt - controller board count,
tabptr - driver table pointer,
sptr - controller board status pointer)
LOGIC...
Allocate memory for motor command transaction data structure (struct motor_trans)
and point the Device Private record field to it.
Initialize the motor command transaction data structure.
...
...
...
Error check "card" index, "signal" index and motor "in_use" indicator.
IF error detected.
Set the MSTA PROBLEM bit ON.
Set RES <- MRES.
Set RMP <- REP <- 0.
ERROR RETURN.
ENDIF
Get motor information - call get_axis_info() via driver table.
Update MSTA.
Set axis assigned to a motor record indicator ON.
Set Initialize encoder indicator based on (MSTA indicates an encoder is
present, AND, UEIP set to YES).
IF Initialize encoder indicator is ON.
...
...
ELSE
Set local encoder ratio to unity.
Set RES <- MRES.
ENDIF
Set Initialize position indicator based on (DVEL != 0, AND, RES != 0,
AND, the above "get_axis_info()" position == 0) [NOTE: non-zero controller
position takes precedence over autorestore position].
Set Command Primitive Initialization string indicator based on (non-NULL "init"
pointer, AND, non-zero string length.
IF (Initialize position, OR, encoder, OR, string indicators are ON)
Create initialization semaphore.
...
...
IF Initialize encoder indicator is ON.
Send Set Encoder Ratio command to controller.
ENDIF
IF Initialize position indicator is ON.
Send Load Position command to controller.
ENDIF
IF Command Primitive Initialization string present.
Send Initialization string to controller.
ENDIF
Send Get Info command to controller.
...
...
ENDIF
Get motor information - call get_axis_info() via driver table.
Set RMP, REP and MSTA based on updated motor information.
NORMAL RETURN.
*/
long motor_init_record_com(struct motorRecord *mr, int brdcnt, struct driver_table *tabptr,
struct board_stat *sptr[])
{
struct motor_dset *pdset = (struct motor_dset *) (mr->dset);
struct board_stat *brdptr;
int card, signal;
BOOLEAN initEncoder, initPos, initString;
struct motor_trans *ptrans;
MOTOR_AXIS_QUERY axis_query;
struct mess_node *motor_call;
double ep_mp[2]; /* encoder pulses, motor pulses */
int rtnStat;
/* allocate space for private field - an motor_trans structure */
mr->dpvt = (struct motor_trans *) malloc(sizeof(struct motor_trans));
/* Initialize the private field. */
ptrans = (struct motor_trans *) mr->dpvt;
ptrans->state = IDLE_STATE;
ptrans->callback_changed = NO;
ptrans->tabptr = tabptr;
FASTLOCKINIT(&ptrans->lock);
motor_call = &(ptrans->motor_call);
callbackSetCallback((void (*)(struct callbackPvt *)) motor_callback,
&(motor_call->callback));
callbackSetPriority(priorityMedium, &(motor_call->callback));
/* check if axis already in use, then mark card, axis as in use */
if (mr->out.type != VME_IO) /* out must be VME_IO. */
{
recGblRecordError(S_dev_badBus, (void *) mr,
(char *) "motor_init_record_com(): Illegal OUT Bus Type");
return(S_dev_badBus);
}
card = mr->out.value.vmeio.card;
brdptr = sptr[card];
signal = mr->out.value.vmeio.signal;
rtnStat = 0;
if (card < 0 || card >= brdcnt || brdptr->exists == NO)
{
recGblRecordError(S_db_badField, (void *) mr,
(char *) "motor_init_record_com(): card does not exist!");
rtnStat = S_db_badField;
}
else if (signal < 0 || signal >= brdptr->total_axis)
{
recGblRecordError(S_db_badField, (void *) mr,
(char *) "devOMS (init_record_com) signal does not exist!");
rtnStat = S_db_badField;
}
else if (brdptr->axis_stat[signal].in_use == YES)
{
recGblRecordError(S_db_badField, (void *) mr,
(char *) "devOmsCom (init_record_com) motor already in use!");
rtnStat = S_db_badField;
}
if (rtnStat)
{
/* Initialize readback fields for simulation */
mr->msta = RA_PROBLEM;
mr->res = mr->mres;
mr->rmp = 0; /* raw motor pulse count */
mr->rep = 0; /* raw encoder pulse count */
return(rtnStat);
}
/* query motor for all info to fill into record */
(tabptr->get_axis_info) (card, signal, &axis_query, tabptr);
mr->msta = axis_query.status; /* status info */
brdptr->axis_stat[signal].in_use = ON;
/*jps: setting the encoder ratio was moved from init_record() remove callbacks during iocInit */
/*
* Set the encoder ratio. Note this is blatantly device dependent.
* Determine the number of encoder pulses and the number of motor pulses
* per engineering unit (EGU). Send an array containing this information
* to device support.
*/
initEncoder = ((mr->msta & EA_PRESENT) && mr->ueip) ? ON : OFF;
if (initEncoder == ON)
{
if (fabs(mr->mres) < 1.e-9)
mr->mres = 1.;
if (fabs(mr->eres) < 1.e-9)
mr->eres = mr->mres;
{
int m;
for (m = 10000000; (m > 1) && (fabs(m / mr->eres) > 1.e6 ||
fabs(m / mr->mres) > 1.e6); m /= 10);
ep_mp[0] = m / mr->eres; /* encoder pulses per ... */
ep_mp[1] = m / mr->mres; /* motor pulses */
}
mr->res = mr->eres;
}
else
{
ep_mp[0] = 1.;
ep_mp[1] = 1.;
mr->res = mr->mres;
}
initPos = (mr->dval != 0 && mr->res != 0 && axis_query.position == 0) ? ON : OFF;
/* Test for command primitive initialization string. */
initString = (mr->init != NULL && strlen(mr->init)) ? ON : OFF;
/* Program the device if an encoder is present */
if (initPos == ON || initEncoder == ON || initString == ON)
{
/* Semaphore used to hold initialization until device is programmed - cleared by callback. */
ptrans->initSem = semBCreate(SEM_Q_FIFO, SEM_EMPTY);
/* Switch to special init callback so that record will not be processed during iocInit. */
callbackSetCallback((void (*)(struct callbackPvt *)) motor_init_callback,
&(motor_call->callback));
if (initEncoder == ON)
{
(*pdset->start_trans)(mr);
(*pdset->build_trans)(SET_ENC_RATIO, ep_mp, mr);
(*pdset->end_trans)(mr);
}
if (initPos == ON)
{
double setPos = mr->dval / mr->res;
(*pdset->start_trans)(mr);
(*pdset->build_trans)(LOAD_POS, &setPos, mr);
(*pdset->end_trans)(mr);
}
if (initString == ON)
{
(*pdset->start_trans)(mr);
(*pdset->build_trans)(PRIMITIVE, NULL, mr);
(*pdset->end_trans)(mr);
}
/* Changing encoder ratio may have changed the readback value. */
(*pdset->start_trans)(mr);
(*pdset->build_trans)(GET_INFO, NULL, mr);
(*pdset->end_trans)(mr);
/* Wait for callback w/timeout */
if ((rtnStat = semTake(ptrans->initSem, SEM_TIMEOUT)) == ERROR)
recGblRecordError(S_dev_NoInit, (void *) mr,
(char *) "dev_NoInit (init_record_com: callback2 timeout");
semDelete(ptrans->initSem);
/* Restore regular record callback */
callbackSetCallback((void (*)(struct callbackPvt *)) motor_callback,
&(motor_call->callback));
}
/* query motor for all info to fill into record */
(tabptr->get_axis_info) (card, signal, &axis_query, tabptr);
mr->rmp = axis_query.position; /* raw motor pulse count */
mr->rep = axis_query.encoder_position; /* raw encoder pulse count */
mr->msta = axis_query.status; /* status info */
return(OK);
}
/*
FUNCTION... long motor_update_values(struct motorRecord *)
USAGE... Update the following motor record fields with the latest driver data:
RMP - Raw Motor Position.
REP - Raw Encoder Position.
RVEL - Raw Velocity.
MSTA - Motor Status.
NOTES... This function MUST BE reentrant.
*/
long motor_update_values(struct motorRecord * mr)
{
struct motor_trans *ptrans;
long rc;
rc = NOTHING_DONE;
ptrans = (struct motor_trans *) mr->dpvt;
FASTLOCK(&ptrans->lock);
/* raw motor pulse count */
if (ptrans->callback_changed == YES)
{
mr->rmp = ptrans->motor_pos;
mr->rep = ptrans->encoder_pos;
mr->rvel = ptrans->vel;
mr->msta = ptrans->status;
ptrans->callback_changed = NO;
rc = CALLBACK_DATA;
}
FASTUNLOCK(&ptrans->lock);
return (rc);
}
/*
FUNCTION... long motor_start_trans_com(struct motorRecord *, struct board_stat **, const char *)
USAGE... Start building a transaction.
NOTES... This function MUST BE reentrant.
*/
long motor_start_trans_com(struct motorRecord *mr, struct board_stat **sptr,
const char *init_str)
{
struct board_stat *brdptr;
int card = mr->out.value.vmeio.card;
int axis = mr->out.value.vmeio.signal;
struct motor_trans *trans = (struct motor_trans *) mr->dpvt;
struct mess_node *motor_call;
if (!mr->dpvt)
return (S_dev_NoInit);
motor_call = &(trans->motor_call);
/* initialize area to device private field to store command that is to be
* built and mark as command build in progress. */
trans->state = BUILD_STATE;
motor_call->card = card;
motor_call->signal = axis;
motor_call->type = UNDEFINED;
motor_call->mrecord = (struct dbCommon *) mr;
motor_call->message[0] = (char) NULL;
motor_call->postmsgptr = (char) NULL;
motor_call->termstring = (char) NULL;
if (init_str != NULL)
strcat(motor_call->message, init_str);
/* Check for card/signal before building command - allow callback to
* continue if no card/signal for simulation mode. */
brdptr = sptr[card];
if (brdptr->exists == YES && axis < brdptr->total_axis)
motor_call->message[1] = brdptr->axis_stat[axis].name;
else
motor_call->message[1] = '*';
return (0);
}
/*
FUNCTION... long motor_end_trans_com(struct motorRecord *, struct driver_table *, char *)
USAGE... Finish building a transaction.
NOTES... This function MUST BE reentrant.
*/
long motor_end_trans_com(struct motorRecord *mr, struct driver_table *tabptr,
char *cmnd_line_terminator)
{
struct motor_trans *trans = (struct motor_trans *) mr->dpvt;
struct mess_node *motor_call;
long rc;
rc = OK;
motor_call = &(trans->motor_call);
if ((*trans->tabptr->card_array)[motor_call->card] == NULL)
return(rc = ERROR);
switch (trans->state)
{
case BUILD_STATE:
/* shut off command build in process thing */
trans->state = IDLE_STATE;
rc = (*tabptr->send) (motor_call, tabptr, cmnd_line_terminator);
break;
case IDLE_STATE:
default:
rc = ERROR;
}
return (rc);
}
/*
FUNCTION... static void motor_callback(struct mess_node * motor_return)
USAGE... Callback from driver for a motor in motion.
NOTES... This function MUST BE reentrant.
*/
static void motor_callback(struct mess_node * motor_return)
{
struct motorRecord *mr = (struct motorRecord *) motor_return->mrecord;
struct rset *prset = (struct rset *) (motor_return->mrecord->rset);
struct motor_trans *ptrans;
ptrans = (struct motor_trans *) mr->dpvt;
FASTLOCK(&ptrans->lock);
ptrans->callback_changed = YES;
ptrans->motor_pos = motor_return->position;
ptrans->encoder_pos = motor_return->encoder_position;
ptrans->vel = motor_return->velocity;
ptrans->status = motor_return->status;
FASTUNLOCK(&ptrans->lock);
/* free the return data buffer */
(ptrans->tabptr->free) (motor_return, ptrans->tabptr);
dbScanLock((struct dbCommon *) mr);
(*prset->process) ((struct dbCommon *) mr);
dbScanUnlock((struct dbCommon *) mr);
return;
}
/* callback from OMS driver for init_record - duplicate of
* the motor_callback without the call to process the record */
static void motor_init_callback(struct mess_node * motor_return)
{
struct motorRecord *mr = (struct motorRecord *) motor_return->mrecord;
struct motor_trans *ptrans;
ptrans = (struct motor_trans *) mr->dpvt;
FASTLOCK(&ptrans->lock);
ptrans->callback_changed = YES;
ptrans->motor_pos = motor_return->position;
ptrans->encoder_pos = motor_return->encoder_position;
ptrans->vel = motor_return->velocity;
ptrans->status = motor_return->status;
/* free the return data buffer */
(ptrans->tabptr->free) (motor_return, ptrans->tabptr);
semGive(ptrans->initSem);
FASTUNLOCK(&ptrans->lock);
return;
}
+86
View File
@@ -0,0 +1,86 @@
/*
FILENAME... motordevCom.h
USAGE... This file contains definitions and structures that
are common to all motor record device support modules.
Version: $Revision: 1.1 $
Modified By: $Author: sluiter $
Last Modified: $Date: 2000-02-08 22:18:44 $
*/
/*
* Original Author: Ron Sluiter
* Date: 12/22/98
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contract
* W-31-109-ENG-38 at Argonne National Laboratory.
*
* Beamline Controls & Data Acquisition Group
* Experimental Facilities Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
*/
#ifndef INCmotordevCom
#define INCmotordevCom 1
#include "motor.h"
#include "motordrvCom.h"
#define IDLE_STATE 0
#define BUILD_STATE 1
#define YES 1
#define NO 0
#define SEM_TIMEOUT 50
/* Axis status. */
struct axis_stat
{
char name;
BOOLEAN in_use; /* Indicates axis assigned to a motor record. */
};
/* Controller board status. */
struct board_stat
{
int exists;
char *name;
int total_axis;
struct axis_stat *axis_stat;
};
/* the device private data structure of the record */
struct motor_trans
{
int state;
FAST_LOCK lock;
struct mess_node motor_call;
int callback_changed;
int motor_pos;
int encoder_pos;
int vel;
unsigned long status;
SEM_ID initSem;
struct driver_table *tabptr;
BOOLEAN dpm; /* For OMS VME58 only, drive power monitoring. */
};
extern long motor_update_values(struct motorRecord *);
extern long motor_init_com(int, int, struct driver_table *,
struct board_stat ***);
extern long motor_init_record_com(struct motorRecord *, int,
struct driver_table *, struct board_stat *[]);
extern long motor_start_trans_com(struct motorRecord *, struct board_stat *[],
const char *);
extern long motor_end_trans_com(struct motorRecord *, struct driver_table *, char *);
#endif /* INCmotordevComh */
+544
View File
@@ -0,0 +1,544 @@
/*
FILENAME... motordrvCom.c
USAGE... This file contains driver functions that are common
to all motor record driver modules.
Version: $Revision: 1.1 $
Modified By: $Author: sluiter $
Last Modified: $Date: 2000-02-08 22:18:44 $
*/
/*
* Original Author: Jim Kowalkowski
* Current Author: Joe Sullivan
* Date: 11/14/94
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 01-18-93 jbk initialized
* .02 11-14-94 jps copy drvOms.c and modify to point to vme58 driver
* ...
* .14 02-10-95 jps first released version w/interrupt supprt
* .15 02-16-95 jps add better simulation mode support
* .16 03-10-95 jps limit switch handling improvements
* .17 04-11-95 jps misc. fixes
* .18 09-27-95 jps fix GET_INFO latency bug, add axis argument to
* set_status() create add debug verbosity of 3
* .19 05-03-96 jps convert 32bit card accesses to 16bit - vme58PCB
* version D does not support 32bit accesses.
* .20 06-20-96 jps add more security to message parameters (card #)
* .20a 02-19-97 tmm fixed for EPICS 3.13
*/
#include <vxWorks.h>
#include <taskLib.h>
#include <stdlib.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#include <callback.h>
}
#else
#include <callback.h>
#endif
#include <fast_lock.h>
#include <tickLib.h>
#include "motor.h"
#include "motordrvCom.h"
/* Function declarations. */
static int query_axis(int card, struct driver_table *tabptr, ULONG tick);
static void process_messages(struct driver_table *tabptr, ULONG tick);
static struct mess_node *get_head_node(struct driver_table *tabptr);
static struct mess_node *motor_malloc(struct circ_queue *freelistptr, FAST_LOCK *lockptr);
char oms_trans_axis[] = {'X', 'Y', 'Z', 'T', 'U', 'V', 'R', 'S'};
/*
* FUNCION... motor_task()
* LOGIC:
* WHILE FOREVER
* IF no motors in motion for this board type.
* Set "wait_time" to WAIT_FOREVER.
* ELSE
* Set "tick_curr" to time elapsed since last timeout return from semaphore pend.
* Limit "wait_time" to between 2 and "motor_scan_rate" RTOS ticks.
* IF elapsed time (i.e., "tick_curr") < user delay.
* Set "wait_time" to (user delay - elapsed time).
* IF "wait_time" < 2 OS tick.
* Set "wait_time" to 2 OS tick.
* ENDIF
* ELSE
* Set "wait_time" to to 2 OS tick.
* ENDIF
* ENDIF
* Pend on semaphore with "wait_time" timeout argument - call semTake().
* Update elapsed time.
* IF the "any_motor_in_motion" indicator is ON.
* IF VME58 instance of this task.
* Start data area update on all cards - Call start_status().
* ENDIF
* FOR each OMS board.
* IF motor data structure defined, AND, motor-in-motion indicator ON.
* Update OMS board status - call query_axis().
* ENDIF
* ENDFOR
* ENDIF
* IF someone posted the semaphore.
* Process commands - call process_messages().
* ENDIF
* ENDWHILE
*
* NOTES... This function MUST BE reentrant.
*/
/*****************************************************/
int motor_task(int scan_rate, int msw_tab_ptr, int lsw_tab_ptr, int a4, int a5,
int a6, int a7, int a8, int a9, int a10)
{
struct driver_table *tabptr;
ULONG tick_used, tick_curr;
STATUS sem_ret;
int wait_time, itera;
if (sizeof(int) >= sizeof(char *))
tabptr = (struct driver_table *) msw_tab_ptr;
else
tabptr = (struct driver_table *) ((long) msw_tab_ptr << 16 | (long) lsw_tab_ptr);
tick_used = tickGet();
for(;;)
{
if (*tabptr->any_inmotion_ptr == 0)
wait_time = WAIT_FOREVER;
else
{
tick_curr = tickGet() - tick_used;
if (tick_curr < (ULONG) scan_rate)
{
wait_time = scan_rate - tick_curr;
if (wait_time < 1)
wait_time = 1;
}
else
wait_time = 1;
}
sem_ret = semTake(*tabptr->semptr, wait_time);
tick_used = tickGet();
if (*tabptr->any_inmotion_ptr)
{
if (tabptr->strtstat != NULL)
(*tabptr->strtstat) (ALL_CARDS); /* Start data area update on motor cards */
for (itera = 0; itera < *tabptr->cardcnt_ptr; itera++)
{
struct controller *brdptr = (*tabptr->card_array)[itera];
if (brdptr != NULL && brdptr->motor_in_motion)
query_axis(itera, tabptr, tick_used);
}
}
if (sem_ret != ERROR)
process_messages(tabptr, tick_used);
}
return(0);
}
static int query_axis(int card, struct driver_table *tabptr, ULONG tick)
{
int index;
struct controller *brdptr;
brdptr = (*tabptr->card_array)[card];
for (index = 0; index < brdptr->total_axis; index++)
{
register struct mess_info *motor_info;
register struct mess_node *motor_motion;
ULONG delay;
motor_info = &(brdptr->motor_info[index]);
if ((motor_motion = motor_info->motor_motion) != 0)
{
if (tick >= motor_info->status_delay)
delay = tick - motor_info->status_delay;
else
delay = tick + (0xFFFFFFFF - motor_info->status_delay);
}
if (motor_motion && (delay >= 2) && (*tabptr->setstat) (card, index))
{
struct mess_node *mess_ret;
motor_motion->position = motor_info->position;
motor_motion->encoder_position = motor_info->encoder_position;
motor_motion->velocity = motor_info->velocity;
motor_motion->status = motor_info->status;
mess_ret = (struct mess_node *) motor_malloc(tabptr->freeptr, tabptr->freelockptr);
mess_ret->callback = motor_motion->callback;
mess_ret->mrecord = motor_motion->mrecord;
mess_ret->position = motor_motion->position;
mess_ret->encoder_position = motor_motion->encoder_position;
mess_ret->velocity = motor_motion->velocity;
mess_ret->status = motor_motion->status;
mess_ret->type = motor_motion->type;
if (motor_motion->status & RA_OVERTRAVEL ||
motor_motion->status & RA_DONE ||
motor_motion->status & RA_PROBLEM)
{
(*tabptr->query_done) (card, index, motor_motion);
brdptr->motor_in_motion--;
motor_free(motor_motion, tabptr);
motor_motion = (struct mess_node *) NULL;
motor_info->motor_motion = (struct mess_node *) NULL;
}
callbackRequest((CALLBACK *) mess_ret);
if (brdptr->motor_in_motion == 0)
{
SET_MM_OFF(*tabptr->any_inmotion_ptr, card);
}
}
}
return (0);
}
static void process_messages(struct driver_table *tabptr, ULONG tick)
{
struct mess_node *node, *motor_motion;
ULONG delay;
while ((node = get_head_node(tabptr)))
{
if ((node->card >= 0 && node->card < *tabptr->cardcnt_ptr) &&
(*tabptr->card_array)[node->card] &&
(node->signal >= 0 && node->signal < (*tabptr->card_array)[node->card]->total_axis))
{
struct mess_info *motor_info;
motor_info = &((*tabptr->card_array)[node->card]->motor_info[node->signal]);
motor_motion = motor_info->motor_motion;
switch (node->type)
{
case QUERY:
(*tabptr->sendmsg) (node->card, node->message, (char) NULL);
(*tabptr->getmsg) (node->card, node->message, 1);
callbackRequest((CALLBACK *) node);
break;
case VELOCITY:
(*tabptr->sendmsg) (node->card, node->message, (char) NULL);
/*
* this is tricky - another motion is here there is a very
* large assumption being made here: that the person who sent
* the previous motion is the same one that is sending this
* one, if he weren't, the guy that sent the original would
* never get notified of finish motion. This makes sense in
* record processing since only one record can be assigned to
* an axis and sent commands to it. An improvement would be
* to check and see if the record pointers were the same, if
* they were not, then send a finish message to the previous
* registered motion guy.
*/
if (!motor_motion) /* if NULL */
(*tabptr->card_array)[node->card]->motor_in_motion++;
else
motor_free(motor_motion, tabptr);
SET_MM_ON(*tabptr->any_inmotion_ptr, node->card);
motor_info->motor_motion = node;
motor_info->status_delay = tick;
break;
case MOTION:
(*tabptr->sendmsg) (node->card, node->message, (char) NULL);
/* this is tricky - see velocity comment */
if (!motor_motion) /* if NULL */
(*tabptr->card_array)[node->card]->motor_in_motion++;
else
motor_free(motor_motion, tabptr);
SET_MM_ON(*tabptr->any_inmotion_ptr, node->card);
motor_info->no_motion_count = 0;
motor_info->motor_motion = node;
motor_info->status_delay = tick;
break;
case INFO:
if (tick >= motor_info->status_delay)
delay = tick - motor_info->status_delay;
else
delay = tick + (0xFFFFFFFF - motor_info->status_delay);
if (delay < 2) /* Status update delay - needed for OMS. */
taskDelay((int) (2 - delay)); /* 2 RTOS tick delay. */
if (tabptr->strtstat != NULL)
(*tabptr->strtstat) (node->card);
(*tabptr->setstat) (node->card, node->signal);
node->position = motor_info->position;
node->encoder_position = motor_info->encoder_position;
node->status = motor_info->status;
/*=============================================================================
* node->status & RA_DONE is not a reliable indicator of anything, in this case,
* since set_status() didn't ask the OMS controller to set the "Done" flag on
* command completion.
* Nevertheless, recMotor:process() needs to know whether the motor has stopped,
* and this we can tell by looking for a struct motor_motion.
==============================================================================*/
if (motor_motion)
node->status &= ~RA_DONE;
else
node->status |= RA_DONE;
callbackRequest((CALLBACK *) node);
break;
case MOVE_TERM:
(*tabptr->sendmsg) (node->card, node->message, (char) NULL);
motor_free(node, tabptr); /* free message buffer */
break;
default:
(*tabptr->sendmsg) (node->card, node->message, (char) NULL);
motor_free(node, tabptr); /* free message buffer */
motor_info->status_delay = tick;
break;
}
}
else
{
node->position = 0;
node->encoder_position = 0;
node->velocity = 0;
node->status = RA_PROBLEM;
callbackRequest((CALLBACK *) node);
}
}
}
/*****************************************************/
/* Get a message off the queue */
/* get_head_node() */
/*****************************************************/
static struct mess_node *get_head_node(struct driver_table *tabptr)
{
struct circ_queue *qptr;
struct mess_node *node;
FASTLOCK(tabptr->quelockptr);
qptr = tabptr->queptr;
node = qptr->head;
/* delete node from list */
if (node)
{
if (node == qptr->head)
qptr->head = node->next;
if (node == qptr->tail)
qptr->tail = NULL;
}
FASTUNLOCK(tabptr->quelockptr);
return (node);
}
/*
* FUNCTION... motor_send()
*
* USAGE... Send a command to the motor task queue.
*
* LOGIC...
* Allocate message node - call motor_malloc().
* Copy info. from input node to new node.
* Process node based on "type".
* Lock motor task queue.
* Insert new node at tail of queue.
*/
int motor_send(struct mess_node *u_msg, struct driver_table *tabptr, char *cmnd_line_terminator)
{
struct mess_node *new_message;
struct circ_queue *qptr;
new_message = motor_malloc(tabptr->freeptr, tabptr->freelockptr);
new_message->callback = u_msg->callback;
new_message->next = (struct mess_node *) NULL;
new_message->type = u_msg->type;
new_message->signal = u_msg->signal;
new_message->card = u_msg->card;
new_message->mrecord = u_msg->mrecord;
new_message->status = 0;
strcpy(new_message->message, u_msg->message);
new_message->postmsgptr = u_msg->postmsgptr;
new_message->termstring = u_msg->termstring;
switch (new_message->type)
{
case MOVE_TERM:
case MOTION:
if (new_message->termstring != NULL)
strcat(new_message->message, new_message->termstring);
break;
case VELOCITY:
case IMMEDIATE:
case QUERY:
case INFO:
break;
default:
return (-1);
}
strcat(new_message->message, cmnd_line_terminator);
FASTLOCK(tabptr->quelockptr);
qptr = tabptr->queptr;
if (qptr->tail)
{
qptr->tail->next = new_message;
qptr->tail = new_message;
}
else
{
qptr->tail = new_message;
qptr->head = new_message;
}
FASTUNLOCK(tabptr->quelockptr);
semGive(*tabptr->semptr);
return (0);
}
static struct mess_node *motor_malloc(struct circ_queue *freelistptr, FAST_LOCK *lockptr)
{
struct mess_node *node;
FASTLOCK(lockptr);
if (!freelistptr->head)
node = (struct mess_node *) malloc(sizeof(struct mess_node));
else
{
node = freelistptr->head;
freelistptr->head = node->next;
if (!freelistptr->head)
freelistptr->tail = (struct mess_node *) NULL;
}
FASTUNLOCK(lockptr);
return (node);
}
int motor_free(struct mess_node * node, struct driver_table *tabptr)
{
struct circ_queue *freelistptr;
freelistptr = tabptr->freeptr;
FASTLOCK(tabptr->freelockptr);
node->next = (struct mess_node *) NULL;
if (freelistptr->tail)
{
freelistptr->tail->next = node;
freelistptr->tail = node;
}
else
{
freelistptr->head = node;
freelistptr->tail = node;
}
FASTUNLOCK(tabptr->freelockptr);
return (0);
}
/*---------------------------------------------------------------------*/
/*
* both of these routines are only to be used at initialization time - before
* queuing transactions starts for an axis. These routines do no locking,
* since transactions will not enter the queue for an axis until after they
* have run.
*/
/* return the card information to caller */
int motor_card_info(int card, MOTOR_CARD_QUERY * cq, struct driver_table *tabptr)
{
struct controller *brdptr;
brdptr = (*tabptr->card_array)[card];
if (card >= 0 &&
card < *tabptr->cardcnt_ptr &&
brdptr != NULL)
{
cq->total_axis = brdptr->total_axis;
cq->card_name = brdptr->ident;
cq->axis_names = oms_trans_axis;
}
else
cq->total_axis = 0;
return (0);
}
/* return information for an axis to the caller */
int motor_axis_info(int card, int signal, MOTOR_AXIS_QUERY * aq, struct driver_table *tabptr)
{
struct controller *brdptr;
brdptr = (*tabptr->card_array)[card];
if (card >= 0 &&
card < *tabptr->cardcnt_ptr &&
brdptr != NULL &&
signal >= 0 &&
signal < brdptr->total_axis)
{
aq->position = brdptr->motor_info[signal].position;
aq->encoder_position =
brdptr->motor_info[signal].encoder_position;
aq->status = brdptr->motor_info[signal].status;
}
else
{
aq->position = aq->encoder_position = 0;
aq->status = RA_PROBLEM;
}
return (0);
}
+205
View File
@@ -0,0 +1,205 @@
/*
FILENAME... motordrvCom.h
USAGE... This file contains definitions and structures that
are common to all motor record driver support modules.
Version: $Revision: 1.1 $
Modified By: $Author: sluiter $
Last Modified: $Date: 2000-02-08 22:18:45 $
*/
/*
* Current Author: Ron Sluiter
* Date: 12/22/98
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
*/
#ifndef INCmotordrvComh
#define INCmotordrvComh 1
#include <rngLib.h>
/*
Valid command types for the driver. The order is of importance; 0 is
lowest importance. Device support will choose the greatest one to
use as the driver transaction type.
*/
#define UNDEFINED (unsigned char)0 /* garbage type */
#define IMMEDIATE (unsigned char)1 /* 'i' an execute immediate, no reply */
#define MOVE_TERM (unsigned char)2 /* 't' terminate a previous active motion */
#define MOTION (unsigned char)3 /* 'm' will produce motion updates */
#define VELOCITY (unsigned char)4 /* 'v' make motion updates till MOVE_TERM */
#define INFO (unsigned char)5 /* 'f' get curr motor/encoder pos and stat */
#define QUERY (unsigned char)6 /* 'q' specialty type, not needed */
/* Macros used to set/clear bits in any_motor_in_motion variable. */
#define SET_MM_ON(v,a) v|=(1<<a)
#define SET_MM_OFF(v,a) v&=~(1<<a)
/* Misc. defines. */
#define ALL_CARDS -1
#define FLUSH -1 /* The 3rd argument of driver_table's getmsg() can indicate
either FLUSH the buffer or the # of commands to process. */
/* message queue management - device and driver support only */
struct mess_node
{
CALLBACK callback;
int signal;
int card;
unsigned char type;
char message[MAX_MSG_SIZE];
long position;
long encoder_position;
long velocity;
unsigned long status;
struct dbCommon *mrecord; /* "Hidden" pointer to motor record. */
struct mess_node *next;
char *postmsgptr;
char const *termstring; /* Termination string for STOP_AXIS command
(see process_messages()). */
};
/* initial position query to driver - device and driver support only */
typedef struct mess_card_query
{
char *card_name;
char *axis_names;
int total_axis;
} MOTOR_CARD_QUERY;
typedef struct mess_axis_query
{
long position;
long encoder_position;
unsigned long status;
} MOTOR_AXIS_QUERY;
struct axis_status
{
char direction;
char done;
char overtravel;
char home;
};
struct encoder_status
{
char slip_enable;
char pos_enable;
char slip_detect;
char pos_dead;
char axis_home;
char unused;
};
struct circ_queue /* Circular queue structure. */
{
struct mess_node *head;
struct mess_node *tail;
};
/*----------------motor state info-----------------*/
struct irqdatastr /* Used only for VME44. */
{
/* Interrupt Handling control elements */
int irqErrno; /* Error indicator from isr */
uint8_t irqEnable;
RING_ID recv_rng; /* message receiving control */
SEM_ID recv_sem;
RING_ID send_rng; /* message transmitting control */
SEM_ID send_sem;
};
struct mess_info
{
struct mess_node *motor_motion; /* in motion, NULL/node */
int encoder_present; /* one YES/NO for each axis */
int32_t position; /* one pos for each axis */
int32_t encoder_position; /* one pos for each axis */
int32_t velocity; /* Raw velocity readback(not implemented) */
int no_motion_count;
ULONG status_delay; /* Insure 10ms delay between motion/velocity
* commands and status query. */
unsigned long status; /* one pos for each axis */
int pid_present; /* PID control indicator for VME58 (YES/NO). */
double high_limit; /* MM4000 only; Controller's high travel limit. */
double low_limit; /* MM4000 only; Controller's low travel limit. */
};
struct controller /* Controller board information. */
{
int motor_in_motion;/* count of motors in motion */
char ident[50]; /* identification string for this card */
int total_axis; /* total axis on this card */
char *localaddr; /* address of this card */
struct irqdatastr *irqdata; /* VME44 only; IRQ data. */
void *DevicePrivate; /* Pointer to device specific structure. For
MM3000/4000 = (struct MMcontroller *); otherwise Null. */
struct mess_info motor_info[MAX_AXIS];
};
/* The "driver_table" structure allows device level access to driver level
data and functions; it supports "common code" for all motor record device
drivers.
*/
struct driver_table
{
int (*init) (void);
int (*send) (struct mess_node *, struct driver_table *, char *);
int (*free) (struct mess_node *, struct driver_table *);
int (*get_card_info) (int, MOTOR_CARD_QUERY *, struct driver_table *);
int (*get_axis_info) (int, int, MOTOR_AXIS_QUERY *, struct driver_table *);
struct circ_queue *queptr;
FAST_LOCK *quelockptr;
struct circ_queue *freeptr;
FAST_LOCK *freelockptr;
SEM_ID *semptr;
struct controller ***card_array;
int *cardcnt_ptr;
int *any_inmotion_ptr;
int (*sendmsg) (int, char const *, char);
int (*getmsg) (int, char *, int);
int (*setstat) (int, int);
void (*query_done) (int, int, struct mess_node *);
void (*strtstat) (int); /* Optional; start status function or NULL. */
const BOOLEAN *const init_indicator; /* Driver initialized indicator. */
};
/* Function prototypes. */
extern int motor_send(struct mess_node *, struct driver_table *, char *);
extern int motor_free(struct mess_node *, struct driver_table *);
extern int motor_card_info(int, MOTOR_CARD_QUERY *, struct driver_table *);
extern int motor_axis_info(int, int, MOTOR_AXIS_QUERY *, struct driver_table *);
extern int motor_task(int a1, int, int, int, int, int, int, int, int, int a10);
#endif /* INCmotordrvComh */
+47
View File
@@ -0,0 +1,47 @@
/*
FILENAME: motordrvComCode.h
USAGE... This file contains local variables that are allocated
in each motor record driver. The variables are allocated
in each driver by including this file.
Version: $Revision: 1.1 $
Modified By: $Author: sluiter $
Last Modified: $Date: 2000-02-08 22:18:45 $
*/
/*
* Original Author: Ron Sluiter
* Date: 08/20/99
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contract
* W-31-109-ENG-38 at Argonne National Laboratory.
*
* Beamline Controls & Data Acquisition Group
* Experimental Facilities Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
*/
#ifndef INCmotordrvComCode
#define INCmotordrvComCode 1
/* --- Local data common to each driver. --- */
STATIC volatile int motor_scan_rate = SCAN_RATE;
STATIC struct controller **motor_state;
STATIC int total_cards;
STATIC int any_motor_in_motion;
STATIC struct circ_queue mess_queue; /* in message queue head */
STATIC FAST_LOCK queue_lock;
STATIC struct circ_queue free_list;
STATIC FAST_LOCK freelist_lock;
STATIC SEM_ID motor_sem;
STATIC BOOLEAN initialized = OFF; /* Driver initialized indicator. */
#endif /* INCmotordrvComCode */
+3
View File
@@ -0,0 +1,3 @@
TOP=../..
include $(TOP)/config/CONFIG_APP
include $(TOP)/config/RULES_ARCHS
+11
View File
@@ -0,0 +1,11 @@
# Makefile.Host
TOP = ../../..
include $(TOP)/config/CONFIG_APP
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
DBDINSTALL += devNewport.dbd
include $(TOP)/config/RULES.Host
#----------------------------------------
# ADD RULES AFTER THIS LINE
+36
View File
@@ -0,0 +1,36 @@
# Makefile.Vx
TOP = ../../..
include $(TOP)/config/CONFIG_APP
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
# The following is required for Hideos.
USR_INCLUDES += -I$(HIDEOS)/include
USR_CXXFLAGS += -DOS_EQ_VXWORKS
# The following are used for debugging messages.
#USR_CFLAGS += -DDEBUG
#USR_CXXFLAGS += -DDEBUG
# The following is required for all Newport Motion Master device drivers.
# NOTE: GPIB and/or serial communication require Hideos support.
SRCS.c += ../gpibIO.c
SRCS.cc += ../serialIO.cc
LIBOBJS += gpibIO.o serialIO.o
# The following is required for the Newport MM3000 (i.e., MM3000) device driver.
SRCS.c += ../devMM3000.c ../drvMM3000.c
LIBOBJS += devMM3000.o drvMM3000.o
# The following is required for the Newport MM4000/5 (i.e., MM4000) device driver.
SRCS.c += ../devMM4000.c ../drvMM4000.c
LIBOBJS += devMM4000.o drvMM4000.o
LIBNAME = NewportLib
#Note that the command line that builds the
#library $(LIBNAME) may be HUGE (>3kB)
#
include $(TOP)/config/RULES.Vx
#----------------------------------------
# ADD RULES AFTER THIS LINE
+362
View File
@@ -0,0 +1,362 @@
/*
FILENAME... devMM3000.c
USAGE... Motor record device level support for Newport MM3000.
Version: $Revision: 1.1 $
Modified By: $Author: sluiter $
Last Modified: $Date: 2000-02-08 22:18:51 $
*/
/*
* Original Author: Mark Rivers
* Date: 10/16/97
* Current Author: Ron Sluiter
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .00 10-16-97 mlr initialized from devOms58
* .01 07-19-99 rls initialized from drvMM4000
* ...
*/
#include <vxWorks.h>
#include <stdioLib.h>
#include <string.h>
#include <semLib.h> /* jps: include for init_record wait */
#ifdef __cplusplus
extern "C" {
#include <epicsDynLink.h>
}
#else
#include <epicsDynLink.h>
#endif
#include <sysSymTbl.h> /* for sysSymTbl*/
#include <alarm.h>
#include <callback.h>
#include <dbDefs.h>
#include <dbAccess.h>
#include <dbCommon.h>
#include <fast_lock.h>
#include <recSup.h>
#include <devSup.h>
#include <drvSup.h>
#include "motorRecord.h"
#include "motor.h"
#include "motordevCom.h"
#include "drvMMCom.h"
#define STATIC static
#define ABS(f) ((f)>0 ? (f) : -(f))
#define SIGN(f) (long)((f)>0 ? 1 : -1)
#define MAX(a,b) ((a)>(b) ? (a) : (b))
#define MIN(a,b) ((a)<(b) ? (a) : (b))
/* ----------------Create the dsets for devMM3000----------------- */
/* static long report(); */
STATIC struct driver_table *drvtabptr;
STATIC long MM3000_init(int);
STATIC long MM3000_init_record(struct motorRecord *);
STATIC long MM3000_start_trans(struct motorRecord *);
STATIC long MM3000_build_trans(motor_cmnd, double *, struct motorRecord *);
STATIC long MM3000_end_trans(struct motorRecord *);
struct motor_dset devMM3000 =
{
{8, NULL, MM3000_init, MM3000_init_record, NULL},
motor_update_values,
MM3000_start_trans,
MM3000_build_trans,
MM3000_end_trans
};
/* --------------------------- program data --------------------- */
/* This table is used to define the command types */
static int MM3000_table[] = {
MOTION, /* MOVE_ABS */
MOTION, /* MOVE_REL */
MOTION, /* HOME_FOR */
MOTION, /* HOME_REV */
IMMEDIATE, /* LOAD_POS */
IMMEDIATE, /* SET_VEL_BASE */
IMMEDIATE, /* SET_VELOCITY */
IMMEDIATE, /* SET_ACCEL */
IMMEDIATE, /* GO */
IMMEDIATE, /* SET_ENC_RATIO */
INFO, /* GET_INFO */
MOVE_TERM, /* STOP_AXIS */
VELOCITY, /* JOG */
IMMEDIATE, /* SET_PGAIN */
IMMEDIATE, /* SET_IGAIN */
IMMEDIATE, /* SET_DGAIN */
IMMEDIATE, /* ENABLE_TORQUE */
IMMEDIATE, /* DISABL_TORQUE */
IMMEDIATE, /* PRIMITIVE */
IMMEDIATE, /* SET_HIGH_LIMIT */
IMMEDIATE, /* SET_LOW_LIMIT */
};
static struct board_stat **MM3000_cards;
/* --------------------------- program data --------------------- */
/* initialize device support for MM3000 stepper motor */
STATIC long MM3000_init(int after)
{
SYM_TYPE type;
long rtnval;
if (after == 0)
{
rtnval = symFindByNameEPICS(sysSymTbl, "_MM3000_access",
(char **) &drvtabptr, &type);
if (rtnval != OK)
return(rtnval);
/*
IF before DB initialization.
Initialize MM3000 driver (i.e., call init()). See comment in
drvMM3000.c init().
ENDIF
*/
(drvtabptr->init)();
}
rtnval = motor_init_com(after, *drvtabptr->cardcnt_ptr, drvtabptr, &MM3000_cards);
return(rtnval);
}
/* initialize a record instance */
STATIC long MM3000_init_record(struct motorRecord *mr)
{
return(motor_init_record_com(mr, *drvtabptr->cardcnt_ptr, drvtabptr, MM3000_cards));
}
/* start building a transaction */
STATIC long MM3000_start_trans(struct motorRecord *mr)
{
return(motor_start_trans_com(mr, MM3000_cards, NULL));
}
/* end building a transaction */
STATIC long MM3000_end_trans(struct motorRecord *mr)
{
struct motor_trans *trans = (struct motor_trans *) mr->dpvt;
struct mess_node *motor_call;
char *msgptr;
int last;
/* Remove trailing ';'s from message. */
motor_call = &(trans->motor_call);
msgptr = motor_call->message;
last = strlen(msgptr) - 1;
if (msgptr[last] == ';')
msgptr[last] = NULL;
return(motor_end_trans_com(mr, drvtabptr, "\r"));
}
/* add a part to the transaction */
STATIC long MM3000_build_trans(motor_cmnd command, double *parms, struct motorRecord *mr)
{
struct motor_trans *trans = (struct motor_trans *) mr->dpvt;
struct mess_node *motor_call;
struct controller *brdptr;
struct MMcontroller *cntrl;
char buff[30];
int axis, card;
int intval;
long rtnval;
rtnval = OK;
buff[0] = '\0';
motor_call = &(trans->motor_call);
card = motor_call->card;
axis = motor_call->signal + 1;
brdptr = (*trans->tabptr->card_array)[card];
if (brdptr == NULL)
return(rtnval = ERROR);
cntrl = brdptr->DevicePrivate;
if (MM3000_table[command] > motor_call->type)
motor_call->type = MM3000_table[command];
if (trans->state != BUILD_STATE)
return(rtnval = ERROR);
if (command == PRIMITIVE && mr->init != NULL && strlen(mr->init) != 0)
{
strcat(motor_call->message, mr->init);
strcat(motor_call->message, "\r");
}
switch (command)
{
case SET_PGAIN:
case SET_IGAIN:
case SET_DGAIN:
{
double temp_dbl;
temp_dbl = parms[0] * 32767.0;
intval = NINT(temp_dbl);
break;
}
case MOVE_ABS:
case MOVE_REL:
case HOME_FOR:
case HOME_REV:
case JOG:
if (strlen(mr->prem) != 0)
{
strcat(motor_call->message, mr->prem);
strcat(motor_call->message, ";");
}
if (strlen(mr->post) != 0)
motor_call->postmsgptr = (char *) &mr->post;
default:
intval = NINT(parms[0]);
break;
}
switch (command)
{
case MOVE_ABS:
sprintf(buff, "%dPA%d;", axis, intval);
break;
case MOVE_REL:
sprintf(buff, "%dPR%d;", axis, intval);
break;
case HOME_FOR:
case HOME_REV:
sprintf(buff, "%dOR1;", axis);
break;
case LOAD_POS:
if (*parms == 0.0)
sprintf(buff, "%dDH", axis);
else
rtnval = ERROR;
break;
case SET_VEL_BASE:
if (cntrl->type[axis - 1] == STEPPER)
{
if (intval < 100)
intval = 100;
sprintf(buff, "%dVB%d;", axis, intval);
}
break;
case SET_VELOCITY:
if (cntrl->type[axis - 1] == STEPPER && intval < 100)
intval = 100;
sprintf(buff, "%dVA%d;", axis, intval);
break;
case SET_ACCEL:
/*
* The value passed is in steps/sec/sec.
* Convert to user units/sec/sec
*/
if (cntrl->type[axis - 1] == STEPPER && intval < 15000)
intval = 15000;
if (cntrl->type[axis - 1] == DC && intval < 250)
intval = 250;
sprintf(buff, "%dAC%d;", axis, intval);
break;
case GO:
/*
* The MM3000 starts moving immediately on move commands, GO command
* does nothing
*/
break;
case SET_ENC_RATIO:
/* MM3000 valid encoder ratio values < 10,000. */
while (parms[0] > 10000.0 || parms[1] > 10000.0)
{
parms[0] /= 10;
parms[1] /= 10;
}
if (cntrl->type[axis - 1] == STEPPER)
sprintf(buff, "%dER%d:%d", axis, (int) parms[0], (int) parms[1]);
break;
case GET_INFO:
/* These commands are not actually done by sending a message, but
rather they will indirectly cause the driver to read the status
of all motors */
break;
case STOP_AXIS:
sprintf(buff, "%dST;", axis);
break;
case JOG:
/*
* MM3000 does not have a jog command. Simulate with move absolute
* to the appropriate software limit. We can move to MM3000 soft limits.
* If the record soft limits are set tighter than the MM3000 limits
* the record will prevent JOG motion beyond its soft limits
*/
sprintf(buff, "%dVA%d;", axis, ABS(intval));
strcat(motor_call->message, buff);
if (intval > 0)
sprintf(buff, "%dPA%d;", axis, (int) (mr->dhlm / mr->res));
else
sprintf(buff, "%dPA%d;", axis, (int) (mr->dllm / mr->res));
break;
case SET_PGAIN:
sprintf(buff, "%dKP%d;%dUF;", axis, intval, axis);
break;
case SET_IGAIN:
sprintf(buff, "%dKI%d;%dUF;", axis, intval, axis);
break;
case SET_DGAIN:
sprintf(buff, "%dKD%d;%dUF;", axis, intval, axis);
break;
case ENABLE_TORQUE:
sprintf(buff, "MO;");
break;
case DISABL_TORQUE:
sprintf(buff, "MF;");
break;
case SET_HIGH_LIMIT:
case SET_LOW_LIMIT:
sprintf(buff, "%dSL%d;", axis, intval);
break;
default:
rtnval = ERROR;
}
strcat(motor_call->message, buff);
return(rtnval);
}
+371
View File
@@ -0,0 +1,371 @@
/*
FILENAME... devMM4000.c
USAGE... Motor record device level support for Newport MM4000.
Version: $Revision: 1.1 $
Modified By: $Author: sluiter $
Last Modified: $Date: 2000-02-08 22:18:52 $
*/
/*
* Original Author: Mark Rivers
* Date: 10/20/97
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 10-20-97 mlr initialized from drvOms58
* .02 10-30-97 mlr Replaced driver calls with gpipIO functions
* .03 10-30-98 mlr Minor code cleanup, improved formatting
* .04 02-01-99 mlr Added temporary fix to delay reading motor
* positions at the end of a move.
*/
#include <vxWorks.h>
#include <stdioLib.h>
#include <string.h>
#include <semLib.h> /* jps: include for init_record wait */
#include <logLib.h>
#ifdef __cplusplus
extern "C" {
#include <epicsDynLink.h>
}
#else
#include <epicsDynLink.h>
#endif
#include <sysSymTbl.h> /* for sysSymTbl*/
#include <alarm.h>
#include <callback.h>
#include <dbDefs.h>
#include <dbAccess.h>
#include <dbCommon.h>
#include <fast_lock.h>
#include <recSup.h>
#include <devSup.h>
#include <drvSup.h>
#include "motorRecord.h"
#include "motor.h"
#include "motordevCom.h"
#include "drvMMCom.h"
#define STATIC static
#define ABS(f) ((f)>0 ? (f) : -(f))
#define SIGN(f) (long)((f)>0 ? 1 : -1)
#define MAX(a,b) ((a)>(b) ? (a) : (b))
#define MIN(a,b) ((a)<(b) ? (a) : (b))
/* ----------------Create the dsets for devMM4000----------------- */
STATIC struct driver_table *drvtabptr;
STATIC long MM4000_init(int);
STATIC long MM4000_init_record(struct motorRecord *);
STATIC long MM4000_start_trans(struct motorRecord *);
STATIC long MM4000_build_trans(motor_cmnd, double *, struct motorRecord *);
STATIC long MM4000_end_trans(struct motorRecord *);
struct motor_dset devMM4000 =
{
{8, NULL, MM4000_init, MM4000_init_record, NULL},
motor_update_values,
MM4000_start_trans,
MM4000_build_trans,
MM4000_end_trans
};
/* --------------------------- program data --------------------- */
/* This table is used to define the command types */
static int MM4000_table[] = {
MOTION, /* MOVE_ABS */
MOTION, /* MOVE_REL */
MOTION, /* HOME_FOR */
MOTION, /* HOME_REV */
IMMEDIATE, /* LOAD_POS */
IMMEDIATE, /* SET_VEL_BASE */
IMMEDIATE, /* SET_VELOCITY */
IMMEDIATE, /* SET_ACCEL */
IMMEDIATE, /* GO */
IMMEDIATE, /* SET_ENC_RATIO */
INFO, /* GET_INFO */
MOVE_TERM, /* STOP_AXIS */
VELOCITY, /* JOG */
IMMEDIATE, /* SET_PGAIN */
IMMEDIATE, /* SET_IGAIN */
IMMEDIATE, /* SET_DGAIN */
IMMEDIATE, /* ENABLE_TORQUE */
IMMEDIATE, /* DISABL_TORQUE */
IMMEDIATE, /* PRIMITIVE */
IMMEDIATE, /* SET_HIGH_LIMIT */
IMMEDIATE /* SET_LOW_LIMIT */
};
static struct board_stat **MM4000_cards;
/* --------------------------- program data --------------------- */
/* initialize device support for MM4000 stepper motor */
STATIC long MM4000_init(int after)
{
SYM_TYPE type;
long rtnval;
if (after == 0)
{
rtnval = symFindByNameEPICS(sysSymTbl, "_MM4000_access",
(char **) &drvtabptr, &type);
if (rtnval != OK)
return(rtnval);
/*
IF before DB initialization.
Initialize MM4000 driver (i.e., call init()). See comment in
drvMM4000.c init().
ENDIF
*/
(drvtabptr->init)();
}
rtnval = motor_init_com(after, *drvtabptr->cardcnt_ptr, drvtabptr, &MM4000_cards);
return(rtnval);
}
/* initialize a record instance */
STATIC long MM4000_init_record(struct motorRecord *mr)
{
return(motor_init_record_com(mr, *drvtabptr->cardcnt_ptr, drvtabptr, MM4000_cards));
}
/* start building a transaction */
STATIC long MM4000_start_trans(struct motorRecord *mr)
{
return(motor_start_trans_com(mr, MM4000_cards, NULL));
}
/* end building a transaction */
STATIC long MM4000_end_trans(struct motorRecord *mr)
{
return(motor_end_trans_com(mr, drvtabptr, "\r"));
}
/* add a part to the transaction */
STATIC long MM4000_build_trans(motor_cmnd command, double *parms, struct motorRecord *mr)
{
struct motor_trans *trans = (struct motor_trans *) mr->dpvt;
struct mess_node *motor_call;
struct controller *brdptr;
struct mess_info *motor_info;
struct MMcontroller *cntrl;
char buff[110];
int axis, card, maxdigits, size;
double dval, cntrl_units;
long rtnval;
rtnval = OK;
buff[0] = '\0';
dval = *parms;
motor_call = &(trans->motor_call);
card = motor_call->card;
axis = motor_call->signal + 1;
brdptr = (*trans->tabptr->card_array)[card];
if (brdptr == NULL)
return(rtnval = ERROR);
cntrl = (struct MMcontroller *) brdptr->DevicePrivate;
cntrl_units = dval * cntrl->drive_resolution[axis - 1];
maxdigits = cntrl->res_decpts[axis - 1];
if (MM4000_table[command] > motor_call->type)
motor_call->type = MM4000_table[command];
if (trans->state != BUILD_STATE)
return(rtnval = ERROR);
if (command == PRIMITIVE && mr->init != NULL && strlen(mr->init) != 0)
{
strcat(motor_call->message, mr->init);
strcat(motor_call->message, "\r");
}
switch (command)
{
case MOVE_ABS:
case MOVE_REL:
case HOME_FOR:
case HOME_REV:
case JOG:
if (strlen(mr->prem) != 0)
{
strcat(motor_call->message, mr->prem);
strcat(motor_call->message, ";");
}
if (strlen(mr->post) != 0)
motor_call->postmsgptr = (char *) &mr->post;
break;
default:
break;
}
switch (command)
{
case MOVE_ABS:
sprintf(buff, "%dPA%.*f;", axis, maxdigits, cntrl_units);
break;
case MOVE_REL:
sprintf(buff, "%dPR%.*f;", axis, maxdigits, cntrl_units);
break;
case HOME_FOR:
case HOME_REV:
sprintf(buff, "%dOR;", axis);
break;
case LOAD_POS:
if (cntrl->model == MM4000)
sprintf(buff, "%dSH%.*f;%dDH;%dSH%.*f", axis, maxdigits, cntrl_units,
axis, axis, maxdigits, cntrl->home_preset[axis - 1]);
break;
case SET_VEL_BASE:
break; /* MM4000 does not use base velocity */
case SET_VELOCITY:
sprintf(buff, "%dVA%.*f;", axis, maxdigits, cntrl_units);
break;
case SET_ACCEL:
/*
* The value passed is in steps/sec/sec.
* Convert to user units/sec/sec
*/
sprintf(buff, "%dAC%.*f;", axis, maxdigits, cntrl_units);
break;
case GO:
/*
* The MM4000 starts moving immediately on move commands, GO command
* does nothing
*/
break;
case SET_ENC_RATIO:
/*
* The MM4000 does not have the concept of encoder ratio, ignore this
* command
*/
break;
case GET_INFO:
/* These commands are not actually done by sending a message, but
rather they will indirectly cause the driver to read the status
of all motors */
break;
case STOP_AXIS:
sprintf(buff, "%dST;", axis);
break;
case JOG:
/*
* MM4000 does not have a jog command. Simulate with move absolute
* to the appropriate software limit. We can move to MM4000 soft limits.
* If the record soft limits are set tighter than the MM4000 limits
* the record will prevent JOG motion beyond its soft limits
*/
sprintf(buff, "%dVA%.*f;", axis, maxdigits, ABS(cntrl_units));
strcat(motor_call->message, buff);
if (dval > 0.)
sprintf(buff, "%dPA%.*f;", axis, maxdigits, mr->dhlm);
else
sprintf(buff, "%dPA%.*f;", axis, maxdigits, mr->dllm);
break;
case SET_PGAIN:
sprintf(buff, "%dKP%f;%dUF;", axis, dval, axis);
break;
case SET_IGAIN:
sprintf(buff, "%dKI%f;%dUF;", axis, dval, axis);
break;
case SET_DGAIN:
sprintf(buff, "%dKD%f;%dUF;", axis, dval, axis);
break;
case ENABLE_TORQUE:
sprintf(buff, "MO;");
break;
case DISABL_TORQUE:
sprintf(buff, "MF;");
break;
case SET_HIGH_LIMIT:
motor_info = &(*trans->tabptr->card_array)[card]->motor_info[axis - 1];
trans->state = IDLE_STATE; /* No command sent to the controller. */
if (cntrl_units > motor_info->high_limit)
{
mr->dhlm = motor_info->high_limit;
rtnval = ERROR;
}
break;
case SET_LOW_LIMIT:
motor_info = &(*trans->tabptr->card_array)[card]->motor_info[axis - 1];
trans->state = IDLE_STATE; /* No command sent to the controller. */
if (cntrl_units < motor_info->low_limit)
{
mr->dllm = motor_info->low_limit;
rtnval = ERROR;
}
break;
default:
rtnval = ERROR;
}
size = strlen(buff);
if (size > sizeof(buff) || (strlen(motor_call->message) + size) > MAX_MSG_SIZE)
logMsg((char *) "devMM4000.c:MM4000_build_trans(): buffer overflow.\n",
0, 0, 0, 0, 0, 0);
else
strcat(motor_call->message, buff);
return(rtnval);
}
+6
View File
@@ -0,0 +1,6 @@
# Newport MM3000 and MM4000/5 Driver support.
device(motor,VME_IO,devMM3000,"MM3000")
device(motor,VME_IO,devMM4000,"MM4000")
driver(drvMM3000)
driver(drvMM4000)
+759
View File
@@ -0,0 +1,759 @@
/*
FILENAME... drvMM3000.c
USAGE... Motor record driver level support for Newport MM3000.
Version: $Revision: 1.1 $
Modified By: $Author: sluiter $
Last Modified: $Date: 2000-02-08 22:18:52 $
*/
/*
* Original Author: Mark Rivers
* Date: 10/20/97
* Current Author: Ron Sluiter
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 10-20-97 mlr initialized from drvOms58
* .02 10-30-97 mlr Replaced driver calls with gpipIO functions
* .03 10-30-98 mlr Minor code cleanup, improved formatting
* .04 02-01-99 mlr Added temporary fix to delay reading motor
* positions at the end of a move.
*/
#include <vxWorks.h>
#include <stdioLib.h>
#include <sysLib.h>
#include <string.h>
#include <taskLib.h>
#include <rngLib.h>
#include <alarm.h>
#include <dbDefs.h>
#include <dbAccess.h>
#include <fast_lock.h>
#include <recSup.h>
#include <devSup.h>
#include <errMdef.h>
#include <logLib.h>
#include "motor.h"
#include "drvMMCom.h"
#include "gpibIO.h"
#include "serialIO.h"
#define STATIC static
#define READ_RESOLUTION "TU;\r"
#define READ_STATUS "MS;\r"
#define READ_POSITION "TP;\r"
#define STOP_ALL "ST\r"
#define MOTOR_ON "MO;\r"
#define REMOTE_MODE "MR;\r"
#define GET_IDENT "VE\r"
#define INPUT_TERMINATOR '\n'
/* Status byte bits */
#define M_AXIS_MOVING 0x01
#define M_MOTOR_POWER 0x02
#define M_MOTOR_DIRECTION 0x04
#define M_PLUS_LIMIT 0x08
#define M_MINUS_LIMIT 0x10
#define M_HOME_SIGNAL 0x20
#define MM3000_NUM_CARDS 4
#define BUFF_SIZE 100 /* Maximum length of string to/from MM3000 */
/*----------------debugging-----------------*/
#ifdef DEBUG
#define Debug(l, f, args...) { if(l<=drvMM3000debug) printf(f,## args); }
#else
#define Debug(l, f, args...)
#endif
int MM3000_num_cards = 0;
volatile int drvMM3000debug = 0;
/* Local data required for every driver; see "motordrvComCode.h" */
#include "motordrvComCode.h"
/* This is a temporary fix to introduce a delayed reading of the motor
* position after a move completes
*/
volatile double drvMM3000ReadbackDelay = 0.;
/*----------------functions-----------------*/
STATIC int recv_mess(int card, char *com, int flag);
STATIC int send_mess(int card, char const *com, char c);
STATIC int set_status(int card, int signal);
static long report(int level);
static long init();
STATIC int motor_init();
STATIC void query_done(int, int, struct mess_node *);
/*----------------functions-----------------*/
struct driver_table MM3000_access =
{
motor_init,
motor_send,
motor_free,
motor_card_info,
motor_axis_info,
&mess_queue,
&queue_lock,
&free_list,
&freelist_lock,
&motor_sem,
&motor_state,
&total_cards,
&any_motor_in_motion,
send_mess,
recv_mess,
set_status,
query_done,
NULL,
&initialized
};
struct
{
long number;
#ifdef __cplusplus
long (*report) (int);
long (*init) (void);
#else
DRVSUPFUN report;
DRVSUPFUN init;
#endif
} drvMM3000 = {2, report, init};
/*********************************************************
* Print out driver status report
*********************************************************/
static long report(int level)
{
int card;
if (MM3000_num_cards <=0)
printf(" No MM3000 controllers configured.\n");
else
{
for (card = 0; card < MM3000_num_cards; card++)
{
struct controller *brdptr = motor_state[card];
if (brdptr == NULL)
printf(" MM3000 controller %d connection failed.\n", card);
else
{
struct MMcontroller *cntrl;
cntrl = (struct MMcontroller *) brdptr->DevicePrivate;
switch (cntrl->port_type)
{
case RS232_PORT:
printf(" MM3000 controller %d port type = RS-232, id: %s \n",
card,
brdptr->ident);
break;
case GPIB_PORT:
printf(" MM3000 controller %d port type = GPIB, id: %s \n",
card,
brdptr->ident);
break;
default:
printf(" MM3000 controller %d port type = Unknown, id: %s \n",
card,
brdptr->ident);
break;
}
}
}
}
return (0);
}
static long init()
{
/*
* We cannot call motor_init() here, because that function can do GPIB I/O,
* and hence requires that the drvGPIB have already been initialized.
* That cannot be guaranteed, so we need to call motor_init from device
* support
*/
/* Check for setup */
if (MM3000_num_cards <= 0)
{
Debug(1, "init(): MM3000 driver disabled. MM3000Setup() missing from startup script.\n");
}
return ((long) 0);
}
STATIC void query_done(int card, int axis, struct mess_node *nodeptr)
{
}
/**************************************************************
* Parse status and position strings for a card and signal
* set_status()
************************************************************/
STATIC int set_status(int card, int signal)
{
struct MMcontroller *cntrl;
struct mess_node *nodeptr;
register struct mess_info *motor_info;
/* Message parsing variables */
char *cptr, *tok_save;
char inbuff[BUFF_SIZE], outbuff[BUFF_SIZE];
int itera, status, rtn_state;
double motorData;
recv_mess(card, inbuff, FLUSH);
sprintf(outbuff, "%dMS\r", signal + 1);
send_mess(card, outbuff, NULL);
itera = 0;
do
{
recv_mess(card, inbuff, 1);
itera++;
if (itera < 10 && strlen(inbuff) == 0)
{
Debug(2, "set_status(): empty MS response message.\n");
send_mess(card, outbuff, NULL);
}
} while((strlen(inbuff) != 1) && itera < 10);
if (itera == 10)
{
Debug(4, "set_status(): MS response error.\n");
return(0);
}
status = inbuff[0];
Debug(5, "set_status(): status byte = %x\n", status);
cntrl = (struct MMcontroller *) motor_state[card]->DevicePrivate;
motor_info = &(motor_state[card]->motor_info[signal]);
nodeptr = motor_info->motor_motion;
if (status & M_MOTOR_DIRECTION)
motor_info->status |= RA_DIRECTION;
else
motor_info->status &= ~RA_DIRECTION;
if (status & M_AXIS_MOVING)
motor_info->status &= ~RA_DONE;
else
{
motor_info->status |= RA_DONE;
/* TEMPORARY FIX, Mark Rivers, 2/1/99. The MM3000 has reported that the
* motor is done moving, which means that the "jerk time" is done. However,
* the axis can still be settling. For now we put in a delay and poll the
* motor position again. This is not a good long-term solution.
*/
if (motor_info->pid_present == YES && drvMM3000ReadbackDelay != 0.)
{
taskDelay((int)(drvMM3000ReadbackDelay * sysClkRateGet()));
send_mess(card, READ_POSITION, NULL);
recv_mess(card, cntrl->position_string, 1);
}
}
if ((status & M_PLUS_LIMIT) || (status & M_MINUS_LIMIT))
motor_info->status |= RA_OVERTRAVEL;
else
motor_info->status &= ~RA_OVERTRAVEL;
if (status & M_HOME_SIGNAL)
motor_info->status |= RA_HOME;
else
motor_info->status &= ~RA_HOME;
if (status & M_MOTOR_POWER)
motor_info->status &= ~EA_POSITION;
else
motor_info->status |= EA_POSITION;
/* encoder status */
motor_info->status &= ~EA_SLIP;
motor_info->status &= ~EA_SLIP_STALL;
motor_info->status &= ~EA_HOME;
sprintf(outbuff, "%dTP\r", signal + 1);
send_mess(card, outbuff, NULL);
recv_mess(card, inbuff, 1);
tok_save = NULL;
cptr = strtok_r(inbuff, " ", &tok_save);
motorData = atof(cptr);
if (motorData == motor_info->position)
motor_info->no_motion_count++;
else
{
motor_info->position = (int32_t) motorData;
if (motor_state[card]->motor_info[signal].encoder_present == YES)
motor_info->encoder_position = (int32_t) motorData;
else
motor_info->encoder_position = 0;
motor_info->no_motion_count = 0;
}
motor_info->status &= ~RA_PROBLEM;
/* Parse motor velocity? */
/* NEEDS WORK */
motor_info->velocity = 0;
if (!(motor_info->status & RA_DIRECTION))
motor_info->velocity *= -1;
rtn_state = (!motor_info->no_motion_count ||
(motor_info->status & (RA_OVERTRAVEL | RA_DONE | RA_PROBLEM))) ? 1 : 0;
/* Test for post-move string. */
if ((motor_info->status & RA_DONE || motor_info->status & RA_OVERTRAVEL) &&
nodeptr != 0 && nodeptr->postmsgptr != 0)
{
strcpy(outbuff, nodeptr->postmsgptr);
strcat(outbuff, "\r");
send_mess(card, outbuff, NULL);
nodeptr->postmsgptr = NULL;
}
return (rtn_state);
}
/*****************************************************/
/* send a message to the MM3000 board */
/* send_mess() */
/*****************************************************/
STATIC int send_mess(int card, char const *com, char inchar)
{
struct MMcontroller *cntrl;
if (strlen(com) > MAX_MSG_SIZE)
{
logMsg((char *) "drvMM3000:send_mess(); message size violation.\n",
0, 0, 0, 0, 0, 0);
return (-1);
}
if (!motor_state[card])
{
logMsg((char *) "drvMM3000:send_mess() - invalid card #%d\n", card,
0, 0, 0, 0, 0);
return (-1);
}
if (inchar != (char) NULL)
{
logMsg((char *) "drvMM3000:send_mess() - invalid argument = %c\n", inchar,
0, 0, 0, 0, 0);
return (-1);
}
Debug(2, "send_mess(): message = %s\n", com);
cntrl = (struct MMcontroller *) motor_state[card]->DevicePrivate;
if (cntrl->port_type == GPIB_PORT)
gpibIOSend(cntrl->gpibInfo, com, strlen(com), GPIB_TIMEOUT);
else
serialIOSend(cntrl->serialInfo, com, strlen(com), SERIAL_TIMEOUT);
return (0);
}
/*
* FUNCTION... recv_mess(int card, char *com, int amount)
*
* INPUT ARGUMENTS...
* card - controller card # (0,1,...).
* *com - caller's response buffer.
* amount | -1 = flush controller's output buffer.
* | >= 1 = the # of command responses to retrieve into caller's
* response buffer.
*
* LOGIC...
* IF controller card does not exist.
* ERROR RETURN.
* ENDIF
* IF "amount" indicates buffer flush.
* WHILE characters left in input buffer.
* Call omsGet().
* ENDWHILE
* ENDIF
*
* FOR each message requested (i.e. "amount").
* Initialize head and tail pointers.
* Initialize retry counter and state indicator.
* WHILE retry count not exhausted, AND, state indicator is NOT at END.
* IF characters left in controller's input buffer.
* Process input character.
* ELSE IF command error occured - call omsError().
* ERROR RETURN.
* ENDIF
* ENDWHILE
* IF retry count exhausted.
* Terminate receive buffer.
* ERROR RETURN.
* ENDIF
* Terminate command response.
* ENDFOR
*
* IF commands processed.
* Terminate response buffer.
* ELSE
* Clear response buffer.
* ENDIF
* NORMAL RETURN.
*/
STATIC int recv_mess(int card, char *com, int amount)
{
struct MMcontroller *cntrl;
int len=0;
/* Check that card exists */
if (!motor_state[card])
return (-1);
cntrl = (struct MMcontroller *) motor_state[card]->DevicePrivate;
if (amount == -1)
{
do
{
if (cntrl->port_type == GPIB_PORT)
len = gpibIORecv(cntrl->gpibInfo, com, BUFF_SIZE, INPUT_TERMINATOR, NULL);
else
len = serialIORecv(cntrl->serialInfo, com, BUFF_SIZE, INPUT_TERMINATOR, NULL);
Debug(2, "recv_mess(): flushed message = \"%s\"\n", com);
} while(len != 0);
}
else
{
if (cntrl->port_type == GPIB_PORT)
len = gpibIORecv(cntrl->gpibInfo, com, BUFF_SIZE, INPUT_TERMINATOR, GPIB_TIMEOUT);
else
len = serialIORecv(cntrl->serialInfo, com, BUFF_SIZE, INPUT_TERMINATOR, SERIAL_TIMEOUT);
}
if (len == 0)
com[0] = '\0';
else
com[len-2] = '\0'; /* Terminate at carriage return. */
Debug(2, "recv_mess(): message = \"%s\"\n", com);
return (len);
}
/*****************************************************/
/* Setup system configuration */
/* MM3000Setup() */
/*****************************************************/
int MM3000Setup(int num_cards, /* maximum number of controllers in system. */
int num_channels, /* NOT Used. */
int scan_rate) /* polling rate - 1/60 sec units. */
{
int itera;
if (num_cards < 1 || num_cards > MM3000_NUM_CARDS)
MM3000_num_cards = MM3000_NUM_CARDS;
else
MM3000_num_cards = num_cards;
/* Set motor polling task rate */
if (scan_rate >= 1 && scan_rate <= sysClkRateGet())
motor_scan_rate = sysClkRateGet() / scan_rate;
else
motor_scan_rate = SCAN_RATE;
/*
* Allocate space for motor_state structures. Note this must be done
* before MM3000Config is called, so it cannot be done in motor_init()
* This means that we must allocate space for a card without knowing
* if it really exists, which is not a serious problem
*/
motor_state = (struct controller **) malloc(MM3000_num_cards *
sizeof(struct controller *));
for (itera = 0; itera < MM3000_num_cards; itera++)
motor_state[itera] = (struct controller *) NULL;
return (0);
}
/*****************************************************/
/* Configure a controller */
/* MM3000Config() */
/*****************************************************/
int MM3000Config(int card, /* card being configured */
int port_type, /* GPIB_PORT or RS232_PORT */
int addr1, /* = link for GPIB or hideos_card for RS-232 */
int addr2) /* GPIB address or hideos_task */
{
struct MMcontroller *cntrl;
if (card < 0 || card >= MM3000_num_cards)
return (ERROR);
motor_state[card] = (struct controller *) malloc(sizeof(struct controller));
motor_state[card]->DevicePrivate = malloc(sizeof(struct MMcontroller));
cntrl = (struct MMcontroller *) motor_state[card]->DevicePrivate;
switch (port_type)
{
case GPIB_PORT:
cntrl->port_type = port_type;
cntrl->gpib_link = addr1;
cntrl->gpib_address = addr2;
break;
case RS232_PORT:
cntrl->port_type = port_type;
cntrl->serial_card = addr1;
strcpy(cntrl->serial_task, (char *) addr2);
break;
default:
return (ERROR);
}
return (0);
}
/*****************************************************/
/* initialize all software and hardware */
/* This is called from the initialization routine in */
/* device support. */
/* motor_init() */
/*****************************************************/
STATIC int motor_init()
{
struct controller *brdptr;
struct MMcontroller *cntrl;
int card_index, motor_index, arg3, arg4;
char axis_pos[BUFF_SIZE];
char buff[BUFF_SIZE];
char *tok_save, *bufptr;
int total_axis = 0;
int status;
BOOLEAN errind;
initialized = ON; /* Indicate that driver is initialized. */
/* Check for setup */
if (MM3000_num_cards <= 0)
return (ERROR);
tok_save = NULL;
for (card_index = 0; card_index < MM3000_num_cards; card_index++)
{
if (!motor_state[card_index])
continue;
brdptr = motor_state[card_index];
total_cards = card_index + 1;
cntrl = (struct MMcontroller *) brdptr->DevicePrivate;
/* Initialize communications channel */
errind = OFF;
switch (cntrl->port_type)
{
case GPIB_PORT:
cntrl->gpibInfo = gpibIOInit(cntrl->gpib_link,
cntrl->gpib_address);
if (cntrl->gpibInfo == NULL)
errind = ON;
break;
case RS232_PORT:
cntrl->serialInfo = serialIOInit(cntrl->serial_card,
cntrl->serial_task);
if (cntrl->serialInfo == NULL)
errind = ON;
break;
}
if (errind == OFF)
{
/* Send a message to the board, see if it exists */
/* flush any junk at input port - should not be any data available */
do
recv_mess(card_index, buff, FLUSH);
while (strlen(buff) != 0);
send_mess(card_index, GET_IDENT, NULL);
status = recv_mess(card_index, axis_pos, 1);
/* Return value is length of response string */
}
if (errind == OFF && status > 0)
{
brdptr->localaddr = (char *) NULL;
brdptr->motor_in_motion = 0;
send_mess(card_index, STOP_ALL, NULL); /* Stop all motors */
send_mess(card_index, GET_IDENT, NULL); /* Read controller ID string */
recv_mess(card_index, buff, 1);
strcpy(brdptr->ident, &buff[0]); /* Skip "VE" */
send_mess(card_index, "RC\r", NULL);
recv_mess(card_index, buff, 1);
bufptr = strtok_r(buff, "=", &tok_save);
bufptr = strtok_r(NULL, " ", &tok_save);
/* The return string will tell us how many axes this controller has */
for (total_axis = 0; bufptr != NULL; total_axis++)
{
if (strcmp(bufptr, "unused") == 0)
{
cntrl->type[total_axis] = UNUSED;
bufptr = NULL;
}
else
{
if (strcmp(bufptr, "stepper1.5M") == 0)
cntrl->type[total_axis] = STEPPER;
else if (strcmp(bufptr, "dc") == 0)
cntrl->type[total_axis] = DC;
else
logMsg((char *) "drvMM3000:motor_init() - invalid RC response = %s\n",
bufptr, 0, 0, 0, 0, 0);
bufptr = strtok_r(NULL, "=", &tok_save);
bufptr = strtok_r(NULL, " ", &tok_save);
}
/* Initialize. */
brdptr->motor_info[total_axis].motor_motion = NULL;
}
brdptr->total_axis = total_axis;
for (motor_index = 0; motor_index < total_axis; motor_index++)
{
struct mess_info *motor_info = &brdptr->motor_info[motor_index];
motor_info->status = 0;
motor_info->no_motion_count = 0;
motor_info->encoder_position = 0;
motor_info->position = 0;
if (cntrl->type[total_axis] == DC)
motor_info->encoder_present = YES;
else
{
double pre_position, mod_position;
int fm_value;
sprintf(buff, "%dFM?\r", motor_index + 1);
send_mess(card_index, buff, NULL);
recv_mess(card_index, buff, 1);
fm_value = atoi(&buff[3]);
sprintf(buff, "%dTP\r", motor_index + 1);
send_mess(card_index, buff, NULL);
recv_mess(card_index, buff, 1);
pre_position = atof(&buff[3]);
fm_value = (fm_value == 0) ? 1 : 0;
sprintf(buff, "%dFM%d\r", motor_index + 1, fm_value);
send_mess(card_index, buff, NULL);
sprintf(buff, "%dTP\r", motor_index + 1);
send_mess(card_index, buff, NULL);
recv_mess(card_index, buff, 1);
mod_position = atof(&buff[3]);
fm_value = (fm_value == 0) ? 1 : 0;
sprintf(buff, "%dFM%d\r", motor_index + 1, fm_value);
send_mess(card_index, buff, NULL);
if (pre_position == mod_position)
motor_info->encoder_present = NO;
else
motor_info->encoder_present = YES;
}
if (motor_info->encoder_present == YES)
{
motor_info->status |= EA_PRESENT;
motor_info->pid_present = YES;
motor_info->status |= GAIN_SUPPORT;
}
set_status(card_index, motor_index); /* Read status of each motor */
}
}
else
motor_state[card_index] = (struct controller *) NULL;
}
motor_sem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);
any_motor_in_motion = 0;
FASTLOCKINIT(&queue_lock);
FASTLOCK(&queue_lock);
mess_queue.head = (struct mess_node *) NULL;
mess_queue.tail = (struct mess_node *) NULL;
FASTUNLOCK(&queue_lock);
FASTLOCKINIT(&freelist_lock);
FASTLOCK(&freelist_lock);
free_list.head = (struct mess_node *) NULL;
free_list.tail = (struct mess_node *) NULL;
FASTUNLOCK(&freelist_lock);
if (sizeof(int) >= sizeof(char *))
{
arg3 = (int) (&MM3000_access);
arg4 = 0;
}
else
{
arg3 = (int) ((long) &MM3000_access >> 16);
arg4 = (int) ((long) &MM3000_access & 0xFFFF);
}
taskSpawn((char *) "MM3000_motor", 64, VX_FP_TASK | VX_STDIO, 5000, motor_task,
motor_scan_rate, arg3, arg4, 0, 0, 0, 0, 0, 0, 0);
return (0);
}
/*---------------------------------------------------------------------*/
+769
View File
@@ -0,0 +1,769 @@
/*
FILENAME... drvMM4000.c
USAGE... Motor record driver level support for Newport MM4000.
Version: $Revision: 1.1 $
Modified By: $Author: sluiter $
Last Modified: $Date: 2000-02-08 22:18:53 $
*/
/*
* Original Author: Mark Rivers
* Date: 10/20/97
* Current Author: Ron Sluiter
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 10-20-97 mlr initialized from drvOms58
* .02 10-30-97 mlr Replaced driver calls with gpipIO functions
* .03 10-30-98 mlr Minor code cleanup, improved formatting
* .04 02-01-99 mlr Added temporary fix to delay reading motor
* positions at the end of a move.
* .05 10-13-99 rls modified for standardized motor record.
*/
#include <vxWorks.h>
#include <stdioLib.h>
#include <sysLib.h>
#include <string.h>
#include <taskLib.h>
#include <math.h>
#include <rngLib.h>
#include <alarm.h>
#include <dbDefs.h>
#include <dbAccess.h>
#include <fast_lock.h>
#include <recSup.h>
#include <devSup.h>
#include <errMdef.h>
#include <logLib.h>
#include "motor.h"
#include "drvMMCom.h"
#include "gpibIO.h"
#include "serialIO.h"
#define STATIC static
#define READ_RESOLUTION "TU;\r"
#define READ_STATUS "MS;\r"
#define READ_POSITION "TP;\r"
#define STOP_ALL "ST;\r"
#define MOTOR_ON "MO;\r"
#define GET_IDENT "VE;\r"
#define INPUT_TERMINATOR '\r'
/* Status byte bits */
#define M_AXIS_MOVING 0x01
#define M_MOTOR_POWER 0x02
#define M_MOTOR_DIRECTION 0x04
#define M_PLUS_LIMIT 0x08
#define M_MINUS_LIMIT 0x10
#define M_HOME_SIGNAL 0x20
#define MM4000_NUM_CARDS 4
#define BUFF_SIZE 100 /* Maximum length of string to/from MM4000 */
/*----------------debugging-----------------*/
#ifdef DEBUG
#define Debug(l, f, args...) { if(l<=drvMM4000debug) printf(f,## args); }
#else
#define Debug(l, f, args...)
#endif
/* --- Local data. --- */
int MM4000_num_cards = 0;
volatile int drvMM4000debug = 0;
/* Local data required for every driver; see "motordrvComCode.h" */
#include "motordrvComCode.h"
/* This is a temporary fix to introduce a delayed reading of the motor
* position after a move completes
*/
volatile double drvMM4000ReadbackDelay = 0.;
/*----------------functions-----------------*/
STATIC int recv_mess(int, char *, int);
STATIC int send_mess(int card, char const *com, char c);
STATIC void start_status(int card);
STATIC int set_status(int card, int signal);
static long report(int level);
static long init();
STATIC int motor_init();
STATIC void query_done(int, int, struct mess_node *);
/*----------------functions-----------------*/
struct driver_table MM4000_access =
{
motor_init,
motor_send,
motor_free,
motor_card_info,
motor_axis_info,
&mess_queue,
&queue_lock,
&free_list,
&freelist_lock,
&motor_sem,
&motor_state,
&total_cards,
&any_motor_in_motion,
send_mess,
recv_mess,
set_status,
query_done,
start_status,
&initialized
};
struct
{
long number;
#ifdef __cplusplus
long (*report) (int);
long (*init) (void);
#else
DRVSUPFUN report;
DRVSUPFUN init;
#endif
} drvMM4000 = {2, report, init};
/*********************************************************
* Print out driver status report
*********************************************************/
static long report(int level)
{
int card;
if (MM4000_num_cards <=0)
printf(" No MM4000 controllers configured.\n");
else
{
for (card = 0; card < MM4000_num_cards; card++)
{
struct controller *brdptr = motor_state[card];
if (brdptr == NULL)
printf(" MM4000 controller %d connection failed.\n", card);
else
{
struct MMcontroller *cntrl;
cntrl = (struct MMcontroller *) brdptr->DevicePrivate;
switch (cntrl->port_type)
{
case RS232_PORT:
printf(" MM4000 controller %d port type = RS-232, id: %s \n",
card,
brdptr->ident);
break;
case GPIB_PORT:
printf(" MM4000 controller %d port type = GPIB, id: %s \n",
card,
brdptr->ident);
break;
default:
printf(" MM4000 controller %d port type = Unknown, id: %s \n",
card,
brdptr->ident);
break;
}
}
}
}
return (0);
}
static long init()
{
/*
* We cannot call motor_init() here, because that function can do GPIB I/O,
* and hence requires that the drvGPIB have already been initialized.
* That cannot be guaranteed, so we need to call motor_init from device
* support
*/
/* Check for setup */
if (MM4000_num_cards <= 0)
{
Debug(1, "init(): MM4000 driver disabled. MM4000Setup() missing from startup script.\n");
}
return ((long) 0);
}
STATIC void query_done(int card, int axis, struct mess_node *nodeptr)
{
}
/*********************************************************
* Read the status and position of all motors on a card
* start_status(int card)
* if card == -1 then start all cards
*********************************************************/
STATIC void start_status(int card)
{
struct MMcontroller *cntrl;
int itera, status;
if (card >= 0)
{
cntrl = (struct MMcontroller *) motor_state[card]->DevicePrivate;
send_mess(card, READ_STATUS, NULL);
status = recv_mess(card, cntrl->status_string, 1);
if (status <= 0)
cntrl->status = COMM_ERR;
else
{
cntrl->status = NORMAL;
send_mess(card, READ_POSITION, NULL);
recv_mess(card, cntrl->position_string, 1);
}
}
else
{
/*
* For efficiency we send messages to all cards, then read all
* responses. This minimizes the latency due to processing on each card
*/
for (itera = 0; (itera < total_cards) && motor_state[itera]; itera++)
send_mess(itera, READ_STATUS, NULL);
for (itera = 0; (itera < total_cards) && motor_state[itera]; itera++)
{
cntrl = (struct MMcontroller *) motor_state[itera]->DevicePrivate;
status = recv_mess(itera, cntrl->status_string, 1);
if (status <= 0)
cntrl->status = COMM_ERR;
else
cntrl->status = NORMAL;
}
for (itera = 0; (itera < total_cards) && motor_state[itera]; itera++)
send_mess(itera, READ_POSITION, NULL);
for (itera = 0; (itera < total_cards) && motor_state[itera]; itera++)
{
cntrl = (struct MMcontroller *) motor_state[itera]->DevicePrivate;
recv_mess(itera, cntrl->position_string, 1);
}
}
}
/**************************************************************
* Parse status and position strings for a card and signal
* set_status()
************************************************************/
STATIC int set_status(int card, int signal)
{
struct MMcontroller *cntrl;
struct mess_node *nodeptr;
register struct mess_info *motor_info;
/* Message parsing variables */
char *p, *tok_save;
char buff[BUFF_SIZE];
int i, pos, status;
int rtn_state;
double motorData;
cntrl = (struct MMcontroller *) motor_state[card]->DevicePrivate;
motor_info = &(motor_state[card]->motor_info[signal]);
nodeptr = motor_info->motor_motion;
/*
* Parse the status string
* Status string format: 1MSx,2MSy,3MSz,... where x, y and z are the status
* bytes for the motors
*/
pos = signal*5 + 3; /* Offset in status string */
status = cntrl->status_string[pos];
Debug(5, "set_status(): status byte = %x\n", status);
if (status & M_MOTOR_DIRECTION)
motor_info->status |= RA_DIRECTION;
else
motor_info->status &= ~RA_DIRECTION;
if (status & M_AXIS_MOVING)
motor_info->status &= ~RA_DONE;
else
{
motor_info->status |= RA_DONE;
/* TEMPORARY FIX, Mark Rivers, 2/1/99. The MM4000 has reported that the
* motor is done moving, which means that the "jerk time" is done. However,
* the axis can still be settling. For now we put in a delay and poll the
* motor position again. This is not a good long-term solution.
*/
if (motor_info->pid_present == YES && drvMM4000ReadbackDelay != 0.)
{
taskDelay((int)(drvMM4000ReadbackDelay * sysClkRateGet()));
send_mess(card, READ_POSITION, NULL);
recv_mess(card, cntrl->position_string, 1);
}
}
if ((status & M_PLUS_LIMIT) || (status & M_MINUS_LIMIT))
motor_info->status |= RA_OVERTRAVEL;
else
motor_info->status &= ~RA_OVERTRAVEL;
if (status & M_HOME_SIGNAL)
motor_info->status |= RA_HOME;
else
motor_info->status &= ~RA_HOME;
if (status & M_MOTOR_POWER)
motor_info->status &= ~EA_POSITION;
else
motor_info->status |= EA_POSITION;
/* encoder status */
motor_info->status &= ~EA_SLIP;
motor_info->status &= ~EA_SLIP_STALL;
motor_info->status &= ~EA_HOME;
/*
* Parse motor position
* Position string format: 1TP5.012,2TP1.123,3TP-100.567,...
* Skip to substring for this motor, convert to double
*/
strcpy(buff, cntrl->position_string);
tok_save = NULL;
p = strtok_r(buff, ",", &tok_save);
for (i=0; i<signal; i++) p = strtok_r(NULL, ",", &tok_save);
Debug(6, "set_status(): position substring = %s\n", p);
motorData = atof(p+3) / cntrl->drive_resolution[signal];
if (motorData == motor_info->position)
motor_info->no_motion_count++;
else
{
motor_info->position = NINT(motorData);
if (motor_state[card]->motor_info[signal].encoder_present == YES)
motor_info->encoder_position = (int32_t) motorData;
else
motor_info->encoder_position = 0;
motor_info->no_motion_count = 0;
}
motor_info->status &= ~RA_PROBLEM;
/* Parse motor velocity? */
/* NEEDS WORK */
motor_info->velocity = 0;
if (!(motor_info->status & RA_DIRECTION))
motor_info->velocity *= -1;
rtn_state = (!motor_info->no_motion_count ||
(motor_info->status & (RA_OVERTRAVEL | RA_DONE | RA_PROBLEM))) ? 1 : 0;
/* Test for post-move string. */
if ((motor_info->status & RA_DONE || motor_info->status & RA_OVERTRAVEL) &&
nodeptr != 0 && nodeptr->postmsgptr != 0)
{
strcpy(buff, nodeptr->postmsgptr);
strcat(buff, "\r");
send_mess(card, buff, NULL);
nodeptr->postmsgptr = NULL;
}
if (cntrl->status == COMM_ERR)
motor_info->status |= CNTRL_COMM_ERR;
else
motor_info->status &= ~CNTRL_COMM_ERR;
return(rtn_state);
}
/*****************************************************/
/* send a message to the MM4000 board */
/* send_mess() */
/*****************************************************/
STATIC int send_mess(int card, char const *com, char inchar)
{
struct MMcontroller *cntrl;
if (strlen(com) > MAX_MSG_SIZE)
{
logMsg((char *) "drvMM4000.c:send_mess(); message size violation.\n",
0, 0, 0, 0, 0, 0);
return (-1);
}
if (!motor_state[card])
{
logMsg((char *) "drvMM4000.c:send_mess() - invalid card #%d\n", card,
0, 0, 0, 0, 0);
return (-1);
}
if (inchar != (char) NULL)
{
logMsg((char *) "drvMM4000.c:send_mess() - invalid argument = %c\n", inchar,
0, 0, 0, 0, 0);
return (-1);
}
Debug(2, "send_mess(): message = %s\n", com);
cntrl = (struct MMcontroller *) motor_state[card]->DevicePrivate;
switch (cntrl->port_type)
{
case GPIB_PORT:
gpibIOSend(cntrl->gpibInfo, com, strlen(com), GPIB_TIMEOUT);
break;
case RS232_PORT:
serialIOSend(cntrl->serialInfo, com, strlen(com), SERIAL_TIMEOUT);
break;
}
return (0);
}
/*****************************************************/
/* receive a message from the MM4000 board */
/* recv_mess() */
/*****************************************************/
STATIC int recv_mess(int card, char *com, int flag)
{
struct MMcontroller *cntrl;
int timeout;
int len=0;
/* Check that card exists */
if (!motor_state[card])
return (-1);
cntrl = (struct MMcontroller *) motor_state[card]->DevicePrivate;
switch (cntrl->port_type)
{
case GPIB_PORT:
if (flag == FLUSH)
timeout = 0;
else
timeout = GPIB_TIMEOUT;
len = gpibIORecv(cntrl->gpibInfo, com, BUFF_SIZE, INPUT_TERMINATOR,
timeout);
break;
case RS232_PORT:
if (flag == FLUSH)
timeout = 0;
else
timeout = SERIAL_TIMEOUT;
len = serialIORecv(cntrl->serialInfo, com, BUFF_SIZE,
INPUT_TERMINATOR, timeout);
break;
}
if (len == 0)
com[0] = '\0';
else
com[len-1] = '\0';
Debug(2, "recv_mess(): message = \"%s\"\n", com);
return (len);
}
/*****************************************************/
/* Setup system configuration */
/* MM4000Setup() */
/*****************************************************/
int MM4000Setup(int num_cards, /* maximum number of controllers in system. */
int num_channels, /* NOT Used. */
int scan_rate) /* polling rate - 1/60 sec units. */
{
int itera;
if (num_cards < 1 || num_cards > MM4000_NUM_CARDS)
MM4000_num_cards = MM4000_NUM_CARDS;
else
MM4000_num_cards = num_cards;
/* Set motor polling task rate */
if (scan_rate >= 1 && scan_rate <= sysClkRateGet())
motor_scan_rate = sysClkRateGet() / scan_rate;
else
motor_scan_rate = SCAN_RATE;
/*
* Allocate space for motor_state structures. Note this must be done
* before MM4000Config is called, so it cannot be done in motor_init()
* This means that we must allocate space for a card without knowing
* if it really exists, which is not a serious problem
*/
motor_state = (struct controller **) malloc(MM4000_num_cards *
sizeof(struct controller *));
for (itera = 0; itera < MM4000_num_cards; itera++)
motor_state[itera] = (struct controller *) NULL;
return (0);
}
/*****************************************************/
/* Configure a controller */
/* MM4000Config() */
/*****************************************************/
int MM4000Config(int card, /* card being configured */
int port_type, /* GPIB_PORT or RS232_PORT */
int addr1, /* = link for GPIB or hideos_card for RS-232 */
int addr2) /* GPIB address or hideos_task */
{
struct MMcontroller *cntrl;
if (card < 0 || card >= MM4000_num_cards)
return (ERROR);
motor_state[card] = (struct controller *) malloc(sizeof(struct controller));
motor_state[card]->DevicePrivate = malloc(sizeof(struct MMcontroller));
cntrl = (struct MMcontroller *) motor_state[card]->DevicePrivate;
switch (port_type)
{
case GPIB_PORT:
cntrl->port_type = port_type;
cntrl->gpib_link = addr1;
cntrl->gpib_address = addr2;
break;
case RS232_PORT:
cntrl->port_type = port_type;
cntrl->serial_card = addr1;
strcpy(cntrl->serial_task, (char *) addr2);
break;
default:
return (ERROR);
}
return (0);
}
/*****************************************************/
/* initialize all software and hardware */
/* This is called from the initialization routine in */
/* device support. */
/* motor_init() */
/*****************************************************/
STATIC int motor_init()
{
struct controller *brdptr;
struct MMcontroller *cntrl;
int card_index, motor_index, arg3, arg4;
char axis_pos[BUFF_SIZE];
char buff[BUFF_SIZE];
char *tok_save, *pos_ptr;
int total_axis = 0;
int status, model_num, digits;
BOOLEAN errind;
initialized = ON; /* Indicate that driver is initialized. */
/* Check for setup */
if (MM4000_num_cards <= 0)
return (ERROR);
for (card_index = 0; card_index < MM4000_num_cards; card_index++)
{
if (!motor_state[card_index])
continue;
brdptr = motor_state[card_index];
total_cards = card_index + 1;
cntrl = (struct MMcontroller *) brdptr->DevicePrivate;
/* Initialize communications channel */
errind = OFF;
switch (cntrl->port_type)
{
case GPIB_PORT:
cntrl->gpibInfo = gpibIOInit(cntrl->gpib_link,
cntrl->gpib_address);
if (cntrl->gpibInfo == NULL)
errind = ON;
break;
case RS232_PORT:
cntrl->serialInfo = serialIOInit(cntrl->serial_card,
cntrl->serial_task);
if (cntrl->serialInfo == NULL)
errind = ON;
break;
}
if (errind == OFF)
{
int retry = 0;
/* Send a message to the board, see if it exists */
/* flush any junk at input port - should not be any data available */
do
recv_mess(card_index, buff, FLUSH);
while (strlen(buff) != 0);
do
{
send_mess(card_index, READ_POSITION, NULL);
status = recv_mess(card_index, axis_pos, 1);
retry++;
/* Return value is length of response string */
} while(status == 0 && retry < 3);
}
if (errind == OFF && status > 0)
{
brdptr->localaddr = (char *) NULL;
brdptr->motor_in_motion = 0;
send_mess(card_index, STOP_ALL, NULL); /* Stop all motors */
send_mess(card_index, GET_IDENT, NULL); /* Read controller ID string */
recv_mess(card_index, buff, 1);
strcpy(brdptr->ident, &buff[2]); /* Skip "VE" */
/* Set Motion Master model indicator. */
pos_ptr = strstr(brdptr->ident, "MM");
model_num = atoi(pos_ptr + 2);
if (model_num == 4000)
cntrl->model = MM4000;
else if (model_num == 4005)
cntrl->model = MM4005;
else
{
logMsg((char *) "drvMM4000.c:motor_init() - invalid model = %s\n", (int) brdptr->ident,
0, 0, 0, 0, 0);
return (ERROR);
}
send_mess(card_index, READ_POSITION, NULL);
recv_mess(card_index, axis_pos, 1);
/* The return string will tell us how many axes this controller has */
for (total_axis = 0, tok_save = NULL, pos_ptr = strtok_r(axis_pos, ",", &tok_save);
pos_ptr != 0; pos_ptr = strtok_r(NULL, ",", &tok_save), total_axis++)
brdptr->motor_info[total_axis].motor_motion = NULL;
brdptr->total_axis = total_axis;
start_status(card_index);
for (motor_index = 0; motor_index < total_axis; motor_index++)
{
struct mess_info *motor_info = &brdptr->motor_info[motor_index];
int loop_state;
motor_info->status = 0;
motor_info->no_motion_count = 0;
motor_info->encoder_position = 0;
motor_info->position = 0;
/* Determine if encoder present based on open/closed loop mode. */
sprintf(buff, "%dTC\r", motor_index + 1);
send_mess(card_index, buff, NULL);
recv_mess(card_index, buff, 1);
loop_state = atoi(&buff[3]); /* Skip first 3 characters */
if (loop_state != 0)
{
motor_info->encoder_present = YES;
motor_info->status |= EA_PRESENT;
motor_info->pid_present = YES;
motor_info->status |= GAIN_SUPPORT;
}
/* Determine drive resolution. */
sprintf(buff, "%dTU\r", motor_index + 1);
send_mess(card_index, buff, NULL);
recv_mess(card_index, buff, 1);
cntrl->drive_resolution[motor_index] = atof(&buff[3]);
digits = -log10(cntrl->drive_resolution[motor_index]) + 2;
if (digits < 1)
digits = 1;
cntrl->res_decpts[motor_index] = digits;
/* Save home preset position. */
sprintf(buff, "%dXH\r", motor_index + 1);
send_mess(card_index, buff, NULL);
recv_mess(card_index, buff, 1);
cntrl->home_preset[motor_index] = atof(&buff[3]);
/* Determine low limit */
sprintf(buff, "%dTL\r", motor_index + 1);
send_mess(card_index, buff, NULL);
recv_mess(card_index, buff, 1);
motor_info->low_limit = atof(&buff[3]);
/* Determine high limit */
sprintf(buff, "%dTR\r", motor_index + 1);
send_mess(card_index, buff, NULL);
recv_mess(card_index, buff, 1);
motor_info->high_limit = atof(&buff[3]);
set_status(card_index, motor_index); /* Read status of each motor */
}
}
else
motor_state[card_index] = (struct controller *) NULL;
}
motor_sem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);
any_motor_in_motion = 0;
FASTLOCKINIT(&queue_lock);
FASTLOCK(&queue_lock);
mess_queue.head = (struct mess_node *) NULL;
mess_queue.tail = (struct mess_node *) NULL;
FASTUNLOCK(&queue_lock);
FASTLOCKINIT(&freelist_lock);
FASTLOCK(&freelist_lock);
free_list.head = (struct mess_node *) NULL;
free_list.tail = (struct mess_node *) NULL;
FASTUNLOCK(&freelist_lock);
if (sizeof(int) >= sizeof(char *))
{
arg3 = (int) (&MM4000_access);
arg4 = 0;
}
else
{
arg3 = (int) ((long) &MM4000_access >> 16);
arg4 = (int) ((long) &MM4000_access & 0xFFFF);
}
taskSpawn((char *) "MM4000_motor", 64, VX_FP_TASK | VX_STDIO, 5000, motor_task,
motor_scan_rate, arg3, arg4, 0, 0, 0, 0, 0, 0, 0);
return (0);
}
+104
View File
@@ -0,0 +1,104 @@
/*
FILENAME... drvMMCom.h
USAGE... This file contains Newport Motion Master (MM) driver "include"
information that is specific to Motion Master models 3000/4000.
Version: $Revision: 1.1 $
Modified By: $Author: sluiter $
Last Modified: $Date: 2000-02-08 22:18:53 $
*/
/*
* Original Author: Mark Rivers
* Current Author: Mark Rivers
* Date: 10/16/97
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 01-18-93 mlr initialized from drvOms58
*/
#ifndef INCdrvMMComh
#define INCdrvMMComh 1
#include "motordrvCom.h"
#define MAX_UNIT_LENGTH 5 /* Maximum length of controller position units string. */
#define GPIB_PORT 0
#define RS232_PORT 1
#define GPIB_TIMEOUT 2000 /* Command timeout in msec */
#define SERIAL_TIMEOUT 2000 /* Command timeout in msec */
enum MM_model
{
MM4000,
MM4005
};
enum MM_motor_type
{
UNUSED,
STEPPER,
DC
};
enum MM_comm_status
{
NORMAL,
COMM_ERR
};
#ifndef __cplusplus
typedef enum MM_model MM_model;
typedef enum MM_motor_type MM_motor_type;
typedef enum MM_comm_status MM_comm_status;
#endif
/* Motion Master specific data is stored in this structure. */
struct MMcontroller
{
int port_type; /* GPIB_PORT or RS232_PORT */
struct serialInfo *serialInfo; /* For RS-232 */
int gpib_link;
int gpib_address;
struct gpibInfo *gpibInfo; /* For GPIB */
int serial_card; /* Card on which Hideos is running */
char serial_task[20]; /* Hideos task name for serial port */
char status_string[80]; /* String containing status of motors */
char position_string[80]; /* String containing position of motors */
MM_model model; /* Motion Master Model. */
MM_motor_type type[4]; /* For MM3000 only; Motor type array. */
/* For MM4000/5 only; controller resolution array (from TU command).
* Units are in [Controller EGU's / Record RAW units].
*/
double drive_resolution[MAX_AXIS];
int res_decpts[MAX_AXIS]; /* Drive resolution significant dec. pts. */
double home_preset[MAX_AXIS]; /* Controller's home preset position (XF command). */
MM_comm_status status; /* Controller communication status. */
};
#endif /* INCdrvMMComh */
+154
View File
@@ -0,0 +1,154 @@
/* gpibIO.c - Routines for asynchronous EPICS GPIB I/O
*
* Author: Mark Rivers
* Date: October 30, 1997
*
* Modification Log:
* -----------------
* .01 30-Oct-1997 mlr Initial release
*
* These routines provide a simple interface to GPIB I/O. They hide the
* details of the driver. They are intended for use by applications which do
* simple synchronous reads and writes, and no control operations like Serial
* Poll.
*/
#include <stdlib.h>
#include <stdio.h>
#include <vxWorks.h>
#include <gpibIO.h>
#define GPIB_SEND 0
#define GPIB_RECEIVE 1
#ifdef NODEBUG
#define Debug(l,f,v) ;
#else
#define Debug(l,f,v) { if(l<=gpibIODebug) printf(f,v); }
#endif
volatile int gpibIODebug = 0;
extern struct drvGpibSet drvGpib; /* entry points to driver functions */
struct gpibMessage {
struct dpvtGpibHead head;
int function;
int address;
int timeout;
int status;
int buff_len;
int terminator;
SEM_ID semID;
char *buffer;
};
static int gpibIOCallback(struct gpibMessage *gpibMessage);
struct gpibInfo *gpibIOInit(int link, int address)
{
struct gpibInfo *gpibInfo;
gpibInfo = (struct gpibInfo *) malloc(sizeof(struct gpibInfo));
gpibInfo->head.workStart = (int (*)()) gpibIOCallback;
gpibInfo->head.link = link;
gpibInfo->head.device = address;
gpibInfo->address = address;
gpibInfo->semID = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);
(*(drvGpib.ioctl))(GPIB_IO, link, NULL, IBGENLINK, 0, NULL);
(*(drvGpib.ioctl))(GPIB_IO, link, NULL, IBGETLINK, 0, &gpibInfo->head.pibLink);
Debug(1, "gpibIOInit, address = %d\n", address);
return(gpibInfo);
}
int gpibIOSend(struct gpibInfo *info, char const *buffer, int buff_len, int timeout)
{
struct gpibMessage *gpibMessage;
int status;
gpibMessage = (struct gpibMessage *) malloc(sizeof(struct gpibMessage));
gpibMessage->address = info->address;
gpibMessage->buffer = (char *) buffer;
gpibMessage->buff_len = buff_len;
gpibMessage->timeout = timeout;
gpibMessage->function = GPIB_SEND;
gpibMessage->head = info->head;
gpibMessage->semID = info->semID;
Debug(2, "gpibIOSend, calling driver, buffer = %s\n", buffer);
(*(drvGpib.qGpibReq))(gpibMessage, IB_Q_LOW);
semTake(gpibMessage->semID, WAIT_FOREVER);
status = gpibMessage->status;
free(gpibMessage);
return (status);
}
int gpibIORecv(struct gpibInfo *info, char *buffer, int buff_len,
int terminator, int timeout)
{
struct gpibMessage *gpibMessage;
int status;
gpibMessage = (struct gpibMessage *) malloc(sizeof(struct gpibMessage));
gpibMessage->address = info->address;
gpibMessage->buffer = buffer;
gpibMessage->buff_len = buff_len;
gpibMessage->terminator = terminator;
gpibMessage->timeout = timeout;
gpibMessage->function = GPIB_RECEIVE;
gpibMessage->head = info->head;
gpibMessage->semID = info->semID;
Debug(2, "gpibIORecv, calling driver, address = %d\n", info->address);
(*(drvGpib.qGpibReq))(gpibMessage, IB_Q_LOW);
semTake(gpibMessage->semID, WAIT_FOREVER);
status = gpibMessage->status;
free(gpibMessage);
return (status);
}
static int gpibIOCallback(struct gpibMessage *gpibMessage)
{
int timeout;
int nrequest, nactual;
timeout = gpibMessage->timeout * sysClkRateGet() / 1000;
if (timeout < 1) timeout = 1;
switch(gpibMessage->function)
{
case GPIB_SEND:
Debug(2, "gpibIOCallback, GPIB_SEND, message=%s\n",
gpibMessage->buffer);
/* write the message to the GPIB listen address */
nrequest = gpibMessage->buff_len;
nactual =(*(drvGpib.writeIb))(gpibMessage->head.pibLink,
gpibMessage->address, gpibMessage->buffer,
nrequest, timeout);
if (nactual != nrequest) {
/* Something is wrong if we couldn't write everything */
Debug(1, "Error writing GPIB, requested length=%d\n", nrequest);
Debug(1, " actual length=%d\n", nactual);
}
gpibMessage->status = nactual;
semGive(gpibMessage->semID); /* gpibIOSend is waiting for this */
break;
case GPIB_RECEIVE:
/* Read a message from the GPIB listen address */
nrequest = gpibMessage->buff_len;
nactual = (*(drvGpib.readIbEos))(gpibMessage->head.pibLink,
gpibMessage->address, gpibMessage->buffer,
nrequest, timeout, gpibMessage->terminator);
/* Append a null terminator if there is room */
if (nactual < nrequest) gpibMessage->buffer[nactual]='\0';
Debug(2, "gpibIOCallback, GPIB_RECEIVE, message=%s\n",
gpibMessage->buffer);
Debug(2, "gpibIOCallback, GPIB_RECEIVE, nrequest=%d\n", nrequest);
Debug(2, "gpibIOCallback, GPIB_RECEIVE, nactual=%d\n", nactual);
gpibMessage->status = nactual;
semGive(gpibMessage->semID); /* gpibIORecv is waiting for this */
break;
}
return (0);
}
+15
View File
@@ -0,0 +1,15 @@
#include <rngLib.h>
#include <dbCommon.h>
#include <drvSup.h>
#include <drvGpibInterface.h>
struct gpibInfo
{
struct dpvtGpibHead head;
int address;
SEM_ID semID;
};
struct gpibInfo *gpibIOInit(int link, int address);
int gpibIOSend(struct gpibInfo *info, char const *buffer, int buff_len, int timeout);
int gpibIORecv(struct gpibInfo *info, char *buffer, int buff_len,
int terminator, int timeout);
+148
View File
@@ -0,0 +1,148 @@
/*
* These routines provide a simple interface to Hideos serial I/O. They hide
* the details of Hideos. They are intended for use by applications which do
* simple synchronous reads and writes, and no control operations like setting
* baud rates, etc.
*/
#include "serialIO.h"
#ifdef DEBUG
#define Debug(l, f, args...) {if (l <= serialIODebug) printf(f, ## args);}
#else
#define Debug(l, f, args...)
#endif
int serialIODebug = 0;
struct serialInfo *cc_serialIOInit(int card, char *taskName)
{
struct serialInfo *serialInfo;
int status;
serialInfo = (struct serialInfo *) malloc(sizeof(struct serialInfo));
serialInfo->bpd = new BPD;
status = serialInfo->bpd->Bind(serialInfo->td, card, taskName);
if (status < 0) {
Debug(1,
"serialIOInit: Cannot bind to remote Hideos task %s\n", taskName);
return (NULL);
}
else
Debug(1,
"serialIOInit: Bound to remote Hideos task %s\n", taskName);
return (serialInfo);
}
int cc_serialIOSend(struct serialInfo *serialInfo, char const *buffer,
int buffer_len, int timeout)
{
int status;
IndirectStringMsg* sm = (IndirectStringMsg*)
hideos_resources->GetMessagePool()->GetMessage(IndirectStringMsgType);
Message* rm;
if (sm->IsQueued()) {
printf("serialIOSend: 1st message %lx already queued!!\n", (long)sm);
exit(1);
}
sm->SetWrite(timeout);
sm->SetBuffers((unsigned char *)buffer,buffer_len,NULL,0);
status = serialInfo->bpd->Send(serialInfo->td, sm);
if (status != 0) {
Debug(1, "serialIOSend: error sending message %s\n", buffer);
hideos_resources->GetMessagePool()->FreeMessage(sm);
goto done;
}
Debug(2, "serialIOSend: sent message %s\n", buffer);
status = serialInfo->bpd->Receive((Message *&)rm);
if (status != 0) {
Debug(1, "serialIOSend: error receiving message, status=%d\n", status);
goto done;
}
Debug(2, "serialIOSend: received message, status=%d\n", status);
hideos_resources->GetMessagePool()->FreeMessage(rm);
done:
return (status);
}
int cc_serialIORecv(struct serialInfo *serialInfo, char *buffer,
int buffer_len, int terminator, int timeout)
{
int status, nrec = 0;
IndirectStringMsg* sm = (IndirectStringMsg*)
hideos_resources->GetMessagePool()->GetMessage(IndirectStringMsgType);
IndirectStringMsg* rm;
if (sm->IsQueued()) {
printf("serialIORecv: message %lx already queued!!\n", (long)sm);
exit(1);
}
sm->SetRead(timeout);
if (terminator == -1)
sm->SetComplexDelimiter((char *)NULL, (char *)NULL, 0);
else
sm->SetSimpleDelimiter(terminator);
sm->SetBuffers(NULL, 0, (unsigned char *)buffer, buffer_len);
status = serialInfo->bpd->Send(serialInfo->td, sm);
if (status != 0) {
Debug(1, "serialIORecv: error sending message, status = %d\n", status);
hideos_resources->GetMessagePool()->FreeMessage(sm);
goto doneRecv;
}
Debug(2, "serialIORecv: sent message status = %d\n", status);
status = serialInfo->bpd->Receive((Message *&)rm);
if (status < 0) {
Debug(1,"serialIORecv: error receiving message, status = %d\n",
status);
goto doneRecv;
}
nrec = sm->GetDestSize();
if (sm->return_code != 0) {
Debug(1,"serialIORecv: error, return code = %ld\n", sm->return_code);
}
Debug(2,"serialIORecv: Received %d bytes\n", nrec);
/* Append a NULL byte to the response if there is room */
if (nrec < buffer_len) buffer[nrec] = '\0';
Debug(2,"serialIORecv: Received message = %s\n", buffer);
hideos_resources->GetMessagePool()->FreeMessage(rm);
doneRecv:
return (nrec);
}
extern "C"
{
struct serialInfo *serialIOInit(int card, char *taskName)
{
return (cc_serialIOInit(card, taskName));
}
int serialIOSend(struct serialInfo *serialInfo, char const *buffer,
int buffer_len, int timeout)
{
return (cc_serialIOSend(serialInfo, buffer, buffer_len, timeout));
}
int serialIORecv(struct serialInfo *serialInfo, char *buffer, int buffer_len,
int terminator, int timeout)
{
return (cc_serialIORecv(serialInfo, buffer, buffer_len,
terminator, timeout));
}
}
+51
View File
@@ -0,0 +1,51 @@
#ifdef __cplusplus
#include "vxWorks.h"
#include "vme.h"
#include "iv.h"
#include "stdio.h"
#include "string.h"
#include "cacheLib.h"
#include "taskLib.h"
#include "gen/all_msg_ids.h"
#include "msg/serial_config_msg.h"
#include "msg/string_msg.h"
#include "hideos/globals.h"
#include "hideos/resources.h"
#include "hideos/msgpool.h"
#include "hideos/registry.h"
#include "hideos/drvBp.h"
struct serialInfo
{
BPD *bpd;
TD td;
};
/* Function prototypes */
struct serialInfo *cc_serialIOInit(int card, char *task);
int cc_serialIOSend(struct serialInfo *serialInfo, char const *buffer,
int buffer_len, int timeout);
#else /* For C just define serialInfo as a dummy structure since it can't
understand the include files which define what it really is */
struct serialInfo
{
int dummy;
};
#endif
#ifdef __cplusplus
extern "C"
{
#endif
struct serialInfo *serialIOInit(int card, char *task);
int serialIOSend(struct serialInfo *serialInfo, char const *buffer,
int buffer_len, int timeout);
int serialIORecv(struct serialInfo *serialInfo, char *buffer, int buffer_len,
int terminator, int timeout);
#ifdef __cplusplus
}
#endif
+3
View File
@@ -0,0 +1,3 @@
TOP=../..
include $(TOP)/config/CONFIG_APP
include $(TOP)/config/RULES_ARCHS
+11
View File
@@ -0,0 +1,11 @@
# Makefile.Host
TOP = ../../..
include $(TOP)/config/CONFIG_APP
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
DBDINSTALL += devOms.dbd
include $(TOP)/config/RULES.Host
#----------------------------------------
# ADD RULES AFTER THIS LINE
+30
View File
@@ -0,0 +1,30 @@
# Makefile.Vx
TOP = ../../..
include $(TOP)/config/CONFIG_APP
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
# The following are used for debugging messages.
#USR_CFLAGS += -DDEBUG
#USR_CXXFLAGS += -DDEBUG
# The following is required for all OMS device drivers.
SRCS.c += ../devOmsCom.c
LIBOBJS += devOmsCom.o
# The following is required for the OMS VME8/44 (i.e., devOMS) device driver.
SRCS.c += ../devOms.c ../drvOms.c
LIBOBJS += devOms.o drvOms.o
# The following is required for the OMS VME58 (i.e., devOms58) device driver.
SRCS.c += ../devOms58.c ../drvOms58.c
LIBOBJS += devOms58.o drvOms58.o
LIBNAME = OmsLib
#Note that the command line that builds the
#library $(LIBNAME) may be HUGE (>3kB)
#
include $(TOP)/config/RULES.Vx
#----------------------------------------
# ADD RULES AFTER THIS LINE
+132
View File
@@ -0,0 +1,132 @@
/*
FILENAME... devOms.c
USAGE... Device level support for OMS VME8 and VME44 models.
Version: $Revision: 1.1 $
Modified By: $Author: sluiter $
Last Modified: $Date: 2000-02-08 22:19:00 $
*/
/*
* Original Author: Jim Kowalkowski
* Date: 01/18/93
* Current Author: Ron Sluiter
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 01-18-93 jbk initialized
* ...
* .03 03-19-96 tmm v1.10: modified encoder-ratio calculation
* .04 11-26-96 jps allow for bumpless-reboot on position
* .04a 02-19-97 tmm fixed for EPICS 3.13
*/
#include <vxWorks.h>
#include <semLib.h> /* jps: include for init_record wait */
#include <alarm.h>
#include <callback.h>
#include <dbDefs.h>
#include <dbAccess.h>
#include <dbCommon.h>
#include <fast_lock.h>
#include <devSup.h>
#include <drvSup.h>
#ifdef __cplusplus
extern "C" {
#include <recSup.h>
#include <errlog.h>
}
#else
#include <recSup.h>
#include <errlog.h>
#endif
#include "motorRecord.h"
#include "motor.h"
#include "drvOms.h"
#include "devOmsCom.h"
#define STATIC static
extern int oms44_num_cards;
extern struct driver_table oms_access;
/* ----------------Create the dsets for devOMS----------------- */
STATIC long oms_init(int);
STATIC long oms_init_record(struct motorRecord *);
STATIC long oms_start_trans(struct motorRecord *);
STATIC long oms_end_trans(struct motorRecord *);
struct motor_dset devOMS =
{
{8, NULL, oms_init, oms_init_record, NULL},
motor_update_values,
oms_start_trans,
oms_build_trans,
oms_end_trans
};
STATIC struct board_stat **oms_cards;
STATIC const char errmsg[] = {"\n\n!!!ERROR!!! - Oms driver uninitialized.\n"};
STATIC long oms_init(int after)
{
if (*(oms_access.init_indicator) == NO)
{
errlogSevPrintf(errlogMinor, "%s", errmsg);
return(ERROR);
}
else
return(motor_init_com(after, oms44_num_cards, &oms_access, &oms_cards));
}
STATIC long oms_init_record(struct motorRecord *mr)
{
return(motor_init_record_com(mr, oms44_num_cards, &oms_access, oms_cards));
}
STATIC long oms_start_trans(struct motorRecord *mr)
{
struct motor_trans *trans;
long rtnval;
rtnval = motor_start_trans_com(mr, oms_cards, ADDRESS);
/* Initialize a STOP_AXIS command termination string pointer. */
trans = (struct motor_trans *) mr->dpvt;
trans->motor_call.termstring = " ID";
return(rtnval);
}
STATIC long oms_end_trans(struct motorRecord *mr)
{
if (*(oms_access.init_indicator) == NO)
{
errlogSevPrintf(errlogMinor, "%s", errmsg);
return(ERROR);
}
else
return(motor_end_trans_com(mr, &oms_access, "\r"));
}
+6
View File
@@ -0,0 +1,6 @@
# Oregon Micro Systems VME8/44 and VME58 Driver support.
device(motor,VME_IO,devOMS,"OMS VME8/44")
device(motor,VME_IO,devOms58,"OMS VME58")
driver(drvOms)
driver(drvOms58)
+136
View File
@@ -0,0 +1,136 @@
/*
FILENAME... devOms58.c
USAGE... Motor record device level support for OMS VME58.
Version: $Revision: 1.1 $
Modified By: $Author: sluiter $
Last Modified: $Date: 2000-02-08 22:19:01 $
*/
/*
* Original Author: Jim Kowalkowski
* Current Author: Joe Sullivan
* Date: 11/14/94
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 01-18-93 jbk initialized
* .02 11-14-94 jps copy devOMS.c and modify to point to vme58 driver
* .03 03-19-96 tmm v1.10: modified encoder-ratio calculation
* .04 06-20-96 jps allow for bumpless-reboot on position
* .04a 02-19-97 tmm fixed for EPICS 3.13
* ...
*/
#include <vxWorks.h>
#include <semLib.h> /* jps: include for init_record wait */
#include <alarm.h>
#include <callback.h>
#include <dbDefs.h>
#include <dbAccess.h>
#include <dbCommon.h>
#include <fast_lock.h>
#include <devSup.h>
#include <drvSup.h>
#ifdef __cplusplus
extern "C" {
#include <recSup.h>
#include <errlog.h>
}
#else
#include <recSup.h>
#include <errlog.h>
#endif
#include "motorRecord.h"
#include "motor.h"
#include "drvOms58.h"
#include "devOmsCom.h"
#define STATIC static
extern int oms58_num_cards;
extern struct driver_table oms58_access;
/* ----------------Create the dsets for devOMS----------------- */
STATIC long oms_init(int after);
STATIC long oms_init_record(struct motorRecord *);
STATIC long oms_start_trans(struct motorRecord *);
STATIC long oms_end_trans(struct motorRecord *);
struct motor_dset devOms58 =
{
{8, NULL, oms_init, oms_init_record, NULL},
motor_update_values,
oms_start_trans,
oms_build_trans,
oms_end_trans
};
STATIC struct board_stat **oms_cards;
STATIC const char errmsg[] = {"\n\n!!!ERROR!!! - Oms58 driver uninitialized.\n"};
STATIC long oms_init(int after)
{
if (*(oms58_access.init_indicator) == NO)
{
errlogSevPrintf(errlogMinor, "%s", errmsg);
return(ERROR);
}
else
return(motor_init_com(after, oms58_num_cards, &oms58_access, &oms_cards));
}
STATIC long oms_init_record(struct motorRecord *mr)
{
return(motor_init_record_com(mr, oms58_num_cards, &oms58_access, oms_cards));
}
STATIC long oms_start_trans(struct motorRecord *mr)
{
struct motor_trans *trans;
long rtnval;
rtnval = motor_start_trans_com(mr, oms_cards, ADDRESS);
/* Initialize a STOP_AXIS command termination string pointer. */
trans = (struct motor_trans *) mr->dpvt;
trans->motor_call.termstring = " ID";
return(rtnval);
}
STATIC long oms_end_trans(struct motorRecord *mr)
{
if (*(oms58_access.init_indicator) == NO)
{
errlogSevPrintf(errlogMinor, "%s", errmsg);
return(ERROR);
}
else
return(motor_end_trans_com(mr, &oms58_access, "\r"));
}
+220
View File
@@ -0,0 +1,220 @@
/*
FILENAME... devOmsCom.c
USAGE... Data and functions common to all OMS device level support.
Version: $Revision: 1.1 $
Modified By: $Author: sluiter $
Last Modified: $Date: 2000-02-08 22:19:01 $
*/
/*
* Original Author: Jim Kowalkowski
* Current Author: Joe Sullivan
* Date: 11/14/94
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 01-18-93 jbk initialized
* .02 11-14-94 jps copy devOMS.c and modify to point to vme58 driver
* .03 03-19-96 tmm v1.10: modified encoder-ratio calculation
* .04 06-20-96 jps allow for bumpless-reboot on position
* .04a 02-19-97 tmm fixed for EPICS 3.13
*/
#include <vxWorks.h>
#include <string.h>
#include <logLib.h>
#include <math.h>
#include "motorRecord.h"
#include "motor.h"
#include "motordevCom.h"
/* Command set used by record support. WARNING! this must match
"motor_cmnds" in motor.h .
*/
struct motor_table
{
unsigned char type;
const char *command;
int num_parms;
};
struct motor_table const oms_table[] =
{
{MOTION, " MA", 1}, /* MOVE_ABS */
{MOTION, " MR", 1}, /* MOVE_REL */
{MOTION, " HM", 1}, /* HOME_FOR */
{MOTION, " HR", 1}, /* HOME_REV */
{IMMEDIATE, " LP", 1}, /* LOAD_POS */
{IMMEDIATE, " VB", 1}, /* SET_VELO_BASE */
{IMMEDIATE, " VL", 1}, /* SET_VELO */
{IMMEDIATE, " AC", 1}, /* SET_ACCEL */
{IMMEDIATE, " GD", 0}, /* jps: from GO to GD */
{IMMEDIATE, " ER", 2}, /* SET_ENC_RATIO */
{INFO, " ", 0}, /* GET_INFO */
{MOVE_TERM, " ST", 0}, /* STOP_AXIS */
{VELOCITY, " JG", 1}, /* JOG */
{IMMEDIATE," KP", 1}, /* SET_PGAIN */
{IMMEDIATE," KI", 1}, /* SET_IGAIN */
{IMMEDIATE," KD", 1}, /* SET_DGAIN */
{IMMEDIATE," HN", 0}, /* ENABLE_TORQUE */
{IMMEDIATE," HF", 0}, /* DISABL_TORQUE */
{IMMEDIATE, "", 0}, /* PRIMITIVE */
{IMMEDIATE, "", 0}, /* SET_HIGH_LIMIT */
{IMMEDIATE, "", 0}, /* SET_LOW_LIMIT */
};
/* add a part to the transaction */
long oms_build_trans(motor_cmnd command, double *parms, struct motorRecord *mr)
{
struct motor_trans *trans = (struct motor_trans *) mr->dpvt;
struct mess_node *motor_call;
char buffer[40];
unsigned char cmnd_type;
long rtnind;
rtnind = OK;
motor_call = &trans->motor_call;
cmnd_type = oms_table[command].type;
if (cmnd_type > motor_call->type)
motor_call->type = cmnd_type;
/* concatenate onto the dpvt message field */
if (trans->state == BUILD_STATE)
{
if ((command == PRIMITIVE) && (mr->init != NULL) &&
(strlen(mr->init) != 0))
{
extern struct driver_table oms58_access;
/* Test for a "device directive" in the Initialization string. */
if ((mr->init[0] == '@') && (trans->tabptr == &oms58_access))
{
char *end = strrchr(&mr->init[1], '@');
if (end != NULL)
{
struct driver_table *tabptr = trans->tabptr;
int size = (end - &mr->init[0]) + 1;
strncpy(buffer, mr->init, size);
buffer[size] = NULL;
if (strcmp(buffer, "@DPM_ON@") == 0)
{
int response, bitselect;
char respbuf[10];
(*tabptr->getmsg)(motor_call->card, respbuf, -1);
(*tabptr->sendmsg)(motor_call->card, "RB\r",
(char) NULL);
(*tabptr->getmsg)(motor_call->card, respbuf, 1);
if (sscanf(respbuf, "%x", &response) == 0)
response = 0; /* Force an error. */
bitselect = (1 << motor_call->signal);
if ((response & bitselect) == 0)
trans->dpm = ON;
else
logMsg((char *) "Invalid VME58 configuration; RB = 0x%x\n",
response, 0, 0, 0, 0 ,0);
}
end++;
strcpy(buffer, end);
}
else
strcpy(buffer, mr->init);
}
else
strcpy(buffer, mr->init);
strcat(motor_call->message, " ");
strcat(motor_call->message, buffer);
}
else
{
int first_one, itera;
if (command == SET_HIGH_LIMIT || command == SET_LOW_LIMIT)
trans->state = IDLE_STATE; /* No command sent to the controller. */
else
{
/* Silently enforce minimum range on KP command. */
if (command == SET_PGAIN && *parms < 0.00005)
{
*parms = 0.00005;
mr->pcof = 0.00005;
rtnind = ERROR;
}
if (cmnd_type == MOTION || cmnd_type == VELOCITY)
{
if (strlen(mr->prem) != 0)
{
strcat(motor_call->message, mr->prem);
strcat(motor_call->message, " ");
}
if (strlen(mr->post) != 0)
motor_call->postmsgptr = (char *) &mr->post;
}
/* put in command */
strcat(motor_call->message, oms_table[command].command);
/* put in parameters */
for (first_one = YES, itera = 0; itera < oms_table[command].num_parms;
itera++)
{
if (first_one == YES)
first_one = NO;
else
strcat(motor_call->message, ",");
if (command == SET_PGAIN || command == SET_IGAIN ||
command == SET_DGAIN)
{
*parms *= 1999.9;
sprintf(buffer, "%.1f", parms[itera]);
}
else if (command == SET_VELOCITY) /* OMS errors if VB = VL. */
{
long vbase = NINT(mr->vbas / fabs(mr->res));
long vel = NINT(parms[itera]);
if (vel <= vbase)
vel = vbase + 1;
sprintf(buffer, "%ld", vel);
}
else
sprintf(buffer, "%ld", NINT(parms[itera]));
strcat(motor_call->message, buffer);
}
}
}
}
else
rtnind = ERROR;
return(rtnind);
}
+41
View File
@@ -0,0 +1,41 @@
/*
FILENAME.. devOmsCom.h
USAGE... This file contains OMS device information that is
common to all OMS device support modules.
Version: $Revision: 1.1 $
Modified By: $Author: sluiter $
Last Modified: $Date: 2000-02-08 22:19:01 $
*/
/*
* Original Author: Ron Sluiter
* Date: 12/22/98
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contract
* W-31-109-ENG-38 at Argonne National Laboratory.
*
* Beamline Controls & Data Acquisition Group
* Experimental Facilities Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
*/
#ifndef INCdevOmsComh
#define INCdevOmsComh 1
#include "motordevCom.h"
#define ADDRESS "A?"
extern long oms_build_trans(motor_cmnd, double *, struct motorRecord *);
#endif /* INCdevOmsComh */
File diff suppressed because it is too large Load Diff
+95
View File
@@ -0,0 +1,95 @@
/*
FILENAME... drvOms.h
USAGE... This file contains OMS driver "include" information that is
specific to OMS models VME8 and VME44.
Version: $Revision: 1.1 $
Modified By: $Author: sluiter $
Last Modified: $Date: 2000-02-08 22:19:02 $
*/
/*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
*/
#ifndef INCdrvOmsh
#define INCdrvOmsh 1
#include "drvOmsCom.h"
/*
* VME8/44 default profile
*/
#define OMS_NUM_CARDS 8
#define OMS_NUM_CHANNELS 8
#define OMS_NUM_ADDRS 0xFC00
#define OMS_BRD_SIZE 0x10 /* card address boundary */
#define OMS_RESP_Q_SZ 0x100 /* maximum oms response message size */
/* status register */
#define STAT_IRQ 0x80
#define STAT_TRANS_BUF_EMPTY 0x40
#define STAT_INPUT_BUF_FULL 0x20
#define STAT_DONE 0x10
#define STAT_OVERTRAVEL 0x08
#define STAT_ENCODER_REQ 0x04
#define STAT_UNUSED 0x02
#define STAT_ERROR 0x01
/* done flag register */
#define DONE_X 0x01
#define DONE_Y 0x02
#define DONE_Z 0x04
#define DONE_T 0x08
#define DONE_U 0x10
#define DONE_V 0x20
#define DONE_R 0x40
#define DONE_S 0x80
/* interrupt control register */
#define IRQ_ENABLE 0x80
#define IRQ_TRANS_BUF 0x40
#define IRQ_INPUT_BUF 0x20
#define IRQ_DONE 0x10
#define IRQ_ENABLE_ALL (IRQ_ENABLE|IRQ_DONE|IRQ_INPUT_BUF)
struct vmex_motor
{
uint8_t unused0;
uint8_t data;
uint8_t unused1;
uint8_t done;
uint8_t unused2;
uint8_t control;
uint8_t unused3;
uint8_t status;
uint8_t unused4;
uint8_t vector;
uint8_t unused5[6];
};
#endif /* INCdrvOmsh */
File diff suppressed because it is too large Load Diff
+261
View File
@@ -0,0 +1,261 @@
/*
FILENAME... drvOms58.h
USAGE... OMS driver level "include" information that is specific to OMS
model VME58.
Version: $Revision: 1.1 $
Modified By: $Author: sluiter $
Last Modified: $Date: 2000-02-08 22:19:03 $
*/
/*
* Original Author: Jim Kowalkowski
* Current Author: Joe Sullivan
* Date: 11/14/94
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 01-18-93 jbk initialized
* .02 11-14-94 jps copy drvOms.c and modify to point to vme58 driver
* ...
* .06 12-07-94 jps first released version w/interrupt supprt
* .07 12-20-94 jps rearrange the device init routines
* .08 05-03-96 jps convert 32bit card accesses to 16bit - vme58PCB
* version D does not support 32bit accesses.
* .09 05-09-97 jps increase maximum card count to 15
*
*/
#ifndef INCdrvOms58h
#define INCdrvOms58h 1
#include "drvOmsCom.h"
/*
* VME58 default profile
*/
#define OMS_NUM_CARDS 15
#define OMS_NUM_CHANNELS 8
#define OMS_NUM_ADDRS 0x4000
#define OMS_BRD_SIZE 0x1000 /* card address boundary */
#define BUFFER_SIZE 256
/* Board control register structures */
/* VME58 DUAL-PORT MEMORY MAP */
typedef struct
{
uint16_t encPos[2];
uint16_t cmndPos[2];
uint16_t cmndVel[2];
uint16_t accel[2];
uint16_t maxVel[2];
uint16_t baseVel[2];
uint16_t dFltrGain[2];
uint16_t dFltrPole[2];
uint16_t dFltrZero[2];
uint16_t reserved[46];
} MOTOR_DATA_REGS;
/* Definitions for VME58 I/O Registers */
/* Control Register - Offset = 0x0FE1 */
typedef union
{
uint8_t All;
struct
{
uint8_t intReqEna:1; /* Master interrupt request enable */
uint8_t ioIntEna:1; /* I/O bits 0 and 1 interrupt enable */
uint8_t directInEna:1; /* Interrupt request to the VME58 ? */
uint8_t doneIntEna:1; /* Done detect interrupt enable */
uint8_t otIntEna:1; /* Overtravel detect interrupt enable */
uint8_t slipIntEna:1; /* Encoder slip detect interrupt enable */
uint8_t:1; /* Unused */
uint8_t update:1; /* Data area update request */
} Bits;
} CNTRL_REG;
/* Status Register - Offset = 0x0FE3 */
typedef union
{
uint8_t All;
struct
{
uint8_t interrupt:1; /* Interrupt dectect */
uint8_t directIn:1; /* Direct input interrupt detect */
uint8_t directOut:1; /* Direct ouput interrupt detect */
uint8_t done:1; /* Motion done detect */
uint8_t overtravel:1; /* Overtravel detect */
uint8_t encoderSlip:1; /* Encoder slip detect */
uint8_t cardOK:1; /* Powerup initilization complete */
uint8_t cmndError:1; /* Command error dectect */
} Bits;
} STATUS_REG;
/* I/O Register(0-7) - Offset = 0x0FE5 */
typedef union
{
uint8_t All;
struct
{
uint8_t io_7:1; /* Bit 7 */
uint8_t io_6:1; /* Bit 6 */
uint8_t io_5:1; /* Bit 5 */
uint8_t io_4:1; /* Bit 4 */
uint8_t io_3:1; /* Bit 3 */
uint8_t io_2:1; /* Bit 2 */
uint8_t io_1:1; /* Bit 1 */
uint8_t io_0:1; /* Bit 0 */
} Bits;
} IO_LOW_REG;
/* Slip Flag Register - Offset = 0x0FE7 */
typedef union
{
uint8_t All;
struct
{
uint8_t slip_s:1; /* status of S axis */
uint8_t slip_r:1; /* status of R axis */
uint8_t slip_v:1; /* status of V axis */
uint8_t slip_u:1; /* status of U axis */
uint8_t slip_t:1; /* status of T axis */
uint8_t slip_z:1; /* status of Z axis */
uint8_t slip_y:1; /* status of Y axis */
uint8_t slip_x:1; /* status of X axis */
} Bits;
} SLIP_REG;
/* Done Flag Register - Offset = 0x0FE9 */
typedef union
{
uint8_t All;
struct
{
uint8_t done_s:1; /* status of S axis */
uint8_t done_r:1; /* status of R axis */
uint8_t done_v:1; /* status of V axis */
uint8_t done_u:1; /* status of U axis */
uint8_t done_t:1; /* status of T axis */
uint8_t done_z:1; /* status of Z axis */
uint8_t done_y:1; /* status of Y axis */
uint8_t done_x:1; /* status of X axis */
} Bits;
} DONE_REG;
/* I/O High Register(8-13) - Offset = 0x0FEB */
typedef union
{
uint8_t All;
struct
{
uint8_t:1; /* Unused */
uint8_t:1; /* Unused */
uint8_t io_13:1; /* Bit 13 */
uint8_t io_12:1; /* Bit 12 */
uint8_t io_11:1; /* Bit 11 */
uint8_t io_10:1; /* Bit 10 */
uint8_t io_9:1; /* Bit 9 */
uint8_t io_8:1; /* Bit 8 */
} Bits;
} IO_HIGH_REG;
/* Limit Switch Status Register - Offset = 0x0FED */
typedef union
{
uint8_t All;
struct
{
uint8_t limit_s:1; /* status of S axis */
uint8_t limit_r:1; /* status of R axis */
uint8_t limit_v:1; /* status of V axis */
uint8_t limit_u:1; /* status of U axis */
uint8_t limit_t:1; /* status of T axis */
uint8_t limit_z:1; /* status of Z axis */
uint8_t limit_y:1; /* status of Y axis */
uint8_t limit_x:1; /* status of X axis */
} Bits;
} LIMIT_REG;
/* Home Switch Status Register - Offset = 0x0FEF */
typedef union
{
uint8_t All;
struct
{
uint8_t home_s:1; /* status of S axis */
uint8_t home_r:1; /* status of R axis */
uint8_t home_v:1; /* status of V axis */
uint8_t home_u:1; /* status of U axis */
uint8_t home_t:1; /* status of T axis */
uint8_t home_z:1; /* status of Z axis */
uint8_t home_y:1; /* status of Y axis */
uint8_t home_x:1; /* status of X axis */
} Bits;
} HOME_REG;
typedef struct
{
uint8_t unused00;
uint8_t cntrlReg; /* Control Register - Read/Write */
uint8_t unused02;
uint8_t statusReg; /* Status Register - Read */
uint8_t unused04;
uint8_t ioLowReg; /* IO bits 0-7 status register - Read */
uint8_t unused06;
uint8_t slipReg; /* Encoder slip status register - Read */
uint8_t unused08;
uint8_t doneReg; /* Axis done status register - Read */
uint8_t unused0A;
uint8_t ioHighReg; /* IO bits 8-13 status register - Read */
uint8_t unused0C;
uint8_t limitReg; /* Limit switch status register - Read */
uint8_t unused0E;
uint8_t homeReg; /* Home switch status register - Read */
uint8_t unusedF0;
uint8_t intVector; /* Interrupt vector */
} MOTOR_CNTRL_REGS;
/* OMS VME dual port memory map */
struct vmex_motor
{
int16_t inPutIndex;
int16_t outGetIndex;
int16_t inBuffer[BUFFER_SIZE];
int16_t reserved0[254];
MOTOR_DATA_REGS data[OMS_NUM_CHANNELS];
int16_t outPutIndex;
int16_t inGetIndex;
int16_t outBuffer[BUFFER_SIZE];
int16_t reserved1[750];
MOTOR_CNTRL_REGS control;
};
#endif /* INCdrvOms58h */
+62
View File
@@ -0,0 +1,62 @@
/*
FILENAME... drvOmsCom.h
USAGE... This file contains OMS driver "include" information
that is common to all OMS models.
Version: $Revision: 1.1 $
Modified By: $Author: sluiter $
Last Modified: $Date: 2000-02-08 22:19:03 $
*/
/*
* Original Author: Ron Sluiter
* Date: 12/18/98
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
*/
#ifndef INCdrvOmsComh
#define INCdrvOmsComh 1
#include "motordrvCom.h"
/* Default profile. */
#define OMS_INTERRUPT_TYPE intVME
#define OMS_ADDRS_TYPE atVMEA16
#define OMS_INT_VECTOR 180 /* default interrupt vector (64-255) */
#define OMS_INT_LEVEL 5 /* default interrupt level (1-6) */
/* OMS Command strings. */
#define AXIS_STOP "A? ST\n"
#define GET_IDENT "WY\n"
#define ERROR_CLEAR "IC\n"
#define STOP_ALL "AA SA\n"
#define ALL_POS "AA RP\n"
/* Global data. */
extern char oms_trans_axis[];
#endif /* INCdrvOmsComh */
+3
View File
@@ -0,0 +1,3 @@
TOP=../..
include $(TOP)/config/CONFIG_APP
include $(TOP)/config/RULES_ARCHS
+11
View File
@@ -0,0 +1,11 @@
# Makefile.Host
TOP = ../../..
include $(TOP)/config/CONFIG_APP
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
DBDINSTALL += devSoftMotor.dbd
include $(TOP)/config/RULES.Host
#----------------------------------------
# ADD RULES AFTER THIS LINE
+18
View File
@@ -0,0 +1,18 @@
# Makefile.Vx
TOP = ../../..
include $(TOP)/config/CONFIG_APP
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
# The following is required for the Soft Channel (i.e., MotorSoft) device driver.
SRCS.c += ../devSoft.c ../devSoftAux.c
LIBOBJS += devSoft.o devSoftAux.o
LIBNAME = SoftMotorLib
#Note that the command line that builds the
#library $(LIBNAME) may be HUGE (>3kB)
#
include $(TOP)/config/RULES.Vx
#----------------------------------------
# ADD RULES AFTER THIS LINE
+185
View File
@@ -0,0 +1,185 @@
/*
FILENAME... devSoft.c
USAGE... Motor record device level support for Soft channel.
Modified By: $Author: sluiter $
Last Modified: $Date: 2000-02-08 22:19:25 $
*/
/*
* Original Author: Ron Sluiter
* Date: 06/15/99
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contract
* W-31-109-ENG-38 at Argonne National Laboratory.
*
* Beamline Controls & Data Acquisition Group
* Experimental Facilities Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
*/
/*
NOTES...
- Can't call CA functions until after dbLockInitRecords() has
been called and initialized lock sets.
*/
#include <vxWorks.h>
#include <dbDefs.h>
#include <dbFldTypes.h>
#ifdef __cplusplus
extern "C" {
#include <dbEvent.h>
#include <recSup.h>
}
#else
#include <dbEvent.h>
#include <recSup.h>
#endif
#include "motorRecord.h"
#include "motor.h"
#include "devSoft.h"
#define STATIC static
STATIC long update(struct motorRecord *);
STATIC long start(struct motorRecord *);
STATIC long build(motor_cmnd, double *, struct motorRecord *);
STATIC long end(struct motorRecord *);
STATIC void soft_process(struct motorRecord *);
struct motor_dset devMotorSoft =
{
{8, NULL, soft_init, soft_init_record, NULL},
update,
start,
build,
end
};
STATIC long update(struct motorRecord *mr)
{
struct soft_private *ptr = (struct soft_private *) mr->dpvt;
mr->msta = ptr->dinp_value ? RA_DONE : 0;
return(ptr->callback_flag);
}
STATIC long start(struct motorRecord *mr)
{
return((long) OK);
}
STATIC long end(struct motorRecord *mr)
{
struct soft_private *ptr = (struct soft_private *) mr->dpvt;
if (ptr->default_done_behavior == YES)
mr->msta = RA_DONE;
return((long) OK);
}
STATIC long build(motor_cmnd command, double *parms, struct motorRecord *mr)
{
const short int stop = 1;
long status;
switch (command)
{
case MOVE_ABS:
case MOVE_REL:
status = dbPutLink(&mr->out, DBR_DOUBLE, &mr->dval, 1);
break;
case STOP_AXIS:
status = dbPutLink(&mr->stoo, DBR_SHORT, &stop, 1);
break;
case SET_HIGH_LIMIT:
case SET_LOW_LIMIT:
status = OK;
break;
default:
status = ERROR;
}
return(status);
}
/*
FUNCTION... void soft_dinp_func(struct motorRecord *, short)
USAGE... Update soft channel device input links and
process soft channel motor record when done moving.
LOGIC...
Get DINP_VALUE via DINP link.
IF Get() succeeds and DINP_VALUE is true
Process soft channel record
ENDIF
*/
void soft_dinp_func(struct motorRecord *mr, short newdinp)
{
struct soft_private *ptr = (struct soft_private *) mr->dpvt;
ptr->dinp_value = newdinp;
if (ptr->dinp_value)
soft_process(mr);
}
void soft_rinp_func(struct motorRecord *mr, long newrinp)
{
mr->rmp = newrinp;
soft_process(mr);
}
void soft_rdbl_func(struct motorRecord *mr, double newrdbl)
{
newrdbl = newrdbl / mr->res;
if ((mr->msta & EA_PRESENT) && mr->ueip)
mr->rep = NINT(newrdbl);
else
mr->rmp = NINT(newrdbl);
soft_process(mr);
}
/*
FUNCTION... STATIC void soft_process(struct motorRecord *)
USAGE... Process the soft channel motor record.
LOGIC...
Lock soft channel record - call dbScanLock().
Set call back flag to CALLBACK_DATA so readback will get updated.
Process soft channel record - call process().
Unlock soft channel record - call dbScanUnlock().
*/
STATIC void soft_process(struct motorRecord *mr)
{
struct soft_private *ptr = (struct soft_private *) mr->dpvt;
struct rset *prset = (struct rset *) (mr->rset);
dbScanLock((struct dbCommon *) mr);
ptr->callback_flag = CALLBACK_DATA;
(*prset->process) (mr); /* Process the soft channel record. */
ptr->callback_flag = NOTHING_DONE;
dbScanUnlock((struct dbCommon *) mr);
}
+51
View File
@@ -0,0 +1,51 @@
/*
FILENAME.. devSoft.h
USAGE... This file contains information that is common to
all Soft channel device support modules.
Version: $Revision: 1.1 $
Modified By: $Author: sluiter $
Last Modified: $Date: 2000-02-08 22:19:26 $
*/
/*
* Original Author: Ron Sluiter
* Date: 03/25/99
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contract
* W-31-109-ENG-38 at Argonne National Laboratory.
*
* Beamline Controls & Data Acquisition Group
* Experimental Facilities Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
*/
#ifndef INCdevSofth
#define INCdevSofth 1
extern long soft_init(int);
extern long soft_init_record(struct motorRecord *);
extern void soft_dinp_func(struct motorRecord *, short);
extern void soft_rdbl_func(struct motorRecord *, double);
extern void soft_rinp_func(struct motorRecord *, long);
#define MAXMSGS 20
struct soft_private
{
long callback_flag;
short dinp_value; /* Value from DINP link. */
BOOLEAN default_done_behavior; /* If the DINP is not programmed, exhibit
* "immediate done" default behavior. */
};
#endif /* INCdevSofth */
+152
View File
@@ -0,0 +1,152 @@
/*
FILENAME... devSoftAux.c
USAGE... Motor record device level support for Soft channel.
Version: $Revision: 1.1 $
Modified By: $Author: sluiter $
Last Modified: $Date: 2000-02-08 22:19:26 $
*/
/*
* Original Author: Ron Sluiter
* Date: 06/15/99
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contract
* W-31-109-ENG-38 at Argonne National Laboratory.
*
* Beamline Controls & Data Acquisition Group
* Experimental Facilities Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
*/
/*
Notes:
- A Channel Access (CA) oriented file had to be created separate from the
primary devSoft.c file because CA and record access code cannot both reside
in the same file; each defines (redefines) the DBR's.
*/
#include <vxWorks.h>
#include <taskLib.h>
#include <msgQLib.h>
#include <cadef.h>
#include "motorRecord.h"
typedef enum BOOLEAN_VALUES {OFF = 0, ON = 1} BOOLEAN;
#include "devSoft.h"
#define STATIC static
STATIC void soft_dinp(struct event_handler_args);
STATIC void soft_rdbl(struct event_handler_args);
STATIC void soft_rinp(struct event_handler_args);
STATIC int soft_motor_task(int, int, int, int, int, int, int, int, int, int);
STATIC SEM_ID soft_motor_sem;
STATIC MSG_Q_ID soft_motor_msgQ;
STATIC void soft_dinp(struct event_handler_args args)
{
soft_dinp_func((struct motorRecord *) args.usr, *((short *) args.dbr));
}
STATIC void soft_rdbl(struct event_handler_args args)
{
soft_rdbl_func((struct motorRecord *) args.usr, *((double *) args.dbr));
}
STATIC void soft_rinp(struct event_handler_args args)
{
soft_rinp_func((struct motorRecord *) args.usr, *((long *) args.dbr));
}
long soft_init(int after)
{
if (after == 0)
{
soft_motor_sem = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);
soft_motor_msgQ = msgQCreate(MAXMSGS, 4, MSG_Q_FIFO);
taskSpawn((char *) "soft_motor", 64, VX_FP_TASK | VX_STDIO, 5000,
soft_motor_task, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
else
semGive(soft_motor_sem); /* Start soft_motor_task(). */
return((long) OK);
}
long soft_init_record(struct motorRecord *mr)
{
struct soft_private *ptr;
STATUS status;
static int count = 0;
if (++count > MAXMSGS)
return(ERROR);
status = msgQSend(soft_motor_msgQ, (char *) &mr, 4, NO_WAIT,
MSG_PRI_NORMAL);
/* Allocate space for private field. */
mr->dpvt = (struct soft_private *) malloc(sizeof(struct soft_private));
ptr = (struct soft_private *) mr->dpvt;
ptr->dinp_value = mr->dmov; /* Must match after initialzation. */
return ((long) status);
}
STATIC int soft_motor_task(int a1, int a2, int a3, int a4, int a5, int a6,
int a7, int a8, int a9, int a10)
{
struct motorRecord *mr;
chid dinp, rdbl, rinp;
semTake(soft_motor_sem, WAIT_FOREVER); /* Wait for dbLockInitRecords() to execute. */
SEVCHK(ca_task_initialize(),"ca_task_initialize");
while (msgQReceive(soft_motor_msgQ, (char *) &mr, 4, NO_WAIT) != ERROR)
{
if (mr->dinp.value.constantStr == NULL)
{
struct soft_private *ptr = (struct soft_private *) mr->dpvt;
ptr->default_done_behavior = ON;
}
else
{
SEVCHK(ca_search(mr->dinp.value.pv_link.pvname, &dinp),
"ca_search() failure");
SEVCHK(ca_add_event(DBR_SHORT, dinp, soft_dinp, mr, NULL),"ca_add_event() failure");
SEVCHK(ca_pend_io((float) 5.0), "DINP link failure");
}
if (mr->urip != 0)
{
SEVCHK(ca_search(mr->rdbl.value.pv_link.pvname, &rdbl),"ca_search() failure");
SEVCHK(ca_add_event(DBR_DOUBLE, rdbl, soft_rdbl, mr, NULL),"ca_add_event() failure");
SEVCHK(ca_pend_io((float) 5.0), "RDBL link failure");
}
if (mr->rinp.value.constantStr != NULL)
{
SEVCHK(ca_search(mr->rinp.value.pv_link.pvname, &rinp),"ca_search() failure");
SEVCHK(ca_add_event(DBR_LONG, rinp, soft_rinp, mr, NULL),"ca_add_event() failure");
SEVCHK(ca_pend_io((float) 5.0), "RINP link failure");
}
}
msgQDelete(soft_motor_msgQ);
taskSuspend(NULL); /* Wait Forever. */
return(ERROR);
}
+3
View File
@@ -0,0 +1,3 @@
# Soft Channel driver support.
device(motor,CONSTANT,devMotorSoft,"Soft Channel")
+3
View File
@@ -0,0 +1,3 @@
TOP=../..
include $(TOP)/config/CONFIG_APP
include $(TOP)/config/RULES_ARCHS
+11
View File
@@ -0,0 +1,11 @@
# Makefile.Host
TOP = ../../..
include $(TOP)/config/CONFIG_APP
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
DBDINSTALL += devHighland.dbd
include $(TOP)/config/RULES.Host
#----------------------------------------
# ADD RULES AFTER THIS LINE
+22
View File
@@ -0,0 +1,22 @@
# Makefile.Vx
TOP = ../../..
include $(TOP)/config/CONFIG_APP
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
# The following are used for debugging messages.
#USR_CFLAGS += -DDEBUG
#USR_CXXFLAGS += -DDEBUG
# The following is required for the Highland V544 (i.e., V544) device driver.
SRCS.c += ../devV544.c ../drvV544.c
LIBOBJS += devV544.o drvV544.o
LIBNAME = V544Lib
#Note that the command line that builds the
#library $(LIBNAME) may be HUGE (>3kB)
#
include $(TOP)/config/RULES.Vx
#----------------------------------------
# ADD RULES AFTER THIS LINE
+4
View File
@@ -0,0 +1,4 @@
# Highland V544 driver support.
device(motor,VME_IO,devV544,"Highland V544")
driver(drvV544)
+688
View File
@@ -0,0 +1,688 @@
/* File: devV544.c */
/* Version: 1.2a */
/* Date Last Modified: 2/19/97 */
/* Device Support Routines for motor */
/*
* Original Author: Jim Kowalkowski
* Current Author: Joe Sullivan
* Date: 11/14/94
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 01-18-93 jbk initialized
* .02 11-14-94 jps copy devOms.c and modify to point to vme58 driver
* .03 ??-??-95 jps copy devOms58.c and modify to point to Highland driver
* .04 06-20-96 jps allow for bumpless-reboot on position
* .04a 02-19-97 tmm fixed for EPICS 3.13
*/
#define VERSION 1.3
#include <vxWorks.h>
#include <stdioLib.h>
#include <string.h>
#include <math.h>
#include <semLib.h> /* jps: include for init_record wait */
#include <alarm.h>
#ifdef __cplusplus
extern "C" {
#include <callback.h>
#include <dbAccess.h>
#include <recSup.h>
}
#else
#include <callback.h>
#include <dbAccess.h>
#include <recSup.h>
#endif
#include <dbDefs.h>
#include <dbCommon.h>
#include <fast_lock.h>
#include <devSup.h>
#include <drvSup.h>
#include "motorRecord.h"
#include "motor.h"
#include "drvV544.h"
#define PRIVATE_FUNCTIONS 0 /* normal:1, debug:0 */
#define STATIC
#ifdef NODEBUG
#define Debug(L,FMT,V) ;
#else
#define Debug(L,FMT,V) { if(L <= devV544debug) \
{ printf("%s(%d):",__FILE__,__LINE__); \
printf(FMT,V); } }
#endif
#define NINT(f) (long)((f)>0 ? (f)+0.5 : (f)-0.5) /* tmm */
/* ----------------Create the dsets for devV544----------------- */
static long report();
STATIC long v544_init(int after);
STATIC long v544_init_record(struct motorRecord *);
STATIC long get_ioint_info();
STATIC long v544_start_trans(struct motorRecord *);
STATIC long v544_update_values(struct motorRecord *);
STATIC long v544_build_trans(motor_cmnd, double *, struct motorRecord *);
STATIC long v544_end_trans(struct motorRecord *);
STATIC void v544_callback(MOTOR_RETURN *);
STATIC void v544_init_callback(MOTOR_RETURN *);
struct motor_dset devV544 =
{
{8, NULL, v544_init, v544_init_record, NULL},
v544_update_values,
v544_start_trans,
v544_build_trans,
v544_end_trans
};
/* xxDSET devXxxx={ 5,report,init,init_rec,get_ioint_info,read_write}; */
/* --------------------------- program data --------------------- */
volatile int devV544debug = 0;
struct v544_motor_table
{
int type;
uint16_t command;
int num_parms;
};
static struct v544_motor_table v544_table[] = {
/* type cmd num_parms */
{MOTION, CS_MOVE_ABS, 1}, /* MOVE_ABS */
{MOTION, CS_MOVE_INC, 1}, /* MOVE_REL */
{MOTION, CS_SEEK_INDX, 1}, /* HOME_FOR */
{MOTION, CS_SEEK_INDX, 1}, /* HOME_REV */
{IMMEDIATE, CS_SET_POSITION, 1}, /* LOAD_POS */
{IMMEDIATE, -1, 1}, /* SET_VELO_BASE - Not supported */
{IMMEDIATE, -1, 1}, /* SET_VELO */
{IMMEDIATE, -1, 1}, /* SET_ACCEL */
{IMMEDIATE, CS_GO, 0}, /* GO */
{IMMEDIATE, -1, 2}, /* SET_ENC_RATIO - Not supported */
{INFO, -1, 0}, /* GET_INFO */
{MOVE_TERM, CS_IDLE, 0}, /* STOP_AXIS */
{VELOCITY, -1, 1}, /* JOG */
{UNDEFINED, 0, 0}, /* SET_PGAIN */
{UNDEFINED, 0, 0}, /* SET_IGAIN */
{UNDEFINED, 0, 0}, /* SET_DGAIN */
{UNDEFINED, 0, 0}, /* ENABLE_TORQUE */
{UNDEFINED, 0, 0}, /* DISABL_TORQUE */
{UNDEFINED, 0, 0}, /* PRIMITIVE */
{UNDEFINED, 0, 0}, /* SET_HIGH_LIMIT */
{UNDEFINED, 0, 0} /* SET_LOW_LIMIT */
};
/* status of an axis of a board, one present for each axis of each board*/
struct a_axis
{
/*??? int encoder_present; */
int in_use;
};
/* the device private data structure of the record */
struct v544_trans
{
int state;
FAST_LOCK lock;
MOTOR_CALL motor_call;
int callback_changed;
int32_t motor_pos;
int32_t encoder_pos;
int32_t vel;
unsigned long status;
SEM_ID initSem;
};
/* the board status structure */
struct v544_stat
{
int exists;
int total_axis;
struct a_axis *v544_axis;
};
STATIC struct v544_stat *v544_cards;
extern struct v544_support v544_access;
#define IDLE_STATE 0
#define BUILD_STATE 1
#define YES 1
#define NO 0
#define SEM_TIMEOUT 50
/* --------------------------- program data --------------------- */
/* initialize device support for V544 stepper motor */
STATIC long v544_init(int after)
{
MOTOR_CARD_QUERY card_query;
int i, j;
Debug(1, "v544_init: entry...\n", 0);
if (after)
return (0);
/* allocate space for total cards in system */
v544_cards = (struct v544_stat *) malloc(V544_NUM_CARDS *
sizeof(struct v544_stat));
/* for each card ask driver for num of axis supported and name */
for (i = 0; i < V544_NUM_CARDS; i++)
{
(*v544_access.get_card_info) (i, &card_query);
if (card_query.total_axis == 0)
v544_cards[i].exists = NO;
else
{
v544_cards[i].exists = YES;
v544_cards[i].total_axis = card_query.total_axis;
Debug(5, "v544_init: calling malloc(card_query...)\n total axis =%d\n",
v544_cards[i].total_axis);
v544_cards[i].v544_axis = (struct a_axis *) malloc(
card_query.total_axis * sizeof(struct a_axis));
for (j = 0; j < card_query.total_axis; j++)
v544_cards[i].v544_axis[j].in_use = 0;
}
}
Debug(1, "v544_init: exit...\n", 0);
return (0);
}
/* initialize a record instance */
STATIC long v544_init_record(struct motorRecord * mr)
{
int card, signal;
int initEncoder, initPos;
struct v544_trans *ptrans;
MOTOR_AXIS_QUERY axis_query;
MOTOR_CALL *motor_call;
double ep_mp[2]; /* encoder pulses, motor pulses */
int rtnStat;
Debug(1, "v544_init_record: entry...\n", 0);
/* allocate space for private field - an v544_trans structure */
Debug(5, "v544_init_record: calling malloc(...v544_trans...)\n", 0);
mr->dpvt = (struct v544_trans *) malloc(sizeof(struct v544_trans));
ptrans = mr->dpvt;
ptrans->state = IDLE_STATE;
ptrans->callback_changed = NO;
FASTLOCKINIT(&ptrans->lock);
motor_call = &(ptrans->motor_call);
callbackSetCallback((void (*)(struct callbackPvt *)) v544_callback,
&(motor_call->callback));
callbackSetPriority(priorityMedium, &(motor_call->callback));
/* check if axis already in use, then mark card, axis as in use */
/* out must be an VME_IO */
switch (mr->out.type)
{
case (VME_IO):
break;
default:
recGblRecordError(S_dev_badBus, (void *) mr,
(char *) "devV544 (init_record) Illegal OUT Bus Type");
return (S_dev_badBus);
}
card = mr->out.value.vmeio.card;
Debug(5, "v544_init_record: card %d\n", card);
signal = mr->out.value.vmeio.signal;
Debug(5, "v544_init_record: signal %d\n", signal);
rtnStat = 0;
if (card < 0 || card >= V544_NUM_CARDS || v544_cards[card].exists == NO)
{
recGblRecordError(S_db_badField, (void *) mr,
(char *) "devV544 (init_record) card does not exist!");
rtnStat = S_db_badField;
}
else if (signal < 0 || signal >= v544_cards[card].total_axis)
{
recGblRecordError(S_db_badField, (void *) mr,
(char *) "devV544 (init_record) signal does not exist!");
rtnStat = S_db_badField;
}
else if (v544_cards[card].v544_axis[signal].in_use == YES)
{
recGblRecordError(S_db_badField, (void *) mr,
"(char *) devV544 (init_record) motor already in use!");
rtnStat = S_db_badField;
}
if (rtnStat)
{
/* Initialize readback fields for simulation */
mr->msta = RA_PROBLEM;
mr->res = mr->mres;
mr->rmp = 0; /* raw motor pulse count */
mr->rep = 0; /* raw encoder pulse count */
return (rtnStat);
}
else
/* query motor for all info to fill into record */
(*v544_access.get_axis_info) (card, signal, &axis_query);
mr->msta = axis_query.status; /* status info */
v544_cards[card].v544_axis[signal].in_use = YES;
/*jps: setting the encoder ratio was moved from init_record() remove callbacks during iocInit */
/*
* Set the encoder ratio. Note this is blatantly device dependent.
* Determine the number of encoder pulses and the number of motor pulses
* per engineering unit (EGU). Send an array containing this information
* to device support.
*/
if ((initEncoder = (mr->msta & EA_PRESENT)) && mr->ueip)
{
if (fabs(mr->mres) < 1.e-9)
mr->mres = 1.;
if (fabs(mr->eres) < 1.e-9)
mr->eres = mr->mres;
{
int m;
for (m=10000000; (m > 1) &&
(fabs(m/mr->eres) > 1.e6 || fabs(m/mr->mres) > 1.e6);
m /= 10)
;
Debug(5, "oms58_init_record: ER mult = %d\n", m);
ep_mp[0] = m / mr->eres; /* encoder pulses per ...*/
ep_mp[1] = m / mr->mres; /* motor pulses */
}
mr->res = mr->eres;
}
else
{
ep_mp[0] = 1.;
ep_mp[1] = 1.;
mr->res = mr->mres;
}
/* Program the device if an initial position is programmed or
* an encoder is present. */
if ((initPos = (mr->dval && mr->res)) || initEncoder)
{
/*
* Semaphore used to hold initialization until device is programmed -
* cleared by callback
*/
ptrans->initSem = semBCreate(SEM_Q_FIFO, SEM_EMPTY);
/*
* Switch to special init callback so that record will not be
* processed during iocInit
*/
callbackSetCallback((void (*)(struct callbackPvt *)) v544_init_callback,
&(motor_call->callback));
if (initEncoder)
{
Debug(7, "v544_init_record: encoder pulses per EGU = %f\n", ep_mp[0]);
Debug(7, "v544_init_record: motor pulses per EGU = %f\n", ep_mp[1]);
v544_start_trans(mr);
v544_build_trans(SET_ENC_RATIO, ep_mp, mr);
v544_end_trans(mr);
}
if (initPos)
{
double setPos = mr->dval / mr->res;
Debug(7, "v544_init_record: initial raw position = %f\n", setPos);
v544_start_trans(mr);
v544_build_trans(LOAD_POS, &setPos, mr);
v544_end_trans(mr);
}
/* Changing positions or encoder ratio may have
* changed the readback value. */
v544_start_trans(mr);
v544_build_trans(GET_INFO, NULL, mr);
v544_end_trans(mr);
/* Wait for callback w/timeout */
if ((rtnStat = semTake(ptrans->initSem, SEM_TIMEOUT)) == ERROR)
{
recGblRecordError(S_dev_NoInit, (void *) mr,
(char *) "dev_NoInit (init_record: callback2 timeout");
}
semDelete(ptrans->initSem);
/* Restore regular record callback */
callbackSetCallback((void (*)(struct callbackPvt *)) v544_callback,
&(motor_call->callback));
}
/* query motor for all info to fill into record */
(*v544_access.get_axis_info) (card, signal, &axis_query);
mr->rmp = axis_query.position; /* raw motor pulse count */
mr->rep = axis_query.encoder_position; /* raw encoder pulse count */
mr->msta = axis_query.status; /* status info */
Debug(7, "v544_init_record: rmp = %f\n", mr->rmp);
Debug(7, "v544_init_record: rep = %f\n", mr->rep);
Debug(7, "v544_init_record: msta = %d\n", mr->msta);
Debug(1, "v544_init_record: exit...\n", 0);
return (0);
}
STATIC long v544_update_values(struct motorRecord * mr)
{
struct v544_trans *ptrans;
long rc;
rc = NOTHING_DONE;
ptrans = (struct v544_trans *) mr->dpvt;
FASTLOCK(&ptrans->lock);
/* raw motor pulse count */
if (ptrans->callback_changed == YES)
{
mr->rmp = ptrans->motor_pos;
mr->rep = ptrans->encoder_pos;
mr->rvel = ptrans->vel;
mr->msta = ptrans->status;
ptrans->callback_changed = NO;
rc = CALLBACK_DATA;
}
FASTUNLOCK(&ptrans->lock);
return (rc);
}
/* start building a transaction */
STATIC long v544_start_trans(struct motorRecord * mr)
{
int card = mr->out.value.vmeio.card;
int axis = mr->out.value.vmeio.signal;
struct v544_trans *trans = (struct v544_trans *) mr->dpvt;
MOTOR_CALL *motor_call;
CMND_DEF *motor_cmnd;
if (!mr->dpvt)
return (S_dev_NoInit);
motor_call = &(trans->motor_call);
/*
* initialize area to device private field to store command that is to be
* built and mark as command build in progress
*/
trans->state = BUILD_STATE;
motor_call->card = card;
motor_call->signal = axis;
motor_call->type = UNDEFINED;
motor_call->precord = (struct dbCommon *) mr;
motor_call->cmndCnt = 0;
motor_call->cmndIndx = 0;
/* Zero command define structure */
bzero((void *)motor_call->cmndList, sizeof(CMND_DEF)*MAX_LIST_SIZE);
Debug(3, "v544_start_trans: motor %d command started\n", (card * V544_NUM_CHANNELS) + axis);
return (0);
}
/* end building a transaction */
STATIC long v544_end_trans(struct motorRecord * mr)
{
struct v544_trans *trans = (struct v544_trans *) mr->dpvt;
MOTOR_CALL *motor_call;
long rc;
int card = mr->out.value.vmeio.card;
int axis = mr->out.value.vmeio.signal;
rc = 0;
motor_call = &(trans->motor_call);
switch (trans->state)
{
case BUILD_STATE:
/* shut off command build in process thing */
trans->state = IDLE_STATE;
Debug(3, "v544_end_trans: motor %d command send\n", (card * V544_NUM_CHANNELS) + axis);
rc = (*v544_access.send) (motor_call);
break;
case IDLE_STATE:
default:
rc = ERROR;
}
return (rc);
}
/* add a part to the transaction */
STATIC long v544_build_trans(motor_cmnd command, double *parms, struct motorRecord * mr)
{
struct v544_trans *trans = (struct v544_trans *) mr->dpvt;
MOTOR_CALL *motor_call;
char buffer[20];
int parmInt;
int first_one, i;
motor_call = &(trans->motor_call);
if (v544_table[command].type == UNDEFINED)
return(OK);
if (v544_table[command].type > motor_call->type)
motor_call->type = v544_table[command].type;
if (devV544debug >= 4)
printf("build_trans: v544 cmndCode %d, cmndParm %12.4f\n", command, *parms);
/* concatenate onto the dpvt message field */
switch (trans->state)
{
case BUILD_STATE:
/* put in command */
switch (command)
{
case MOVE_ABS:
case MOVE_REL:
case HOME_FOR:
case HOME_REV:
motor_call->cmndList[motor_call->cmndCnt].cmnd |= v544_table[command].command;
motor_call->cmndList[motor_call->cmndCnt].pos = NINT(*parms);
break;
case LOAD_POS:
motor_call->cmndList[motor_call->cmndCnt].cmnd |= v544_table[command].command;
motor_call->cmndList[motor_call->cmndCnt].pos = NINT(*parms);
motor_call->cmndCnt++;
break;
case SET_VEL_BASE:
break;
case SET_VELOCITY:
parmInt = NINT(*parms/(double)VEL_SCALE);
motor_call->cmndList[motor_call->cmndCnt].vel = parmInt;
break;
case SET_ACCEL:
parmInt = NINT(*parms/(double)(ACC_SCALE*ACC_SCALE));
if (parmInt == 0)
parmInt = 1;
motor_call->cmndList[motor_call->cmndCnt].accel = parmInt;
break;
case GO:
motor_call->cmndList[motor_call->cmndCnt].cmnd |= v544_table[command].command;
motor_call->cmndCnt++;
break;
case SET_ENC_RATIO:
case GET_INFO:
motor_call->cmndCnt++;
break;
case STOP_AXIS:
motor_call->cmndList[motor_call->cmndCnt].cmnd |= CS_IDLE;
motor_call->cmndList[motor_call->cmndCnt].vel = 0;
motor_call->cmndCnt++;
break;
case JOG:
if (*parms >= 0)
{
motor_call->cmndList[motor_call->cmndCnt].cmnd |= CS_RUN_CW;
parmInt = NINT(*parms/(double)VEL_SCALE);
motor_call->cmndList[motor_call->cmndCnt].vel = parmInt;
}
else
{
motor_call->cmndList[motor_call->cmndCnt].cmnd |= CS_RUN_CCW;
parmInt = -1*NINT(*parms/(double)VEL_SCALE);
motor_call->cmndList[motor_call->cmndCnt].vel = parmInt;
}
motor_call->cmndCnt++;
break;
default:
break;
}
break;
case IDLE_STATE:
default:
return ERROR;
}
return (0);
}
/* callback from V544 driver for motor in motion */
STATIC void v544_callback(MOTOR_RETURN * motor_return)
{
struct motorRecord *mr = (struct motorRecord *) motor_return->precord;
struct rset *prset = (struct rset *) (motor_return->precord->rset);
struct v544_trans *ptrans;
Debug(5, "v544_callback: entry\n", 0);
ptrans = (struct v544_trans *) mr->dpvt;
Debug(6, "v544_callback: FASTLOCK\n", 0);
FASTLOCK(&ptrans->lock);
/* raw motor pulse count */
Debug(6, "v544_callback: update ptrans\n", 0);
ptrans->callback_changed = YES;
ptrans->motor_pos = motor_return->position;
/* raw encoder pulse count */
ptrans->encoder_pos = motor_return->encoder_position;
/* raw motor velocity */
ptrans->vel = motor_return->velocity*VEL_SCALE;
ptrans->status = motor_return->status; /* status */
if (devV544debug >= 4)
printf("v544 callback: pos %d, enc %d, vel %d, status =0x%04x\n",
ptrans->motor_pos, ptrans->encoder_pos,ptrans->vel, ptrans->status);
Debug(6, "v544_callback: FASTUNLOCK\n", 0);
FASTUNLOCK(&ptrans->lock);
/* free the return data buffer */
Debug(6, "v544_callback: free MOTOR_RETURN\n", 0);
(*v544_access.free) (motor_return);
Debug(6, "v544_callback: dbScanLock\n", 0);
dbScanLock((struct dbCommon *) mr);
Debug(6, "v544_callback: calling process\n", 0);
(*prset->process) ((struct dbCommon *) mr);
Debug(6, "v544_callback: dbScanUnlock\n", 0);
dbScanUnlock((struct dbCommon *) mr);
Debug(5, "v544_callback: exit..\n", 0);
return;
}
/* callback from V544 driver for init_record - duplicate of
* the v544_callback without the call to process the record */
STATIC void v544_init_callback(MOTOR_RETURN * motor_return)
{
struct motorRecord *mr = (struct motorRecord *) motor_return->precord;
struct rset *prset = (struct rset *) (motor_return->precord->rset);
struct v544_trans *ptrans;
Debug(5, "v544_init_callback: entry\n", 0);
ptrans = (struct v544_trans *) mr->dpvt;
Debug(6, "v544_init_callback: FASTLOCK\n", 0);
FASTLOCK(&ptrans->lock);
/* raw motor pulse count */
Debug(6, "v544_init_callback: update ptrans\n", 0);
ptrans->callback_changed = YES;
ptrans->motor_pos = motor_return->position;
/* raw encoder pulse count */
ptrans->encoder_pos = motor_return->encoder_position;
/* raw motor velocity */
ptrans->vel = motor_return->velocity;
ptrans->status = motor_return->status; /* status */
/* free the return data buffer */
Debug(6, "v544_init_callback: free MOTOR_RETURN\n", 0);
(*v544_access.free) (motor_return);
/* Continue record init. */
Debug(6, "v544_init_callback: Continue sig.\n", 0);
semGive(ptrans->initSem);
Debug(6, "v544_init_callback: FASTUNLOCK\n", 0);
FASTUNLOCK(&ptrans->lock);
Debug(5, "v544_init_callback: exit\n", 0);
return;
}
File diff suppressed because it is too large Load Diff
+266
View File
@@ -0,0 +1,266 @@
/* File: drvV544.h */
/* Version: 1.2 */
/* Date Last Modified: 8/6/96 */
/* Device Support Definitions for Highland motor controller */
/*
* Original Author: Jim Kowalkowski
* Current Author: Joe Sullivan
* Date: 11/14/94
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 01-18-93 jbk initialized
* .02 11-14-94 jps copy drvOms.h and modify to point to vme58 driver
* .03 ??-??-95 jps copy drvOms58.h and modify to point to Highland driver
* ...
*/
/*
* valid command types for the driver, the order is importance, 0 is of
* lowest importance. Device support will choose the greatest one to use as
* the driver transaction type.
*/
#define UNDEFINED (unsigned char)0 /* garbage type */
#define IMMEDIATE (unsigned char)1 /* 'i' an execute immediate, no reply */
#define MOVE_TERM (unsigned char)2 /* 't' terminate a previous active
* motion */
#define MOTION (unsigned char)3 /* 'm' will produce motion updates */
#define VELOCITY (unsigned char)4 /* 'v' make motion updates till
* MOVE_TERM */
#define INFO (unsigned char)5 /* 'f' get curr motor/encoder pos and
* stat */
#define QUERY (unsigned char)6 /* 'q' specialty type, not needed */
/* Set list size to accomidate the 2 move commands
* when all parameters are being changed */
#define MAX_LIST_SIZE 10
typedef struct CmndDef
{
int32_t pos;
uint16_t vel;
uint16_t accel;
uint16_t standby; /* Drive current standby percent */
uint16_t cmnd; /* Motion command (ABS,INC, HOME,...) */
} CMND_DEF;
/* message queue management - device and driver support only */
struct mess_node
{
CALLBACK callback;
int signal;
int card;
unsigned char type;
int cmndCnt; /* Number of commands in list */
int cmndIndx; /* Current command index */
CMND_DEF cmndList[MAX_LIST_SIZE];
long position;
long encoder_position;
long velocity;
unsigned long status;
struct dbCommon *precord;
struct mess_node *next;
};
typedef struct mess_node MOTOR_CALL;
typedef struct mess_node MOTOR_RETURN;
typedef struct mess_card_query
{
char *card_name;
int total_axis;
} MOTOR_CARD_QUERY;
typedef struct mess_axis_query
{
long position;
long encoder_position;
unsigned long status;
} MOTOR_AXIS_QUERY;
struct v544_support
{
int (*send) (MOTOR_CALL *);
int (*free) (struct mess_node *);
int (*get_card_info) (int, MOTOR_CARD_QUERY *);
int (*get_axis_info) (int, int signal, MOTOR_AXIS_QUERY *);
};
/* Board control register structures */
/*
* V544 default profile
*/
#define V544_NUM_CARDS 8
#define V544_NUM_CHANNELS 4
#define V544_INTERRUPT_TYPE intVME
#define V544_ADDRS_TYPE atVMEA16
#define V544_NUM_ADDRS 0xDD00
#define V544_INT_LEVEL 5 /* default interrupt level (1-6) */
#define V544_BRD_SIZE 0x80 /* card address boundary */
/* Board type info */
#define V544_LABEL "V544"
#define V544_VXID 0xFEEE /* Manufacturer registered VXI ID */
#define BRD_OFFSET 0x80
#define VXI_BASE 0xC000
#define VXI_VECTOR(v) ((v-VXI_BASE)/0x40)
/* V544 Parameter Scaling Constants */
/* (assuming half step scaling by the record */
#define STEP_SCALE 256 /* Position scaling per step */
#define VEL_SCALE 16 /* Velocity scaling per step/sec */
#define ACC_SCALE 256 /* Acceleration scaling per step/sec/sec */
/* V544 Parameter Limits */
#define MAX_VEL 4096
#define MAX_ACCEL 65535
#define MAX_POS (2**31)
/* V544 Control Register Definitions */
/* VCSR register status bits (board level status) */
#define VCSR_RES 0x1 /* Soft Reset */
#define VCSR_SFT 0x4 /* Self-test passed */
#define VCSR_RDY 0x8 /* Ready to operate */
#define VCSR_MOD 0x4000 /* Module ID 'off' */
/* VCSR register commands (board level commands) */
#define VCSR_FRZ 0x8000 /* Board level freeze on motor positions updates */
/* OPTS (options) register status masks */
#define OPTS_ENC 0x1 /* Encoder option enabled */
#define OPTS_ACT 0x2 /* Encoder FPGA installed */
#define OPTS_SER 0x4 /* Serial port enabled */
/* Per-axis Command/Status register definitions */
/* status bits */
#define CS_BUSY_TO 2 /* Maximum command ACK lantency in polling cycles */
#define CS_ABORT_TO 1900 /* Dwell loop count for send_cmnd() check of abort bug */
#define CS_ERROR 0x8000 /* Command error flag */
#define CS_DONE 0x80 /* Command completed flag */
#define CS_BUSY 0x40 /* Command accepted */
/* CMD codes (bits 0-4)*/
#define CS_IDLE 0x0 /* Soft-reset - update status/position regs */
#define CS_RUN_CW 0x1 /* Constant velocity - CW */
#define CS_RUN_CCW 0x2 /* Constant velocity - CCW */
#define CS_MOVE_ABS 0x3 /* Move to absolute motor position */
#define CS_MOVE_INC 0x4 /* Move to relative motor position */
#define CS_SEEK_INDX 0x5 /* Stop ABS move at index switch */
#define CS_BACKOFF_CW 0x6 /* Stop CW ABS move when off limit switch */
#define CS_BACKOFF_CCW 0x7 /* Stop CCW ABS move when off limit switch */
#define CS_SET_ZERO 0x8 /* Set absolute zero to motor position */
#define CS_SET_POSITION 0x9 /* Set logical position to motor position */
#define CS_READ_THETA 0xA /* Read motor quadrant/microstep position */
/* Operations */
#define CS_GO 0x20 /* Initiate command execution */
/* Interrupt control */
#define CS_IEN_ON 0x4000 /* Bit 14 - Enable interrupt on "DONE" */
#define CS_IEN_OFF 0x0
/* Motor PARKing currents (bits 9,10) */
#define CS_PARK_100 0x000 /* Part motor at 100% current */
#define CS_PARK_75 0x200 /* 75% */
#define CS_PARK_50 0x400 /* 50% */
#define CS_PARK_0 0x600 /* 0% */
/* V544 STATus Register Bit Mapping */
#define STAT_BADC 0x8000 /* Illegal command detected */
#define STAT_SPRA 0x20 /* Spare limit switch status */
#define STAT_MOTR 0x8 /* Motor driver error */
#define STAT_CCWL 0x4 /* CCW limit switch status */
#define STAT_CWL 0x2 /* CW limit switch status */
#define STAT_INDX 0x1 /* INDEX switch status */
/* V544 DUAL-PORT MEMORY MAP */
/* Define byte offsets into axis control registers */
#define COARSE_POS_ID 0x0
#define FINE_POS_ID 0x2
#define VELOCITY_ID 0x4
#define ACCEL_ID 0x6
#define LS_DECEL_ID 0x8
#define AUX_REG_ID 0xa
#define STAT_REG_ID 0xc
#define CMND_REG_ID 0xe
/*
* union posReg{
* int32_t all;
* struct {
* int16_t high;
* int16_t low;
* } split;
* };
*/
typedef struct pos_reg_str {
int16_t coarse;
uint16_t fine;
} POS_REG;
/* Define register structure */
typedef struct motor_reg_str {
POS_REG pos;
uint16_t velocity;
uint16_t accel;
uint16_t lsDecel;
uint16_t aux;
uint16_t status;
uint16_t cmnd;
} MOTOR_REG;
struct vmex_motor {
uint16_t vxid;
uint16_t vtype;
uint16_t vcsr;
uint16_t pgid;
uint16_t prev;
uint16_t vsn;
uint16_t opts;
uint16_t scan;
/* Programming/Status Registers */
MOTOR_REG motorReg[V544_NUM_CHANNELS];
/* Readback Registers */
POS_REG posReg[V544_NUM_CHANNELS]; /* Actual positions */
uint16_t velReg[V544_NUM_CHANNELS]; /* Actual velocity */
};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff