/*	SW-COMPUTER Factory Control Program
	WWW.EXTREME-FIRE.COM
	Target controller = ATTINY85-20PU microcontroller from ATMEL.
	Clock speed = 8 MHz internal.
	Development platform = AVR Studio 4  Version 4.13 Service Pack 2  Build 571.
	C compiler = WinAVR 20070525
	Programer = AVR Dragon
	Hardware Schematic = SW-COMPUTER all versions.
	Hardware Layout = SW-COMPUTER all versions.

This program is fully released to the Public Domain by Terry Fritz 2007 / 2008.
*/
/*
History
	001 - Initial release  Nov. 23, 2007  Terry Fritz
	002 - Next generation  Nov. 25, 2007  Terry Fritz
	002.40 - First real beta release.
	002.41 - Fixed Vibration timing problems, set peak current to 250 amps.	Dec. 26, 2007  9:24AM
	002.42 - Added Temperature sensing.  Dec. 26, 2007  1:10PM
	002.43 - Removed high battery voltage speed reduction.  High battery voltage to 17.0V.  Dec. 27, 2007 11:16PM
				Timing rechecked Dec 28, 2007 2:19PM
	002.44 - Added event logging, LiPO monitor support, PWM and BurstTimeFactor low limits.  Dec. 30, 2007 10:00AM
	002.45 - Added LiPO Monitoring. Redid Port control. Jan 1, 2008  8:51AM
	002.46 - Speeded up programing vibrations and Burst to Full auto time.  Check LiPO alert 3 times. Jan. 7, 2008 4:30PM
	002.50 - Released 
	002.60 - Tweaked LiPO Function.  Lowered trip points from 3000mV to 1000mV.  Changed installed detection from
				2000mV to 750mV.  Feb. 15, 2008  11:10PM
			Added FiringCounter (was unused MotorRunningLoopCounter).
			Added battery voltage reset after 300 trigger cycles and DeadTime.
			Fixed self test 6 limits.
	002.70 - Fixed LiPO limits per WolfDragon.  Mar. 14, 2008  4:28AM



Known issues:
	To use pin 1 (AUX1) the Reset Disable Fuse has to be set and the Debug Wire Enable Fuse has to be clear.
		( RSTDISBL = 1  DWEN = 0 )  They use pin 1 by defalt and will keep the pin high if not fixed.

	Set BrownOut voltage to 4.3V in fuses.

Future changes:
	None

			


/////////////////////////////
Begin DonP modifications log:
donp@unconventional-airsoft.com

The base code is "Version270".
My version "1.2"

	Mar 20, 2008	- Modified default mode to be "3 Round Burst / Auto" 
					(used to be "Normal").  Changed in several places due to 
					unfamiliarity with code (not clear what is/isn't used).  Works.

	Mar 25, 2008	- Added defines for behaviour changes (i.e. no fall into fullauto, etc)
					- Chip Marking: One bold sharpie dot = version 270 / 1.0

	Mar 28, 2008	- Added prng() function for use as an RNG.  Sample usage:
					    long int x;
					    // Use drain voltage as seed value
					    x = prng((long int)GetVoltage(Drain,0));
					    // x is a 32 bit number
					    x &= 0x0000FF00; // Axe all but these 8 bits
						x = (x >> 8) // Shift over
					    // x should now be between 0 and 255
						// Using the indicated bits seems to work better
						// for lower values and seeds.
					- Changed VersionNumber to be my own (was 0x27, changed to 0x12)
					- Working on adding GunJam structure.

	Apr 4, 2008		- Got gun jam simulations working.  Turned on by enabling
					  a #define option.  Mutually exclusive with LiPO board support.
					  requires a switch between GND and AUX0 to trigger when bolt is worked.
					- Added code to modify LiPO stuff if LiPO support not defined.
					

TODO: 	

Feature (select via trigger pulls) for semi-random jams.


Feature - count shots in semi, so as to emulate the 3rd burst from the original
M16A1 (a mechanical ratchet that fires 3 shots in burst mode, but counted partial 
bursts and semi-auto shots).  Can't easily do the partial burst thing, but the semi-
should be easy.

		

End DonP
/////////////////////////////



*/


// 20080325 DonP - Begin defines for behaviour
// Modify these to change how the chip works (comment out = disable)
//
// These are optional (leave in, or comment out)
// ---------------------------------------------
#define	FULLAUTOAFTERBURSTENABLE	// Enable fullauto after burst
//
// These are mutex (only one should be left in)
// ---------------------------------------------------------------
// Can only have one of the following enabled
#define BURSTMODEDEFAULT		// SAFE-SEMI-BURST/FULL factory default
//#define NORMALMODEDEFAULT		// SAFE-SEMI-FULL factory default
//#define SEMILOCKED				// SAFE-SEMI-SEMI is only mode possible
//
// Only one of the following should be enabled
#define	GUNJAMSIMULATIONENABLE		// Enable simulated gun jams.
//#define LIPOENABLE				// Enable all li-poly stuff (ie daughterboard talking)
//
//
// These only mean something if gunjamsimulations are enabled above
// ----------------------------------------------------------------
#define GUNJAMTRIPWIRE	25		// Number of firing events before a possible Jam (0-255)
#define JAMCHANCE		128		// Chance of an actual jam once it's time for one (0-255)
								// (e.g. If pseudorandom(0-255) <= JAMCHANCE, then JAM)
#define	AUTOJAMCHANCE	8	 	// Chance of firing in auto causing a JAM next time around.
								// (same as above: 0-255) 0=never, 255=always, 64=1/4

//
// End DonP




/*	Include libraries */
	#include <avr/io.h> /* For all the odd ATTINY stuff */

/*	Define ATTINY85 ports (PBx) to the gun controls */
/*	Note that these port numbers or NOT the physical pin numbers! */
	#define Drain 2			/* FET drain voltage port */
	#define Aux0 3			/* AUX 0 I/O port */
	#define Trigger 4		/* Trigger voltage port */
	#define Aux1 5			/* AUX 0 I/O port */
	#define Ground 10  		/* Internal A/D ground used for calibration */
	#define Temperature 11	/* Internal temperature probe.  Only use in mode 2 */
			
