
//****************************************************************************
// wigwag.c - Open Source Wig-Wag Project
//
// Written in 2012 by Greg McHugh, gregmchugh@hotmail.com
// Modified by Paul A. Fisher, rv7a.n18pf@gmail.com (starting w/ver 0.4)
//
// Creative Commons CC0 1.0 Universal Public Domain Dedication
// To the extent possible under law, the author(s) have dedicated all 
// copyright and related and neighboring rights to this software to the 
// public domain worldwide. This software is distributed without any 
// warranty. For more detailed information on this public domain dedication,
// see // http://creativecommons.org/publicdomain/zero/1.0/
//****************************************************************************
//
// This software is designed to be flashed into the PIC12F683 microcontroller
// contained in the Open Source Wig-Wag controller. Details on the functional
// requirements for the software are provided in the description of the
// controller module.
//
// Target Hardware: Open Source Wig-Wag Controller Rev. C
// Target Micro: Microchip PIC12F683
// Development Tools:	MPLAB IDE Ver. 8.84
//			HI-TECH C Compiler (Lite Mode) Ver. 9.83
//			PICkit 1 Flash Starter Kit
//			PICkit 1 Classic Flash Ver. 1.74
//
// Version History:
//	Ver. 0.1	Release for hardware integration testing
//	Ver. 0.2	Enabled Watch Dog Timer. Changed GPIO bit
//			operations to be flexible when modifying
//			I/O configuration. Updated hardware I/O
//			configuration to match final hardware. Set
//			default wig-wag flash rate to 45 per min.
//	Ver. 0.3	Modified logic to allow control of
//			individual lamps along with wig-wag.
//	ver. 0.4	Modified to flash lamps at the start of the wig-wag 
//			cycle and implement a "LED/Tungsten" switch.  LED's 
//			flash, Tungsten's only wigwag.  08Nov2012 PAF
//	ver. 0.5	Modified for the new V2 hardware from Bob.  This 
//			required port re-assignments from the previous 
//			version. All other functionality remains unchanged.
//	ver. 0.6	Modified to put key variables in absolute memory 
//			addresses. 30Nov2012 PAF
//	ver. 0.7	Changed the variables to EEPROM (to hopefully 
//			simplify changing it later).  While I was at it
//			also added two different switch functionalities.
//			01Dec2012 PAF
//	ver. 1.0	Swapped the LED/Tungsten with the option A/B of switch
//			functionality.  Final (?) changes for the 1.0 release
//			of the product.  07Dec2012 PAF
//
//****************************************************************************
//
//  PIC12F683 Port assignments and functions:
//	GP0 (pin 7) input   Option A/B for switch functionality
//			    (see note 2 on wiring diagram)
//	GP2 (pin 5) output  Landing light (lamp 2 in code below)
//	GP3 (pin 4) input   Landing light switch
//	GP4 (pin 3) input   Taxi light switch
//	GP5 (pin 2) output  Taxi light (lamp 1 in code below)
//	
#include <htc.h>
#include <stdlib.h>

// Set up the Configuration Bits for the Micro (see PIC12F683 Datasheet).
// Note that in-circuit debug requires the external reset line (MCLR) to
// be active, Power Up Timer disabled, and Watch Dog Timer disabled.

#ifdef __DEBUG
__CONFIG( FCMEN_OFF & IESO_OFF  & BOREN_OFF & CPD_OFF & CP_OFF
		& MCLRE_ON  & PWRTE_OFF & WDTE_OFF  & FOSC_INTOSCIO );
#else
__CONFIG( FCMEN_OFF & IESO_OFF  & BOREN_OFF & CPD_OFF & CP_OFF
		& MCLRE_OFF & PWRTE_OFF & WDTE_ON   & FOSC_INTOSCIO );
#endif
 
// Hardware I/O Definitions
//
// Notes:	Hardware switch inputs read Low when switch is activated.
//		Wig-Wag mode is indicated by both control inputs switched
//		low within 1 sec time interval. Otherwise, each control
//		input switched low turns on the associated lamp.
//		Output pins are set High to activate Lamps	

