***********************************************************************************
Hytec 8601 Step Motor Controller Asyn Driver Notes

Author: Jim Chen, Hytec Electronics Ltd
		jim.chen@hytec-electronics.co.uk

First Initial: 12/NOV/2010
Last updated: 05/MAY/2011

This driver is based on "model 3" motor asyn driver defined by Mark Rivers. 
***********************************************************************************

Support modules
===============

1). asyn driver version asyn4-13-1 or later.

2). ipac module version ipac-2.11 plus one of the Hytec carrier card drivers such as 
	drvHy8002.c for VxWorks or RTEMS under VME64x with 8002/8003/8004 carriers, 
	drvHyLinuxCarrier.c for Linux with IOC9010/PCIe6335/uTCA7002 carriers, 
	drvHyRTEMSCarrier.c for RTEMS with IOC9010 blade.

3). devLib2 version 2.1 or later if using RTEMS on IOC9010 blade.

4). motor module version later than motorR6-5-1. Currently the 'model 3' version can be 
	downloaded from subversion https://subversion.xor.aps.anl.gov/synApps/motor/trunk.
	This will be formally released some stage on the official EPICS website.

5). EPICS core R3.14.8.2 or later.

6). RTEMS R4.9.4 or later.


Hytec 8002/8003/8004 carrier card configuration
===============================================

This board is a 6U VME64x carrier card. It provides four single size IP slots and 
is configurable for many parameters. It supports interrupt level from 0 to 7. Any 
IP cards can be enabled/disabled interrupt. The clock can be set to either 8MHz or 
32MHz. For IPAC Memory space if required, the base address can be defined automatically 
by geographical addressing when VME64x crate is used. For other type of crates, 
the base address can be set by configuring a series of jumpers on the board or by 
passing parameter to the memory offset register in the ipacAddCarrier call.

The IPAC Carrier Driver for this board is found in the file drvHy8002.c which 
implements two   commands ipacAddHy8002 and Hy8002CarrierInfo. The ipacAddHy8002 
command is used to add a Hytec 8002/8003 board to the system. The Hy8002CarrierInfo 
reports hardware information for a specified board or all boards in the system if 
the parameter passed down is 0xFFFF. These commands are registered by the registrar 
routine Hy8002Registrar to add them to the iocsh and link the driver into a final 
IOC executable, for which it must be listed in the IOC's .dbd file thus:

	registrar(Hy8002Registrar)


1). Configuration Command and Parameter
--------------------------------------

- int ipacAddHYy8002(const char *cardParams);

The parameter string should comprise two (2) to six (6) parameters which are comma 
separated. The first two are mandate and have to be separated only by one comma. 
The others are key/value pairs and are optional. The format is defined as

s,i,IPMEM=d,IPCLCK=d,ROAK=d,MEMOFFS=d

where d is a decimal integer number.
s		defines the VME slot number of the carrier card. Valid number is 2 ~ 21 if 
		1MB memory space is specified or 2~15 if 2MB memory space is specified
i		defines the interrupt level. Valid number is 0 ~ 7.
IPMEM=d 	defines the maximum memory size of the IP module. The value d has to be 1, 2, 
		4 or 8 that represent 1MB, 2MB, 4MB or 8MB respectively. Default is 1.
IPCLCK=d 	defines the clock that its value has to be either 8 for 8MHz or 32 for 
		32Mhz. Default is 8.
ROAK=d 		if d =1, it defines carrier card to release the interrupt upon the 
		acknowledgment. If d=0, the interrupt is released by user interrupt 
		service routine. Default is 0.
MEMOFFS=d	this is used to define the A32 memory space base address when geographic 
		addressing is not preferred. The value of MEMOFFS defines A16 ~ A31 of 
		the base address as shown below. It is derived from the VME slot number 
		of the carrier card plus the VME_A32_MSTR_LOCAL defined in the config.h 
		file of the BSP.

D15 D14 D13 D12 D11 D10 D09 D08 D07 D06 D05 D04 D03 D02 D01 D00
A31 A30 A29 A28 A27 A26 A25 A24 A23 A22  x   x   x   x   x   x

X= don't care, normally 0. So the actual meaningful bit starts from A22.

The reason it starts from A22 is that the minimum VME carrier memory assignment is 
4MB, i.e. each IP card has 1MB as defined by the IPMEM=1. As such, a carrier in 
slot 1 would have base address 0x00400000 plus the VME_A32_MSTR_LOCAL define. 
Of course slot 1 is occupied by processor. So for slot 2, the base address is 0x00800000 
and for slot 3, the base address is 0x00C00000 and so forth. 