/*	Variable definitions */

	// DonP 20080328
	// Co-opted this for my own purposes
	//#define VersionNumber 0x27						/* Software version number programed to EEPROM */
	#define VersionNumber 0x12

	#define HighBatteryVoltage 17000				/* In mV */
	#define LowBatteryVoltage 7000					/* In mV */
	#define TriggerFiringVoltagePercent 80			/* Perent */
	#define BatteryVoltageDroopWarningPercent 85	/* Percent */
	#define TriggerHoldVoltage 3000					/* In mV */

	#define DrainCurrentPeakLimit 250				/* In Amps */
	#define RDSon 2870								/* In uOhms */ 
	#define TemperatureLimit 75						/* In Celcius */ 
	
	#define BreakTime 100							/* mS */
	#define VibrateFrequency 40						/* Hz */
		
	#define Normal 1								/* These are GunModes made easy to read */
	#define ThreeRound 2
	#define ThreeRoundAuto 2
	#define SemiOnly 3								/* Semi Only gun function */

	#define Neutral 0x29							/* Port control values Modified for LiPO */
	#define Break 0x28
	#define Motor 0x2B

/* Function prototypes */
	void MotorOFF(void);						/* Turns the motor off */
	void MotorON(void);							/* Turns the motor on */
	void BreakOFF(void);						/* Turns the break off */
	void BreakON(void);							/* Turns the break on */
	void EnergizeFETs(void);					/* Enables the FETs in a controled way */
	void Timer(long Time);						/* A mS timer  Timer(123) = 123mS */
	void FastTimer(long Time);					/* An unclaibrated ~15uS fast timer */
	long GetVoltage(unsigned char Sensor, unsigned char ADCRange);	/* Gets the Voltage (in mV) from the chosen source (Drain, Trigger, Aux) */
	void Vibrate(void);							/* Vibrates the gun's motor for signaling the user */
	void CalibrateAtoD(void);					/* Calibrates A/D convertor */
	void EEPROM_write(unsigned char ucAdress, unsigned char ucData);	/* Write to EEPROM */
	unsigned char EEPROM_read(unsigned char ucAddress);					/* Read from EEPROM */
	void ProgramEEPROM(void);					/*  User programming of the EEPROM */
	void ReadEEPROM(void);						/*  Get stored EEPROM data */
	void SetBurstTime(long ShotCounter);		/*	Set burst time function */
	void MotorPWM(char PWMduty);				/*	Motor PWM function */
	void SetAUXPorts(void);						/*  Setup AUX ports */
	void FactoryDiagnostics(void);				/*  Factory test */
	void EventLog(char);						/*  Event logging */
	unsigned char LiPOStatus(void);				/*  LiPO battery check */
	long int prng(long int seed);				// DonP 20080328 - Pseudo RNG
	unsigned char BoltSwitchStatus(void);		// DonP 20080404 - External switch on AUX0

/*	Initialize variables */

	/* long is a 4 byte integer */
	long  BatteryVoltageDroopWarning = 5500;/* mV */
	long  TriggerFiringVoltage = 6000;		/* mV */
	long  InitialBatteryVoltage = 0;		/* mV */
	long  BurstTimeFactor = 80;				/* The single to three round burst ratio DEFAULT if EEPROM = FF*/
											/* 100 = 3.00 X the single shot time. 2X+100% */
	long  BurstTimeFactorPercent = 0;		/* Real BurstTimeFactorPercent */
	unsigned char  MotorSpeed = 254;		/* 0 to 254 PWM value */
	
	long  VibrateTime = 500;				/* Time the vibrator is on in mS*/

	long  SingleShotTime = 70;				/* Default loops for single shot */
	long  BurstTime = 205;					/* Default motor on time for a burst */

	long  TimeFactor = 100;					/* Adjust for 1mS step.  Magically, the calibration factor = 100 */
	long  SingleShotTimeOld = 0;			/* Old value of single shot time */
	long  TimeFactorFast = 1;				/* A sub mS timer counter */
	unsigned char  ErrorCode = 0;			/* The Errors are each given a unique number */
	long  TriggerVoltage = 0;				/* The voltage on the trigger input in mV */
	long  DrainCurrent = 0;					/* The current through the drive FET in Amps */
	long  DrainVoltage = 0;					/* The voltage on the MOSFET drains mV */
	long  counter2 = 0;						/* Counter for Vibrate function */
	long  Time = 0;							/* Variable for Timer function */
	long  counter = 0;						/* Counter for Timer function */
	unsigned char  Sensor = 0;				/* Input pin number for ADC */
	long  Voltage = 0;						/* A/D convertor returned voltage in mV */
	long  count = 0;						/* local counter */
	long  VoltageOffset0 = 0;				/* A/D 5.0V REF offest error */
	long  VoltageOffset1 = 0;				/* A/D 2.56V REF offest error */
	long  VoltageOffset2 = 0;				/* A/D 1.1V REF offest error */
	unsigned long  FiringCounter = 0;		/* Counts firing cycles*/

	// DonP 20080320 - changed default
	#ifdef BURSTMODEDEFAULT
		unsigned char  GunMode = ThreeRoundAuto;/* 1=trigger control (Normal) 2=burst (ThreeRoundAuto) */
	#endif
	#ifdef NORMALMODEDEFAULT
		unsigned char  GunMode = Normal;		/* 1=trigger control (Normal) 2=burst (ThreeRoundAuto) */	
	#endif
	#ifdef SEMILOCKED
		unsigned char  GunMode = SemiOnly;		/* 1=trigger control (Normal) */	
	#endif
	// original line below:
	//unsigned char  GunMode = Normal;		/* 1=trigger control (Normal) 2=burst (ThreeRoundAuto) */
	// End DonP

	long  ShotCounter = 0;					/* Shot count inside a burst */
	unsigned char  TriggerHold = 0;			/* Hold further shooting until trigger is released */

	long  ModeCounter = 0;					/* A main loop counter for the threeround auto fuction */
	
	// DonP 20090320 - changed default
	/* Operating mode DEFAULT if EEPROM = FF : 1 = Normal  2 = ThreeRound  3 = Semi Only*/
	#ifdef BURSTMODEDEFAULT
		unsigned char  GunFunction = ThreeRound;
	#endif
	#ifdef NORMALMODEDEFAULT
		unsigned char  GunFunction = Normal;	
	#endif
	#ifdef SEMILOCKED
		unsigned char  GunFunction = SemiOnly;
	#endif
	// original below
	//unsigned char  GunFunction = Normal;	/* Operating mode DEFAULT if EEPROM = FF */
											/* 1 = Normal  2 = ThreeRound  3 = Semi Only*/
    // End DonP

	// DonP 20080328 - Added variables to support the "Simulate Jams" feature
	#ifdef GUNJAMSIMULATIONENABLE
		long int pseudorandom;				// For making prng() work
		unsigned long TriggerPulls = 0;		// Basically a copy of 'FiringEvents' which we can muck with
		unsigned char JammedFlag = 0;		// Whether gun is JAMMED
	#endif
	// End DonP

	unsigned char ucAddress = 0x00;			/* For EEPROM functions */
	unsigned char ucData = 0x00;			/* For EEPROM functions */
	unsigned char  ADCRange = 0;			/* 0 = 5.0V REF   1 = 2.56V REF  2 = 1.1V REF */
	long  count1 = 0;						/* Error loop counter */
	long  counter3 = 0;						/* Timer counter */
	long  counter4 = 0;						/* FastTimer counter */
	long  SBTCounter = 0;					/* Burst time function counter */
	unsigned char  PWMduty = 0x00;			/* Motor PWM control variable */
	long  counter5 = 0;						/* Master Reset time counter */
	long  GunFunctionFlag = 0;				/* Counter used in mode programing */
	unsigned char BatteryVoltageEEPROM = 0;	/* Stored initial battery voltage */
	unsigned char DroopWarningFlag = 0;		/* Droop warning counter */
	long counter6 = 0;						/* Motor softstart counter */
	long RampSpeed = 0;						/* MotorRampSpeed variable */
	long TestValue = 0;						/* Factory test variable */
	unsigned char TestCode = 0;				/* Facotry test variable */
	unsigned char TestCounter = 0;			/* Factory test counter */
	unsigned char Event = 0;				/* Event logging variable */
	unsigned char EventOld = 0;				/* Event logging variable */
	unsigned char LiPO = 0;					/* LiPO battery status */
	long Aux0Data = 0;						/* Aux port 0 data variable */
	long Aux1Data = 0;						/* Aux port 1 data variable */
	unsigned char LiPOInstalled = 0;		/* LiPO Installed flag */
	unsigned char BatteryLimitReset = 0;	/* LiPO Installed flag */
	long DeadTimeCounter = 0;				/* Counter used to find battery rest time */
	
