forked from epics_driver_modules/motorBase
Version 4-1
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
#CONFIG
|
||||
# Add any changes to make rules here
|
||||
CROSS_COMPILER_TARGET_ARCHS = mv167
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
#RULES.Host
|
||||
|
||||
include $(EPICS_BASE)/config/RULES.Host
|
||||
@@ -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)/* : ;
|
||||
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
#RULES.iocBoot
|
||||
DIRS += $(wildcard ioc*)
|
||||
DIRS += $(wildcard as*)
|
||||
include $(EPICS_BASE)/config/RULES_DIRS
|
||||
@@ -0,0 +1,2 @@
|
||||
#RULES_ARCHS
|
||||
include $(EPICS_BASE)/config/RULES_ARCHS
|
||||
@@ -0,0 +1,2 @@
|
||||
#RULES_DIRS
|
||||
include $(EPICS_BASE)/config/RULES_DIRS
|
||||
@@ -0,0 +1,5 @@
|
||||
#RULES_TOP
|
||||
include $(EPICS_BASE)/config/RULES_TOP
|
||||
|
||||
uninstall::
|
||||
@$(RMDIR) $(INSTALL_DB)
|
||||
@@ -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;
|
||||
@@ -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: $!";
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
TOP=..
|
||||
include $(TOP)/config/CONFIG_APP
|
||||
include $(EPICS_BASE)/config/RULES_ARCHS
|
||||
@@ -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
@@ -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> <u>VME58</u>
|
||||
<br>devOms.c devOms58.c
|
||||
<br>drvOms.h drvOms58.h
|
||||
<br>drvOms.c 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>
|
||||
|
||||
<u>MM4000</u>
|
||||
<br>devMM3000.c devMM4000.c
|
||||
<br>drvMM3000.c 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>
|
||||
@@ -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. All the errors caused
|
||||
the same problem. Namely, erroneous retries occurred intermittently
|
||||
when multiple axes were commanded to move on the same controller.
|
||||
This error occurred because old position data was being passed back from
|
||||
the driver after Done was detected. 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>
|
||||
<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. 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>
|
||||
<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. 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. 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. 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).
|
||||
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. <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. The motor record clears the HOM[F/R]
|
||||
field when the homing operation is done (i.e., completed or terminated).
|
||||
<br>
|
||||
<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.
|
||||
In other words, the controller's target position takes precedence over
|
||||
the autorestore value when both systems have non-zero DVAL values.
|
||||
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>
|
||||
<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. 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.
|
||||
Driver level software detects and saves which controller it is communicating
|
||||
with at boot-up. Currently, there are two functional differences
|
||||
between the two models.</li>
|
||||
|
||||
<ol>
|
||||
<li>
|
||||
The MM4005's position cannot be set by a host. 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. User access to the controller's
|
||||
front panel is required to scroll the front panel display through all eight
|
||||
axes. 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>. 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. A <i>Controller communication error</i>
|
||||
bit was allocated in the MSTA field to help aid user's in diagnosing a
|
||||
controller communication error. Currently, only the MM4000/5 device
|
||||
driver sets this error indicator. 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. User's who want their MM4000/5
|
||||
in remote mode at boot-up can add the remote mode command ("MR") to
|
||||
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. 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. 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.
|
||||
In other words, the user can only define the current controller position
|
||||
as being the zero position. This limitation is reflected in the motor
|
||||
record device support. 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.
|
||||
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. For example, if the minimum is entered and it exceeds the
|
||||
maximum, then the maximum is set to the new minimum value. 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.
|
||||
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. 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. </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>
|
||||
<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. This document describes changes made to the motor record
|
||||
and its' associated device driver support between versions 3.4 and 3.5.
|
||||
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. Each of the following
|
||||
fields is defined as a character string:
|
||||
<blockquote> 1. INIT - Sent at record initialization.
|
||||
<br> 2. PREM - Sent before every command string that causes motion.
|
||||
<br> 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. 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". In addition, valid values for the GAIN field have been changed
|
||||
to [0.0, for MM4000 - 0.00005, for OMS] <= GAIN <= 1. 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>
|
||||
<br>
|
||||
<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. 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>
|
||||
User I/O #0 <> X axis
|
||||
<br>
|
||||
" " 1 <> Y "
|
||||
<br>
|
||||
.....................
|
||||
<br>
|
||||
" " 7 <> S "
|
||||
<li>
|
||||
Drive-power monitoring defaults to disabled at boot-up. Request enabling
|
||||
drive-power monitoring by entering the device directive "@DPM_ON@" command
|
||||
into the motor record initialization field (i.e., INIT). 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. 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. 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.
|
||||
First, to solve the problem of nonlinear position conversion in the data
|
||||
base, rather than in the record. 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. The new fields are all database links associated
|
||||
with existing motor record fields. The new links and their associated
|
||||
fields are listed in the table below:
|
||||
<br>
|
||||
<br>
|
||||
<center><table BORDER NOSAVE >
|
||||
<tr ALIGN=CENTER NOSAVE>
|
||||
<td NOSAVE><u>Link</u></td>
|
||||
|
||||
<td><u>Link Type</u></td>
|
||||
|
||||
<td> <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. These links are ignored when using any
|
||||
other Motor Record device driver. 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. Users must choose either a dial input link (RDBL) or a raw
|
||||
input link (RINP), but not both.
|
||||
<br>
|
||||
<br>
|
||||
<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.
|
||||
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. User
|
||||
and dial travel limit values are valid if they are within the travel limits
|
||||
set on the front panel of the MM4000 controller. 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). 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>
|
||||
<u>For all OMS controllers</u></blockquote>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
CKP = GAIN
|
||||
CKP = 1999.9 * GAIN</li>
|
||||
|
||||
<li>
|
||||
CKI = 2 * GAIN
|
||||
CKI = 2 * 1999.9 * GAIN</li>
|
||||
|
||||
<li>
|
||||
CKD = 3 * GAIN
|
||||
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.
|
||||
This version (i.e., version 1.3) is functionally the same as the earlier
|
||||
release (i.e., version 1.2). No new features have been added.
|
||||
<br>
|
||||
<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. 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).
|
||||
The EPICS device driver support task (i.e., tmotor) would query the OMS
|
||||
motion controller for status information immediately after a motion command.
|
||||
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. 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.
|
||||
Sometimes they exhibited themselves intermittently, other times bad data
|
||||
stayed in the record. 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. 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). A motor lacking
|
||||
either of the above two features is classified as a stepper and is supported
|
||||
by the standard motor record features. The following three motor
|
||||
record fields support servo motors:
|
||||
<p> 1. GAIN - Closed loop position response gain of the motor.
|
||||
<br> 2. CNEN - Enable/disable closed loop position control request.
|
||||
<br> 3. STEN - Closed loop position control status (i.e.,
|
||||
<br> 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). 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. Bit#11 of the MSTA field is set on/off based on
|
||||
the results of this test. 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. Otherwise, they are disabled and bit#11 of the MSTA
|
||||
is set off. 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. 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.
|
||||
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. 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. Each field is defined as a character string.
|
||||
<p> 1. INIT - Sent at record initialization.
|
||||
<br> 2. PREM - Sent before every command string that causes motion.
|
||||
<br> 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. Command primitives that
|
||||
result in a response from the motion control board are valid, but the response
|
||||
is not processed.
|
||||
<br>
|
||||
<br>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,4 @@
|
||||
TOP = ..
|
||||
include $(TOP)/config/CONFIG_APP
|
||||
DIRS += $(wildcard *ioc*)
|
||||
include $(TOP)/config/RULES.iocBoot
|
||||
@@ -0,0 +1,4 @@
|
||||
TOP = ../..
|
||||
include $(TOP)/config/CONFIG_APP
|
||||
ARCH = mv167
|
||||
include $(TOP)/config/RULES.ioc
|
||||
@@ -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>
|
||||
@@ -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")
|
||||
@@ -0,0 +1,3 @@
|
||||
TOP=../..
|
||||
include $(TOP)/config/CONFIG_APP
|
||||
include $(TOP)/config/RULES_ARCHS
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
TOP=../..
|
||||
include $(TOP)/config/CONFIG_APP
|
||||
include $(TOP)/config/RULES_ARCHS
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -0,0 +1,3 @@
|
||||
TOP=../..
|
||||
include $(TOP)/config/CONFIG_APP
|
||||
include $(TOP)/config/RULES_ARCHS
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------*/
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
TOP=../..
|
||||
include $(TOP)/config/CONFIG_APP
|
||||
include $(TOP)/config/RULES_ARCHS
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -0,0 +1,3 @@
|
||||
TOP=../..
|
||||
include $(TOP)/config/CONFIG_APP
|
||||
include $(TOP)/config/RULES_ARCHS
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# Soft Channel driver support.
|
||||
device(motor,CONSTANT,devMotorSoft,"Soft Channel")
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
TOP=../..
|
||||
include $(TOP)/config/CONFIG_APP
|
||||
include $(TOP)/config/RULES_ARCHS
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
# Highland V544 driver support.
|
||||
device(motor,VME_IO,devV544,"Highland V544")
|
||||
driver(drvV544)
|
||||
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user