/****************************************************************************
**                                                                      	*
**  FILE        :  mvp.c                                                	*
**                                                                      	*
**  DESCRIPTION :  Program to control Buoyancy Engine and logging			*
**                 instruments on MBARI Vertical Profiler               	*
**                                                                      	*
**  COPYRIGHT   :  2000 Monterey Bay Aquarium Research Institute        	*
**                                                                      	*
**	REVISION HISTORY:														*
**		1.0.0	Initial release (prm)										*
**		1.1.0	Added yoyo command (prm)									*
**		1.2.0	Added motor brake (prm)										*
**		1.3.0	Added motor soft-start (prm)								*
**		1.3.1	Improved accuracy of pot position to percent calculation.	*
**				Added second argument to yo-yo command (prm)            	*
**		1.4.0	Added interrupt sampling of pot position					*
**		1.5.0	Moved all motor position checking to interrupt routine	 	*
**		1.6.0	Added deployment mode with low-power sleep					*
**																			*
****************************************************************************/
#ifndef PRECOMPHDRS 
#include	<TT8.h>			/* Tattletale Model 8 Definitions */
#include	<tat332.h>		/* 68332 Tattletale (7,8) Hardware Definitions */
#include	<sim332.h>		/* 68332 System Integration Module Definitions */
#include	<qsm332.h>		/* 68332 Queued Serial Module Definitions */
#include	<tpu332.h>		/* 68332 Time Processing Unit Definitions */
#include	<dio332.h>		/* 68332 Digital I/O Port Pin Definitions */
#include	<tt8pic.h>		/* Model 8 PIC Parallel Slave Port Definitions */

#include	<tt8lib.h>		/* definitions and prototypes for Model 8 library */
#include	<userio.h>		/* common convenient user I/O routines */

#include	<assert.h>
#include	<ctype.h>
#include	<errno.h>
#include	<float.h>
#include	<limits.h>
#include	<locale.h>
#include	<math.h>
#include	<setjmp.h>
#include	<signal.h>
#include	<stdarg.h>
#include	<stddef.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<time.h>
#endif 
 
//#include	"mvp.h"
#include	"pwm.h"
#include	"newsleep.h"
#include	"instrument.h"

#define VERSION "1.6.0"
#define MXLINE  80			/* max command line length */
#define MXARGS	3			/* max number of command line arguments */
#define TRUE    1
#define FALSE   0
#define ON		1
#define OFF		0
#define FORWARD	1
#define REVERSE	0
#define NUMCOMMANDS (sizeof(commands)/sizeof(commands[0]))
#define CMD_BUF_SIZE    132
#define NO_VAL	32767		/* indicates no value for command argument */
#define PROMPT  "\nMVP> "
#define BS		0x08		/* backspace char */
#define ESC		0x1b		/* escape char */
#define MTR_CUR	0			/* A/D chan for motor current */
#define POT_POS	1			/* A/D chan for pot position */
#define MTR_PWM	15			/* TPU chan for motor pwm */
#define PWM_PER	400			/* PWM period, 250 ns per count for 16 MHz clk */
#define PWM_STEP	10		/* number of PWM counts to increase each 100 ms */
#define NUMSAMPS	16		/* number of AtoD samples to average */
#define LOLIM	614			/* lower limit of engine travel on 0-4095 scale */
#define UPLIM	3614		/* upper limit of engine travel on 0-4095 scale */
#define RANGE	(UPLIM-LOLIM)	/* total range of allowed motion */
#define HALF_PCT	RANGE/200	/* 0.5% of total range */
#define ONE_PCT		RANGE/100	/* 1% of total range */
#define FIVE_PCT	RANGE/20	/* 5% of total range */
#define PITDLY 2				/* delay time for periodic interrupt (102.4 msec) */
#define MINUTES	6000L		/* one minute of ticks at 100 ticks per second */




/* Define error conditions for buoyancy engine */
typedef enum {
	kNone,
	kCurrentTrip,
	kOperatorAbort,
	kPotError
} BEFault;