int main(void)
{
/*	MAIN POWER UP */

	/*	Wait 2 seconds for hardware to stabilize */
	Timer(2000);  
	ErrorCode = 1;
	// DonP 20080320
	// Original line below:
	#ifdef BURSTMODEDEFAULT
		GunMode = ThreeRoundAuto;
	#endif
	#ifdef NORMALMODEDEFAULT
		GunMode = Normal;
	#endif
	#ifdef SEMILOCKED
		GunMode = SemiOnly;
	#endif

	// Original line below:
	//GunMode = Normal;
	// End DonP


	/*	Setup A/D parameters */
	CalibrateAtoD();

	/*	Check trigger voltage */
	if (GetVoltage(Trigger,0) > 500) {ErrorCode = 4;}

		/*  Get EEPROM data */
	ReadEEPROM();

	/*	Energize drive circuits */
	EnergizeFETs();

	/*  Setup AUX ports */
	SetAUXPorts();

	/*	Check drain voltage */
	DrainVoltage = (GetVoltage(Drain,0));
	if (DrainVoltage < LowBatteryVoltage) {ErrorCode = 2;}
	if (DrainVoltage > HighBatteryVoltage) {EventLog(1); ErrorCode = 3;}

	/*  Set Battery Limits */
	if (InitialBatteryVoltage == 0) {InitialBatteryVoltage = (GetVoltage(Drain,0));}
	TriggerFiringVoltage = (InitialBatteryVoltage * TriggerFiringVoltagePercent) / 100;
	BatteryVoltageDroopWarning = (InitialBatteryVoltage * BatteryVoltageDroopWarningPercent) / 100;

	
	// DonP 20080404 - Modified to disable the LiPo stuff (ie for daughterboard support
	// of the ProjectWolfDragon board) if LIPOENABLE is not defined.
	#ifdef LIPOENABLE
		LiPO = LiPOStatus();
	#else
		LiPO = 0;			// LiPOStatus() function returns 0 if no LiPO
		LiPOInstalled = 0;	// 0 means no LiPO installed (set from within LiPOStatus())
	#endif
	// Original line is simply LiPO = LiPOStatus();
	// End DonP
		
	/*	Vibrate status to motor */
	for (count1 = 0; count1 < ErrorCode; count1++)
	{
	Timer(500); Vibrate();
	}

	/*Factory Test */
	FactoryDiagnostics();

	/*  If the battery voltage is not safe, force a shutdown */
Halt:
	if (ErrorCode > 1) {goto Halt;}
		/*	Error Codes
		1	Normal operation
		2	Battery voltage too low
		3	Battery voltage too high
		4   Trigger voltage too high 
		16	Low battery
		17  High motor peak current 
		18  Overheating
		*/

	/*	Wait for trigger pull to enter programming mode (2 seconds) */
	for (count=0; count < 1185; count++)
	{
	if (GetVoltage(Trigger,0) > TriggerFiringVoltage) {ProgramEEPROM();}
	}

	
/*	MAIN SCANNING LOOP */
	Vibrate();Vibrate();Vibrate();Vibrate();
	if (LiPOInstalled != 0) {Vibrate();Vibrate();Vibrate();Vibrate();}
	
MainLoop:
	ModeCounter++; DeadTimeCounter++;
	/*	Set motor to off no matter what is going on */
	MotorOFF(); 

	// DonP 20080404 - Unset the JammedFlag if we're jammed and the bolt is worked
	// (only if GUNJAMSIMULATIONENABLE defined)
	// There is a chance this poll won't catch the working of the switch, but
	// it seems to be OK.
	#ifdef GUNJAMSIMULATIONENABLE
		if( JammedFlag==1 )
		{
			if( BoltSwitchStatus() != 0 )	// Poll the Bolt switch (0=open)
			{
				JammedFlag = 0;		// Clear the Jam
				TriggerPulls = 0; 	// Reset this counter too
			}
		}
	#endif
	// End DonP

	/*	Check trigger voltage */
	TriggerVoltage = GetVoltage(Trigger,0);
	if ((TriggerVoltage > TriggerFiringVoltage) && (TriggerHold == 0)) 
	{
		
		if (LiPOInstalled == 0)
		{
			/*	Check trigger voltage incase the battery is dying. Check three times to stop false warnings */
			if (TriggerVoltage < BatteryVoltageDroopWarning) {DroopWarningFlag++;} else {DroopWarningFlag = 0; goto Fire;}
			if (DroopWarningFlag > 2) {ErrorCode = 16;}
			goto Fire;
		}
		else
		{
			LiPO = LiPOStatus();
			if (LiPO == 1) {DroopWarningFlag = 0; goto Fire;}
			DroopWarningFlag++;
			if ((LiPO == 3) && (DroopWarningFlag > 2)) {(ErrorCode = 16); goto CeaseFire;}
			if ((LiPO == 2) && (DroopWarningFlag > 2)) {ErrorCode = 16; goto Fire;}
			goto Fire;
		}

	}
	if (TriggerVoltage < 500) {TriggerHold = 0; GunMode = Normal; ModeCounter = 0;}

	// 20080325 DonP - disable falling into fullauto after 3 round burst if 
	// FULLAUTOAFTERBURSTENABLE is not defined.
	//
	#ifdef FULLAUTOAFTERBURSTENABLE
		/* Goto full auto in three round burst mode if trigger is held down 1/2 second longer */
		if ((ModeCounter > 167) && (TriggerHold == 1) && GunFunction != 3) {GunMode = ThreeRoundAuto; goto Fire;}
	#endif
	// The block above replaced the original lines below:
	//(begin)
	/* Goto full auto in three round burst mode if trigger is held down 1/2 second longer */
	//if ((ModeCounter > 167) && (TriggerHold == 1) && GunFunction != 3) {GunMode = ThreeRoundAuto; goto Fire;}
	//(end)
	// End DonP	


	
/*  Reset Battery Limits after 300 triggers*/
	if ((BatteryLimitReset == 0) && (FiringCounter > 300) && (DeadTimeCounter > 1000))
		{InitialBatteryVoltage = (GetVoltage(Drain,0));
		TriggerFiringVoltage = (InitialBatteryVoltage * TriggerFiringVoltagePercent) / 100;
		BatteryVoltageDroopWarning = (InitialBatteryVoltage * BatteryVoltageDroopWarningPercent) / 100;
		EventLog(4);
		BatteryLimitReset = 1;
		}
	
	/*	Return to top and loop again */
goto MainLoop;


/*	FIRING LOOP */
Fire:
	// Gun Jam Code - DonP 20080404
	// Set 'JammedFlag' if it's time to Jam
	// (Unset it in main loop if the Bolt Switch is worked)
	// If we try to fire while Jammed, just vibrate.
	#ifdef GUNJAMSIMULATIONENABLE	// This code does not exist unless JAMs are enabled
		TriggerPulls++;		// Increment total trigger pulls - DonP 20080404
		// Check if it's time for a jam, AND if so whether JAMCHANCE comes through
		// (JAMCHANCE is a 0-255 value representing whether the jam actually happens)
		if( TriggerPulls >= GUNJAMTRIPWIRE )	// Is it time for a Jam?
		{
		    TriggerPulls = 0;	// Reset the trigger pulls counter
			// Now determine whether we *actually* jam (if we're already jammed, 
			// JammedFlag stays at 1)
			// Get a pseudorandom value between 0 and 255
			pseudorandom = prng((long int)(DeadTimeCounter+pseudorandom));	// Some values as rng seed
		    pseudorandom &= 0x0000FF00; // Axe all but these 8 bits of the 32bit number
			pseudorandom = (pseudorandom >> 8);	// Move them over...
			//
			if( pseudorandom <= JAMCHANCE )	// If random(0-255) <= 0-255, then JAM
				JammedFlag = 1;		// Gun jammed status flag on!
		}
		if( JammedFlag )
		{
				Timer(1);
				Vibrate();
				while( (TriggerVoltage >= TriggerHoldVoltage) )		// Wait till trigger released
					TriggerVoltage = GetVoltage(Trigger,1);			// Get Trigger voltage (i.e. status)
				goto CeaseFire;		// Continue on to same place, as though we had fired.
		}
	#endif
	// End DonP
/* MOTOR IS ON!!!!! */
	MotorON();
	Timer(1);	/* A delay to avoid the motor start up electrical noise burst */
	ShotCounter = 0;
		
	FiringCounter++;  /* increment firing loop counter */

ShotDetect:  /* Main firing loop. */
	ShotCounter++;
	TriggerVoltage = GetVoltage(Trigger,1);	/* Get trigger voltage */
	if (TriggerVoltage < TriggerHoldVoltage)  /* Is trigger off now? */
	{
		if ((GunFunction == Normal) || (GunMode == ThreeRoundAuto)) 
		{
			GunMode = Normal; goto CeaseFire;	  /* normally just CeaseFire */
		}
		if (TriggerHold == 0) 
		{
			goto CeaseFire;
		}  
	}
	if ((GunMode == ThreeRoundAuto) || (GunFunction == Normal)) 
	{
		// DonP 20080404 - Added to make jamming more likely after using burst or auto
		#ifdef GUNJAMSIMULATIONENABLE	// This code does not exist unless JAMs are enabled
			// While firing in auto or burst modes, there is a chance we'll JAM
			// Get a pseudorandom value between 0 and 255
			if( TriggerPulls < GUNJAMTRIPWIRE )	// If we're not already going to jam...
			{
				pseudorandom = prng((long int)(TriggerVoltage+pseudorandom));	// Some values as rng seed
			    pseudorandom &= 0x0000FF00; // Axe all but these 8 bits of the 32bit number
				pseudorandom = (pseudorandom >> 8);	// Move them over...
				//
				if( pseudorandom <= AUTOJAMCHANCE )	// If random(0-255) <= 0-255, then JAM
				{
					TriggerPulls = GUNJAMTRIPWIRE;	// Ensure a JAM happens
					JammedFlag = 1;	// Use this to tell CeaseFire to vibe
					goto CeaseFire;
				}
			}
		#endif
		// End DonP
		goto ShotDetect;	/* Keep cycling in auto modes */
	} 
	if (ShotCounter > BurstTime) 
	{
		TriggerHold = 1; goto CeaseFire;	/* Three round time delay wait */
	}  
	goto ShotDetect;
	

/*	FIRING HALT */

CeaseFire:
	/*	The motor ON and OFF functions have a 1mS anti cross conduction safety delay built in */
	/*	Turn motor to off */
	MotorOFF();

	/*	Turn break to on */
	BreakON();

	/*	Leave the break on for some time in mS */
	Timer(BreakTime);

	/*	Turn break to off */
	BreakOFF();
	/*  If in semi mode, set burst time */
	if (TriggerHold ==0) {SetBurstTime(ShotCounter);}

	/*  Check unit temperature */
	if (GetVoltage(Temperature,2) > 283 + ((TemperatureLimit * 13) / 10)) {EventLog(3); ErrorCode = 18;}

	ModeCounter = 0; DeadTimeCounter = 0;

	/*  If an error occured report it */
	if (ErrorCode == 0) {goto MainLoop;}
		/*	16 - Low battery - Vibrate motor once after firing
			17 - High motor peak current - Vibrate motor twice
			18 - Overheating - Vibrate motor three times
		*/
		Timer(100);
		if (ErrorCode == 16) {Vibrate();Timer(500);}
		if (ErrorCode == 17) {Vibrate(); Timer(500); Vibrate();Timer(500);}
		if (ErrorCode == 18) {Vibrate(); Timer(500); Vibrate(); Timer(500); Vibrate();Timer(500);}

		ErrorCode = 0;

	// DonP
	if( JammedFlag )		// We should only hit this if jammed while in auto mode
	{
			Timer(1);
			Vibrate();
			while( (TriggerVoltage >= TriggerHoldVoltage) )		// Wait till trigger released
				TriggerVoltage = GetVoltage(Trigger,1);
	}	
	// End DonP

goto MainLoop;

}  /* End of main */