#define DIGINRAW0	GP0		// Switch functionality: High=A
#define DIGINRAW1	GP4		// Lamp 1 Command Input
#define DIGINRAW2	GP3		// Lamp 2 Command Input
#define LAMP1ON		0b00100000	// Lamp 1 Control Output (GP5)
#define LAMP2ON		0b00000100	// Lamp 2 Control Output (GP2)
#define BOTHLAMPSON	0b00100100	// Both Lamps On
#define LAMPSOFFMASK	0b11011011	// Both Lamps Off Mask

// DeBounce Definitions1
//
// Notes: 	The major source for bounce in this application is the 
//		closure of hardware switches which ground input pins and
//		transition the inputs from High to Low. See the 
//		DeBounce function for details on the logic used to 
//		debounce the raw inputs and the use of the following
//		parameters in the debounce logic. Note that protection
//		is also	provided for transitions from Low to High.

#define VALIDHIGHCOUNT0	5	// Number of constant raw readings needed to
#define VALIDLOWCOUNT0	20	// trigger valid debounced input values for
#define VALIDHIGHCOUNT1	5	// the three input pins. Values can be
#define VALIDLOWCOUNT1	20	// adjusted for specific switch
#define VALIDHIGHCOUNT2 5	// characteristics.
#define VALIDLOWCOUNT2	20	// 

#define ONESEC 1000		// One second time interval for control logic
				// 	measured in main loop cycles.
//****************************************************************************
// Wig-Wag Logic Definitions
//	We put 4 variables in EEPROM so they can be overridden when the code
//	is written to the chip without the need for recompiliation.
//
//	The Variables are:
//	   WigwagSwitchTime -  addr 00 and 01 (0x029A = 666 decimal)
//	   FlashTimerExpired - addr 02 and 03 (0x00DE = 222 decimal)
//	   MaxFlashes -        addr 04 and 05 (0x0003 = 3 decimal)
//	   FlashLamps -        addr 06 (must be 0x00 or 0x01)
//
//	Each parameter to the EEPROM_DATA macro is a single address starting
//	with zero.
//****************************************************************************
__EEPROM_DATA(0x02,0x9A,0x00,0xDE,0x00,0x03,0x01,0);

// WigwagSwitchTime = 0x029A or 666   Time duration for Tungsten 
// 				      wigwag interval (no flash),
// 				      measured in main loop cycles.
//
// FlashTimerExpired = 0x00DE or 222  Time duration for LED flash 
//				      sequence (1/3 wigwagtime).
//
// MaxFlashes = 0x0003 or 3	      Number of times the LED will 
//				      flash before switching lamps.
//
// FlashLamps = 0x01		      Controls how the lamps are wigwag'ed.
//				      0x01 (default) makes the lamps flash
//				      the MaxFlashes number of times before
//				      changing to the other lamp. Each "flash"
//				      lasts for FlashTimerExpired cycles.
//				      0x00 makes the lamps simply alternate
//				      after WigwagSwitchTime cycles.
//				      Originally LED's would flash and 
//				      tungsten bulbs would alternate.  But
//				      testing showed good results by flashing
//				      tungsten bulbs as well, but the code was
//				      maintained in case it would be useful in
//				      the future.

int WigwagSwitchTime=0;
int FlashTimerExpired=0;
int MaxFlashes=0;
int FlashLamps=0;

#define OFF	0
#define ON	1

// Function Prototypes

void DeBounce( unsigned char DigInRaw, unsigned char *DigIn,
		unsigned char *LowCount, unsigned char *HighCount,
		unsigned char ValidLowCount, unsigned char ValidHighCount,
		long int Time, long int *TimeLow);

//***************************************************************************
// Main() - Main Routine
//***************************************************************************