If the VME_A32_MSTR_LOCAL is defined as 0x20000000, then for slot 3, the derived base 
address is 0x00C00000 + 0x20000000 = 0x200C00000. Hence the MEMOFFS = 8204 (decimal, i.e. 0x200C). 
For slot 5, the derived base address will be 0x01400000 + 0x20000000 = 0x21400000. 
Hence the MEMOFFS = 8512 (decimal, i.e. 0x2140). And so forth.

- int Hy8002CarrierInfo(int carrier);

where 'carrier' is the registered carrier number in the system. If it is specified, 
this function prints out the specified carrier hardware information. If carrier = 0xFFFF, 
then all carriers' hardware information will be printed out. 


2). Configuration Examples
-------------------------

ipacAddHy8002("3,2") 

This indicates that the carrier is in slot 3 and the interrupt level is set to 2. 
IP memory uses default 1MB. Clock uses default 8MHz. RORA as default. use geographical 
addressing etc.

ipacAddHy8002("5,4,IPMEM=1,IPCLCK=8,ROAK=1,MEMOFFS=192 ") 

Here the slot is 5, interrupt level is 4. IP memory size is 1MB, clock uses 8MHz. 
Use ROAK but not using geographic addressing. The memory offset is 192 which means its 
base address is 0x00C00000 assuming the VME_A32_MSTR_LOCAL is set to 0x00000000.

3). Interrupt Commands Supported
-------------------------------

The interrupt level can be set by the second parameter of the ipacAddHy8002 routine. 
Individual IP module can be set to generate interrupt or not. The commands supported 
for ipmIrqCmd are illustrated below.  

cmd			Value Returned
ipac_irqGetLevel	Carrier interrupt level (0 ~ 7)
ipac_irqEnable		0 = OK
ipac_irqDisable		0 = OK
ipac_irqPoll		>0 if the interrupt line is active, else 0
(other commands)	S_IPAC_notImplemented


Hy8601 IP Asyn Driver Usage
============================

1). Configuration shell command
-------------------------------

The configuration ioc shell command takes the form below.

int Hytec8601Configure(char *portName,
				  	epicsUInt16 numAxes,
				  	epicsUInt16 movingPollPeriod,
					epicsUInt16 idlePollPeriod,
				  	epicsUInt16 cardnum,
                 	epicsUInt16 ip_carrier,
                 	epicsUInt16 ipslot,
                 	epicsUInt16 vector,
                 	epicsUInt16 useencoder,
                 	epicsDouble encoderRatio0,
                 	epicsDouble encoderRatio1,
                 	epicsDouble encoderRatio2,
                 	epicsDouble encoderRatio3)

Where:
    (1) portName   asyn port name
    (2) numAxes    number of axes 
    (3) movingPollPeriod  status polling time period when motor is moving in ms
    (4) idlePollPeriod    status polling time period when motor is stopped in ms
    (5) cardnum    Arbitrary card number to assign to this controller (currently not used)
    (6) ip_carrier which previously configured IP carrier in the IOC
    (7) ipslot     which IP Slot on carrier card (0=A etc.)
    (8) vector     which Interrupt Vector (0 - Find One ?)
    (9) useencoder - bit0 for axis0, bit1 for axis1, bit2 for axis2 and bit3 for axis3. 
				   Other bits not used. 1=use encoder, 0=don't use encoder !
    (10) encoderRatio0  - axis0 hardware encoder ratio
    (11) encoderRatio1  - axis1 hardware encoder ratio
    (12) encoderRatio2  - axis2 hardware encoder ratio
    (13) encoderRatio3  - axis3 hardware encoder ratio

Example:

	Hytec8601Configure("Hy8601", 4, 500, 1000, 0, IPAC0, 0, 70, 15, 0.25, 0.25, 0.25, 0.25)

This configures the 8601 card with 
	port name: Hy8601
	4 axes
	poll status every 500ms when moving
	poll status every 1000ms when  stopped
	card number = 0 
	carrier serial number = 0
	ipslot = site A
	interrupt vector = 70
	all 4 axes use encoder
	all 4 axes have quardrature encoder hence their ratios are 0.25

Please note, since the parameters in the function is more than 10, in VxWorks, the start up 
script loading will complain about " too many parameters..." In this case, adding

	iocsh()

before this routine in start up script will solve the problem.


2). Database definition file
----------------------------

