aboutsummaryrefslogtreecommitdiffstats
path: root/firmware
diff options
context:
space:
mode:
authorNicholas Corgan <nick.corgan@ettus.com>2013-06-07 13:04:51 -0700
committerNicholas Corgan <nick.corgan@ettus.com>2013-06-07 13:04:51 -0700
commit9fefb30f210f86c8c5964ca8ee80f63ad8856d02 (patch)
tree08c6851146d43cb045853c6ecb5069763a20e688 /firmware
parent265daa586730b95d803a694548479cb790ea80fd (diff)
downloaduhd-9fefb30f210f86c8c5964ca8ee80f63ad8856d02.tar.gz
uhd-9fefb30f210f86c8c5964ca8ee80f63ad8856d02.tar.bz2
uhd-9fefb30f210f86c8c5964ca8ee80f63ad8856d02.zip
OctoClock support
Diffstat (limited to 'firmware')
-rw-r--r--firmware/octoclock/Makefile56
-rw-r--r--firmware/octoclock/OctoClock-io.h70
-rw-r--r--firmware/octoclock/OctoClock.c844
3 files changed, 970 insertions, 0 deletions
diff --git a/firmware/octoclock/Makefile b/firmware/octoclock/Makefile
new file mode 100644
index 000000000..6ef662bc6
--- /dev/null
+++ b/firmware/octoclock/Makefile
@@ -0,0 +1,56 @@
+#
+# Copyright 2009 Ettus Research LLC
+#
+
+##################################################
+# Compiler
+##################################################
+CC = avr-gcc
+OBJCOPY = avr-objcopy
+STRIP = avr-strip
+OBJDUMP = avr-objdump
+SREC = srec_cat
+#CFLAGS = -O2 -std=gnu99 -fshort-enums -pedantic-errors -Wall -Werror \
+# -Wstrict-prototypes -Wmissing-prototypes -Wcast-align -Wshadow
+CFLAGS = -std=gnu99 -O2
+
+#-D IO_DEBUG
+
+##################################################
+# Files
+##################################################
+HDRS = OctoClock-io.h
+SRCS = OctoClock.c
+TARGET = octoclock_fw
+
+##################################################
+# Device
+##################################################
+MMCU = atmega128
+PROGRAMMER = avrisp2
+PORT = usb
+AVRDUDE = avrdude -p $(MMCU) -c $(PROGRAMMER) -P $(PORT)
+
+##################################################
+# Global Targets
+##################################################
+all: $(TARGET).hex
+
+clean:
+ $(RM) *.o *.elf *.hex
+
+install: all
+ $(AVRDUDE) -U flash:w:$(TARGET).hex:i
+
+##################################################
+# Dependency Targets
+##################################################
+
+$(TARGET).hex: $(TARGET).elf
+ $(OBJCOPY) -O ihex $< $@
+
+$(TARGET).elf: $(SRCS:.c=.o)
+ $(CC) -mmcu=$(MMCU) $^ -o $@
+
+%.o: %.c $(HDRS) Makefile
+ $(CC) -mmcu=$(MMCU) -c $< -o $@ $(CFLAGS)
diff --git a/firmware/octoclock/OctoClock-io.h b/firmware/octoclock/OctoClock-io.h
new file mode 100644
index 000000000..88cd1499b
--- /dev/null
+++ b/firmware/octoclock/OctoClock-io.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2009 Ettus Research LLC
+ */
+
+#ifndef IO_H
+#define IO_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+
+typedef int8_t i8;
+typedef int16_t i16;
+typedef int32_t i32;
+typedef int64_t i64;
+
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+
+typedef float f32;
+typedef double f64;
+typedef long double f128;
+
+typedef int error_indicator; /* A type for functions, usually... */
+typedef unsigned int boolean; /* "natural" type */
+
+typedef char c8; /* 8-bit character */
+typedef unsigned char uc8; /* 8-bit character, unsigned*/
+/* it is sometimes useful to*/
+/* make the distinction*/
+
+#ifdef FALSE
+#undef FALSE
+#endif
+#define FALSE (0)
+
+#ifdef TRUE
+#undef TRUE
+#endif
+#define TRUE (!FALSE)
+
+#define is :{
+#define esac break;}
+
+
+// This other crap can be deleted, below, between the #if 0 and #endif inclusive
+
+#if 0
+
+#define IO_PX(port, pin) ((uint8_t)(((port - 'A') << 4) + pin))
+#define IO_PA(pin) IO_PX('A', pin)
+#define IO_PB(pin) IO_PX('B', pin)
+#define IO_PC(pin) IO_PX('C', pin)
+#define IO_PD(pin) IO_PX('D', pin)
+
+typedef const uint8_t io_pin_t;
+
+void io_output_pin(io_pin_t pin);
+void io_input_pin(io_pin_t pin);
+void io_set_pin(io_pin_t pin);
+void io_clear_pin(io_pin_t pin);
+bool io_test_pin(io_pin_t pin);
+
+#endif //if 0
+
+#endif /* IO_H */
diff --git a/firmware/octoclock/OctoClock.c b/firmware/octoclock/OctoClock.c
new file mode 100644
index 000000000..07397601d
--- /dev/null
+++ b/firmware/octoclock/OctoClock.c
@@ -0,0 +1,844 @@
+/*
+ * 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 */