/* FUNCTIONS */

/* For safety in that "nothing can go wrong", the motor drive ports are controlled directly */
void MotorOFF(void)
	{
	MotorPWM(0);
	}


void MotorON(void)
	{
	PORTB = Neutral; 	/* Be REALLY(!!) sure break is off */
	Timer(1);		/* Wait 1mS for anti-cross conduction */
	PORTB = Motor;	/* motor is on for peak current test */
		Timer(1);	/* Wait to stabalize */
		/*	Check drain current for over peak current limit */
		DrainCurrent = GetVoltage(Drain,1);
		PORTB = Neutral;	/* motor is off after test */
		if ((DrainCurrent * 1000 / RDSon) > DrainCurrentPeakLimit) {PORTB = Neutral; EventLog(2); ErrorCode = 17; return;} /* Shutdown if overcurrent */

		MotorPWM(1);
	/* Soft Start */
		for (counter6 = 0; counter6 < 51; counter6++)
			{
				RampSpeed = MotorSpeed / 2 + (MotorSpeed * counter6) / 100;
				OCR0B = (char)RampSpeed;
			} 
	}


void BreakOFF(void)
	{PORTB = Neutral;}	/* The break signal is negative logic */


void BreakON(void)
	{
	PORTB = Neutral;	/* Be REALLY(!!) sure motor is off */
	Timer(1);		/* Wait 1mS for anti-cross conduction */
	PORTB = Break;	/* The break signal is negative logic */
	} 