Database definition file is: HytecMotorDriver.dbd.

3). Databases
-------------

Two databases can be loaded:

	dbLoadRecords("db/basic_asyn_motor.db")
	dbLoadRecords("db/HytecMotorControl.db", "dev=IP8601,area=TEST,locn=LAB,PORT=Hy8601")

Where:
	basic_asyn_motor.db is the motor record database. It initialises 4 axes.
	HytecMotorControl.db is the special controls for Hy8601 motor controller. It contains few extra asyn records 
		including POWER, BRAKE controls, synchronous read of absolute position and enocder position and firmware
		version etc as below.

	record(bo,"$(dev):$(area):$(locn):POWER") {
	    field(DTYP,"asynInt32")
	    field(OUT,"@asyn($(PORT) 0)HYTEC_POWER")
	    field(VAL, "1")
	    field(ONAM, "On")
	    field(ZNAM, "Off")
	}

	record(bo,"$(dev):$(area):$(locn):BRAKE") {
	    field(DTYP,"asynInt32")
	    field(OUT,"@asyn($(PORT) 0)HYTEC_BRAKE")
	    field(VAL, "1")
	    field(ZNAM, "Set")
	    field(ONAM, "Rls")
	}

	record(ai,"$(dev):$(area):$(locn):POSN") {
	    field(PINI, "YES")
	    field(DTYP,"asynInt32")
	    field(INP,"@asyn($(PORT) 0)MOTOR_POSITION")
	    field(SCAN, "1 second")
	}

	record(ai,"$(dev):$(area):$(locn):EN_POSN") {
	    field(PINI, "YES")
	    field(DTYP,"asynInt32")
	    field(INP,"@asyn($(PORT) 0)MOTOR_ENCODER_POSITION")
	    field(SCAN, "1 second")
	}
	
	record(ai,"$(dev):$(area):$(locn):FIRMWARE_VERSION") {
    	field(PINI, "YES")
    	field(DTYP,"asynInt32")
    	field(INP,"@asyn($(PORT) 0)HYTEC_FWVERSION")
    	field(SCAN, "1 second")
	}

There are other records in the HytecMotorControl.db which can return the positions synchronously etc. 

These positions are raw values in terms of the "counts". These records can be read by "ai" records 
and can be converted to engineering values by EGUF, EGUL etc.

Notes:

	- The power control uses AUX1 of the 8601 CSR. As it indicates in the record above, command

		caput $(dev):$(area):$(locn):POWER 1

	  means power "On" and it sets the AUX1 bit, i.e. AUX1 output high (1).

		caput $(dev):$(area):$(locn):POWER 0

	  means power "Off" so it clears AUX1 bit, i.e. AUX1 output low (0). Command

	- The brake control uses AUX2 of the 8601 CSR. As it indicates in the record above, command

		caput $(dev):$(area):$(locn):BRAKE 1

	  means to release the brake. It clears AUX2 bit, i.e. AUX2 output low (0). Command

		caput $(dev):$(area):$(locn):BRAKE 0

	  means set the brake. It sets the AUX2 bit, i.e. AUX2 output high (1).

	- The firmware version record returns the IP card PCB board issue number and the firmware version
	
		caget $(dev):$(area):$(locn):FIRMWARE_VERSION
		
	  will return something like '2210' for example, whereas the first 2 is the PCB issue number and 
	  210 is the firmware version.
	
4). To build an example application 
-----------------------------------

To build an example to test the 8601 asyn driver, use EPICS makeBaseApp.pl script to create 
the example as usual. Then modify the following files to include the driver module(s).

	- <TOP>/configure/RELEASE

		Change the EPICS_BASE and SUPPORT to the proper directories.
		Add ASYN, IPAC and MOTOR moudles:
		
		ASYN=$(SUPPORT)/asyn/asyn4-14
		IPAC=$(SUPPORT)/ipac/ipac-2.11
		MOTOR=<the motor module dir>/motorR6-5-2
		
	- <TOP>xxxApp/src/Makefile

		In the Makefile of the example src, add following lines:
		
		example_DBD += asyn.dbd
		example_DBD += motorSupport.dbd
		example_DBD += motorRecord.dbd
		example_DBD += drvIpac.dbd
		example_DBD += HytecMotorDriver.dbd
		 
		example_LIBS += HytecMotor
		example_LIBS += motor
		example_LIBS += Ipac
		example_LIBS += asyn
		

	- <TOP>/xxxApp/Db/Makefile

		Copy HytecMotorControl.db to the Db directory and add the following lines in the Makefile of 
		the Db directory:
		
		DB += basic_asyn_motor.db
		DB += HytecMotorControl.db
		
	- Build the application from <TOP>
	
	- Modify st.cmd in iocBoot/iocexample as per next section and run the start up script from here.