void main(void)
{
	unsigned char DigIn0 = 1;	// DeBounced digital inputs. 
	unsigned char DigIn1 = 1;	// Initialized to the Lamps Off mode.
	unsigned char DigIn2 = 1;	// 

	unsigned char HighCount0 = 0;	// Counts for the number of constant
	unsigned char LowCount0  = 0;	// High or Low raw input readings.
	unsigned char HighCount1 = 0;	// Only one counter is active
	unsigned char LowCount1  = 0;	// depending on the last raw value.
	unsigned char HighCount2 = 0;	
	unsigned char LowCount2  = 0;
	unsigned char WigWag = OFF;	// Wig-Wag state, set to OFF or ON
	unsigned int  WigWagTimer = 0;	// Timer for wig-wag interval
	long int ElapsedTime = 0;	// Elapsed time since reset - 
					// used to test time between switches 
					// for wigwag functionality.
	long int TimeLow0;		// Times of last transitions to low
	long int TimeLow1;		// for all control inputs
	long int TimeLow2;		
	unsigned int  FlashCount = 0;	// Number of times the current LED
					// has flashed so far.
	unsigned int  LampFlashing = 1;	// Lamp that is currently "on"
					// (or possibly flashing)
	unsigned char FlashState = OFF;	// Flashing state of the lamp that 
					// is currently "on"
	unsigned int  FlashTimer = 0;	// Timer for LED on and off state	
	unsigned char CurrentlyFlashing = OFF;
					// While wigwag, are we flashing 
					// or steady? This was originally 
					// used for the LED, but its use 
					// changed as we added the switch
					// for LED vs. Tungsten.  LED's flash,
					// Tungsten's don't.
	unsigned int LampFunction = 0;  // To control lamp function based on 
					// the position of the switches.
	unsigned int EachLamp = 0;      // Switch functionality (option A or B)


	//***************
	// Initialization
	//***************
	
	// Read data from EEPROM for some Static variables
	WigwagSwitchTime = (eeprom_read(0)*256)+eeprom_read(1);
	FlashTimerExpired = (eeprom_read(2)*256)+eeprom_read(3);
	MaxFlashes = (eeprom_read(4)*256)+eeprom_read(5);
	FlashLamps = eeprom_read(6);
	// FlashLamps = 0;	// To alternate lights (rather than flash them)
				// simply uncomment this variable and re-compile.
				// or poke 0x00 into EEPROM location 06. 
	

	// Set Up Micro I/O

	GPIO   = 0b00000000;	// From initialization example in datasheet
	CMCON0 = 0b00000111;	// Configure comparator inputs as digital I/O
	ANSEL  = 0b00000000;	// Configure A/D inputs as digital I/O
	TRISIO = 0b00011011;	// GPIO 0,1,3,4 inputs, GPIO 2,5 outputs
	GPIO   = 0b00000000;	// Initialize all outputs off

	// Reset the Watch Dog Timer and set up a 1 millisec Main Processing
	// Loop using Timer0. With the default 4MHz internal clock, Timer0
	// is driven at 1MHz, and with the 1:4 prescaler assigned to Timer0
	// it will overflow every 1.024 millisec (8 bit timer). The Watch Dog
	// Timer is enabled with the nominal 17 millisec time out.

	CLRWDT();		// Reset the Watch Dog Timer
	OPTION_REG = 0b11010001;// Timer0 internal clock with 1:4 prescale
	TMR0 = 0;		// Clear Timer0 to start the count
	T0IF = 0;		// Clear Timer0 overflow flag

	// Initialize the Wig-Wag and flashing functions
	WigWag = OFF;
	FlashCount = 0;
	LampFlashing = 1;	// By default always start with lamp 1
	FlashState = OFF;
	FlashTimer = 0;
	CurrentlyFlashing = OFF;

	//***********************
	// Main Processing Loop *
	//***********************
	
	while(1)
	{
		if ( T0IF )		// Wait for Timer0 overflow
		{
			T0IF = 0;		// Reset Timer0 overflow flag
			++ElapsedTime;		// Update Elapsed Time 
						// (timing for switch change)

			DeBounce( DIGINRAW0, &DigIn0, &LowCount0, &HighCount0,
				VALIDLOWCOUNT0, VALIDHIGHCOUNT0,
				ElapsedTime, &TimeLow0); // LED vs. Tungsten 
			DeBounce( DIGINRAW1, &DigIn1, &LowCount1, &HighCount1,
				VALIDLOWCOUNT1, VALIDHIGHCOUNT1,
				ElapsedTime, &TimeLow1); // Lamp 1 switch
			DeBounce( DIGINRAW2, &DigIn2, &LowCount2, &HighCount2,
				VALIDLOWCOUNT2, VALIDHIGHCOUNT2,
				ElapsedTime, &TimeLow2); // Lamp 2 switch

			if ( DigIn0 ) EachLamp = 1;	// Option "A" functionality
			else EachLamp = 0;		// Option "B" functionality
						
			// Control Lamps based on current debounced inputs
			// Set the variable "LampFunction" to indicate function
			// based on switch state.
			// Assuming the "EachLamp" variable is 1, then:
			//  States of Lamp 1 and Lamp 2 switches:
			//   High-High	1- Both Lamps Off ("on" switches pull them low)
			//   High-Low	2- Lamp 1 off; Lamp 2 on
			//   Low-High	3- Lamp 1 on; Lamp 2 off
			//   Low-Low	4- Wigwag (if time between switches < ONESEC)
			//   Low-Low	5- Both lamps on (if time between switches > ONESEC)
			//
			// Assuming the "EachLamp variable is zero, then:
			//  States of Lamp 1 and Lamp 2 switches:
			//   High-High	1- Both Lamps Off ("on" switches pull them low)
			//   High-Low	4- Wigwag
			//   Low-High	4- Wigwag
			//   Low-Low	5- Both lamps on 

			if ( DigIn1 && DigIn2 ) LampFunction=1;	// High-High 
			else if ( DigIn1 && !DigIn2 )	// High-Low 
			{
				if (EachLamp == 1) LampFunction = 2;
				else LampFunction = 4;
			}
			else if ( !DigIn1 && DigIn2 )	// Low-High
			{
				if (EachLamp == 1) LampFunction = 3;
				else LampFunction = 4;
			}
			else if ( !DigIn1 && !DigIn2 )	// Low-Low
			{
				if (EachLamp == 1) 
				{
					if ( labs(TimeLow1 - TimeLow2) >= ONESEC) LampFunction=5;
					else LampFunction = 4;
				}
				else LampFunction = 5;
			}
			else LampFunction = 0;		// Should never get here!
			


			if ( LampFunction == 1 )	// Both Lamps OFF
			{
				GPIO &= LAMPSOFFMASK;	// Clear both Lamp outputs 
				WigWag = OFF;		// Reset all other states and counters
				FlashCount = 0;		// (gives the chip something to do!)
				LampFlashing = 1;
				FlashState = OFF;
				FlashTimer = 0;
				CurrentlyFlashing = OFF;
			}
			else if ( LampFunction == 2 )	// Lamp 2 ON Only
			{
				GPIO = ( ( GPIO & LAMPSOFFMASK) | LAMP2ON );	
				WigWag = OFF;
				FlashCount = 0;
				LampFlashing = 1;
				FlashState = OFF;
				FlashTimer = 0;
				CurrentlyFlashing = OFF;
			}
			else if ( LampFunction == 3 )	// Lamp 1 ON Only
			{
				GPIO = ( ( GPIO & LAMPSOFFMASK ) | LAMP1ON );
				WigWag = OFF;
				FlashCount = 0;
				LampFlashing = 1;
				FlashState = OFF;
				FlashTimer = 0;
				CurrentlyFlashing = OFF;
			}
			else if ( LampFunction == 5 )	// Both Lamps ON
			{
				GPIO |= BOTHLAMPSON;	// Set both lamps on
				WigWag = OFF;
				FlashCount = 0;
				LampFlashing = 1;
				FlashState = OFF;
				FlashTimer = 0;
				CurrentlyFlashing = OFF;
			}
			else if (LampFunction == 4)	// WigWag (this is where all the work is!)
			{
				if ( !WigWag )		// If we haven't started wigwaging - start now!
				{
					WigWag = ON;
					FlashCount = 0;
					FlashTimer = 0;
					WigWagTimer = 0;
					FlashState = ON;
					LampFlashing = 1;
					if ( FlashLamps ) CurrentlyFlashing = ON;	// LED's, so flash 'em
					else CurrentlyFlashing = OFF;		// Tungsten's - so gentle wigwag
					GPIO = ( ( GPIO & LAMPSOFFMASK ) | LAMP1ON );  // First lamp is always 1
				}
				else if ( ++WigWagTimer >= WigwagSwitchTime )	
				{	// Wigwag timer has expired - time to switch lamps 
					if (LampFlashing == 1)
					{ 
						LampFlashing = 2;
						GPIO = ( ( GPIO & LAMPSOFFMASK ) | LAMP2ON );
					}
					else
					{
						LampFlashing = 1;
						GPIO = ( ( GPIO & LAMPSOFFMASK ) | LAMP1ON );
					}
					WigWagTimer = 0;
					FlashCount = 0;
					FlashTimer = 0;
					FlashState = ON;
					if ( FlashLamps ) CurrentlyFlashing = ON;
					else CurrentlyFlashing = OFF;
				}
				else if ( CurrentlyFlashing )
				{	// We've got LED's so they need to flash
					if ( ++FlashTimer >= FlashTimerExpired )
					{	// Flashing but time to change state of the flashing lamp
						if ( FlashState )
						{	// Lamp is on, so turn it off
							GPIO &= LAMPSOFFMASK;	// Clear both Lamp outputs 
							FlashState = OFF;
							FlashTimer = 0;
							WigWagTimer = 0; 
						}
						else if ( ++FlashCount >= MaxFlashes) // are we done flashing?
						{	// We're done flashing - turn the lamps off and expire wigwagtimer
							GPIO &= LAMPSOFFMASK;	// Make sure both Lamp outputs are off 
							FlashState = OFF;
							FlashCount = 0;
							FlashTimer = 0;
							WigWagTimer = WigwagSwitchTime - 1; // Switch lamps next time through
						}
						else
						{	// Lamp is off, so turn it back on (whichever one is flashing)
							if (LampFlashing == 1) GPIO = ( ( GPIO & LAMPSOFFMASK ) | LAMP1ON );
							else GPIO = ( ( GPIO & LAMPSOFFMASK ) | LAMP2ON );
							FlashState = ON;
							FlashTimer = 0;
							WigWagTimer = 0;
						}
					} // We're flashing but the timer hasn't expired - continue
				} // Not Flashing (not LED's) - so just wait for wigwagtimer to expire
			} // End of the WigWag section (whew!)
			CLRWDT();	// Reset the Watch Dog Timer
		} 
	} 
}

//*****************************************************************************
// DeBounce Function - Looks for a series of constant raw inputs to produce
//			a valid debounced input. The debounced value will not
//			be changed until a valid series of raw inputs occurs
//			indicating a change in value. The specific number of
//			constant inputs that will trigger the debounce can
//			be specified for each switch input and for transitions
//			from Low to High and from High to Low. Note that only
//			one counter is active at any time depending on the
//			state of the last raw input processed.
//****************************************************************************

void DeBounce( unsigned char DigInRaw, unsigned char *DigIn,
				unsigned char *LowCount, unsigned char *HighCount,
				unsigned char ValidLowCount, unsigned char ValidHighCount,
				long int Time, long int *TimeLow)
{	
	if ( DigInRaw && (*HighCount < ValidHighCount) ) //Check for High input
	{
		++*HighCount;
		*LowCount = 0;
		if ( *HighCount == ValidHighCount )
			*DigIn = DigInRaw;
	}
	else if ( !DigInRaw && (*LowCount < ValidLowCount) ) // Check for Low input
	{		
		++*LowCount;
		*HighCount = 0;
		if ( *LowCount == ValidLowCount )
		{
			*DigIn = DigInRaw;
			*TimeLow = Time - ValidLowCount;
		}
	}
}