void Timer(long Time)  /* Timer(100) = 100mS */
	{Time = Time * TimeFactor; for (counter3 = 0; counter3 <= Time; counter3++) {}}


void FastTimer(long Time) /* Timer(50) = 672uS */
	{Time = Time * TimeFactorFast; for (counter4 = 0; counter4 <= Time; counter4++) {}}


void EnergizeFETs(void)
	{
	/*	Energize break circuit */
	/*	Break is on PB0 */
	PORTB = 0x00;	/* Set pullups off */ 
	DDRB = 0x01;	/* Set port B pin 0 for output */
	PORTB = Neutral;	/* Set port B pin 0 to 1 */
	
	/*	Wait 1000mS for capacitive break circuit to stabilize (very important!!) */
	Timer(1000);  

	/*	Energize motor circuit */
	/*	Motor is on PB1 */
	DDRB = 0x03;	/* Set port B pins 0 and 1 for output */
	PORTB = Neutral;	/* Set port B pin 0 to 1 and pin 1 to 0 */
	/*	Wait 1mS */
	Timer(1);
	}


void SetAUXPorts(void)
	{
	/*  Set AUX ports */
	/* Turn on pullups for Aux0 and Aux1. Set motor off and break off */
	/* Pullups are set in the Nuetral, Break, and Motor definitions. */
	DDRB = 0x03;
	PORTB = Neutral;
	}


long GetVoltage(unsigned char Sensor, unsigned char ADCRange)
	{
	/* Select input Source */
	if (ADCRange == 0)								/* 0-20V range */
		{
		if (Sensor == Aux1) {ADMUX = 0x00;}
		if (Sensor == Drain) {ADMUX = 0x01;}
		if (Sensor == Trigger) {ADMUX = 0x02;}
		if (Sensor == Aux0) {ADMUX = 0x03;}
		if (Sensor == Ground) {ADMUX = 0x0D;}		/* Internal ground for calibration */
		}
	if (ADCRange == 1)								/* 0-10.23V range */
		{
		if (Sensor == Aux1) {ADMUX = 0x90;}
		if (Sensor == Drain) {ADMUX = 0x91;}
		if (Sensor == Trigger) {ADMUX = 0x92;}
		if (Sensor == Aux0) {ADMUX = 0x93;}
		if (Sensor == Ground) {ADMUX = 0x9D;}		/* Internal ground for calibration */
		}
	if (ADCRange == 2)								/* 0-1.1V range */
		{
		if (Sensor == Aux1) {ADMUX = 0x80;}
		if (Sensor == Drain) {ADMUX = 0x81;}
		if (Sensor == Trigger) {ADMUX = 0x82;}
		if (Sensor == Aux0) {ADMUX = 0x83;}
		if (Sensor == Ground) {ADMUX = 0x8D;}		/* Internal ground for calibration */
		if (Sensor == Temperature) {ADMUX = 0x8F;}	/* Can only be used with 1.1V referance */
		}
	/* Start A/D conversion */
	ADCSRA = 0b11000110;  						/* Start A/D conversion (bit 6 is set) 125kHz clock*/
	/* Test if done (bit 6 goes clear, but is set when A/D is complete) */
	do {} while (bit_is_set(ADCSRA,6));
	/* Get voltage Vref = 5000mV  Rdivider = 1/4  Full range is 20V */
	Voltage = ADCW;								/* This word is the 10 bit A/D result */
	if (ADCRange == 0)
	{
		Voltage = Voltage - VoltageOffset0;  	/* A/D Offset error adjustment */
		if (Voltage < 0) {Voltage = 0;}
		Voltage = ((Voltage * 20000) / 1023);	/* Convert A/D full range (1023) to 19980mV or 19.98V */
	}
	if (ADCRange == 1)
	{
		Voltage = Voltage - VoltageOffset1;  	/* A/D Offset error adjustment */
		if (Voltage < 0) {Voltage = 0;}
		Voltage = Voltage * 10;					/* Convert A/D full range (1023) to 10230mV or 10.23V */
	} 
	if (ADCRange == 2)
	{
		Voltage = Voltage - VoltageOffset2;  	/* A/D Offset error adjustment */
		if (Voltage < 0) {Voltage = 0;}
		Voltage = Voltage * 1100 / 1023;		/* Convert A/D full range (1023) to 1100mV or 1.100V */
	} 
	return Voltage;
	}


