/* * OctoClock.c * * V1.00 -- May 2013 * * * V1.03 -- Correct the switch to be UP for Internal and DOWN for External * This means that the bat handle "points at" (sort of) the lower-left LED, which * is the "STATUS" LED, which gets lit up when the external 10 MHz is present * The "10 MHz Signal Detect" code accepts a very wide range of "10 MHz" signals * 23 April 2013 * * * V1.02 -- Make LEDs consistent with Chassis - Top LED is INTERNAL; middle is EXTERNAL; bottom is STATUS * * STATUS is ON if the 10 MHz external input is present. 19 April 2013 * * * V1.01: Modify TI chip initialization to be in differentail mode * which allows 10 MHz input down to 0.1 Volts according to the datasheet. * * * New Version that supports CLOCK board Version 1.0 * * Author: Michael@Cheponis.Com with code borrowed liberally from * previous AVR projects * */ /* * Copyright 2013 Ettus Research LLC */ /* CLKSEL0 = 1 SUT1..0 is 11 CKOPT = 0 CKSEL3..1 is 111 => big output swing, long time to start up */ /* NOT in M103 compatibility mode, no WDT, CKOPT full rail-to-rail xtal osc, 16K CK (16K clock cycles), additional delay 65ms for Crystal Oscillator, slowly rising power Very conservative settings; if lower power osc required, change CKOPT to '1' (UNPROGRAMMED) or, if you will, CKOPT = [ ] M103C = [ ] WDTON = [ ] OCDEN = [ ] JTAGEN = [X] SPIEN = [X] EESAVE = [ ] BOOTSZ = 4096W_F000 BOOTRST = [ ] CKOPT = [X] BODLEVEL = 2V7 BODEN = [ ] SUT_CKSEL = EXTHIFXTALRES_16KCK_64MS EXTENDED = 0xFF (valid) HIGH = 0x89 (valid) LOW = 0xFF (valid) */ // No interrupts are required #include "OctoClock-io.h" #include <avr/io.h> #include <avr/interrupt.h> #ifdef On #undef On #endif #ifdef Off #undef OFf #endif #define Off (0) #define On (!Off) // Important for the Serial Port, not used at the moment #define FOSC (7372800) #define BAUD (115200) #define MYUBRR FOSC/16/BAUD-1 #define wait() for(u16 u=14000; u; u--) asm("nop"); enum LEDs {Top,Middle,Bottom}; // Top is 0, Mid is 1, and Bottom is 2 void led(enum LEDs which, int turn_it_on){ u8 LED = 0x20 << which; // selects the proper bit if(turn_it_on) PORTC |= LED; else PORTC &= ~LED; } enum TI_Input_10_MHz {Primary_GPS, Secondary_Ext}; void setup_TI_CDCE18005(enum TI_Input_10_MHz); /***************************************************************************************** SPI routines ******************************************************************************************/ /* All macros evaluate to compile-time constants */ /* *** helper macros * * */ /* turn a numeric literal into a hex constant (avoids problems with leading zeros) 8-bit constants max value 0x11111111, always fits in unsigned long */ #define HEX__(n) 0x##n##LU /* 8-bit conversion function */ #define B8__(x) ((x&0x0000000FLU)?1:0) \ +((x&0x000000F0LU)?2:0) \ +((x&0x00000F00LU)?4:0) \ +((x&0x0000F000LU)?8:0) \ +((x&0x000F0000LU)?16:0) \ +((x&0x00F00000LU)?32:0) \ +((x&0x0F000000LU)?64:0) \ +((x&0xF0000000LU)?128:0) // Damn, that is SERIOUS magic ... ;-) Yes, I know how it works // but it's pretty cool.... /* *** user macros *** */ /* for upto 8-bit binary constants */ #define Bits_8(d) ((unsigned char)B8__(HEX__(d))) /* for upto 16-bit binary constants, MSB first */ #define Bits_16(dmsb,dlsb) (((unsigned short)Bits_8(dmsb)<<8) \ + Bits_8(dlsb)) /* for upto 32-bit binary constants, MSB first */ #define Bits_32(dmsb,db2,db3,dlsb) (((unsigned long)Bits_8(dmsb)<<24) \ + ((unsigned long)Bits_8(db2)<<16) \ + ((unsigned long)Bits_8(db3)<<8) \ + Bits_8(dlsb)) /* Sample usage: Bits_8(01010101) = 85 Bits_16(10101010,01010101) = 43605 Bits_32(10000000,11111111,10101010,01010101) = 2164238933 */ enum CDCE18005 {Reg0, Reg1, Reg2, Reg3, Reg4, Reg5, Reg6, Reg7, Reg8_Status_Control, Read_Command=0xE, RAM_EEPROM_Unlock=0x1F, RAM_EEPROM_Lock=0x3f} TI_CDCE18005; // Table of 32-bit constants to be written to the TI chip's registers. // Damn, inconsistent data sheet! Special settigns see p35 of TI datasheet // For the GPS's 10 MHz output u32 table_Pri_Ref[] = { Bits_32(1,01010100,0,0), // Reg 0 Bits_32(1,01010100,0,0), // Outputs LVCMOS Positive&Negative Active - Non-inverted Bits_32(1,01010100,0,0), Bits_32(1,01010100,0,0), Bits_32(1,01010100,0,0), // All have output divide ratio to be 1; Aux Output is OFF Bits_32(0,0,1001,11010100), // Reg 5 LVCMOS in; p31 of TI datasheet Bits_32(1,0,0010000,0), // Reg 6 // SCAS863A � NOVEMBER 2008 � REVISED JUNE 2011 Bits_32(1,01000000,0,0), // Reg 7 // 76543210 Bits_32(0,0,1,10000000) // Reg8 Status/Control }; // Looks like it's doing the correct thing re: SPI interface // This is *definitely* AC coupled. I removed those resistors to +3.3 and ground // signal looked no different with differential measurement. Added 240+470 to // center tap of secondary side to bias up to approx 1.2V for proper LVDS // // For the External 10 MHz input LVDS with external termination -- Effectively DC coupled u32 table_Sec_Ref[] = { Bits_32(0001,01010100,0,100000),// Reg 0 -- use Secondary Reference for all channels Bits_32(0001,01010100,0,100000),// Outputs LVCMOS Positive&Negative Active - Non-inverted Bits_32(0001,01010100,0,100000), Bits_32(0001,01010100,0,100000), Bits_32(0001,01010100,0,100000), // Bits_32(0,0,00001000,10010111), // Reg 5 LVDS with External Termination p32 of TI datasheet // Bits_32(0,0,00001000,11010111), // Reg 5 LVDS with INTERNAL Termination p32 of TI datasheet // May 2013 -- Turn OFF the LVDS Safe Mode, as it supposedly causes input thresholds to be increased. // Bits_32(0,0,1001,10011011), // Reg 5, try again. Pretty soon, try new board... Bits_32(0,0,1,10011011), // Reg 5, Failsafe OFF b5.11 = 0 // Bits_32(0,0,1001,11011011), // Reg 5, try again. Pretty soon, try new board... // Try with DC input termination; bit 6 is a "1" 2013 March // Seems to not work correctly. // Bits_32(1,0,0000000,0), // Reg 6; note that 6.12 must be 1 for LVDS w/External Termination, 0 int // Bits_32(1,0,0000000,0), // Reg 6; try Internal and DC coupling Bits_32(1,0,10000,0), // Reg 6; try again Bits_32(1,01000000,0,0), Bits_32(0,0,1,10000000) // Reg8 Status/Control }; //; Table 19 conflicts with Tables 5 thru 9 - in how LVCMOS outputs are defined // extra error in Table 9, for bits 24 and 25 // // Could combine these into just table[][] with 1st subscript being 0 or 1 for Primary or Secondary // Maybe want to to that. int table_size = sizeof (table_Pri_Ref) / sizeof(u32); //int table_size = 1; // Testing read and write of Register 0 -- don't want excess SPI transactions //NOTE!!! Still need to shift left by 4 and OR in register, as defined in TI_CDCE18005 enum, above. enum Levels {Lo, Hi}; #define CLK (PA0) // Shift by 0 bits (PA.0) #define CE_ (PA1) // Is really the "Chip Disable" signal, as Hi disables SPI #define MOSI (PA2) #define MISO (PA3) #define PD_ (PA4) #define SYNC_ (PA5) void set_bit(u8 bit_number, enum Levels bit_value){ if(bit_value == Hi) PORTA |= 1<<bit_number; else PORTA &= ~ (1<<bit_number); } bool get_bit(u8 bit_number){ asm("nop"); u8 portA = PINA; // Maybe something is strange they way PORTA is read? // USART_Transmit( hex_table [0xf & (portA >> 4)], Control ); // USART_Transmit( hex_table [0xf & portA], Control ); // USART_Transmit(CR, Control); USART_Transmit(LF,Control); return (portA & 1<< bit_number) > 0 ? TRUE : FALSE; //return (portA & 8) != 0; // It's always MISO, so nail it for the moment } void send_SPI(u32 bits){ // Send 32 bits to TI chip, LSB first. // Don't worry about reading any bits back at this // time, although for production, may want to do that // as an error-check / integrity check. /* #define CLK (PA0) // Shift by 0 bits (PA.0) #define CE_ (PA1) // Is really the "Chip Disable" signal, as Hi disables SPI #define MOSI (PA2) #define MISO (PA3) #define PD_ (PA4) #define SYNC_ (PA5) */ //Basically, when the clock is low, one can set MOSI to anything, as it's ignored. set_bit(CE_, Lo); // Start SPI transaction with TI chip for (u8 i=0; i<32; i++){ // Foreach bit we need to send set_bit(MOSI, ((bits & (1UL<<i)) ? Hi : Lo) ); // LSB first asm("nop"); // Need a little more delay before L->H on clock; (REALLY?) set_bit(CLK, Hi); set_bit(CLK, Lo); // Pulse the clock to clock in the bit } // USART_Transmit(CR, Control); USART_Transmit(LF,Control); //set_bit(MOSI, Lo); // Not needed, but keeps all bits zeros except /CE when idle set_bit(CE_, Hi); // OK, transaction is over // USART_Transmit(CR, Control); USART_Transmit(LF,Control); } // Takes about 7.6 ms to init all regs (as seen on scope) // There is a very interesting phenomenon that is occurring --- The bit-to-bit time // at the beginning of transmission is 15 usec. However, as the number of bits // shifted to the left increases (as i increases in the for() loop ) // the time between bits elongates. It's about 37 usec between bits // 30 and 31 (the last 2 bits). It's kinda cool, because it's easy to // know when the new word begins because the clock pulses will be // closer together. // See if it checks: (15+37)/2 = 26 usec between average bits // 32 bits * 9 words * 26 usec = 7.49 ms --- but have to add // in the little bit of time that CE_ goes high; so 7.6 ms // is a very reasonable number. (Assumes linear increase in // time as the number of shifts goes up, which seems to // work OK here.) // // Of course, using a table instead of doing those shifts all the // time would fix this; but it (should not) doesn't matter for this // SPI interface. // // So far, the first word looks good, and the beginning of writing // Register 1 also looks good. // // enum TI_Input_10_MHz {Primary_GPS, Secondary_Ext}; void reset_TI_CDCE18005(){ // First, reset the chip. Or, if you will, pull /SYNC low then high set_bit(CE_, Hi); set_bit(PD_, Lo); wait(); // This should put the EEPROM bits into the RAM -- we don't care, but should init the chip set_bit(PD_, Hi); // Out of Power Down state wait(); set_bit(SYNC_, Lo); wait(); set_bit(SYNC_, Hi); wait(); // Now, by gosh, that darn chip ought to be fully enabled! } void setup_TI_CDCE18005(enum TI_Input_10_MHz which_input){ // Send the table of data to init the clock distribution chip. Uses SPI. u32 temp; //reset_TI_CDCE18005(); // This REALLY should not be necessary if(which_input == Primary_GPS){ for(u8 i=0; i<table_size; i++){ temp = table_Pri_Ref[i]<<4; temp |= i; // print_u32(temp); // Debug *mac* -- correct send_SPI(temp); // Make sure the register's address is in the LSBs } } else { // is Secondary_Ext -- External 10 MHz input from SMA connector for(u8 i=0; i<table_size; i++){ temp = table_Sec_Ref[i]<<4; temp |= i; send_SPI(temp); // Make sure the register's address is in the LSBs } } } u32 receive_SPI(){ u32 bits; bits = 0; set_bit(CE_, Hi); // Make sure we're inactive set_bit(CLK, Lo); // and clk line is inactive, too set_bit(MOSI,Lo); // Make our bit output zero, for good measure set_bit(CE_, Lo); // Start SPI transaction with TI chip; MOSI is don't care for (u8 i=0; i<32; i++){ // Foreach bit we need to get bits >>= 1; // get ready for next bit - NOTE: Only do this if we REALLY are putting in another bit set_bit(CLK, Hi); // CPU is so slow, it easily meets setup & hold times // 76543210 if( get_bit(MISO) ) bits |= 0x80000000; // because we receive the LSB first set_bit(CLK, Lo); // Pulse the clock to clock in the bit } set_bit(CE_, Hi); // OK, transaction is over return (u32)(bits >> 4); // Ditch the lower 4 bits, which only contain the address } u32 get_TI_CDCE18005(enum CDCE18005 which_register){ u32 get_reg_value; get_reg_value = 0; get_reg_value = (0xf0 & which_register << 4) | Read_Command; send_SPI(get_reg_value); // This tells the TI chip to send us the reg. value requested return receive_SPI(); }; bool check_TI_CDCE18005(enum TI_Input_10_MHz which_input, enum CDCE18005 which_register) { // USART_Transmit(CR, Control); USART_Transmit(LF,Control); //reset_TI_CDCE18005(); if(which_input == Primary_GPS){ u32 read_value = get_TI_CDCE18005(which_register); return read_value == table_Pri_Ref[which_register]; } else { u32 read_value = get_TI_CDCE18005(which_register); return read_value == table_Sec_Ref[which_register]; } }; // This could obviously be done more elegantly to share more code; but this is // simple and easy to understand void Setup_Atmel_IO_Ports(){ ///////////////////////////////////////////////////////////////////////////// /* * PORT A * *pin# Sig Our Functional Name * * p51 PA0 CLK_CDCE to U205 pin 24 -- L-->H edge latches MOSI and MISO in CDCE18005 * p50 PA1 CE_CDCE Low = Chip Enabled for SPI comm to U205 pin 25 * p49 PA2 MOSI_CDCE Goes to CDCE18005 - U205 pin 23 * p48 PA3 MISO_CDCE Input Comes from U205 pin 22 * p47 PA4 PD_CDCE Low = Chip is in Power-Down state; is Hi for normal operation U205 pin 12 * p46 PA5 SYNC_CDCE Low = Chip is sync'd with interal dividers; Hi for normal operation U205 pin 14 * p45 PA6 PPS_SEL Low --> PPS_EXT selected; Hi -> PPS_GPS selected; to U203 pin 1 * p44 PA7 gps_lock Input Comes from M9107 - U206 pin 3 * */ // Bit #: 76543210 PORTA = Bits_8(00110010); // /pd_cdcd, /sync_code, /ce need to be 1 (disabled) to start DDRA = 1<<DDA6 | 1<<DDA5 | 1<<DDA4 | 1<<DDA2 | 1<<DDA1 | 1<<DDA0; //// all bits are outputs, except PA7 (gps_lock) and PA3 (MISO_CDCE) are inputs ///////////////////////////////////////////////////////////////////////////// /* * Port B * *pin# Sig Our Functional Name * * p10 PB0 Ethernet /SEN * p11 PB1 Ethernet SCLK * p12 PB2 Ethernet MOSI * p13 PB3 Ethernet MISO * p14 PB4 Not connected, set as output with value 0 * p15 PB5 Ethernet /RESET -- Set to HI for normal use, weak input * p16 PB6 Ethernet /WOL --- Wake on LAN -- set, weak input * p17 PB7 Not connected, set as output with value 0 * */ PORTB = Bits_8(01100001); // Initial Value is all zeros DDRB = 1<<DDB2 | 1<<DDB4 | 1<<DDB7; // MOSI is an output; the Not Connected pins are also outputs ///////////////////////////////////////////////////////////////////////////// /* * Port C * *pin# Sig Our Functional Name * * p34 PC0 Not connected, set as output with value 0 * p35 PC1 Reference Select Switch INPUT * p36 PC2 Not connected, set as output with value 0 * p37 PC3 Not connected, set as output with value 0 * p38 PC4 Not connected, set as output with value 0 * p40 PC5 "Top LED" of D103 3-stack of green LEDs * p41 PC6 "Middle LED" * p43 PC7 "Bottom LED" * */ PORTC = 0; // Initial Value is all zeros DDRC = ~( 1<<DDC1 ); // All bits are outputs, except PC1. including the 5 Not Connected bits ///////////////////////////////////////////////////////////////////////////// /* * Port D * *pin# Sig Our Functional Name * * p25 PD0 Ethernet /INT input * p26 PD1 GPS NMEA bit, output * p27 PD2 GPS Serial Out (RXD; INT1) INPUT * p28 PD3 GPS Serial In (TXD) OUTPUT * p29 PD4 GPS Present, INPUT hi = Present * p30 PD5 Not connected, set as output with value 0 * p31 PD6 Not connected, set as output with value 0 * p32 PD7 Not connected, set as output with value 0 * */ PORTD = 0; // Initial Value is all zeros DDRD = 1<<DDD1 | 1<<DDD3; ///////////////////////////////////////////////////////////////////////////// /* * Port E * *pin# Sig Dir Our Functional Name * * p2 PE0 In avr_rxd (Also MOSI [PDI] when used for SPI programming of the chip) * p3 PE1 Out avr_txd (Also MISO [PDO] when used for SPI programming of the chip) * p4 PE2 In avr_cts * p5 PE3 Out avr_rts DUE TO MOD, make this an input, too (as we go direct GPSDO to FPGA via level translators) * p6 PE4 In PPS_GPS * p7 PE5 In PPS_EXT_n * p8 PE6 In Not Connected * p9 PE7 In Not Connected * */ PORTE = 0; DDRE = 1<<DDE1; // make outputs, set to zero. PE1 is usart0 TXD ///////////////////////////////////////////////////////////////////////////// /* * Port F * * Split into 2 nibbles; goes to Amp/Filter board to select ENABLE and two bits to select band * one bit per nibble is not connected. * * pin Sig Dir Our Functional Name * num * * p61 PF0 Out J117 pin 3 (J117 pins 1 and 2 are GND) * p60 PF1 Out J117 pin 4 * p59 PF2 Out J117 pin 5 * p58 PF3 Out J117 pin 6 * p57 PF4 Out J118 pin 3 (J118 pins 1 and 2 are GND) * p56 PF5 Out J118 pin 4 * p55 PF6 Out J118 pin 5 * p54 PF7 Out J118 pin 6 * */ PORTF = 0; // Initial Value is all zeros; be sure ENABLE bits are active high!!!! DDRF = 0xff; // All bits are outputs led(Middle,On); setup_TI_CDCE18005(Primary_GPS); // 10 MHz from Internal Source led(Top,On); PORTA |= (1<<PA6); // PPS from Internal source } ///////////////////////////////////////////////////////////////////////////// //enum TI_Input_10_MHz {Primary_GPS, Secondary_Ext}; //setup_TI_CDCE18005(enum TI_Input_10_MHz); bool Global_GPS_Present = (bool)FALSE; bool Global_Ext_Ref_Is_Present = (bool)FALSE; // NOT PRESENT unless proven so... // This was initially global becasue it was to be set in an interrupt routine // But it turned out interrupts were not needed. But kept this in because // although it's a Global, it is the only one, and it makes it easier to // go back and use interrupts if absolutely necessary. It could be // removed and replaced with some local variable that gets passed // around, but, really, it seems OK to me like this. void LEDs_Off(){ led(Top,Off); led(Middle,Off); led(Bottom,Off); } void Force_Internal(){ // led(Middle,On); led(Top,On); led(Middle,Off); led(Bottom,On); setup_TI_CDCE18005(Primary_GPS); // Set PPS to Primary (1) n.b.: "1" in general means "Internal" for all such signals PORTA |= (1<<PA6); // PPS from Internal source } void Force_External(){ // led(Middle, Off); led(Top, Off); led(Middle, On); led(Bottom, On); setup_TI_CDCE18005(Secondary_Ext); // Set PPS to External (0 PORTA &= ~(1<<PA6); // PPS from External source } ///////////////////////////////////////////////////////////////////////////// void Prefer_Internal(){ if(Global_GPS_Present) Force_Internal(); else if(Global_Ext_Ref_Is_Present) Force_External(); else LEDs_Off(); } void Prefer_External(){ // IF EXTERNAL IS OK, then do this stuff // if external is NOT OK, then force Internal if(Global_Ext_Ref_Is_Present) Force_External(); else if(Global_GPS_Present) Force_Internal(); else LEDs_Off(); } // Turns out, we don't need interrupts #if 0 //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; u8 Global_Tick_Counter = (u8)0; u8 Global_Ext_Ref_Detect_Counter = (u8)0; // External Reference Detect interrupt; nominally at 610 Hz (10 MHz / 2**14 ) ISR ( _VECTOR(1)){ asm("cli"); // Global Interrupt Disable --- enable with SEI if desired later Global_Ext_Ref_Detect_Counter++ ; // We reset this elsewhere asm("sei"); // Global Interrupt Enable } // Timer 0 Overflow Handler ISR ( _VECTOR(16)){ static u8 led_state = Off; asm("cli"); // Global Interrupt Disable --- enable with SEI if desired later led_state = (led_state ? Off : On); asm("sei"); // Global Interrupt Enable } //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; void Setup_Atmel_Interrupts(){ // Timer 0 is all we need -- but simplest if both Timer 0 AND IRQ1 (ext_ref_detect 610 Hz signal) also // Nah, don't need this... } #endif bool Check_What_Is_Present(){ Global_GPS_Present = (PIND & (1<<DDD4)) != 0; // See if +5 scaled to 3.3 from GPSDO is there volatile u8 portE = PINE; volatile u8 prev, now; prev = ( portE & (1 << DDE7) ? 1 : 0); // Get PREVIOUS state of the input for(u16 c=1; c; c++){ portE = PINE; now = ( portE & (1 << DDE7) ? 1 : 0); if(prev != now){ Global_Ext_Ref_Is_Present = (bool)TRUE; return (bool)TRUE; } } // Else, if it didn't wiggle in that time, then it didn't wiggle // So ext. is NOT present Global_Ext_Ref_Is_Present = (bool)FALSE; return (bool)FALSE; } bool get_Switch_State(){ u8 portC = PINC; // return (bool)(portC & (1<<DDC1) ? On : Off); return (bool)(portC & (1<<DDC1) ? Off : On); // UP is prefer internal, // DOWN is prefer external } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // M A I N // ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// int main(void){ bool Old_Switch_State, Current_Switch_State, Old_Global_Ext_Ref_Is_Present = FALSE; asm("cli"); // Global Interrupt Disable --- enable with SEI if desired later Setup_Atmel_IO_Ports(); // Setup_Atmel_Interrupts(); /* * DO THIS FOREVER: * * * get_switch_state * * if SWITCH_CHANGED: * * * if PREFER_INTERNAL: * if INTERNAL_PRESENT do_internal * else if EXTERNAL_PRESENT do_external * else LEDs OFF * * if PREFER_EXTERNAL: * if EXTERNAL_PRESENT do_external * else if INTERNAL_PRESENT do_internal * else LEDs OFF * */ Old_Switch_State = ! get_Switch_State(); // Because down below, we use this to get state swap... // So we arbitrarily set the PREVIOUS state to be the "other" state // so that, below, we trigger what happens when the switch changes // This first "change" is therefore artificial to keep the logic, below, cleaner while(TRUE) { Check_What_Is_Present(); // Set "Global_Ext_Ref_Is_Present" and "Global_GPS_Present" // Off means "Prefer External" -- DOWN // On means "Prefer Internal" -- UP Current_Switch_State = get_Switch_State(); if( (Current_Switch_State != Old_Switch_State) || (Global_Ext_Ref_Is_Present != Old_Global_Ext_Ref_Is_Present) ) { Old_Switch_State = Current_Switch_State; Old_Global_Ext_Ref_Is_Present = Global_Ext_Ref_Is_Present; if(Current_Switch_State == On) Prefer_Internal(); else Prefer_External(); } // if() checking for different switch status } // WHILE() loop } /*end "main" of Program 'OctoClock.c */