//**************************************************************************** // 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 #include // 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; } } }