/* Define the routines that do the work for each command */
typedef enum {
	kEmpty,
	kUnknown,
	kSetEngPos,
	kReadEngPos,
	kSetModeDeploy,
	kSetModeYoyo,
	kSetModeTrans,
	kSetClkTime,
	kReadClkTime,
	kExitToMon,
	kHelp
} CmdNum;

typedef struct {
	char    *theCommand;
	char    *theExplanation;
	CmdNum  theNumber;
} ACommand;

ACommand commands[] = {
	"SEP", "[0..100]                  Set engine position", kSetEngPos,
	"REP", "                          Read engine position", kReadEngPos,
	"SMD", "[1..500][0..1440][0..60]  Set mode deploy", kSetModeDeploy,
	"SMY", "[0..99][1..100][1..1440]  Set mode yo-yo (hit any key to abort)", kSetModeYoyo,
	"SMT", "[0,1]                     Set mode transparent", kSetModeTrans,
	"SCT", "                          Set clock time", kSetClkTime,
	"RCT", "                          Read clock time", kReadClkTime,
	"ETM", "                          Exit to TOM8 monitor", kExitToMon,
	"HELP", NULL, kHelp,
	"?", NULL, kHelp,
};
short   cmdPointer, baseCommand;
char    cmdBuffer[CMD_BUF_SIZE+2];
char    waiting, loopMode = FALSE;
short   cmdArg[MXARGS];

short	gDesPosition;		/* destination position of buoyancy engine */
short	gActPosition;		/* actual position of buoyancy engine */
short	gDelta;				/* difference between actual and desired position */
short	gSign;				/* sign of gDelta */
short	gRunning;			/* TRUE if motor running */
short	gPWMHi;				/* PWM duty cycle */
BEFault	gFault;				/* buoyancy engine fault */
//struct	tm	gTime;			/* date and time */
//struct	tm	gWakeupTime;	/* time to wakeup from sleep */
//struct	tm	gRecoverTime;	/* time to recover vehicle */
short	gCasts;				/* total number of casts */
short	gInterval;			/* number of minutes between casts */
static int 	TimeOut;

const char wday[7][4] = {
	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};