void CalibrateAtoD(void)
	{
	/* 5.0V REF  Range = 20V */
	VoltageOffset0 = 0;							/* Clear old value */
	VoltageOffset0 = GetVoltage (Ground, 0);	/* Run the converter with grounded input */
	VoltageOffset0 = ADCW;
	/* 2.56V REF  Range = 10.23V */	
	VoltageOffset1 = 0;							/* Clear old value */
	VoltageOffset1 = GetVoltage (Ground, 1);	/* Run the converter with grounded input */
	VoltageOffset1 = ADCW;						/* Get the raw A/D converter output word */
	/* 1.1V REF  Range = 1.1V */	
	VoltageOffset2 = 0;							/* Clear old value */
	VoltageOffset2 = GetVoltage (Ground, 2);	/* Run the converter with grounded input */
	VoltageOffset2 = ADCW;						/* Get the raw A/D converter output word */
	}


void Vibrate(void)
		{
		/* Vibrate for 1/2 second */
		BreakOFF();
		Timer(1);
		for (counter2 = 0; counter2 < (VibrateFrequency * VibrateTime / 2000); counter2++)
			{
			PORTB = Motor;
			FastTimer(20);		/* Wait 500uS for motor on pulse */
			PORTB = Neutral;
			Timer(1000 / VibrateFrequency);
			}
		}

	/* EEPROM stuff copied directly from ATMEL data sheet */

void EEPROM_write(unsigned char ucAddress, unsigned char ucData)
	{
	/* Wait for completion of previous write */
	while (EECR & (1<<EEPE));
	/* Set programming mode */
	EECR = (0<<EEPM1) | (0<<EEPM0);
	/* Set up address and data registers */
	EEAR = ucAddress;
	EEDR = ucData;
	/* Write logical one to EEMPE */
	EECR |= (1<<EEMPE);
	/* Start eeprom write by setting EEPE */
	EECR |= (1<<EEPE);
	}


unsigned char EEPROM_read(unsigned char ucAddress)
	{
	/* Wait for completion of previous write */
	while (EECR & (1<<EEPE));
	/* Set up address register */
	EEAR = ucAddress;
	/* Start eeprom read by writing EERE */
	EECR |= (1<<EERE);
	/* Return data from data register */
	return EEDR;
	}


