diff options
Diffstat (limited to 'firmware/octoclock')
-rw-r--r-- | firmware/octoclock/Makefile | 16 | ||||
-rw-r--r-- | firmware/octoclock/OctoClock-io.h | 70 | ||||
-rw-r--r-- | firmware/octoclock/OctoClock.c | 1103 |
3 files changed, 438 insertions, 751 deletions
diff --git a/firmware/octoclock/Makefile b/firmware/octoclock/Makefile index 517dee4c5..acf26f617 100644 --- a/firmware/octoclock/Makefile +++ b/firmware/octoclock/Makefile @@ -1,6 +1,19 @@ # # Copyright 2009 Ettus Research LLC # +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# ################################################## # Compiler @@ -19,7 +32,6 @@ CFLAGS = -std=gnu99 -O2 ################################################## # Files ################################################## -HDRS = OctoClock-io.h SRCS = OctoClock.c TARGET = octoclock_fw @@ -52,5 +64,5 @@ $(TARGET).hex: $(TARGET).elf $(TARGET).elf: $(SRCS:.c=.o) $(CC) -mmcu=$(MMCU) $^ -o $@ -%.o: %.c $(HDRS) Makefile +%.o: %.c Makefile $(CC) -mmcu=$(MMCU) -c $< -o $@ $(CFLAGS) diff --git a/firmware/octoclock/OctoClock-io.h b/firmware/octoclock/OctoClock-io.h deleted file mode 100644 index 88cd1499b..000000000 --- a/firmware/octoclock/OctoClock-io.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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 index d81a93fc3..f73c30885 100644 --- a/firmware/octoclock/OctoClock.c +++ b/firmware/octoclock/OctoClock.c @@ -1,844 +1,589 @@ /* - * 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 - * + * Copyright 2013 Ettus Research LLC * - * V1.02 -- Make LEDs consistent with Chassis - Top LED is INTERNAL; middle is EXTERNAL; bottom is STATUS + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * STATUS is ON if the 10 MHz external input is present. 19 April 2013 + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Welcome to the firmware code for the USRP Octoclock accessory product! * - * 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. + * Notes regarding this firmware: + * 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 * + * These settings are very conservative. If a lower power oscillator is + * required, change CKOPT to '1' (UNPROGRAMMED). * - * New Version that supports CLOCK board Version 1.0 + * M103C = [ ] + * WDTON = [ ] + * OCDEN = [ ] + * JTAGEN = [X] + * SPIEN = [X] + * EESAVE = [ ] + * BOOTSZ = 4096W_F000 + * BOOTRST = [ ] + * CKOPT = [X] + * BODLEVEL = 2V7 + * BODEN = [ ] + * SUT_CKSEL = EXTHIFXTALRES_16KCK_64MS * - * Author: Michael@Cheponis.Com with code borrowed liberally from - * previous AVR projects + * EXTENDED = 0xFF (valid) + * HIGH = 0x89 (valid) + * LOW = 0xFF (valid) * */ -/* - * 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 <stdint.h> +#include <stdbool.h> #include <avr/io.h> #include <avr/interrupt.h> - #ifdef On #undef On #endif #ifdef Off -#undef OFf +#undef Off #endif -#define Off (0) -#define On (!Off) - +#define Off (0) +#define On (!Off) -// Important for the Serial Port, not used at the moment -#define FOSC (7372800) -#define BAUD (115200) +#ifdef FALSE +#undef FALSE +#endif -#define MYUBRR FOSC/16/BAUD-1 +#ifdef TRUE +#undef TRUE +#endif +#define FALSE (0) +#define TRUE (!FALSE) -#define wait() for(u16 u=14000; u; u--) asm("nop"); +// Important for the Serial Port, not used at the moment +#define FOSC (7372800) +#define BAUD (115200) +#define MYUBRR FOSC/16/BAUD-1 -enum LEDs {Top,Middle,Bottom}; // Top is 0, Mid is 1, and Bottom is 2 +#define wait() for(uint16_t u=14000; u; u--) asm("nop"); -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; - -} +#define CLK (PA0) // Shift by 0 bits +#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) +// Top is 0, Mid is 1, and Bottom is 2 +enum LEDs {Top, Middle, Bottom}; enum TI_Input_10_MHz {Primary_GPS, Secondary_Ext}; -void setup_TI_CDCE18005(enum TI_Input_10_MHz); - - +enum Levels {Lo, Hi}; +void led(enum LEDs which, int turn_it_on) { -/***************************************************************************************** + // selects the proper bit + uint8_t LED = 0x20 << which; - SPI routines + if(turn_it_on) + PORTC |= LED; + else + PORTC &= ~LED; +} -******************************************************************************************/ +/******************************************************************************* +* SPI routines +*******************************************************************************/ +/* All macros evaluate to compile-time constants */ -/* 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 + * (avoids problems with leading zeros) + * 8-bit constants max value 0x11111111, always fits in unsigned long */ - #define HEX__(n) 0x##n##LU - +#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)) - +#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) + +/* for up to 8-bit binary constants */ +#define Bits_8(d) ((unsigned char)B8__(HEX__(d))) + +/* for up to 16-bit binary constants, MSB first */ +#define Bits_16(dmsb,dlsb) (((unsigned short)Bits_8(dmsb)<<8) \ + + Bits_8(dlsb)) + +/* for up to 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 + * 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 +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. These are +// from the "Special Settings" on Page 35 of the 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 +uint32_t 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 + 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 +// For the External 10 MHz input LVDS with external termination, +// Effectively DC coupled +uint32_t 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,1,10011011), // Reg 5, Failsafe OFF b5.11 = 0 + 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 +// 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(uint32_t); -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. +void set_bit(uint8_t bit_number, enum Levels bit_value) { - -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); + if(bit_value == Hi) + PORTA |= 1<<bit_number; + else + PORTA &= ~ (1<<bit_number); } +bool get_bit(uint8_t bit_number) { + asm("nop"); -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 + uint8_t portA = PINA; + return (portA & 1<< bit_number) > 0 ? TRUE : FALSE; } - -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. +// Don't worry about reading any bits back at this time +void send_SPI(uint32_t bits) { + + // 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 + + // Send each bit, LSB first, add a bit of delay before the clock, and then + // toggle the clock line. + for (uint8_t i=0; i<32; i++) { + set_bit(MOSI, ((bits & (1UL<<i)) ? Hi : Lo) ); + asm("nop"); + set_bit(CLK, Hi); + set_bit(CLK, Lo); + } -/* -#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); + // OK, transaction is over + set_bit(CE_, Hi); } -// 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. -// +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(); + // Out of Power Down state + set_bit(PD_, Hi); + wait(); + set_bit(SYNC_, Lo); + wait(); + set_bit(SYNC_, Hi); - - - -// 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 + wait(); } -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(); -}; +void setup_TI_CDCE18005(enum TI_Input_10_MHz which_input) { + // Send the table of data to init the clock distribution chip. Uses SPI. + uint32_t temp; + + if(which_input == Primary_GPS) { + for(uint8_t i=0; i<table_size; i++){ + temp = table_Pri_Ref[i]<<4; + temp |= i; + 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(uint8_t i=0; i<table_size; i++){ + temp = table_Sec_Ref[i]<<4; + temp |= i; + // Make sure the register's address is in the LSBs + send_SPI(temp); + } + } +} +uint32_t receive_SPI() { + uint32_t bits = 0; -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 + 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 each bit we are receiving, prep, clock in the bit LSB first + for (uint8_t i=0; i<32; i++){ + bits >>= 1; + set_bit(CLK, Hi); + if( get_bit(MISO) ) bits |= 0x80000000; + set_bit(CLK, Lo); + } + // OK, transaction is over + set_bit(CE_, Hi); + // Ditch the lower 4 bits, which only contain the address + return (uint32_t)(bits >> 4); +} +uint32_t get_TI_CDCE18005(enum CDCE18005 which_register){ + uint32_t get_reg_value = 0; + get_reg_value = (0xf0 & which_register << 4) | Read_Command; + // This tells the TI chip to send us the reg. value requested + send_SPI(get_reg_value); + return receive_SPI(); +} +bool check_TI_CDCE18005(enum TI_Input_10_MHz which_input, + enum CDCE18005 which_register) { + if(which_input == Primary_GPS){ + uint32_t read_value = get_TI_CDCE18005(which_register); + return read_value == table_Pri_Ref[which_register]; + } else { + uint32_t read_value = get_TI_CDCE18005(which_register); + return read_value == table_Sec_Ref[which_register]; + } +} -void -Setup_Atmel_IO_Ports(){ - - -///////////////////////////////////////////////////////////////////////////// +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 + * 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 +// /pd_cdcd, /sync_code, /ce need to be 1 (disabled) to start +// all bits are outputs, except PA7 (gps_lock) and PA3 (MISO_CDCE) are inputs +PORTA = Bits_8(00110010); +DDRA = 1<<DDA6 | 1<<DDA5 | 1<<DDA4 | 1<<DDA2 | 1<<DDA1 | 1<<DDA0; - - -///////////////////////////////////////////////////////////////////////////// /* * Port B * - *pin# Sig Our Functional Name + * 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 + * 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 +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 + * 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" + * 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 - +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 + * 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 + * 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<<DDD3; - +PORTD = 0; // Initial Value is all zeros +DDRD = 1<<DDD3; -///////////////////////////////////////////////////////////////////////////// /* * Port E * - *pin# Sig Dir Our Functional Name + * 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 + * 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 + * 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. + * 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 + * pin Sig Dir Our Functional Name * - * 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 + * 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 - +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 - +setup_TI_CDCE18005(Primary_GPS); // 10 MHz from Internal Source +led(Top,On); +PORTA |= (1<<PA6); // PPS from Internal source } -///////////////////////////////////////////////////////////////////////////// +// NOT PRESENT unless proven so... +bool Global_GPS_Present = (bool)FALSE; +bool Global_Ext_Ref_Is_Present = (bool)FALSE; -//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 LEDs_Off(){ + led(Top,Off); + led(Middle,Off); + led(Bottom,Off); } +void Force_Internal(){ + led(Top,On); + led(Middle,Off); + led(Bottom,On); -void -Force_Internal(){ - // led(Middle,On); - led(Top,On); - led(Middle,Off); - led(Bottom,On); + setup_TI_CDCE18005(Primary_GPS); - 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 + // Set PPS to Primary (1) n.b.: "1" in general means "Internal" for all + // such signals + PORTA |= (1<<PA6); } +void Force_External(){ + led(Top, Off); + led(Middle, On); + led(Bottom, On); -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 - + setup_TI_CDCE18005(Secondary_Ext); - Global_Ext_Ref_Detect_Counter++ ; // We reset this elsewhere - - asm("sei"); // Global Interrupt Enable + // Set PPS to External + PORTA &= ~(1<<PA6); } +void Prefer_Internal(){ -// 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 + if(Global_GPS_Present) + Force_Internal(); + else if(Global_Ext_Ref_Is_Present) + Force_External(); + else + LEDs_Off(); } -//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - - -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... +void Prefer_External(){ + // 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(); } -#endif +bool Check_What_Is_Present(){ + // See if +5 scaled to 3.3 from GPSDO is there + Global_GPS_Present = (PIND & (1<<DDD4)) != 0; + volatile uint8_t portE = PINE; + volatile uint8_t prev, now; -bool -Check_What_Is_Present(){ + // Get PREVIOUS state of the input + prev = ( portE & (1 << DDE7) ? 1 : 0); - Global_GPS_Present = (PIND & (1<<DDD4)) != 0; // See if +5 scaled to 3.3 from GPSDO is there + for(uint16_t c=1; c; c++){ + portE = PINE; + now = ( portE & (1 << DDE7) ? 1 : 0); + if(prev != now){ + Global_Ext_Ref_Is_Present = (bool)TRUE; - 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; + 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; +bool get_Switch_State(){ + uint8_t portC = PINC; - // return (bool)(portC & (1<<DDC1) ? On : Off); - return (bool)(portC & (1<<DDC1) ? Off : On); // UP is prefer internal, - // DOWN is prefer external + // UP is prefer internal, + // DOWN is prefer external + return (bool)(portC & (1<<DDC1) ? Off : On); } - -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// -// 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 */ +/******************************************************************************* +* Main Routine +*******************************************************************************/ + +int main(void){ + + bool Old_Switch_State, Current_Switch_State, Old_Global_Ext_Ref_Is_Present = FALSE; + + // Global Interrupt Disable --- enable with SEI if desired later + asm("cli"); + + Setup_Atmel_IO_Ports(); + + /* + * 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) { + // Set "Global_Ext_Ref_Is_Present" and "Global_GPS_Present" + Check_What_Is_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(); + } + } +} |