const char month[12][4] = {
	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

const short n_mdays[12] = {
	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};


/* function prototypes */
void Init(void);
void SetEngPos(short);
void StartEngine(short);
void ReadEngPos(void);
short Cnt2Pct(short);
short Pct2Cnt(short);
void SetModeDeploy(short, short, short);
void SetModeYoyo(short, short, short);
void TransparentSerial(short);
void SetClkTime(void);
void ReadClkTime(void);
int QueryTime(struct tm*);
void Help(void);
void EnaDCDrive(void);
void DisDCDrive(void);
void SoftStart(short *);
short AtoDReadAvg(short, short);
void GetLine(char *, int);
void DoCommand(void);
short ParseCommand(void);
void MakeWordUpper(char *);
void PrepPinsLowPower(void);
void InitPeriodicTimerInterrupt(void);
void SetPeriodicTimer(int delay);
void ServicePeriodicTimerInterrupt(void);

void main(void)
{
	Init();							/* set up hardware and program variables */

	printf("\n\nMVP Controller version %s\n", VERSION);

	while(TRUE) {                   /* loop forever */
		printf(PROMPT);
		kbflush();                  /* clear command buffer */
		GetLine(cmdBuffer, CMD_BUF_SIZE);	/* get next command */
		do {
			DoCommand();            /* go do it */
			waiting = loopMode;
			while(waiting);        	/* pause if in loop mode */
		} while(!kbhit() && loopMode);  /* exit if any key is hit */
		loopMode = FALSE;
	} /* end while */

} /* end main */


/* Initialize hardware and program variables */
void Init(void) {

	InitTT8(NO_WATCHDOG, TT8_TPU);	/* setup Model 8 for running C programs */
	
	PConfOutp(E, 1);				/* config direction pin (Port E bit 1) as output */
	PConfOutp(E, 2);				/* config brake pin (Port E bit 2) as output */
	DisDCDrive();					/* disable the motor driver */
	PClear(E, 2);					/* apply motor brake */
	TPUSetPin(MTR_PWM, OFF);		/* config mtr enable pin (TPU bit 15) as output, and turn off */
	TPUSetupPWM(MTR_PWM, 0, PWM_PER, MiddlePrior); /* start pwm output at 0%*/

	InitPeriodicTimerInterrupt();	/* start periodic interrupt timer */ 
	SetPeriodicTimer(PITDLY);		/* set PITR delay value */

	SetTimeSecs(0L, 0L);			/* just for known starting point */
	SetTickRate(100L);				/* set tick rate used by newsleep commands */
	
	InitSerPorts();					/* init TPU serial ports to talk to instruments */

} /* end function Init() */


/* Get a command line into the command buffer */
void GetLine(char s[], int lim)
{
	char c;
    int i;
    
    i = 0;
    while(--lim > 0 && (c = getchar()) && c != '\n') {
	    if(c == '\x08' || c == '\x10') {	/* if backspace or delete */
	    	if(i > 0) {				/* if not beginning of buffer */
            	i--;				/* throw previous char away */
			} else {
            	putchar('\x20');	/* print a space */
            } /* end if */
	        lim++;
		} else {
	    	s[i++] = c;				/* put char in buffer */
        } /* end if */
    } /* end while */
    s[i] = '\0';					/* mark end of line */
    printf("\n");					/* echo end of line */

} /* end function GetLine() */


/* Read a command from the command buffer and execute it */
void DoCommand(void)
{
	short   cmdNum;
	
	MakeWordUpper(cmdBuffer);
	cmdNum = ParseCommand();
	switch(cmdNum) {
		case kEmpty:
			break;

		case kUnknown:
			printf("Unknown command.");
			loopMode = FALSE;
			break;

		case kHelp:
			Help();
			break;

		case kSetEngPos:
			SetEngPos(cmdArg[0]);
            break;

		case kReadEngPos:
			ReadEngPos();
			break;

		case kSetModeDeploy:
			SetModeDeploy(cmdArg[0], cmdArg[1], cmdArg[2]);
			break;
			
		case kSetModeYoyo:
			SetModeYoyo(cmdArg[0], cmdArg[1], cmdArg[2]);
			break;

		case kSetModeTrans:
			TransparentSerial(cmdArg[0]);
			break;

		case kSetClkTime:
			SetClkTime();
			break;
			
		case kReadClkTime:
			ReadClkTime();
			break;
			
		case kExitToMon:
			ResetToMon();			/* Exit program */
			break;

		default:
			loopMode = FALSE;
			printf("Unrecognized command");
			
	} /* end switch */

} /* end function DoCommand */


/* Set the engine position, given a position on a 0-100 percent scale. */
void SetEngPos(short desPosition)
{
	short	pctPosition, curPosition;
	
	if(desPosition >= 0 && desPosition <= 100) {
		
		StartEngine(desPosition);			/* start the engine moving */
		curPosition = gActPosition;
		while(gRunning) {					/* wait until it stops */
			if(gActPosition > curPosition + ONE_PCT || gActPosition < curPosition - ONE_PCT) {
				curPosition = gActPosition;
				pctPosition = Cnt2Pct(gActPosition); /* convert count to percent */
				printf("Engine at %3d%\n", pctPosition);	/* print current position every one percent */
			} /* end if */
		} /* end while */
				
		pctPosition = Cnt2Pct(gActPosition); /* convert count to percent */
		if(gFault == kCurrentTrip) printf("OVERCURRENT TRIP at %d%\n", pctPosition);
		if(gFault == kOperatorAbort) printf("OPERATOR ABORT at %d%\n", pctPosition);
		if(gFault == kPotError) printf("POSITION POT ERROR at %d%\n", pctPosition);
	} else {
		printf("Position must be in range 0 to 100\n");
	} /* end if */

} /* end function SetEngPos() */


/* Start the engine moving to the desired set point.  This routine returns
	immediately before the engine has reached the destination.
	The engine position (actPosition) is read, and the motor is stopped,
	in an interrupt service routine. */
void StartEngine(short desPosition)
{
	gDesPosition = desPosition;
	gFault = kNone;						/* clear any existing faults */
	gPWMHi = 0;
	TPUChangePWM(MTR_PWM, gPWMHi, PWM_PER); /* set PWM to start value */
	PSet(E, 2);							/* release brake */
	DelayMilliSecs(50L);				/* wait for brake to release */
	EnaDCDrive();						/* power up the motor driver */
	DelayMilliSecs(1L);					/* wait for motor power transient to die */
	gDesPosition = Pct2Cnt(gDesPosition);	/* convert percent to count */
	gDelta = gDesPosition - gActPosition;	/* diff is desired pos minus actual pos */
	gSign = gDelta / abs(gDelta);
	if(gDelta > 0) {					/* EXTEND buoyancy engine */
		PSet(E, 1);						/* set direction forward */
	} else {							/* RETRACT buoyancy engine */
		PClear(E, 1);					/* set direction reverse */
	} /* end if */

	gRunning = TRUE;					/* start the motor running */

} /* end function StartEngine() */



/* Read the engine position */
void ReadEngPos(void)
{
	short	potPosition;

	potPosition = Cnt2Pct(gActPosition); 	/* convert count to percent */
	printf("Engine position is %d%\n", potPosition);
	
} /* end function ReadEngPos() */
	

/* Convert engine position count to percent extension
	Engine lower position limit (LOLIM) is 0%, upper limit (UPLIM) is 100% */
short Cnt2Pct(short count)
{
	return(((long)count + HALF_PCT - LOLIM) * 100 / RANGE); /* convert count to percent */

} /* end function Cnt2Pct() */


/* Convert engine percent extension to position count
	Engine lower position limit (LOLIM) is 0%, upper limit (UPLIM) is 100% */
short Pct2Cnt(short percent)
{
	return((long)percent * RANGE / 100 + LOLIM);	/* convert percent to count */

} /* end function Pct2Cnt() */


/* Go into deployment mode.  This requires using the low-power sleep mode between casts.
	The LP sleep mode is sensitive to which interrupts are occuring, so the Periodic Interrupt
	Timer must be disabled before entering LP sleep.  LP sleep also screws up the TPU PWM
	output, so that must be restarted after wakeup */
void SetModeDeploy(short casts, short interval, short pause)
{
	short numCasts = 0;							/* number of casts completed */
	SLPResult result;							/* reason for waking up */

	if(casts >= 1 && casts <= 500 && interval >= 0 && interval <= 1440 && pause >= 0 && pause <= 60) {
		printf("A total number of %d casts will take place\n", casts);
		printf("The interval between casts will be %d minutes\n", interval);
		printf("The pause while rising to surface will be %d minutes\n", pause);
		if(QueryYesNo("Do you want to deploy now ", FALSE)) {

			printf("\n\nDeployment started ");
			ReadClkTime();
			result = LowPowerSleep(0L);			/* setup sleep clock */
			printf("init result = %d\n", result);
			printf("Setting engine to 50%\n");
			StartEngine(50);
			DelayMilliSecs(300 * 1000L);		// wait 5 minutes
			printf("Setting engine to 0%\n");
			StartEngine(0);
			while(gRunning);					/* wait until engine stops */
			while(numCasts++ < casts) {
				printf("Entering low-power sleep mode\n");
				PrepPinsLowPower();
				SetPeriodicTimer(0);			// stop PIT interrupts
				result = LowPowerSleep(interval * MINUTES); /* sleep until interval has passed */

				// we just woke up from sleep - start another cast
				SetPeriodicTimer(PITDLY);		// restart PIT interrupts
				TPUSetupPWM(MTR_PWM, 0, PWM_PER, MiddlePrior); /* restart pwm output at 0%*/
				printf("wakeup result = %d\n", result);
				printf("Waking from low-power sleep mode\n");
				printf("Starting cast number %d\n", numCasts);
				StartEngine(100);
				printf("Setting engine to 100%\n");
				DelayMilliSecs(pause * 60000L);	/* wait until vehicle at surface */
				printf("At surface, setting engine to 0%\n");
				StartEngine(0);
				while(gRunning);				/* wait until engine stops */
			} /* end while */
			printf("Setting engine to 100%\n");
			StartEngine(100);
			printf("Deployment finished ");
			ReadClkTime();	
		} else {
			printf("\n\nDeployment cancelled\n");
		} /* end if */
	} else {
		printf("Casts must be in range 1 to 500\nInterval must be in range 0 to 1440\nPause must be in range 0 to 60\n");
	} /* end if */
	
} /* end function SetModeDeploy() */


/* Go into yo-yo mode, cyling back and forth from given lower limit to upper limit */
void SetModeYoyo(short llim, short ulim, short pause)
{
	short	cycles = 0;
	
	if(llim >= 0 && llim <= 100 && ulim >= 1 && ulim <= 100 && pause >= 1 && pause <= 1440) {
		while(TRUE)	{							/* do until operator keyboard abort */
			SetEngPos(llim);					/* set engine to lower limit */
			if(gFault != kNone) break;
			DelayMilliSecs(pause * 1000L);		/* pause for given number of seconds */
			SetEngPos(ulim);					/* set engine to upper limit */
			if(gFault != kNone) break;
			printf("%d yo-yo cycles completed\n", ++cycles);
			DelayMilliSecs(pause * 1000L);		/* pause for given time */
		} /* end while */
	} else {
		printf("Lower limit must be in range 0 to 99\nUpper limit must be in range 1 to 100\nPause must be in range 1 to 1440\n");
	} /* end if */
	
} /* end function SetModeYoyo() */


/* Talk transparently through the command port to one of the instruments until ESC is entered */
void TransparentSerial(short instr)
{

	char inchar, outchar;
	short done = FALSE;

	if(instr < 0 || instr > 1) {
		printf("Instrument port must be 0 or 1\n");
		done = TRUE;
	} else {
		PConfOutp(E,3);	PClear(E,3);		/* power up instrument RS232 drivers */
		DelayMilliSecs(50L);				/* wait for power supplies to come up */
	} /* end if */

#if 0										// for DEBUG only
	printf("> ");
	GetLine(string, 16);
	TSerInFlush(SBE19_IN);
	SndInstr(SBE19_OUT, string);
	RcvInstr(SBE19_IN, string, 5);
#endif

	while(!done) {
		if(SerByteAvail()) {
			outchar = SerGetByte();			/* get char from MVP console port */
			if(outchar == '\x1B') {			/* if ESC was entered, we're done */
				done = TRUE;
			} /* end if */
			TSerPutByte((instr * 2) + 1, outchar); /* send char to instrument */
		} /* end if */
		
		if(TSerByteAvail(instr*2)) {
			inchar = TSerGetByte(instr*2);	/* get char from instrument */
			SerPutByte(inchar);				/* send char to MVP port */
		} /* end if */
		
	} /* end while */

	PSet(E,3);								/* shut down instrument RS232 drivers */
	printf("\n\nExiting transparent mode\n\n");

} /* end function TransparentSerial() */


/* Set the real-time clock */
void SetClkTime(void)
{
	struct	tm datetime;			/* date and time */
	
	if(!QueryTime(&datetime)) {
		printf("Clock not set\n");
		return;
	} /* end if */
	SetTimeTM(&datetime, NULL);

} /* end function SetClkTime() */


/* Read the real-time clock */
void ReadClkTime(void)
{
	struct tm* now;
	time_tt cTime;        

	cTime = ttmnow();				/* get the current time */

	now = localtime(&(cTime.secs)); /* convert it to a time structure */

	if(now->tm_year < 70) now->tm_year += 2000;
	else now->tm_year += 1900;    
	printf ("%s %s %2d %d @ %02d:%02d:%02d\n",  wday[now->tm_wday],
												month[now->tm_mon],
												now->tm_mday,
												now->tm_year,
												now->tm_hour,
												now->tm_min,
												now->tm_sec);
		
} /* end function ReadClkTime() */


/* Query the date and time from the user.
	Returns TRUE if date and time are okay, FALSE otherwise */
int QueryTime(struct tm* tm)
{
	int n, illegal;
	char buffer[64];
	
	printf ("Please use Universal Coordinated Time, NOT local time.\n");
	printf ("The time set operation will time out after 30 seconds of inactivity.\n");
	printf ("Hit <return> to abort the time set operation\n");
	printf ("Enter date and time (MM/DD/YY HH:MM:SS) ");
	fflush (stdout);
	
	//  wait no more than 30 seconds between input characters before
	//  timing out
	StopWatchStart();
	n = 0;
//	memset(buffer, 0, sizeof(buffer));		// clear buffer
	while((StopWatchTime() / 1e6) < 30)  {
		if(SerByteAvail()) {				// if char available
			StopWatchStart();
			buffer[n] = SerGetByte();		// put it in buffer
			if(buffer[n] != BS)  {			// if not backspace
				putchar(buffer[n]);			//	put echo char in screen buffer
				if(buffer[n] == '\r')		// if carriage return
					break;					//	we're done
				if(n < (sizeof(buffer)-1))	// if buffer isn't full
					n++;					//	point to next buffer position
				else
					break;  
			} else {						// char is a backspace
				if(n > 0) {					// if buffer not empty
					putchar (buffer[n]);	//	echo backspace
					buffer[n] = 0;			//	and delete last char
					n--;
				} /* end if */
			} /* end if */
			fflush (stdout);				// print echo char to screen 
		} /* end if */
	} /* end while */

	if((StopWatchTime() / 1e6) > 30) {
		printf("Timed out due to inactivity.\n");
		return FALSE;
	} /* end if */      

	buffer[n] = 0;							// terminate string
	putchar('\n');							// put carriage return in screen buffer
	fflush(stdout);							//	and print it
	
	//  we reached this point either because of a timeout or
	//  because the user hit a carriage return
	n = sscanf (buffer, "%d/%d/%d %d:%d:%d", &tm->tm_mon, &tm->tm_mday, &tm->tm_year,
												&tm->tm_hour, &tm->tm_min, &tm->tm_sec);
	if(n != 6) {
		printf ("Date not accepted - entry format is month/day/year hour:min:sec\n");
		return FALSE;
	} // end if
	//  correct month for zero-based reference used by computer
	tm->tm_mon--;

	//  allow multiple formats for the year
	if(tm->tm_year > 1970)
		tm->tm_year -= 1900;
	if((tm->tm_year < 70) && (tm->tm_year >= 0))
		tm->tm_year += 100;
		
	// verify that the entered date and time are legal
	illegal = FALSE;
	if((tm->tm_mon < 0) || (tm->tm_mon >= 12))
		illegal = TRUE;
	if((tm->tm_mday <= 0) || (tm->tm_mday > n_mdays[tm->tm_mon]))  {
		if(((tm->tm_year%4) != 0) || (tm->tm_mon != 1) || (tm->tm_mday != 29))
			illegal = TRUE;
	}       
	if((tm->tm_year < 0) || (tm->tm_year > 206))
		illegal = TRUE;
	if((tm->tm_hour < 0) || (tm->tm_hour > 23))
		illegal = TRUE;
	if((tm->tm_min < 0) || (tm->tm_min > 59))
		illegal = TRUE;
	if((tm->tm_sec < 0) || (tm->tm_sec > 59))
		illegal = TRUE;
	if(illegal) {
		printf ("Date not accepted - illegal date entered\n");
		return FALSE;
	}
	return TRUE;
} /* end function QueryTime() */


/* Enable (power on) the DC Drive module */
void EnaDCDrive(void)
{

	PConfOutp(E, 0);				/* config Port E run/sense pin as output */
	PSet(E, 0);						/* set run/sense hi */
	DelayMilliSecs(2L);				/* delay for 2 ms to latch run/sense */
	PConfInp(E, 0);					/* config run/sense pin as input */

} /* end function EnaDCDrive() */


/* Disable (power off) the DC Drive module */
void DisDCDrive(void)
{

	PConfOutp(E, 0);				/* config Port E run/sense pin as output */
	PClear(E, 0);					/* set run/sense low */

} /* end function DisDCDrive() */


/* Soft-start the motor by ramping up the PWM signal each 10 ms until it reaches 100% */
void SoftStart(short *pwmHi)
{

	ulong now;
	static ulong then;

	now = TensMilliSecs();			/* get current time */
	if(now > then) {				/* have 10 ms passed? */
		*pwmHi += PWM_STEP;			/* increase PWM duty cycle */
		if(*pwmHi > PWM_PER) *pwmHi = PWM_PER; /* limit max value */
		TPUChangePWM(MTR_PWM, *pwmHi, PWM_PER); /* set new value */
		then = now;					/* save current time */
	} /* end if */

} /* end function SoftStart() */


/* Print the help message */
void Help(void)
{
	short	i;
	
	printf("Commands are:\n");
	for(i = 0; i < NUMCOMMANDS; i++) {
		printf(commands[i].theCommand);
		if(commands[i].theExplanation) {
			printf(commands[i].theExplanation);
		} /* end if */
		printf("\n");
	} /* end for */
	
} /* end function Help() */


/* Read the A/D chan numSamps times and average the results */
short AtoDReadAvg(short chan, short numSamps)
{
	unsigned short i, sum = 0;

	if(numSamps > 16) numSamps = 16;		/* limit max samples to prevent math overflow */
	for(i = 0; i < numSamps; i++) {
		sum += AtoDReadWord(chan) / 8;		/* right-justify 12-bit result, and sum */
	} /* end for */
	return sum / numSamps;

} /* end function AtoDReadAvg() */


/* Parse the string in the command buffer */
short ParseCommand(void)
{
	short   i, j, cmdLen;
	char    *p;
	short   returnValue;
	
	returnValue = kUnknown;
	for(i = 0; i < MXARGS; i++) {
		cmdArg[i] = NO_VAL;				/* set to invalid value */
    } /* end for */

	if(!cmdBuffer[0]) {
		returnValue = kEmpty;            /* Empty buffer */
	} else {
		for(i = j = 0; i < NUMCOMMANDS; i++) {
			cmdLen = strlen(commands[i].theCommand);
			if(strncmp(cmdBuffer, commands[i].theCommand, cmdLen) == 0) {
				returnValue = commands[i].theNumber;
				p = cmdBuffer + cmdLen;
				while(*p && j < MXARGS) {
					while(*p && isspace(*p)) p++; /* skip spaces before arg */
					sscanf(p, "%d", &cmdArg[j++]); /* read the argument */
					while(*p && !isspace(*p)) p++; /* skip to next arg */
                } /* end while */
				if(*(p-1) == '*') loopMode = TRUE;
				break;
			} /* end if */
		} /* end for */
	} /* end if */

	return returnValue;

} /* end function ParseCommand */


/* Convert a string to upper case */
void MakeWordUpper(char *p)
{
	for( ; *p && !isspace(*p); p++) *p = toupper(*p);
} /* end function MakeWordUpper() */


/* Prepare TT8 pins for low-power operation.  This is copied from the newsleep.c module */
void PrepPinsLowPower(void)
{
/*
**	68332 PORT F (IRQ's OR I/O)
*/
	// the following two pins are used by interrupts in this program
	PConfInp(F,3);						/* irq3/led (has pullup) */
	PConfInp(F,1); 						/* rxd1 /IRQ1            */

	PConfOutp(F,7);	PClear(F,7);				/* A-D shutdown */
	PConfInp(F,6);						/* connected to, and pulled by TP15 */
	PConfInp(F,5);						/* picirq (PIC asserts)  */
	PInpToOut(F,4);						/* nc                    */
	PInpToOut(F,2);						/* nc                    */
	PConfInp(F,0);						/* sel5vpic (has pullup) */

/*
**	68332 PORT E (BUS SIGNALS OR I/O)
*/
	PInpToOut(E,7);						/* nc */
	PInpToOut(E,6);						/* nc */
	PInpToOut(E,5);						/* nc */
	PInpToOut(E,4);						/* nc */
	PConfOutp(E,3);	PSet(E,3);			/* shut down instrument RS232 drivers */
	PConfOutp(E,2);	PClear(E,2);		/* disable motor brake */
	PInpToOut(E,1);						/* nc */
	PConfOutp(E,0);	PClear(E,0);		/* disable DC Drive power */

/*
**	68332 PORT D (QSM OR I/O)
*/
	PConfBus(D,7);						/* txd RS-232 */
	PConfOutp(D,6);	PSet(D,6);			/* pcs3 a-d cs active low */
	PInpToOut(D,5);						/* pcs2/nc */
	PInpToOut(D,4);						/* pcs1/nc */
	PInpToOut(D,3);						/* pcs0/nc */
	PConfOutp(D,2);	PClear(D,2);		/* sck */
	PConfOutp(D,1);	PClear(D,2);		/* mosi */
	PInpToOut(D,0);						/* miso */

/*
**	68332 TPU PINS
*/
	TPUSetPin(0, 0);					/* nc */
	TPUSetPin(1, 0);					/* nc */
	TPUSetPin(2, 0);					/* nc */
	TPUSetPin(3, 0);					/* nc */
	TPUSetPin(4, 0);					/* nc */
	TPUSetPin(5, 0);					/* nc */
	TPUSetPin(6, 0);					/* nc */
	TPUSetPin(7, 0);					/* nc */
	TPUSetPin(8, 0);					/* nc */
	TPUSetPin(9, 0);					/* nc */
	TPUSetPin(10, 0);					/* nc */
	TPUSetPin(11, 0);					/* nc */
	TPUSetPin(12, 0);					/* nc */
	TPUSetPin(13, 1);					/* txd2 */
	TPUGetPin(14);						/* rxd2 (active in shutdown) */
	TPUSetPin(15, 0);					/* connected to F,6 */

} /* end function PrepPinsLowPower() */


/* Set up the Periodic Timer interrupt */
void InitPeriodicTimerInterrupt(void)
{ 
	static ExcCFrame	esf;

	*PITR = 0;	/* set count to zero - disable timer */
	InstallHandler(ServicePeriodicTimerInterrupt, PIT_INT_VECTOR, &esf);
} /* end function InitPeriodicTimerInterrupt() */


/* Set the Periodic Interrupt Timer delay
	delay is in range 0-255
	period = (delay * 512 * 4) / 40,000 for TT8
	min is 51.2 msec, max is 13.056 sec
	if delay = 0 then stopped
	bit 8 turns on divide-by-512 prescaler */
void SetPeriodicTimer(int delay)
{
	*PITR = delay | 0x0100;	/* set count and enable interrupt */
} /* end function SetPeriodicTimer() */


/*	Service the Periodic Timer interrupt*/
void ServicePeriodicTimerInterrupt(void)
{ 
	if(TimeOut > 0) TimeOut--;
	gActPosition = AtoDReadAvg(POT_POS, NUMSAMPS);	/* read pot position */

	if(gRunning) {						/* if motor is running */
		if(gPWMHi < PWM_PER) {			/* if not at full speed */
			gPWMHi += PWM_STEP;			/*  increase PWM duty cycle */
			if(gPWMHi > PWM_PER) gPWMHi = PWM_PER; /* limit max value */
			TPUChangePWM(MTR_PWM, gPWMHi, PWM_PER); /* set new value */
		} /* end if */
		
		if(!Pin(E, 0)) gFault = kCurrentTrip;	/* check for overcurrent trip */
		if(kbhit()) gFault = kOperatorAbort;	/* check for operator abort */
		if(gActPosition > UPLIM + FIVE_PCT || gActPosition < LOLIM - FIVE_PCT) {
			gFault = kPotError;			/* check for position pot error */
		} /* end if */

		if(gDelta * gSign < 0 || gFault) {	/* if destination reached or fault occurred */
			DisDCDrive();				/* power down the motor driver */
			PClear(E, 2);				/* apply brake */
			gRunning = FALSE;
		} else {
			gDelta = gDesPosition - gActPosition;	/* update position */
		} /* end if */
	} /* end if */

} /* end function ServicePeriodicTimerInterrupt() */