void ProgramEEPROM(void)
	{
	
	/* Change EEPROM Programming */
		/*
		0x00 = Version Number
		0x01 = GunFunction
		0x02 = BurstTimeFactor
		0x03 = SingleShotTime
		0x04 = Motor Speed
		0x05 = Initial Battery Voltage / 100
	*/

	Timer(500);

	Vibrate();
	/* Pull Trigger now to go to set GunFunction by number of trigger pulls.  Limit = 2 */
	GunFunctionFlag = 0;
	for (count=0; count < 600; count++)
	{
	if (GetVoltage(Trigger,0) < 500) {TriggerHold = 0;} /* Tigger hold is a debounce for the trigger switch */
	if (((GetVoltage(Trigger,0) > TriggerFiringVoltage)) && (TriggerHold == 0)) 
		{
		GunFunctionFlag++;
		GunFunction = GunFunctionFlag;
		// DonP 20080325 - Force Semi-only selection if SEMILOCKED defined
		#ifdef SEMILOCKED
			GunFunction = SemiOnly; // Mode 3
		#endif
		// End DonP
		if (GunFunction > 3) {GunFunction = 3;}  /* limit is mode 3 */
		count = 0; TriggerHold = 1;
		}
	}

	Vibrate();Timer(250);Vibrate();
	/* Pull Trigger now to go to decrease BurstTimeFactor by number of trigger pulls.  Limit = 0*/
	for (count=0; count < 600; count++)
	{
	if (GetVoltage(Trigger,0) < 500) {TriggerHold = 0;} /* Tigger hold is a debounce for the trigger switch */
	if (((GetVoltage(Trigger,0) > TriggerFiringVoltage) && (TriggerHold == 0) && (BurstTimeFactor > 0))) 
		{BurstTimeFactor--; count = 0; TriggerHold = 1;}
	}

	Vibrate();Timer(250);Vibrate();Timer(250);Vibrate();
	/* Pull Trigger now to go to increase BurstTimeFactor by number of trigger pulls.  Limit = 250 */
	for (count=0; count < 600; count++)
	{
	if (GetVoltage(Trigger,0) < 500) {TriggerHold = 0;} /* TiggerHold is a debounce for the trigger switch */
	if (((GetVoltage(Trigger,0) > TriggerFiringVoltage) && (TriggerHold == 0) && (BurstTimeFactor < 250))) 
		{BurstTimeFactor++; count = 0; TriggerHold = 1;}
	}

	Vibrate();Timer(250);Vibrate();Timer(250);Vibrate();Timer(250);Vibrate();
	/* Pull Trigger now to go to decrease MotorSpeed by number of trigger pulls.  Limit = 52*/
	for (count=0; count < 600; count++)
	{
	if (GetVoltage(Trigger,0) < 500) {TriggerHold = 0;} /* Tigger hold is a debounce for the trigger switch */
	if (((GetVoltage(Trigger,0) > TriggerFiringVoltage) && (TriggerHold == 0) && (MotorSpeed > 0))) 
		{MotorSpeed = (MotorSpeed * 9) / 10; count = 0; SBTCounter = 0; TriggerHold = 1;}
	}

	Vibrate();Timer(250);Vibrate();Timer(250);Vibrate();Timer(250);Vibrate();Timer(250);Vibrate();
	/* Pull Trigger now to go to increase MotorSpeed by number of trigger pulls.  Limit = 254 */
	for (count=0; count < 600; count++)
	{
	if (GetVoltage(Trigger,0) < 500) {TriggerHold = 0;} /* TiggerHold is a debounce for the trigger switch */
	if (((GetVoltage(Trigger,0) > TriggerFiringVoltage) && (TriggerHold == 0) && (MotorSpeed < 255))) 
		{
			if (MotorSpeed < 232)
			{MotorSpeed = (MotorSpeed * 11) / 10; count = 0; SBTCounter = 0; TriggerHold = 1;}
			else {MotorSpeed = 254;count = 0; SBTCounter = 0; TriggerHold = 1;}
		}
	}

	/* Store EEPROM data */
	/* Gun Function */
	EEPROM_write (0x01, (char)GunFunction);
	/* Burst time factor */
	if (BurstTimeFactor < 50) {BurstTimeFactor = 50;}
	if (BurstTimeFactor >250) {BurstTimeFactor = 250;}
	EEPROM_write(0x02, (char)BurstTimeFactor);
	/* MotorSpeed */
	if (MotorSpeed < 52) {MotorSpeed = 52;}
	if (MotorSpeed >254) {MotorSpeed = 254;}
	EEPROM_write(0x04, (char)MotorSpeed);
	BatteryVoltageEEPROM = (char)(InitialBatteryVoltage / 100);
	EEPROM_write(0x05, (char)BatteryVoltageEEPROM);

	Vibrate();Timer(250);Vibrate();Timer(250);Vibrate();Timer(250);Vibrate();Timer(250);Vibrate();Timer(250);Vibrate();
	/* Pull Trigger now to go to Master Reset */
	counter5 = 0;
	for (count=0; count < 600; count++)
	{
	while (GetVoltage(Trigger,0) > TriggerFiringVoltage) 
		{
			counter5++;
			if (counter5 > 1500)
			{
			EEPROM_write(0x00,0xFF);
			EEPROM_write(0x01,0xFF);
			EEPROM_write(0x02,0xFF);
			EEPROM_write(0x03,0xFF);
			EEPROM_write(0x04,0xFF);
			EEPROM_write(0x05,0xFF);
			Vibrate();Vibrate();Vibrate();Vibrate();Vibrate();Vibrate();Vibrate();Vibrate();Vibrate();Vibrate();
Halt1:		goto Halt1;
			}
		}
	}

	}


void ReadEEPROM(void)
	{
	/* Note - Erased EEPROM = FF not 00 */
	/*
		0x00 = Version Number
		0x01 = GunFunction
		0x02 = BurstTimeFactor
		0x03 = SingleShotTime
		0x04 = Motor Speed
		0x05 = Initial Battery Voltage / 100
	*/
	/* Version */
	EEPROM_write(0x00, (char)VersionNumber);

	/* Gun Function */
	if (EEPROM_read(0x01) != 0xFF) {GunFunction = EEPROM_read(0x01);}

	/* Burst time factor */
	if (EEPROM_read(0x02) != 0xFF) {BurstTimeFactor = EEPROM_read(0x02);}

	/* Single Shot Time */
	if (EEPROM_read(0x03) != 0xFF) 
	{
		SingleShotTime = EEPROM_read(0x03); 
		SBTCounter = 0;

		BurstTimeFactorPercent = 2 * BurstTimeFactor + 100; 		
		BurstTime = (BurstTimeFactorPercent * 117 * SingleShotTime) / 120 / 100 + (37 * (BurstTimeFactorPercent - 100) / 120);


		if (GunFunction == 3) {BurstTime = SingleShotTime;}	/* Semi only mode */
	}

	/* Motor Speed */
	MotorSpeed = EEPROM_read(0x04);

	/*  0x05 = Initial Battery Voltage / 100  */
	if (EEPROM_read(0x05) != 0xFF) {InitialBatteryVoltage = EEPROM_read(0x05) * 100;}
	}


void SetBurstTime(long ShotCounter)
	{
	SBTCounter++;
	/* Shot Three after gun is powered up is the golden shot */
	if (SBTCounter == 3) {SingleShotTime = ShotCounter;}
	/* Do not update if ShotCounter is < 90% or > 110% of SingleShotTime (early user trigger release) */
	if (SBTCounter > 3)
		{
		if ((ShotCounter > (SingleShotTime * 11) / 10) || (ShotCounter < (SingleShotTime * 9) / 10)) {return;}
		}	
	else {SingleShotTime = ((SingleShotTime * 5) + ShotCounter) / (5 + 1);}
/*
	SingleShotTime = 1.17 x ShotCounter + 37 in mS.
	BurstTime = 1.20 x ShotCounter + 37 in mS.
	BurstTime = BurstTimeFactor x 1.17 / 1.20 x SingleShotTime + 37 / 1.20 x (BurstTimeFactor - 1)
*/
	BurstTimeFactorPercent = 2 * BurstTimeFactor + 100; 		
	BurstTime = (BurstTimeFactorPercent * 117 * SingleShotTime) / 120 / 100 + (37 * (BurstTimeFactorPercent - 100) / 120);

	if (BurstTime < SingleShotTime) {BurstTime = SingleShotTime;}	/* Be sure a BurstTime error will not lock out the gun */ 
	/* Store SingleShotTime now */
	if (SingleShotTime < 0) {SingleShotTime = 1;}
	if (SingleShotTime >254) {SingleShotTime = 254;}
	if (SingleShotTime != SingleShotTimeOld)
		{
		EEPROM_write(0x03, (char)SingleShotTime);
		SingleShotTimeOld = SingleShotTime;
		}
		/* Semi only mode */	
		if (SBTCounter < 3) {BurstTime = 2 * SingleShotTime;} else 
			{
			if (GunFunction == 3) {BurstTime = SingleShotTime - 2;}
			}
	}


