trennfix/sw: Single timer version
[eisenbahn.git] / trennfix / sw / mm_switch.c
diff --git a/trennfix/sw/mm_switch.c b/trennfix/sw/mm_switch.c
new file mode 100644 (file)
index 0000000..74939bf
--- /dev/null
@@ -0,0 +1,320 @@
+/******************************************************************************
+ *
+ *  Trennfix firmware -  mm_switch.c
+ *
+ *  Maerklin Motorola switch command receiver
+ *
+ *  Copyright (C) 2017 Philipp Hachtmann
+ *
+ *  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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <avr/io.h>
+#include <avr/eeprom.h>
+#include <avr/interrupt.h>
+#include <avr/pgmspace.h>
+
+#include <util/delay.h>
+#include <stdint.h>
+
+#include "hardware.h"
+#include "mm_switch.h"
+
+ #define DEBUG_TIMEOUT
+#ifdef DEBUG_TIMEOUT
+#define MON(val) setpin(PIN_DRIVE, val)
+#else
+#define MON(xx) ({0;})
+#endif
+
+
+/*
+ * Lookup decoder number.
+ *
+ */
+static uint8_t lookup_decoder(uint8_t mm_byte)
+{
+       switch(mm_byte) {
+               /* 0x00 is invalid */
+       case 0xc0: return 1;
+       case 0x80: return 2;
+        case 0x30: return 3;
+        case 0xf0: return 4;
+        case 0xb0: return 5;
+        case 0x20: return 6;
+        case 0xe0: return 7;
+        case 0xa0: return 8;
+
+        case 0x0c: return 9;
+       case 0xcc: return 10;
+       case 0x8c: return 11;
+        case 0x3c: return 12;
+        case 0xfc: return 13;
+        case 0xbc: return 14;
+        case 0x2c: return 15;
+        case 0xec: return 16;
+        case 0xac: return 17;
+
+        case 0x08: return 18;
+       case 0xc8: return 19;
+       case 0x88: return 20;
+        case 0x38: return 21;
+        case 0xf8: return 22;
+        case 0xb8: return 23;
+        case 0x28: return 24;
+        case 0xe8: return 25;
+       
+       default:
+               return 0;
+       }
+}
+
+static uint8_t lookup_command(uint8_t mm_byte)
+{
+       switch(mm_byte) {
+       case 0xc3: return 1;
+       case 0x03: return 2;
+       case 0xf3: return 3;
+       case 0x33: return 4;
+       case 0xcf: return 5;
+       case 0x0f: return 6;
+       case 0xff: return 7;
+       case 0x3f: return 8;
+       default:
+               return 0;
+        }
+}
+
+static void mm_switch_init(void)
+{
+       /* Get rid of the 1/8 clock prescaler */
+       CLKPR = (1 << CLKPCE);
+       CLKPR = 0;
+               
+       GIMSK |= _BV(PCIE); /* Enable pin change interrupt */
+       PCMSK = _BV(PCINT4);  /* PB4, Rail sense input */
+
+        TCCR0A = 0; /* This is normal mode */
+       TCCR0B = 0; /* Timer off */
+       TIMSK |= _BV(OCIE0A); /* Get a match interrupt */
+       TIMSK |= _BV(TOV0);   /* Overflow interrupt */
+       
+       /* We need 13 + 45 us delay, That's 464 clocks @8MHz*/
+       OCR0A = 91; /* Prescaler 8 is used */
+
+       /* Timer 1 for timeout */
+       /* We set it to 1024us by prescaler 64 and running full 256 */
+       TCCR1 = 7;
+       TIMSK |= _BV(TOIE1); /* Overflow interrupt */
+}
+
+
+static volatile uint8_t shift_sub;
+static volatile uint8_t shift_zero;
+static volatile uint8_t shift_decoder;
+static volatile uint8_t shift_sub_first;
+static volatile uint8_t shift_decoder_first;
+
+/* We will shift from right to left.
+ * XXXXXXXX        XX          XXXXXXXX
+ * shift_decoder   shift_zero  shift_sub
+ *
+ * The bits 7 downto 2 of shift_zero are ignored.
+ */
+
+volatile uint8_t bitno = 0;
+
+static void trigger(void)
+{
+       setpin(PIN_DRIVE, 1);
+       _delay_us(10);
+       setpin(PIN_DRIVE, 0);
+}
+
+enum recstate {
+       IDLE = 0,
+       FIRST_FAST_SAMPLE,
+       FIRST_SLOW_SAMPLE, /* If clock arrives, we stay on the fast path! */
+       FAST_SAMPLE,
+       FAST_WAIT_FOR_CLOCK,
+       SLOW_SAMPLE,
+       SLOW_WAIT_FOR_CLOCK,
+};
+
+static enum recstate recstate = IDLE;
+
+static inline void shift(uint8_t value)
+{
+       shift_decoder <<= 1;
+       if (shift_zero & 2)
+               shift_decoder |= 1;
+       shift_zero <<= 1;
+       if (shift_sub & 0x80)
+               shift_zero |= 1;
+       shift_sub <<= 1;
+       if (value)
+               shift_sub |= 1;
+}
+
+
+ISR(TIMER0_COMPA_vect) {
+
+       static uint8_t patience = 0;
+
+       switch(recstate) {
+       case FIRST_FAST_SAMPLE:
+               recstate = FIRST_SLOW_SAMPLE;
+               TSTART_CLK_TO_SAMPLE_FAST; /* Will not run out in fast! */
+               break;
+
+       case FIRST_SLOW_SAMPLE:
+               bitno = 0;
+
+       case SLOW_SAMPLE:
+               recstate = SLOW_WAIT_FOR_CLOCK;
+               TSTART_CLK_TO_SAMPLE_SLOW;
+               break;
+       
+       case FAST_SAMPLE:
+               recstate = FAST_WAIT_FOR_CLOCK;
+               TSTART_CLK_TO_SAMPLE_FAST;
+               break;
+
+       case FAST_WAIT_FOR_CLOCK: /* A timeout! */
+               if (patience) {
+                       patience--;
+                       TSTART_CLK_TO_SAMPLE_FAST;
+                       return;
+               }
+               recstate = IDLE;
+               TSTOP;
+               return;
+
+       case SLOW_WAIT_FOR_CLOCK:
+               if (patience) {
+                       patience--;
+                       TSTART_CLK_TO_SAMPLE_SLOW;
+                       return;
+               }
+               TSTOP;
+               recstate = IDLE;
+               return;
+       }
+       
+       shift(MM_SENSE);
+       bitno++;
+       
+       if (bitno == 18) { /* Save first received word */
+               shift_sub_first = shift_sub;
+               shift_decoder_first = shift_decoder;
+               patience = 18;
+
+       } 
+       if (bitno == 36) {
+               if ((shift_sub == shift_sub_first) &&
+                   (shift_decoder == shift_decoder_first)) {
+                       uint8_t decoder = lookup_decoder(shift_decoder);
+                       uint8_t command = lookup_command(shift_sub);
+                       if (recstate == SLOW_WAIT_FOR_CLOCK)
+                               trigger();
+                       if (decoder) {
+                               /* Congratulations, we have a valid command */
+                               if (recstate == FAST_WAIT_FOR_CLOCK) {
+                                       //      trigger();
+                                       mm_switch_command(decoder, command);
+                               }
+                       }
+               }
+       }
+}
+
+ISR(BADISR_vect)
+{
+       while(1) {
+               /*
+               setpin(PIN_LED, 1);
+               _delay_ms(30);
+               setpin(PIN_LED, 0);
+               _delay_ms(30);
+               setpin(PIN_LED, 1);
+               _delay_ms(30);
+               setpin(PIN_LED, 0);
+               _delay_ms(2000);
+               */
+       }
+}
+
+ISR(TIM0_OVF_vect)
+{
+       return;
+       while(1) {
+               setpin(PIN_LED, 1);
+               _delay_ms(30);
+               setpin(PIN_LED, 0);
+               _delay_ms(300);
+       }
+       
+}
+
+
+/* Pin change interrupt vector */
+void mm_pinchange_handler(void)
+{
+       static uint8_t sense_last;
+
+       if (MM_SENSE == sense_last)
+               return;
+       sense_last = MM_SENSE;
+       if (!sense_last)
+               return;
+       
+       switch(recstate) {
+       case IDLE:
+               bitno = 0;
+               recstate = FIRST_FAST_SAMPLE;
+               TSTART_CLK_TO_SAMPLE_FAST;
+               break;
+       case FIRST_SLOW_SAMPLE:
+               recstate = FAST_SAMPLE;
+               TSTART_CLK_TO_SAMPLE_FAST;
+               break;
+       case FAST_WAIT_FOR_CLOCK:
+               recstate = FAST_SAMPLE;
+               TSTART_CLK_TO_SAMPLE_FAST;
+               break;
+       case SLOW_WAIT_FOR_CLOCK:
+               recstate = SLOW_SAMPLE;
+               TSTART_CLK_TO_SAMPLE_SLOW;
+               break;
+
+               /* Not expected */
+       case FIRST_FAST_SAMPLE:
+       case FAST_SAMPLE:
+       case SLOW_SAMPLE:
+       default:
+               break;
+       }
+}
+
+/******************************************************************************
+ *                        The end :-)
+ */
+
+
+