aboutsummaryrefslogtreecommitdiffstats
path: root/firmware/octoclock/lib/state.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/octoclock/lib/state.c')
-rw-r--r--firmware/octoclock/lib/state.c124
1 files changed, 124 insertions, 0 deletions
diff --git a/firmware/octoclock/lib/state.c b/firmware/octoclock/lib/state.c
new file mode 100644
index 000000000..0dbcc6ece
--- /dev/null
+++ b/firmware/octoclock/lib/state.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2014 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/>.
+ */
+
+#include <avr/interrupt.h>
+#include <avr/io.h>
+
+#include <debug.h>
+#include <octoclock.h>
+#include <clkdist.h>
+#include <state.h>
+
+void led(LEDs which, int turn_it_on) {
+
+ // selects the proper bit
+ uint8_t LED = 0x20 << which;
+
+ if(turn_it_on)
+ PORTC |= LED;
+ else
+ PORTC &= ~LED;
+}
+
+void LEDs_Off(void){
+ led(Top,false);
+ led(Middle,false);
+ led(Bottom,false);
+}
+
+void force_internal(void){
+ led(Top,true);
+ led(Middle,false);
+ led(Bottom,true);
+
+ setup_TI_CDCE18005(Primary_GPS);
+
+ // Set PPS to Primary (1) n.b.: "1" in general means "Internal" for all
+ // such signals
+ PORTA |= (1<<PA6);
+}
+
+void force_external(void){
+ led(Top, false);
+ led(Middle, true);
+ led(Bottom, true);
+
+ setup_TI_CDCE18005(Secondary_Ext);
+
+ // Set PPS to External
+ PORTA &= ~(1<<PA6);
+}
+
+void prefer_internal(void){
+ // if internal is NOT OK, then force external
+ if(global_gps_present)
+ force_internal();
+ else if(global_ext_ref_is_present)
+ force_external();
+ else
+ LEDs_Off();
+}
+
+void prefer_external(void){
+ // 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();
+}
+
+static uint8_t prev_PE7 = 0;
+static uint32_t timer0_num_overflows = 0;
+
+ISR(TIMER0_OVF_vect){
+ global_gps_present = (PIND & (1<<DDD4));
+
+ // Every ~1/10 second
+ if(!(timer0_num_overflows % 610)){
+ prev_PE7 = (PINE & (1<<DDE7));
+
+ if(get_switch_pos() == UP) prefer_internal();
+ else prefer_external();
+
+ global_ext_ref_is_present = false;
+ }
+
+ if(!global_ext_ref_is_present){
+ global_ext_ref_is_present = (prev_PE7 != (PINE & (1<<DDE7)));
+ }
+
+ timer0_num_overflows++;
+}
+
+ref_t which_ref(void){
+ if(!global_gps_present && !global_ext_ref_is_present) global_which_ref = NO_REF;
+ else if(global_gps_present && !global_ext_ref_is_present) global_which_ref = INTERNAL;
+ else if(!global_gps_present && global_ext_ref_is_present) global_which_ref = EXTERNAL;
+ else global_which_ref = (get_switch_pos() == UP) ? INTERNAL : EXTERNAL;
+
+ return global_which_ref;
+}
+
+switch_pos_t get_switch_pos(void){
+ uint8_t portC = PINC;
+
+ // UP is prefer internal,
+ // DOWN is prefer external
+ return (portC & (1<<DDC1)) ? DOWN : UP;
+}