void MotorPWM(char PWMduty)
	/* Controlling OC0B which is pin 6 to the motor drive */
	{
	if (PWMduty == 0) {TCCR0A = 0x03; GTCCR = 0x80; PORTB = Neutral; return;} /* Shutdown */
	GTCCR = 0x81; 			/* HALT, SYNC, and RESET the PWM counter */
	TCCR0A = 0x23;			/* Enable PWMcontrol of motor and set function */
	TCCR0B = 0x01;			/* Divide by 8 for ~4000hZ */
	TCNT0 = (char)PWMduty;	/* Zero the counter */
	OCR0B = (char)PWMduty;	/* This is the PWM comparator test value */
	TIMSK = 0x08;			/* Be sure the harware interupts are off but compare OS0B */
	GTCCR = 0x00;			/* Start the PWM */
	}	


void FactoryDiagnostics(void)
	/* Factory test */
	{
	/* Test for Trigger Key voltage */
	TestValue = GetVoltage(Trigger,0);
	if ((TestValue < 4392) || (TestValue > 5368)) {return;}
	EventLog(0);
	/* Turn on data ports for output - Only reset when battery disconnected */
	DDRB = 0x2B;
	PORTB = 0x01;
TestResult:
	TestCode = 1;
	PORTB = 0x01; /* Turn FETs off */
	Timer(2000);
	TestValue = GetVoltage(Trigger,0); /* Trigger sense test */
	if ((TestValue < 4734) || (TestValue > 5025)) {TestCode = 2;}
	TestValue = GetVoltage(Trigger,1); /* Trigger sense test */
	if ((TestValue < 4734) || (TestValue > 5025)) {TestCode = 3;}
	TestValue = GetVoltage(Drain,1); /* Drain sense test */
	if ((TestValue < 4734) || (TestValue > 5025)) {TestCode = 4;}
	PORTB = 0x03; /* Turn Q1 Drive FET on */
	Timer(1);
	TestValue = GetVoltage(Drain,1); /* Q1 test */
	if ((TestValue < 0) || (TestValue > 10)) {TestCode = 5;}
	PORTB = 0x01; /* Turn Q1 Drive FET off */
	Timer(1);
	PORTB = 0x00; /* Turn Q2 Break FET on */
	Timer(1);
	TestValue = GetVoltage(Drain,1); /* Q2 test */
	if ((TestValue < 9701) || (TestValue > 10301)) {TestCode = 6;}
	PORTB = 0x01; /* Turn Q2 Break FET off */
	Timer(1);
	/* Blink Error Code to AUX5 LED */
	for (TestCounter = 0; TestCounter < TestCode; TestCounter++)
		{
		PORTB = 0x21; /* LED on */
		Timer(500);	/* Wait 500mS */
		PORTB = 0x01; /* LED off */
		Timer(500);	/* Wait 500mS */
		}
	if (TestCode != 1)
		{
		/* Output bad value visually in mS */
		Timer(1500);
		PORTB = 0x21; /* LED on */
		Timer(TestValue);
		PORTB = 0x01; /* LED off */
		Timer(2000);
		}
goto TestResult; /* Keep Flashing Code */
	/*
	1 = Normal
	2 = 20V Trigger Voltage
	3 = 10V Trigger Voltage
	4 = Drain open circuit voltage
	5 = Q1 FET drive
	6 = Q2 FET drive
	*/
	}


void EventLog(char Event)
	/*	0 = Factory Test Performed	0x40
		1 = High Battery Voltage  	0x41
		2 = Peak Current Limit		0x42
		3 = Over Temperature Limit	0x43
		4 = Battery Voltage Reset	0x44 */
	{
	if (Event > 15) {return;}
	Event = Event + 64;
	EventOld = EEPROM_read((char)(Event));
	EventOld++;
	EEPROM_write((char)Event,(char)EventOld);
	return;
	}


unsigned char LiPOStatus(void)
	/*	0 = No Monitor Present
		1 = LiPO Battery OK
		2 = LiPO Battery Weak
		3 = LiPO Battery Dead  */
	{
	PORTB = Neutral;	/* Be sure pullups are on */
	Timer(1);
	/* Aux voltages are /4 since there are no voltage dividers */
	/* Voltages are im mV */
	Aux0Data = GetVoltage(Aux0,0) / 4;
	Aux1Data = GetVoltage(Aux1,0) / 4;
	if ((Aux0Data < 300) && (Aux1Data < 300)) {LiPOInstalled = 1; return 1;}
	if (Aux1Data > 500) {return 3;}
	if (Aux0Data > 500) {return 2;}
	return 1;
	}


// DonP 20080328
// Pretty good and fast pseudo-random number generator
// From Glen Worstell, in entry A3650 in CC AVR design contest
long int prng(long int seed)		// Call with seed value 1<= seed <= 0x7FFFFFFF-1
{
	// 0x7FFFFFFF is a magic number related to the prng function
	seed=(seed>>16) + ((seed<<15) & 0x7FFFFFFF) - (seed>>21) - ((seed<<10) & 0x7FFFFFFF);
	if( seed<0 ) 
		seed += 0x7FFFFFFF;
	return seed;
}
// End DonP

// DonP 2008040 
// Function to monitor a switch on AUX 0 (nearest GND on port)
// returns 0 for open, 1 for closed.
// Used for unjamming a gun.
// Assumes a switch is connected between AUX0 and GND
// This function based on LiPOStatus()
unsigned char BoltSwitchStatus(void)
	/*	0 = Switch Open
		1 = Switch Closed */
	{
	PORTB = Neutral;	/* Be sure pullups are on */
	Timer(1);
	/* Aux voltages are /4 since there are no voltage dividers */
	/* Voltages are im mV */
	//Aux1Data = GetVoltage(Aux1,0) / 4;
	Aux0Data = GetVoltage(Aux0,0) / 4;
	if (Aux0Data > 3000) 	// Anything above 3V (3000mV) is definately be "switch open"
		return 0;
	else
		return 1;
	}
// End DonP


/* End of program */