5). Start up script example 
---------------------------

The following example is for an IOC that uses RTEMS R4.9.4, MVME5500 processor board.

	# Change directory to TOP of application
	cd("../..")
	iocBoot=pwd()
	ld( "bin/RTEMS-mvme5500/MotionControl.obj")
	#ld < bin/vxWorks-ppc604_long/MotionControl.munch	#for VxWorks

	## Set common environment variables
	#< all/pre_st.cmd
	epicsEnvSet("IOC_NAME",  "MC02")
	epicsEnvSet("LOCA_NAME", "B025")
	epicsEnvSet("ENGINEER", "Condamoor, Shantha")

	epicsEnvSet( "EPICS_CA_MAX_ARRAY_BYTES", "30000")

	# Register all support components
	dbLoadDatabase( "dbd/MotionControl.dbd")
	MotionControl_registerRecordDeviceDriver( pdbbase) 

	bspExtVerbosity=0

	###########################################################
	# Configure Hytec 8002 carriers
	#   8002 carrier VME slot: 3
	#	INT level: 5
	#	Memory per IP: 1MB
	#	Memory mapping offset: 8384
	# ===================================================================
	#The typical output on beatnik looks like this:
	# Cexp@till35>BSP_VMEOutboundPortsShow()
	# Tsi148 Outbound Ports:
	# Port  VME-Addr   Size       PCI-Adrs   Mode:
	# 0:    0x20000000 0x0e000000 0x90000000 A32, SUP, D32, SCT
	# 1:    0x00000000 0x00ff0000 0x9f000000 A24, SUP, D32, SCT
	# 2:    0x00000000 0x00010000 0x9fff0000 A16, SUP, D32, SCT
	# 7:    0x00000000 0x01000000 0x9e000000 CSR, SUP, D32, SCT
	# ===================================================================

	# A32 space configured to start here:  0x20000000
	# Tell the carrier not to use geographic addressing
	IPAC0=ipacAddHy8002("3,5,IPMEM=1,MEMOFFS=8384")

	#
	# Hytec MDS-8 8601 driver setup parameters:
	# int Hytec8601Configure(char *portName,
	#				  epicsUInt16 numAxes,
	#				  epicsUInt16 movingPollPeriod,
	#				  epicsUInt16 idlePollPeriod,
	#				  epicsUInt16 cardnum,
	#                 epicsUInt16 ip_carrier,
	#                 epicsUInt16 ipslot,
	#                 epicsUInt16 vector,
	#                 epicsUInt16 useencoder,
	#                 epicsDouble encoderRatio0,
	#                 epicsDouble encoderRatio1,
	#                 epicsDouble encoderRatio2,
	#                 epicsDouble encoderRatio3)
	#     (1) portName   asyn port name
	#     (2) numAxes    number of axes 
    #	  (3) movingPollPeriod  status polling time period when motor is moving in ms
    #	  (4) idlePollPeriod    status polling time period when motor is stopped in ms
	#     (5) cardnum    Arbitrary card number to assign to this controller, not used
	#     (6) ip_carrier which previously configured IP carrier in the IOC
	#     (7) ipslot     which IP Slot on carrier card (0=A etc.)
	#     (8) vector     which Interrupt Vector (0 - Find One ?)
	#     (9) useencoder - bit0 for axis0, bit1 for axis1, bit2 for axis2 and bit3 for axis3. 
	#					   Other bits not used. 1=use encoder, 0=don't use encoder !
	#     (10) encoderRatio0  - axis0 hardware encoder ratio
	#     (11) encoderRatio1  - axis1 hardware encoder ratio
	#     (12) encoderRatio2  - axis2 hardware encoder ratio
	#     (13) encoderRatio3  - axis3 hardware encoder ratio

	Hytec8601Configure("Hy8601", 4, IPAC0, 0, 0, 70, 0, 0.25, 0.25, 0.25, 0.25)


	# ========================================================
	# New Hytec Motor Databases
	# =======================================================
	dbLoadRecords("db/basic_asyn_motor.db")
	dbLoadRecords("db/HytecMotorControl.db", "dev=IP8601,area=TEST,locn=LAB,PORT=Hy8601")
	# ========================================================

	iocInit()




		


