forked from epics_driver_modules/motorBase
Initial version of ACS Tech80 motor controller
Tested on Nanomotion (picomotor) motor stage
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
FILENAME... ACSTech80Register.cc
|
||||
USAGE... Register ACS Tech80 motor device driver shell commands.
|
||||
|
||||
Version: $Revision: 1.1 $
|
||||
Modified By: $Author: sullivan $
|
||||
Last Modified: $Date: 2006-05-19 16:39:44 $
|
||||
*/
|
||||
|
||||
/*****************************************************************
|
||||
COPYRIGHT NOTIFICATION
|
||||
*****************************************************************
|
||||
|
||||
(C) COPYRIGHT 1993 UNIVERSITY OF CHICAGO
|
||||
|
||||
This software was developed under a United States Government license
|
||||
described on the COPYRIGHT_UniversityOfChicago file included as part
|
||||
of this distribution.
|
||||
**********************************************************************/
|
||||
|
||||
#include <iocsh.h>
|
||||
#include "ACSTech80Register.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
// ACSTech80 Setup arguments
|
||||
static const iocshArg setupArg0 = {"Max. controller count", iocshArgInt};
|
||||
static const iocshArg setupArg1 = {"Polling rate", iocshArgInt};
|
||||
|
||||
// ACSTech80 Config arguments
|
||||
static const iocshArg configArg0 = {"Card being configured", iocshArgInt};
|
||||
static const iocshArg configArg1 = {"asyn port name", iocshArgString};
|
||||
|
||||
static const iocshArg * const ACSTech80SetupArgs[2] = {&setupArg0, &setupArg1};
|
||||
|
||||
static const iocshArg * const ACSTech80ConfigArgs[2] = {&configArg0, &configArg1};
|
||||
|
||||
static const iocshFuncDef setupSPiiPlus = {"SPiiPlusSetup",2, ACSTech80SetupArgs};
|
||||
|
||||
static const iocshFuncDef configSPiiPlus = {"SPiiPlusConfig", 2, ACSTech80ConfigArgs};
|
||||
|
||||
|
||||
static void setupSPiiPlusCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
SPiiPlusSetup(args[0].ival, args[1].ival);
|
||||
}
|
||||
|
||||
static void configSPiiPlusCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
SPiiPlusConfig(args[0].ival, args[1].sval);
|
||||
}
|
||||
|
||||
|
||||
static void ACSTech80Register(void)
|
||||
{
|
||||
|
||||
iocshRegister(&setupSPiiPlus, setupSPiiPlusCallFunc);
|
||||
|
||||
iocshRegister(&configSPiiPlus, configSPiiPlusCallFunc);
|
||||
}
|
||||
|
||||
epicsExportRegistrar(ACSTech80Register);
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
FILENAME... NewportRegister.h
|
||||
USAGE... This file contains function prototypes for Newport IOC shell commands.
|
||||
|
||||
Version: 1.4
|
||||
Modified By: rivers
|
||||
Last Modified: 2004/07/28 18:45:16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Original Author: Ron Sluiter
|
||||
* Date: 05/19/03
|
||||
*
|
||||
* 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:
|
||||
* -----------------
|
||||
*/
|
||||
|
||||
#include "motor.h"
|
||||
#include "motordrvCom.h"
|
||||
|
||||
/* Function prototypes. */
|
||||
extern RTN_STATUS SPiiPlusSetup(int, int);
|
||||
extern RTN_STATUS SPiiPlusConfig(int, const char *);
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Makefile
|
||||
TOP = ../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
# The following are used for debugging messages.
|
||||
#USR_CXXFLAGS += -DDEBUG
|
||||
|
||||
OPT_CXXFLAGS =
|
||||
|
||||
DBD += devSPiiPlus.dbd
|
||||
|
||||
LIBRARY_IOC = acsTech80
|
||||
|
||||
# Intelligent Motion Systems driver support.
|
||||
SRCS += ACSTech80Register.cc
|
||||
SRCS += devSPiiPlus.cc drvSPiiPlus.cc
|
||||
|
||||
acsTech80_LIBS += motor asyn
|
||||
acsTech80_LIBS += $(EPICS_BASE_IOC_LIBS)
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
ACSTech80 SPiiPlus Motor Controller
|
||||
===================================
|
||||
|
||||
|
||||
**** EXAMPLE CONFIGURATION FOR VXWORKS TARGETS ****
|
||||
|
||||
xxxApp/src/Makefile
|
||||
-------------------
|
||||
xxx_vxWorks_LIBS += acsTech80
|
||||
|
||||
xxxApp/src/xxxCommonInclude.dbd
|
||||
--------------------------------
|
||||
include "devSPiiPlus.dbd"
|
||||
|
||||
|
||||
iocBoot/iocxxx/serial.cmd
|
||||
---------------------------
|
||||
|
||||
# serial 61 is connected to the SPiiPlus Motor Controller
|
||||
#drvAsynIPPortConfigure("portName","hostInfo",priority,noAutoConnect,
|
||||
# noProcessEos)
|
||||
drvAsynIPPortConfigure("serial61", "164.54.116.61:701", 0, 0, 0)
|
||||
asynOctetSetInputEos("serial61",0,"\r")
|
||||
asynOctetSetOutputEos("serial61",0,"\r")
|
||||
|
||||
.
|
||||
.
|
||||
.
|
||||
|
||||
# ACS Tech80 - SPiiPlus driver setup parameters:
|
||||
# (1) maximum number of controllers in system
|
||||
# (2) motor task polling rate (min=1Hz, max=60Hz)
|
||||
SPiiPlusSetup(1, 60)
|
||||
|
||||
# ACS Tech80 - SPiiPlus driver configuration parameters:
|
||||
# (1) controller being configured
|
||||
# (2) asyn port name (string)
|
||||
SPiiPlusConfig(0, "serial61")
|
||||
|
||||
|
||||
iocBoot/iocxxx/motor.substitutions
|
||||
----------------------------------
|
||||
{xxx:, 1, m$(N), "SPiiPlus", 0, 0, "motor $(N)", degrees, Pos, 10, 0., 1., 0, 1, .2, 1e-3, 3, 100, -100, ""}
|
||||
{xxx:, 2 m$(N), "SPiiPlus", 0, 1, "motor $(N)", degrees, Pos, 60, 0., 1.5, 0, 1, .2, 1e-3, 3, 100, -100, ""}
|
||||
|
||||
|
||||
****************** SPiiPlus Setup *******************************
|
||||
|
||||
Install the "SPiiPlus_EPICS.prg" file in the SPiiPlus motor controller
|
||||
using the ACSTech80 WINDOWS software.
|
||||
|
||||
This file contains the EPICS interface programs need by the
|
||||
motorRecord driver to control one SPiiPlus motors (AXIS 0). For
|
||||
each motor used, copy the contents of Buffer #0 it's corresponding
|
||||
buffer. (Motor 1 - buffer #0, Motor 2 - buffer #1, ... etc()
|
||||
|
||||
Each motor is assigned a SPiiPlus buffer with a copy of all the programs
|
||||
that are currently in buffer #0.
|
||||
@@ -0,0 +1,274 @@
|
||||
#/ Controller version = 4.50
|
||||
#/ Date = 04/26/2006 16:41
|
||||
#/ User remarks = hello
|
||||
#0
|
||||
!Note: Each motor needs all the programs in BUFFER 0 copied into its corresponding buffer.
|
||||
!Note: ie: motor 1 - buffer #0 , motor 2 - buffer #1 ..... etc.
|
||||
!Note: VEL and ACC are commented out for the AC mode as they are set from EPICS MEDM screens.
|
||||
!Note: VEL and ACC are DEFINED for the DC mode as they are not defined from EPICS MEDM screens
|
||||
!ACC is defined in the homing routines as they are defined from EPICS during Homing but VEL is defined.
|
||||
|
||||
GLOBAL acpar(4)
|
||||
GLOBAL dcpar(4)
|
||||
GLOBAL INT home_F(4)
|
||||
GLOBAL INT home_R(4)
|
||||
GLOBAL target_pos(4)
|
||||
GLOBAL jog_vel(4)
|
||||
GLOBAL Done(4)
|
||||
GLOBAL opReq(4)
|
||||
|
||||
! Each AXIS has a copy of these functions in a corresponding buffer
|
||||
LOCAL Axis
|
||||
Axis = 0
|
||||
|
||||
IF opReq(Axis) = 1
|
||||
call ABS_MOVE
|
||||
ELSEIF opReq(Axis) = 2
|
||||
call REL_MOVE
|
||||
ELSEIF opReq(Axis) = 3
|
||||
call JOG_MOVE
|
||||
ELSEIF opReq(Axis) = 4
|
||||
call HOME_F
|
||||
ELSEIF opReq(Axis) = 5
|
||||
call HOME_R
|
||||
END
|
||||
|
||||
opReq(Axis) = 0; Done(Axis) = 1
|
||||
|
||||
STOP
|
||||
|
||||
!THESE PROGRAMS ARE SMALL ROUTINES THAT ARE REQUIRED FOR CO-ORDINATING MOVEMENT WITH EPICS. THIS IS ESPECIALLY NEEDED
|
||||
!BECAUSE OF THE AC AND DC MODE SWITCHING NEEDED for AB2 amplifier BEFORE AND AFTER EVERY MOVE.
|
||||
!Written by Joe Sullivan (BCDA) and Suresh (8-ID) (March 2006)
|
||||
|
||||
ABS_MOVE:
|
||||
! Switch to AC(Servo) Mode
|
||||
acpar(Axis)=1; TILL acpar(Axis)=0
|
||||
PTP(Axis),target_pos(Axis)
|
||||
TILL ^MST(Axis).#MOVE
|
||||
! Switch to DC(Position) Mode
|
||||
dcpar(Axis)=1; TILL dcpar(Axis)=0
|
||||
RET
|
||||
|
||||
REL_MOVE:
|
||||
acpar(Axis)=1; TILL acpar(Axis)=0
|
||||
PTP/r(Axis),target_pos(Axis)
|
||||
TILL ^MST(Axis).#MOVE
|
||||
dcpar(Axis)=1; TILL dcpar(Axis)=0
|
||||
RET
|
||||
|
||||
|
||||
JOG_MOVE:
|
||||
acpar(Axis)=1; TILL acpar(Axis)=0
|
||||
JOG/v(Axis),jog_vel(Axis)
|
||||
TILL ^MST(Axis).#MOVE
|
||||
dcpar(Axis)=1; TILL dcpar(Axis)=0
|
||||
RET
|
||||
|
||||
|
||||
!These program are for homing X and was written by Aaron Dietrich, ACS-Tech80 and modified by Suresh, 8-ID (March 2006)
|
||||
!EPICS calls these routines when you Home from the MEDM screen.
|
||||
!HOME_R homes in the -ve direction and HOME_F homes in the +ve direction
|
||||
!If you desire to home in the same direction due to constraints, change the last 2 lines
|
||||
!in this program accordingly.
|
||||
!Note: this program is written for AB2 amplifier. If using with AB5, comment out acpar and dcpar lines
|
||||
!and rest should be fine.
|
||||
|
||||
|
||||
HOME_R:
|
||||
! Load tuned parameters for AC mode for AB2 Amplifier
|
||||
!disable this when using AB5 amplifier
|
||||
acpar(Axis)=1; TILL acpar(Axis)=0
|
||||
!VEL(Axis) = 102400*2 !2 mm/sec !defined from EPICS
|
||||
ACC(Axis) = VEL(Axis)*10
|
||||
DEC(Axis) = ACC(Axis)
|
||||
KDEC(Axis) = DEC(Axis)*2
|
||||
JERK(Axis) = ACC(Axis)*20
|
||||
|
||||
DISP "Homing in negative direction is in PROGRESS ......"
|
||||
ENABLE (Axis)
|
||||
FDEF(Axis).#CL = 0 !disable default response of Current Limit fault
|
||||
FDEF(Axis).#CPE = 0 !disable default response of Critical Postion Error fault
|
||||
FDEF(Axis).#SRL = 0 !disable default response of Software Right Limit fault
|
||||
FDEF(Axis).#SLL = 0 !disable default response of Software Left Limit fault
|
||||
|
||||
JOG (Axis), -
|
||||
TILL (ABS(PE(Axis))>10000)
|
||||
JOG (Axis), +
|
||||
IST(Axis).#IND = 0 !enables the index capture
|
||||
TILL IST(Axis).#IND = 1 !wait till index capture happens
|
||||
KILL (Axis)
|
||||
SET FPOS(Axis)=FPOS(Axis)-IND(Axis) !this sets index position as 0 reference point
|
||||
PTP (Axis), 0
|
||||
TILL ^MST(Axis).#MOVE
|
||||
|
||||
FDEF(Axis).#CL = 1 !re-enable default response of Current Limit fault
|
||||
FDEF(Axis).#CPE = 1 !re-enable default response of Critical Postion Error fault
|
||||
FDEF(Axis).#SRL = 1 !re-enable default response of Software Right Limit fault
|
||||
FDEF(Axis).#SLL = 1 !re-enable default response of Software Left Limit fault
|
||||
|
||||
!Put the stage in DC Mode
|
||||
dcpar(Axis)=1; TILL dcpar(Axis)=0
|
||||
!DISABLE (Axis)
|
||||
DISP "Homing is DONE ......"
|
||||
home_R(Axis) = 0;
|
||||
RET
|
||||
|
||||
|
||||
HOME_F:
|
||||
! Load tuned parameters for AC mode for AB2 Amplifier
|
||||
!disable this when using AB5 amplifier
|
||||
acpar(Axis)=1; TILL acpar(Axis)=0
|
||||
|
||||
!VEL(Axis) = 102400*2 !2 mm/sec !defined from EPICS
|
||||
ACC(Axis) = VEL(Axis)*10
|
||||
DEC(Axis) = ACC(Axis)*0.5
|
||||
KDEC(Axis) = DEC(Axis)
|
||||
JERK(Axis) = ACC(Axis)*10
|
||||
|
||||
DISP "Homing in positive direction is in PROGRESS ......"
|
||||
ENABLE (Axis)
|
||||
FDEF(Axis).#CL = 0 !disable default response of Current Limit fault
|
||||
FDEF(Axis).#CPE = 0 !disable default response of Critical Postion Error fault
|
||||
FDEF(Axis).#SRL = 0 !disable default response of Software Right Limit fault
|
||||
FDEF(Axis).#SLL = 0 !disable default response of Software Left Limit fault
|
||||
|
||||
JOG (Axis), +
|
||||
TILL (ABS(PE(Axis))>10000)
|
||||
JOG (Axis), -
|
||||
IST(Axis).#IND = 0 !enables the index capture
|
||||
TILL IST(Axis).#IND = 1 !wait till index capture happens
|
||||
KILL (Axis)
|
||||
SET FPOS(Axis)=FPOS(Axis)-IND(Axis) !this sets index position as 0 reference point
|
||||
PTP (Axis), 0
|
||||
TILL ^MST(Axis).#MOVE
|
||||
|
||||
FDEF(Axis).#CL = 1 !re-enable default response of Current Limit fault
|
||||
FDEF(Axis).#CPE = 1 !re-enable default response of Critical Postion Error fault
|
||||
FDEF(Axis).#SRL = 1 !re-enable default response of Software Right Limit fault
|
||||
FDEF(Axis).#SLL = 1 !re-enable default response of Software Left Limit fault
|
||||
|
||||
!Put the stage in DC Mode
|
||||
dcpar(Axis)=1; TILL dcpar(Axis)=0
|
||||
!DISABLE (Axis)
|
||||
DISP "Homing is DONE ......"
|
||||
home_F(Axis) = 0;
|
||||
RET
|
||||
|
||||
|
||||
!THESE PROGRAMS TUNE PARAMETERS SAVED FOR AB2 and AB5 AND THE GOOD
|
||||
!STAGE TO BE USED IN THE MONO IN 8-ID
|
||||
|
||||
!MOSTLY WE WILL NEVER USE AB5 AMPLIFIER AS THE MOTOR IS ALWAYS SERVOING IN THIS MODE
|
||||
!AND IS NOT GOOD FOR VACUUM USE AS THE MOTOR GETS HOT AND CANNOT BE ENABLED FOR MORE THAN
|
||||
!10 MINUTES OR SO. IN AB2, DEADBAND IS ACTIVE AND SO THE MOTOR IS BASICALLY DISABLED
|
||||
!ONCE THE STAGE IS WITHIN THE DEADBAND MAX WHICH IS LIKE 10 COUNTS IN THE CURRENT TUNING
|
||||
!CONFIGURATION AND IS ABOUT 100 nm.
|
||||
!In the DC mode, the motor can be kept enabled forever in vacuum and holds position to within 1 count
|
||||
!which is currently 10 nm.
|
||||
!Written by Suresh (8-ID) (March 2006)
|
||||
|
||||
!Note: VEL and ACC are commented out for the AC mode as they are set from EPICS MEDM screens.
|
||||
!Note: VEL and ACC are DEFINED for the DC mode as they are not defined from EPICS MEDM screens
|
||||
|
||||
!To switch from DC to AC you must disable the motor
|
||||
AC_TUNED_PAR: ! FOR LONG MOVE
|
||||
!!!! BLOCK !All commands between BLOCK and END will be executed in one controller cycle (1 msec)
|
||||
DISABLE(Axis);
|
||||
SLCPRD(Axis)=1E9; !Set this parameter for Nanomotion with High res. on sin/cos encoder to over come a bug in ACS related to Commutating motors
|
||||
!VEL(Axis)= 102400 * 1.0; !defined from EPICS
|
||||
!ACC(Axis)=VEL(Axis)*10; !defined from EPICS
|
||||
!SET DC_MODE to 0 and SET Nanomotion bit to 1 resp.
|
||||
MFLAGS(Axis).30 = 0;
|
||||
MFLAGS(Axis).7 = 1;
|
||||
XVEL(Axis)=2.048E7;
|
||||
SLPKP(Axis)=300;
|
||||
SLVKP(Axis)=20;
|
||||
SLVKI(Axis)= 1600;
|
||||
SLFRC(Axis)=17;
|
||||
SLDZMIN(Axis)=2;
|
||||
SLDZMAX(Axis)=10;
|
||||
TARGRAD(Axis) = SLDZMAX(Axis);
|
||||
SETTLE(Axis) = 10;
|
||||
DEC(Axis)=ACC(Axis)*0.5;
|
||||
KDEC(Axis)=DEC(Axis);
|
||||
JERK(Axis)=ACC(Axis)*10;
|
||||
!!!! END !All commands between BLOCK and END will be executed in one controller cycle (1 msec)
|
||||
ENABLE(Axis);
|
||||
acpar(Axis)=0;
|
||||
RET
|
||||
|
||||
|
||||
DC_TUNED_PAR:
|
||||
!SET DC_MODE to 1 and SET Nanomotion bit to 0 resp.
|
||||
!!!! BLOCK !All commands between BLOCK and END will be executed in one controller cycle (1 msec)
|
||||
VEL(Axis)=15;
|
||||
ACC(Axis)=VEL(Axis)*10;
|
||||
MFLAGS(Axis).30 = 1;
|
||||
MFLAGS(Axis).7 =0;
|
||||
XVEL(Axis)=2.048E7;
|
||||
SLPKP(Axis)=2500;
|
||||
SLVKP(Axis)=20;
|
||||
SLVKI(Axis)= 9000;
|
||||
SLFRC(Axis)=0;
|
||||
SETTLE(Axis) = 10;
|
||||
DEC(Axis)=ACC(Axis);
|
||||
KDEC(Axis)=DEC(Axis);
|
||||
JERK(Axis)=ACC(Axis)*10;
|
||||
!SLDZMIN(Axis)=2;SLDZMAX(Axis)=10;TARGRAD(Axis) = SLDZMAX(Axis);
|
||||
!!!! END !All commands between BLOCK and END will be executed in one controller cycle (1 msec)
|
||||
dcpar(Axis)=0;
|
||||
RET
|
||||
|
||||
|
||||
ON home_R(0)=1; Axis = 0; CALL HOME_R; RET
|
||||
ON home_F(0)=1; Axis = 0; CALL HOME_F; RET
|
||||
ON acpar(0)=1;Axis=0;CALL AC_TUNED_PAR; RET
|
||||
ON dcpar(0)=1;Axis=0;CALL DC_TUNED_PAR; RET
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#4
|
||||
!THIS BUFFER STOPS MOTION PROGRAMS ON A PER AXIS BASES AND RETURNS TO DC MODE
|
||||
!Done(Axis) Flag is used by EPICS to check when op operation is complete
|
||||
!Written by Joe Sullivan (BCDA) and Suresh (8-ID) (March 2006)
|
||||
|
||||
GLOBAL stop_all(4)
|
||||
GLOBAL Done(4)
|
||||
GLOBAL acpar(4)
|
||||
GLOBAL dcpar(4)
|
||||
LOCAL INT Axis
|
||||
STOP
|
||||
|
||||
STOP_MOVE:
|
||||
|
||||
! Stop corresponding buffer
|
||||
stop Axis
|
||||
|
||||
! Halt motor motion and wait until done
|
||||
HALT(Axis)
|
||||
TILL ^MST(Axis).#MOVE
|
||||
|
||||
!Test that buffer and autoroutine are stopped (with timeout)
|
||||
TILL ^PST(Axis).#RUN & ^PST(Axis).#AUTO,500
|
||||
|
||||
!clear the AC and DC Flags to make sure they are cleared
|
||||
acpar(0)=0;dcpar(0)=0;
|
||||
|
||||
! Switch to DC Mode
|
||||
dcpar(Axis)=1; TILL dcpar(Axis)=0
|
||||
|
||||
! Clear flags
|
||||
stop_all(Axis) = 0; Done(Axis) = 1;
|
||||
|
||||
RET
|
||||
|
||||
ON stop_all(0)=1; Axis=0;CALL STOP_MOVE; RET
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
FILENAME... devSPiiPlus.cc
|
||||
USAGE... Motor record device level support for ACS Tech80 SPiiPlus
|
||||
|
||||
Version: $Revision: 1.1 $
|
||||
Modified By: $Author: sullivan $
|
||||
Last Modified: $Date: 2006-05-19 16:39:45 $
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 04-07-05 jps initialized from devMM4000.cc
|
||||
*/
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "motorRecord.h"
|
||||
#include "motor.h"
|
||||
#include "motordevCom.h"
|
||||
#include "drvSPiiPlus.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
#define STATIC static
|
||||
|
||||
extern struct driver_table SPiiPlus_access;
|
||||
|
||||
/* ----------------Create the dsets for devSPiiPlus----------------- */
|
||||
STATIC struct driver_table *drvtabptr;
|
||||
STATIC long SPiiPlus_init(void *);
|
||||
STATIC long SPiiPlus_init_record(void *);
|
||||
STATIC long SPiiPlus_start_trans(struct motorRecord *);
|
||||
STATIC RTN_STATUS SPiiPlus_build_trans(motor_cmnd, double *, struct motorRecord *);
|
||||
STATIC RTN_STATUS SPiiPlus_end_trans(struct motorRecord *);
|
||||
|
||||
struct motor_dset devSPiiPlus =
|
||||
{
|
||||
{8, NULL, (DEVSUPFUN) SPiiPlus_init, (DEVSUPFUN) SPiiPlus_init_record, NULL},
|
||||
motor_update_values,
|
||||
SPiiPlus_start_trans,
|
||||
SPiiPlus_build_trans,
|
||||
SPiiPlus_end_trans
|
||||
};
|
||||
|
||||
extern "C" {epicsExportAddress(dset,devSPiiPlus);}
|
||||
|
||||
/* --------------------------- program data --------------------- */
|
||||
|
||||
/* This table is used to define the command types */
|
||||
/* WARNING! this must match "motor_cmnd" in motor.h */
|
||||
|
||||
static msg_types SPiiPlus_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 */
|
||||
VELOCITY /* JOG_VELOCITY */
|
||||
};
|
||||
|
||||
|
||||
static struct board_stat **SPiiPlus_cards;
|
||||
|
||||
/* --------------------------- program data --------------------- */
|
||||
|
||||
|
||||
/* initialize device support for SPiiPlus stepper motor */
|
||||
STATIC long SPiiPlus_init(void *arg)
|
||||
{
|
||||
long rtnval;
|
||||
int after = (int) arg;
|
||||
|
||||
if (after == 0)
|
||||
{
|
||||
drvtabptr = &SPiiPlus_access;
|
||||
(drvtabptr->init)();
|
||||
}
|
||||
|
||||
rtnval = motor_init_com(after, *drvtabptr->cardcnt_ptr, drvtabptr, &SPiiPlus_cards);
|
||||
return(rtnval);
|
||||
}
|
||||
|
||||
|
||||
/* initialize a record instance */
|
||||
STATIC long SPiiPlus_init_record(void *arg)
|
||||
{
|
||||
struct motorRecord *mr = (struct motorRecord *) arg;
|
||||
return(motor_init_record_com(mr, *drvtabptr->cardcnt_ptr, drvtabptr, SPiiPlus_cards));
|
||||
}
|
||||
|
||||
|
||||
/* start building a transaction */
|
||||
STATIC long SPiiPlus_start_trans(struct motorRecord *mr)
|
||||
{
|
||||
return(motor_start_trans_com(mr, SPiiPlus_cards));
|
||||
}
|
||||
|
||||
|
||||
/* end building a transaction */
|
||||
STATIC RTN_STATUS SPiiPlus_end_trans(struct motorRecord *mr)
|
||||
{
|
||||
return(motor_end_trans_com(mr, drvtabptr));
|
||||
}
|
||||
|
||||
|
||||
/* add a part to the transaction */
|
||||
STATIC RTN_STATUS SPiiPlus_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 SPiiPlusController *cntrl;
|
||||
char buff[110];
|
||||
int axis, card;
|
||||
int intval;
|
||||
double dval;
|
||||
unsigned int size;
|
||||
RTN_STATUS rtnval;
|
||||
|
||||
rtnval = OK;
|
||||
buff[0] = '\0';
|
||||
|
||||
/* Protect against NULL pointer with WRTITE_MSG(GO/STOP_AXIS/GET_INFO, NULL). */
|
||||
intval = (parms == NULL) ? 0 : NINT(parms[0]);
|
||||
dval = (parms == NULL) ? 0 : *parms;
|
||||
|
||||
motor_call = &(trans->motor_call);
|
||||
card = motor_call->card;
|
||||
axis = motor_call->signal;
|
||||
brdptr = (*trans->tabptr->card_array)[card];
|
||||
if (brdptr == NULL)
|
||||
return(rtnval = ERROR);
|
||||
|
||||
cntrl = (struct SPiiPlusController *) brdptr->DevicePrivate;
|
||||
|
||||
if (SPiiPlus_table[command] > motor_call->type)
|
||||
motor_call->type = SPiiPlus_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, "ptp (%d), %d;", axis, intval);
|
||||
sprintf(buff, "Done(%d)=0;target_pos(%d)=%d;opReq(%d)=%d;", axis, axis, intval, axis, OP_ABS_MOVE);
|
||||
break;
|
||||
|
||||
case MOVE_REL:
|
||||
// sprintf(buff, "ptp/r (%d), %d;", axis, intval);
|
||||
sprintf(buff, "Done(%d)=0;target_pos(%d)=%d;opReq(%d)=%d;", axis, axis, intval, axis, OP_REL_MOVE);
|
||||
break;
|
||||
|
||||
case HOME_FOR:
|
||||
sprintf(buff, "Done(%d)=0;opReq(%d)=%d;", axis, axis, OP_HOME_F);
|
||||
break;
|
||||
|
||||
case HOME_REV:
|
||||
sprintf(buff, "Done(%d)=0;opReq(%d)=%d;", axis, axis, OP_HOME_R);
|
||||
break;
|
||||
|
||||
case LOAD_POS:
|
||||
/* The Feedback position follows the Reference set position */
|
||||
sprintf(buff, "set RPOS(%d)=%d;", axis, intval);
|
||||
break;
|
||||
|
||||
case SET_VEL_BASE:
|
||||
break; /* SPiiPlus does not use base velocity */
|
||||
|
||||
case SET_VELOCITY:
|
||||
sprintf(buff, "VEL%d=%d;", axis, intval);
|
||||
break;
|
||||
|
||||
case SET_ACCEL:
|
||||
sprintf(buff, "ACC%d=%d;", axis, intval);
|
||||
break;
|
||||
|
||||
case GO:
|
||||
// Execute the ACS program buffer that corresponds to the axis
|
||||
// ACS Buffer Execution command must be alone
|
||||
rtnval = motor_end_trans_com(mr, drvtabptr);
|
||||
rtnval = (RTN_STATUS) motor_start_trans_com(mr, SPiiPlus_cards);
|
||||
motor_call->type = SPiiPlus_table[command];
|
||||
|
||||
sprintf(buff, "start %d, 1", axis);
|
||||
break;
|
||||
|
||||
case SET_ENC_RATIO:
|
||||
sprintf(buff, "EFAC%d=%f;", axis, dval);
|
||||
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, "halt (%d);", axis);
|
||||
sprintf(buff, "Done(%d)=0;stop_all(%d)=1;", axis, axis);
|
||||
break;
|
||||
|
||||
case JOG:
|
||||
// sprintf(buff, "jog/v (%d), %d;", axis, intval);
|
||||
sprintf(buff, "Done(%d)=0;jog_vel(%d)=%d; opReq(%d)=%d;", axis, axis, intval, axis, OP_JOG_MOVE);
|
||||
strcat(motor_call->message, buff);
|
||||
|
||||
// ACS Buffer Execution command must be alone
|
||||
rtnval = motor_end_trans_com(mr, drvtabptr);
|
||||
rtnval = (RTN_STATUS) motor_start_trans_com(mr, SPiiPlus_cards);
|
||||
motor_call->type = SPiiPlus_table[command];
|
||||
|
||||
sprintf(buff, "start %d,1", axis);
|
||||
|
||||
break;
|
||||
|
||||
case SET_PGAIN:
|
||||
break;
|
||||
|
||||
case SET_IGAIN:
|
||||
break;
|
||||
|
||||
case SET_DGAIN:
|
||||
break;
|
||||
|
||||
case ENABLE_TORQUE:
|
||||
sprintf(buff, "enable(%d);", axis);
|
||||
break;
|
||||
|
||||
case DISABL_TORQUE:
|
||||
sprintf(buff, "disable(%d);", axis);
|
||||
break;
|
||||
|
||||
case SET_HIGH_LIMIT:
|
||||
motor_info = &(*trans->tabptr->card_array)[card]->motor_info[axis];
|
||||
trans->state = IDLE_STATE; /* No command sent to the controller. */
|
||||
|
||||
// if (intval > motor_info->high_limit)
|
||||
// {
|
||||
// mr->dhlm = motor_info->high_limit * mr->mres;
|
||||
// rtnval = ERROR;
|
||||
// }
|
||||
break;
|
||||
|
||||
case SET_LOW_LIMIT:
|
||||
motor_info = &(*trans->tabptr->card_array)[card]->motor_info[axis];
|
||||
trans->state = IDLE_STATE; /* No command sent to the controller. */
|
||||
|
||||
// if (intval < motor_info->low_limit)
|
||||
// {
|
||||
// mr->dllm = motor_info->low_limit * mr->mres;
|
||||
// rtnval = ERROR;
|
||||
// }
|
||||
break;
|
||||
|
||||
default:
|
||||
rtnval = ERROR;
|
||||
}
|
||||
|
||||
size = strlen(buff);
|
||||
if (size > sizeof(buff) || (strlen(motor_call->message) + size) > MAX_MSG_SIZE)
|
||||
errlogMessage("SPiiPlus_build_trans(): buffer overflow.\n");
|
||||
else
|
||||
strcat(motor_call->message, buff);
|
||||
|
||||
return(rtnval);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# ACS Tech 80 Device Driver support.
|
||||
device(motor,VME_IO,devSPiiPlus,"SPiiPlus")
|
||||
driver(drvSPiiPlus)
|
||||
registrar(ACSTech80Register)
|
||||
#variable(drvSPiiPlusdebug)
|
||||
@@ -0,0 +1,725 @@
|
||||
/*
|
||||
FILENAME... drvSPiiPlus.cc
|
||||
USAGE... Motor record driver level support for ACS Tech80
|
||||
SPiiPlus
|
||||
|
||||
Version: $Revision: 1.1 $
|
||||
Modified By: $Author: sullivan $
|
||||
Last Modified: $Date: 2006-05-19 16:39:45 $
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* Original Author: Mark Rivers
|
||||
* Date: 10/20/97
|
||||
* Current Author: J. Sullivan
|
||||
*
|
||||
* 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 04-08-05 jps initialized from drvSPiiPlus.cc
|
||||
*/
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <epicsThread.h>
|
||||
#include <drvSup.h>
|
||||
#include "motor.h"
|
||||
#include "ACSTech80Register.h"
|
||||
#include "drvSPiiPlus.h"
|
||||
#include "asynOctetSyncIO.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
#define READ_RESOLUTION "TU;"
|
||||
#define READ_FAULT "?D/FAULT(%d)"
|
||||
#define READ_STATUS "?D/MST(%d)"
|
||||
#define READ_POSITION "?RPOS(%d)"
|
||||
#define READ_EA_POSITION "?FPOS(%d)"
|
||||
#define READ_VELOCITY "?FVEL(%d)"
|
||||
#define READ_ALL_POSITION "?FPOS"
|
||||
#define READ_HOME "?opReq(%d)"
|
||||
#define READ_DONE "?Done(%d)"
|
||||
#define STOP_ALL "halt all"
|
||||
#define MOTOR_ON "enable(%d)"
|
||||
|
||||
#define GET_IDENT "?VR"
|
||||
|
||||
#define ACS_EOS "\r" /* End-of-string */
|
||||
|
||||
#define SPiiPlus_NUM_CARDS 8
|
||||
#define BUFF_SIZE 120 /* Maximum length of string to/from SPiiPlus */
|
||||
|
||||
#define TIMEOUT 2.0 /* Command timeout in sec. */
|
||||
|
||||
/*----------------debugging-----------------*/
|
||||
#ifdef __GNUG__
|
||||
#ifdef DEBUG
|
||||
#define Debug(l, f, args...) { if(l<=drvSPiiPlusdebug) printf(f,## args); }
|
||||
#else
|
||||
#define Debug(l, f, args...)
|
||||
#endif
|
||||
#else
|
||||
#define Debug()
|
||||
#endif
|
||||
volatile int drvSPiiPlusdebug = 0;
|
||||
extern "C" {epicsExportAddress(int, drvSPiiPlusdebug);}
|
||||
|
||||
/* --- Local data. --- */
|
||||
int SPiiPlus_num_cards = 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 drvSPiiPlusReadbackDelay = 0.;
|
||||
|
||||
|
||||
/*----------------functions-----------------*/
|
||||
static int recv_mess(int card, char *com, int flag);
|
||||
static RTN_STATUS send_mess(int card, char const *, char *name);
|
||||
static int send_recv_mess(int card, char const *send_com, char *recv_com);
|
||||
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 SPiiPlus_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,
|
||||
NULL
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
long number;
|
||||
#ifdef __cplusplus
|
||||
long (*report) (int);
|
||||
long (*init) (void);
|
||||
#else
|
||||
DRVSUPFUN report;
|
||||
DRVSUPFUN init;
|
||||
#endif
|
||||
} drvSPiiPlus = {2, report, init};
|
||||
|
||||
extern "C" {epicsExportAddress(drvet, drvSPiiPlus);}
|
||||
|
||||
static struct thread_args targs = {SCAN_RATE, &SPiiPlus_access, 0.010};
|
||||
|
||||
/*********************************************************
|
||||
* Print out driver status report
|
||||
*********************************************************/
|
||||
static long report(int level)
|
||||
{
|
||||
int card;
|
||||
|
||||
if (SPiiPlus_num_cards <=0)
|
||||
printf(" No SPiiPlus controllers configured.\n");
|
||||
else
|
||||
{
|
||||
for (card = 0; card < SPiiPlus_num_cards; card++)
|
||||
{
|
||||
struct controller *brdptr = motor_state[card];
|
||||
|
||||
if (brdptr == NULL)
|
||||
printf(" SPiiPlus controller %d connection failed.\n", card);
|
||||
else
|
||||
{
|
||||
struct SPiiPlusController *cntrl;
|
||||
|
||||
cntrl = (struct SPiiPlusController *) brdptr->DevicePrivate;
|
||||
printf(" SPiiPlus controller %d, port=%s, address=%d, id: %s \n",
|
||||
card, cntrl->asyn_port, cntrl->asyn_address,
|
||||
brdptr->ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
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 (SPiiPlus_num_cards <= 0)
|
||||
{
|
||||
Debug(1, "init(): SPiiPlus driver disabled. SPiiPlusSetup() 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)
|
||||
//{
|
||||
//}
|
||||
|
||||
/**************************************************************
|
||||
* Parse status and position strings for a card and signal
|
||||
* set_status()
|
||||
************************************************************/
|
||||
|
||||
static int set_status(int card, int signal)
|
||||
{
|
||||
struct SPiiPlusController *cntrl;
|
||||
struct mess_node *nodeptr;
|
||||
register struct mess_info *motor_info;
|
||||
char send_buff[80];
|
||||
int flags;
|
||||
double vel;
|
||||
MOTOR_STATUS mstat;
|
||||
MOTOR_FAULTS mfault;
|
||||
int rtn_state;
|
||||
int recvCnt;
|
||||
int opReq;
|
||||
long motorData;
|
||||
bool homing;
|
||||
bool plusdir, ls_active = false;
|
||||
msta_field status;
|
||||
|
||||
cntrl = (struct SPiiPlusController *) motor_state[card]->DevicePrivate;
|
||||
motor_info = &(motor_state[card]->motor_info[signal]);
|
||||
status.All = motor_info->status.All;
|
||||
|
||||
sprintf(send_buff, READ_STATUS, signal);
|
||||
recvCnt = send_recv_mess(card, send_buff, cntrl->recv_string[QSTATUS]);
|
||||
if (recvCnt > 0)
|
||||
{
|
||||
cntrl->status = NORMAL;
|
||||
sprintf(send_buff, READ_DONE, signal);
|
||||
recvCnt = send_recv_mess(card, send_buff, cntrl->recv_string[QDONE]);
|
||||
|
||||
sprintf(send_buff, READ_FAULT, signal);
|
||||
recvCnt = send_recv_mess(card, send_buff, cntrl->recv_string[QFAULT]);
|
||||
|
||||
sprintf(send_buff, READ_POSITION, signal);
|
||||
recvCnt = send_recv_mess(card, send_buff, cntrl->recv_string[QPOS]);
|
||||
|
||||
sprintf(send_buff, READ_EA_POSITION, signal);
|
||||
recvCnt = send_recv_mess(card, send_buff, cntrl->recv_string[QEA_POS]);
|
||||
|
||||
sprintf(send_buff, READ_VELOCITY, signal);
|
||||
recvCnt = send_recv_mess(card, send_buff, cntrl->recv_string[QVEL]);
|
||||
|
||||
sprintf(send_buff, READ_HOME, signal);
|
||||
recvCnt = send_recv_mess(card, send_buff, cntrl->recv_string[QHOME]);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cntrl->status == NORMAL)
|
||||
cntrl->status = RETRY;
|
||||
else
|
||||
cntrl->status = COMM_ERR;
|
||||
}
|
||||
|
||||
if (cntrl->status != NORMAL)
|
||||
{
|
||||
if (cntrl->status == COMM_ERR)
|
||||
{
|
||||
status.Bits.CNTRL_COMM_ERR = 1;
|
||||
status.Bits.RA_PROBLEM = 1;
|
||||
rtn_state = 1;
|
||||
goto exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
rtn_state = 0;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
status.Bits.CNTRL_COMM_ERR = 0;
|
||||
|
||||
nodeptr = motor_info->motor_motion;
|
||||
|
||||
/*
|
||||
* Parse the status/fault strings
|
||||
*/
|
||||
flags = atoi(cntrl->recv_string[QSTATUS]);
|
||||
mstat.All = flags;
|
||||
mfault.All = atoi(cntrl->recv_string[QFAULT]);
|
||||
Debug(5, "set_status(): status byte = %x, fault int = %x\n", mstat.All, mfault.All);
|
||||
|
||||
vel = atof(cntrl->recv_string[QVEL]);
|
||||
status.Bits.RA_DIRECTION = (vel >= 0) ? 1 : 0;
|
||||
|
||||
|
||||
plusdir = (status.Bits.RA_DIRECTION) ? true : false;
|
||||
|
||||
status.Bits.RA_DONE = (atoi(cntrl->recv_string[QDONE])) ? 1 : 0;
|
||||
// if (mstat.Bits.inmotion == false && mstat.Bits.inposition == true)
|
||||
// {
|
||||
// status.Bits.RA_DONE = 1;
|
||||
// }
|
||||
// else
|
||||
// status.Bits.RA_DONE = 0;
|
||||
|
||||
|
||||
opReq = atoi(cntrl->recv_string[QHOME]);
|
||||
homing = (opReq == OP_HOME_F || opReq == OP_HOME_R) ? true : false;
|
||||
status.Bits.RA_HOME = status.Bits.RA_DONE;
|
||||
|
||||
|
||||
/* Set Travel limit switch status bits. */
|
||||
if ((mfault.Bits.rl == false && mfault.Bits.srl == false) || homing)
|
||||
status.Bits.RA_PLUS_LS = 0;
|
||||
else
|
||||
{
|
||||
status.Bits.RA_PLUS_LS = 1;
|
||||
if (plusdir == true)
|
||||
ls_active = true;
|
||||
}
|
||||
|
||||
if ((mfault.Bits.ll == false && mfault.Bits.sll == false) || homing)
|
||||
status.Bits.RA_MINUS_LS = 0;
|
||||
else
|
||||
{
|
||||
status.Bits.RA_MINUS_LS = 1;
|
||||
if (plusdir == false)
|
||||
ls_active = true;
|
||||
}
|
||||
|
||||
/* Position maintence enabled */
|
||||
status.Bits.EA_POSITION = (mstat.Bits.enabled) ? 1: 0;
|
||||
|
||||
/* encoder status */
|
||||
status.Bits.EA_SLIP = 0;
|
||||
status.Bits.EA_SLIP_STALL = 0;
|
||||
status.Bits.EA_HOME = 0;
|
||||
|
||||
/*
|
||||
* Parse motor position
|
||||
*/
|
||||
motorData = (long)atof(cntrl->recv_string[QPOS]);
|
||||
|
||||
// printf("motorData=%ld, last_position=%ld, count=%d\n",motorData, motor_info->position, motor_info->no_motion_count);
|
||||
|
||||
if (motorData == motor_info->position)
|
||||
{
|
||||
if (nodeptr != 0) /* Increment counter only if motor is moving. */
|
||||
motor_info->no_motion_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
motor_info->position = motorData;
|
||||
motor_info->no_motion_count = 0;
|
||||
}
|
||||
|
||||
// Always update encoder position - for non-motion monitoring
|
||||
if (motor_state[card]->motor_info[signal].encoder_present == YES)
|
||||
motor_info->encoder_position = (long)(atof(cntrl->recv_string[QEA_POS]));
|
||||
else
|
||||
motor_info->encoder_position = 0;
|
||||
|
||||
|
||||
|
||||
status.Bits.RA_PROBLEM = 0;
|
||||
|
||||
/* Parse motor velocity? */
|
||||
/* NEEDS WORK */
|
||||
|
||||
motor_info->velocity = (int)vel;
|
||||
|
||||
rtn_state = (!motor_info->no_motion_count || ls_active == true ||
|
||||
status.Bits.RA_DONE | status.Bits.RA_PROBLEM) ? 1 : 0;
|
||||
|
||||
/* Test for post-move string. */
|
||||
if ((status.Bits.RA_DONE || ls_active == true) && nodeptr != 0 &&
|
||||
nodeptr->postmsgptr != 0)
|
||||
{
|
||||
strncpy(send_buff, nodeptr->postmsgptr, 80);
|
||||
send_mess(card, send_buff, (char) NULL);
|
||||
nodeptr->postmsgptr = NULL;
|
||||
}
|
||||
|
||||
exit:
|
||||
motor_info->status.All = status.All;
|
||||
return(rtn_state);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************/
|
||||
/* send_receive a message to the SPiiPlus board */
|
||||
/* send_recv_mess() */
|
||||
/*****************************************************/
|
||||
static int send_recv_mess(int card, char const *send_com, char *recv_com)
|
||||
{
|
||||
struct SPiiPlusController *cntrl;
|
||||
int size;
|
||||
size_t nwrite;
|
||||
size_t nread = 0;
|
||||
double timeout = 0.;
|
||||
asynStatus status;
|
||||
int eomReason;
|
||||
|
||||
|
||||
size = strlen(send_com);
|
||||
recv_com[0] = '\0';
|
||||
|
||||
if (size > MAX_MSG_SIZE)
|
||||
{
|
||||
errlogMessage("drvSPiiPlus.c:send_recv_mess(); message size violation.\n");
|
||||
return(ERROR);
|
||||
}
|
||||
else if (size == 0) /* Normal exit on empty input message. */
|
||||
return(OK);
|
||||
|
||||
if (!motor_state[card])
|
||||
{
|
||||
errlogPrintf("drvSPiiPlus.c:send_recv_mess() - invalid card #%d\n", card);
|
||||
return(ERROR);
|
||||
}
|
||||
Debug(2, "send_recv_mess(): message = %s\n", send_com);
|
||||
|
||||
cntrl = (struct SPiiPlusController *) motor_state[card]->DevicePrivate;
|
||||
|
||||
timeout = TIMEOUT;
|
||||
/* flush any junk at input port - should not be any data available */
|
||||
pasynOctetSyncIO->flush(cntrl->pasynUser);
|
||||
|
||||
/* Perform atomic write/read operation */
|
||||
status = pasynOctetSyncIO->writeRead(cntrl->pasynUser, send_com, strlen(send_com),
|
||||
recv_com, ACS_MSG_SIZE,
|
||||
TIMEOUT, &nwrite, &nread, &eomReason);
|
||||
|
||||
if ((status != asynSuccess) || (nread <= 0))
|
||||
{
|
||||
recv_com[0] = '\0';
|
||||
nread = 0;
|
||||
}
|
||||
|
||||
Debug(2, "send_recv_mess(): recv message = \"%s\"\n", recv_com);
|
||||
|
||||
return(nread);
|
||||
}
|
||||
/*****************************************************/
|
||||
/* send a message to the SPiiPlus board */
|
||||
/* send_mess() */
|
||||
/*****************************************************/
|
||||
static RTN_STATUS send_mess(int card, char const *com, char *name)
|
||||
{
|
||||
struct SPiiPlusController *cntrl;
|
||||
int size;
|
||||
size_t nwrite;
|
||||
|
||||
size = strlen(com);
|
||||
|
||||
if (size > MAX_MSG_SIZE)
|
||||
{
|
||||
errlogMessage("drvSPiiPlus.c:send_mess(); message size violation.\n");
|
||||
return(ERROR);
|
||||
}
|
||||
else if (size == 0) /* Normal exit on empty input message. */
|
||||
return(OK);
|
||||
|
||||
if (!motor_state[card])
|
||||
{
|
||||
errlogPrintf("drvSPiiPlus.c:send_mess() - invalid card #%d\n", card);
|
||||
return(ERROR);
|
||||
}
|
||||
|
||||
if (name != NULL)
|
||||
{
|
||||
errlogPrintf("drvSPiiPlus.c:send_mess() - invalid argument = %s\n", name);
|
||||
return(ERROR);
|
||||
}
|
||||
|
||||
Debug(2, "send_mess(): message = %s\n", com);
|
||||
|
||||
cntrl = (struct SPiiPlusController *) motor_state[card]->DevicePrivate;
|
||||
|
||||
/* flush any junk at input port - should not be any data available */
|
||||
pasynOctetSyncIO->flush(cntrl->pasynUser);
|
||||
|
||||
pasynOctetSyncIO->write(cntrl->pasynUser, com, strlen(com),
|
||||
TIMEOUT, &nwrite);
|
||||
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* FUNCTION... recv_mess(int card, char *com, int flag)
|
||||
*
|
||||
* INPUT ARGUMENTS...
|
||||
* card - controller card # (0,1,...).
|
||||
* *com - caller's response buffer.
|
||||
* flag | FLUSH = this flag is ignored - the receive buffer is flushed
|
||||
* on every write (see write_mess())
|
||||
* LOGIC...
|
||||
* IF controller card does not exist.
|
||||
* ERROR RETURN.
|
||||
* ENDIF
|
||||
* NORMAL RETURN.
|
||||
*/
|
||||
|
||||
static int recv_mess(int card, char *com, int flag)
|
||||
{
|
||||
struct SPiiPlusController *cntrl;
|
||||
double timeout = 0.;
|
||||
size_t nread = 0;
|
||||
asynStatus status;
|
||||
int eomReason;
|
||||
|
||||
/* Check that card exists */
|
||||
if (!motor_state[card])
|
||||
return(ERROR);
|
||||
|
||||
cntrl = (struct SPiiPlusController *) motor_state[card]->DevicePrivate;
|
||||
|
||||
timeout = TIMEOUT;
|
||||
|
||||
status = pasynOctetSyncIO->read(cntrl->pasynUser, com, BUFF_SIZE,
|
||||
timeout, &nread, &eomReason);
|
||||
|
||||
if ((status != asynSuccess) || (nread <= 0))
|
||||
{
|
||||
com[0] = '\0';
|
||||
nread = 0;
|
||||
}
|
||||
|
||||
Debug(2, "recv_mess(): message = \"%s\"\n", com);
|
||||
return(nread);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************/
|
||||
/* Setup system configuration */
|
||||
/* SPiiPlusSetup() */
|
||||
/*****************************************************/
|
||||
RTN_STATUS
|
||||
SPiiPlusSetup(int num_cards, /* maximum number of controllers in system. */
|
||||
int scan_rate) /* polling rate - 1/60 sec units. */
|
||||
{
|
||||
int itera;
|
||||
|
||||
if (num_cards < 1 || num_cards > SPiiPlus_NUM_CARDS)
|
||||
SPiiPlus_num_cards = SPiiPlus_NUM_CARDS;
|
||||
else
|
||||
SPiiPlus_num_cards = num_cards;
|
||||
|
||||
/* Set motor polling task rate */
|
||||
if (scan_rate >= 1 && scan_rate <= 60)
|
||||
targs.motor_scan_rate = scan_rate;
|
||||
else
|
||||
targs.motor_scan_rate = SCAN_RATE;
|
||||
|
||||
/*
|
||||
* Allocate space for motor_state structures. Note this must be done
|
||||
* before SPiiPlusConfig 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(SPiiPlus_num_cards *
|
||||
sizeof(struct controller *));
|
||||
|
||||
for (itera = 0; itera < SPiiPlus_num_cards; itera++)
|
||||
motor_state[itera] = (struct controller *) NULL;
|
||||
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************/
|
||||
/* Configure a controller */
|
||||
/* SPiiPlusConfig() */
|
||||
/*****************************************************/
|
||||
RTN_STATUS
|
||||
SPiiPlusConfig(int card, /* card being configured */
|
||||
const char *name) /* asyn port name */
|
||||
{
|
||||
struct SPiiPlusController *cntrl;
|
||||
|
||||
if (card < 0 || card >= SPiiPlus_num_cards)
|
||||
return(ERROR);
|
||||
|
||||
motor_state[card] = (struct controller *) malloc(sizeof(struct controller));
|
||||
motor_state[card]->DevicePrivate = malloc(sizeof(struct SPiiPlusController));
|
||||
cntrl = (struct SPiiPlusController *) motor_state[card]->DevicePrivate;
|
||||
|
||||
strcpy(cntrl->asyn_port, name);
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************/
|
||||
/* 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 SPiiPlusController *cntrl;
|
||||
int card_index, motor_index;
|
||||
char axis_pos[BUFF_SIZE];
|
||||
char buff[BUFF_SIZE];
|
||||
char write_buff[30];
|
||||
char *tok_save, *pos_ptr;
|
||||
int total_axis = 0;
|
||||
int status;
|
||||
asynStatus success_rtn;
|
||||
|
||||
initialized = true; /* Indicate that driver is initialized. */
|
||||
|
||||
/* Check for setup */
|
||||
if (SPiiPlus_num_cards <= 0)
|
||||
return(ERROR);
|
||||
|
||||
for (card_index = 0; card_index < SPiiPlus_num_cards; card_index++)
|
||||
{
|
||||
if (!motor_state[card_index])
|
||||
continue;
|
||||
|
||||
brdptr = motor_state[card_index];
|
||||
brdptr->cmnd_response = true;
|
||||
total_cards = card_index + 1;
|
||||
cntrl = (struct SPiiPlusController *) brdptr->DevicePrivate;
|
||||
|
||||
/* Initialize communications channel */
|
||||
success_rtn = pasynOctetSyncIO->connect(cntrl->asyn_port,
|
||||
cntrl->asyn_address, &cntrl->pasynUser, NULL);
|
||||
|
||||
if (success_rtn == asynSuccess)
|
||||
{
|
||||
int retry = 0;
|
||||
|
||||
/* Set command End-of-string */
|
||||
pasynOctetSyncIO->setInputEos(cntrl->pasynUser,ACS_EOS,strlen(ACS_EOS));
|
||||
pasynOctetSyncIO->setOutputEos(cntrl->pasynUser,ACS_EOS,strlen(ACS_EOS));
|
||||
|
||||
/* Send a message to the board, see if it exists */
|
||||
do
|
||||
{
|
||||
status = send_recv_mess(card_index, READ_ALL_POSITION, axis_pos);
|
||||
retry++;
|
||||
/* Return value is length of response string */
|
||||
} while(status == 0 && retry < 3);
|
||||
}
|
||||
|
||||
if (success_rtn == asynSuccess && status > 0)
|
||||
{
|
||||
brdptr->localaddr = (char *) NULL;
|
||||
brdptr->motor_in_motion = 0;
|
||||
status = send_recv_mess(card_index, STOP_ALL, buff); /* Stop all motors */
|
||||
status = send_recv_mess(card_index, GET_IDENT, buff); /* Read controller ID string */
|
||||
strcpy(brdptr->ident, buff); /* Save Version */
|
||||
|
||||
|
||||
/* 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;
|
||||
|
||||
for (motor_index = 0; motor_index < total_axis; motor_index++)
|
||||
{
|
||||
struct mess_info *motor_info = &brdptr->motor_info[motor_index];
|
||||
|
||||
motor_info->status.All = 0;
|
||||
motor_info->no_motion_count = 0;
|
||||
motor_info->encoder_position = 0;
|
||||
motor_info->position = 0;
|
||||
|
||||
/* Encoder Enable */
|
||||
motor_info->encoder_present = YES;
|
||||
motor_info->status.Bits.EA_PRESENT = 1;
|
||||
motor_info->pid_present = YES;
|
||||
motor_info->status.Bits.GAIN_SUPPORT = 1;
|
||||
|
||||
/* Determine low limit */
|
||||
sprintf(write_buff, "?SLLIMIT(%d)", motor_index);
|
||||
status = send_recv_mess(card_index, write_buff, buff);
|
||||
// motor_info->low_limit = atof(buff);
|
||||
|
||||
/* Determine high limit */
|
||||
sprintf(write_buff, "?SRLIMIT(%d)", motor_index);
|
||||
status = send_recv_mess(card_index, write_buff, buff);
|
||||
// motor_info->high_limit = atof(buff);
|
||||
|
||||
set_status(card_index, motor_index); /* Read status of each motor */
|
||||
}
|
||||
}
|
||||
else
|
||||
motor_state[card_index] = (struct controller *) NULL;
|
||||
}
|
||||
|
||||
any_motor_in_motion = 0;
|
||||
|
||||
mess_queue.head = (struct mess_node *) NULL;
|
||||
mess_queue.tail = (struct mess_node *) NULL;
|
||||
|
||||
free_list.head = (struct mess_node *) NULL;
|
||||
free_list.tail = (struct mess_node *) NULL;
|
||||
|
||||
// epicsThreadCreate((char *) "SPiiPlus_motor", 64, 5000, (EPICSTHREADFUNC) motor_task, (void *) &targs);
|
||||
epicsThreadCreate((char *) "SPiiPlus_motor",
|
||||
epicsThreadPriorityMedium,
|
||||
epicsThreadGetStackSize(epicsThreadStackMedium),
|
||||
(EPICSTHREADFUNC) motor_task, (void *) &targs);
|
||||
|
||||
|
||||
return(OK);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
FILENAME... drvSPiiPlus.h
|
||||
USAGE... This file contains ACS Tech80 driver "include"
|
||||
information that is specific to the SPiiPlus serial controller
|
||||
|
||||
Version: $Revision: 1.1 $
|
||||
Modified By: $Author: sullivan $
|
||||
Last Modified: $Date: 2006-05-19 16:39:46 $
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* Original Author: Mark Rivers
|
||||
* Current Author: J. Sullivan
|
||||
* 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 04-07-05 jps initialized from drvMM4000.cc
|
||||
*/
|
||||
|
||||
#ifndef INCdrvSPiiPlush
|
||||
#define INCdrvSPiiPlush 1
|
||||
|
||||
#include "motor.h"
|
||||
#include "motordrvCom.h"
|
||||
#include "asynDriver.h"
|
||||
#include "asynOctetSyncIO.h"
|
||||
|
||||
#define ACS_MSG_SIZE 120
|
||||
|
||||
// ACS Motion Commands -
|
||||
// These definitions must match the native ACS programming
|
||||
#define OP_ABS_MOVE 1
|
||||
#define OP_REL_MOVE 2
|
||||
#define OP_JOG_MOVE 3
|
||||
#define OP_HOME_F 4
|
||||
#define OP_HOME_R 5
|
||||
|
||||
#define QUERY_CNT 7
|
||||
enum query_types {QSTATUS, QFAULT, QPOS, QEA_POS, QVEL, QHOME, QDONE};
|
||||
|
||||
/* Motion Master specific data is stored in this structure. */
|
||||
struct SPiiPlusController
|
||||
{
|
||||
asynUser *pasynUser; /* For RS-232 */
|
||||
int asyn_address; /* Use for GPIB or other address with asyn */
|
||||
char asyn_port[80]; /* asyn port name */
|
||||
char recv_string[QUERY_CNT][ACS_MSG_SIZE]; /* Query result strings */
|
||||
double home_preset[MAX_AXIS]; /* Controller's home preset position (XF command). */
|
||||
CommStatus status; /* Controller communication status. */
|
||||
};
|
||||
|
||||
|
||||
/* Motor status response for SPiiPlus . */
|
||||
typedef union
|
||||
{
|
||||
epicsUInt8 All;
|
||||
struct
|
||||
{
|
||||
#ifdef MSB_First
|
||||
bool bit7 :1; /* Bit #7 N/A. */
|
||||
bool inaccel :1; /* Motor is accelerating */
|
||||
bool inmotion :1; /* Motor in-motion */
|
||||
bool inposition :1; /* Motor in position */
|
||||
bool bit3 :1; /* Bit #3 N/A */
|
||||
bool bit2 :1; /* Bit #2 N/A */
|
||||
bool openloop :1; /* Motor in open-loop (torque control) mode */
|
||||
bool enabled :1; /* Motor Enabled */
|
||||
#else
|
||||
bool enabled :1; /* Motor Enabled */
|
||||
bool openloop :1; /* Motor in open-loop (torque control) mode */
|
||||
bool bit2 :1; /* Bit #2 N/A */
|
||||
bool bit3 :1; /* Bit #3 N/A */
|
||||
bool inposition :1; /* Motor in position */
|
||||
bool inmotion :1; /* Motor in-motion */
|
||||
bool inaccel :1; /* Motor is accelerating */
|
||||
bool bit7 :1; /* Bit #7 N/A. */
|
||||
#endif
|
||||
} Bits;
|
||||
} MOTOR_STATUS;
|
||||
|
||||
|
||||
/*
|
||||
* Motor fault flags for SPiiPlus
|
||||
* (See page 8-4 in SPiiPlus ACSPL Programmers Guide)
|
||||
*/
|
||||
typedef union
|
||||
{
|
||||
epicsUInt32 All;
|
||||
struct
|
||||
{
|
||||
#ifdef MSB_First
|
||||
bool bit31 :1; /* Bit #31 N/A */
|
||||
bool misc_faults :6; /* Misc. Faults */
|
||||
bool bits21_24 :4; /* Bits #21 - #24 N/A */
|
||||
bool hssinc :1; /* HSSI Not Connected */
|
||||
bool bits19 :1; /* Bits #9 N/A */
|
||||
bool bits18 :1; /* Bits #18 N/A */
|
||||
bool sp :1; /* Servo Processor Alarm */
|
||||
bool limit_error :3; /* Limit Error (Vel, Accel, Current) */
|
||||
bool pos_error :2; /* Position Error */
|
||||
bool enc_error :5; /* Encoder or Drive error */
|
||||
bool sll :1; /* Software Left Limit */
|
||||
bool srl :1; /* Software Right Limit */
|
||||
bool bits2_4 :3; /* Bits #2 - #4 N/A */
|
||||
bool rl :1; /* Left Limit */
|
||||
bool ll :1; /* Right Limit */
|
||||
#else
|
||||
bool ll :1; /* Right Limit */
|
||||
bool rl :1; /* Left Limit */
|
||||
bool bits2_4 :3; /* Bits #2 - #4 N/A */
|
||||
bool srl :1; /* Software Right Limit */
|
||||
bool sll :1; /* Software Left Limit */
|
||||
bool enc_error :5; /* Encoder or Drive error */
|
||||
bool pos_error :2; /* Position Error */
|
||||
bool limit_error :3; /* Limit Error (Vel, Accel, Current) */
|
||||
bool sp :1; /* Servo Processor Alarm */
|
||||
bool bits18 :1; /* Bits #18 N/A */
|
||||
bool bits19 :1; /* Bits #9 N/A */
|
||||
bool hssinc :1; /* HSSI Not Connected */
|
||||
bool bits21_24 :4; /* Bits #21 - #24 N/A */
|
||||
bool misc_faults :6; /* Misc. Faults */
|
||||
bool bit31 :1; /* Bit #31 N/A */
|
||||
#endif
|
||||
} Bits;
|
||||
} MOTOR_FAULTS;
|
||||
|
||||
|
||||
#endif /* INCdrvSPiiPlush */
|
||||
|
||||
Reference in New Issue
Block a user