trennfix/sw: More versatile file structure
[eisenbahn.git] / trennfix / sw / mm / src / mm_switch.c
diff --git a/trennfix/sw/mm/src/mm_switch.c b/trennfix/sw/mm/src/mm_switch.c
new file mode 100644 (file)
index 0000000..2b09e5c
--- /dev/null
@@ -0,0 +1,324 @@
+/******************************************************************************
+ *
+ *  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 <config/hardware.h>
+#include <mm/mm_switch.h>
+
+/*
+ *    Private data types
+ */
+
+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,
+};
+
+/*
+ *
+ * Check for stuff we need
+ *
+ */
+#if !defined(MM_TSTART_FAST) || !defined(MM_TSTART_SLOW) || !defined(MM_TSTOP) \
+       || !defined(MM_SENSE) || !defined(MM_TIMER_INT_VECT)
+
+#error Missing timer start macro MM_TSTART_FAST!
+
+#endif
+
+/*
+ *    Private global variables
+ */
+static volatile uint8_t shift_command;
+static volatile uint8_t shift_function;
+static volatile uint8_t shift_address;
+static enum recstate recstate = IDLE;
+volatile uint8_t bitno = 0;
+
+
+/*
+ * Lookup trinary nibble
+ *
+ * This was implemented using a switch statement before.
+ * Changing the lookup to a table did only add two bytes
+ * of memory and saved ca. 50 bytes program memory.
+ */
+static const uint8_t nibble_table[16]={
+       [0x0] = 0,
+       [0xc] = 1,
+       [0x8] = 2,
+       [0x3] = 3,
+       [0xf] = 4,
+       [0xb] = 5,
+       [0x2] = 6,
+       [0xe] = 7,
+       [0xa] = 8
+};
+#define lookup_nibble(nibble) nibble_table[nibble & 0xf]
+static uint8_t lookup_decoder(uint8_t mm_byte)
+{
+       uint8_t low;
+       uint8_t high;
+       if (mm_byte == 0)
+               return 80;
+       low = lookup_nibble(mm_byte >> 4);
+       high = lookup_nibble(mm_byte & 0xf);
+       if (!low)
+               return 0;
+       return 9 * high + low;
+}
+
+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;
+        }
+}
+
+/* We will shift from right to left.
+ * XXXXXXXX        XX          XXXXXXXX
+ * shift_address   shift_function  shift_command
+ *
+ * The bits 7 downto 2 of shift_function are ignored.
+ */
+
+static inline void shift(uint8_t value)
+{
+       shift_address <<= 1;
+       if (shift_function & 2)
+               shift_address |= 1;
+       shift_function <<= 1;
+       if (shift_command & 0x80)
+               shift_function |= 1;
+       shift_command <<= 1;
+       if (value)
+               shift_command |= 1;
+}
+
+ISR(MM_TIMER_INT_VECT) {
+       static uint8_t tolerated_timeouts = 0;
+
+       static volatile uint8_t shift_command_first;
+       static volatile uint8_t shift_function_first;
+       static volatile uint8_t shift_address_first;
+       uint8_t address;
+       uint8_t command;
+
+#ifdef MM_FILTER_REPEATED
+       static uint8_t address_last = 0xff;
+       static uint8_t function_last = 0xff;
+       static uint8_t command_last = 0xff;
+#endif
+
+       switch(recstate) {
+       case FIRST_FAST_SAMPLE:
+               recstate = FIRST_SLOW_SAMPLE;
+               MM_TSTART_FAST; /* Will not run out in fast! */
+               break;
+
+       case FIRST_SLOW_SAMPLE:
+               bitno = 0;
+
+       case SLOW_SAMPLE:
+               recstate = SLOW_WAIT_FOR_CLOCK;
+               MM_TSTART_SLOW;
+               break;
+       
+       case FAST_SAMPLE:
+               recstate = FAST_WAIT_FOR_CLOCK;
+               MM_TSTART_FAST;
+               break;
+
+       case FAST_WAIT_FOR_CLOCK: /* A timeout! */
+               if (tolerated_timeouts) {
+                       tolerated_timeouts--;
+                       MM_TSTART_FAST;
+                       return;
+               }
+               recstate = IDLE;
+               MM_TSTOP;
+               return;
+
+       case SLOW_WAIT_FOR_CLOCK:
+               if (tolerated_timeouts) {
+                       tolerated_timeouts--;
+                       MM_TSTART_SLOW;
+                       return;
+               }
+       default:
+               MM_TSTOP;
+               recstate = IDLE;
+               return;
+       }
+       
+       shift(MM_SENSE);
+       bitno++;
+
+       if (bitno == 18) { /* Save first received word */
+               shift_address_first  = shift_address;
+               shift_function_first = shift_function;
+               shift_command_first  = shift_command;
+
+               tolerated_timeouts = 18;
+       } 
+
+       if (bitno == 36) {
+               if ((shift_command == shift_command_first) &&
+                   (shift_address == shift_address_first) &&
+                   (shift_function == shift_function_first)) {
+
+#ifdef MM_FILTER_REPEATED
+                       if ((shift_address != address_last) || (shift_command != command_last) ||
+                           shift_function != function_last) {
+#endif
+                               address = lookup_decoder(shift_address);
+                               
+                               if (recstate == SLOW_WAIT_FOR_CLOCK) {
+                                       mm_switch_drive(address, shift_function, shift_command);
+                               } else if (recstate == FAST_WAIT_FOR_CLOCK) {
+                                       command = lookup_command(shift_command);
+                                       mm_switch_command(address, command);
+                               }
+#ifdef MM_FILTER_REPEATED
+                       }
+                       address_last   = shift_address;
+                       function_last  = shift_function;
+                       command_last   = shift_command;
+#endif
+               }
+               
+       }
+}
+
+//void __attribute((weak)) mm_switch_drive(uint8_t address, uint8_t function, uint8_t 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;
+               MM_TSTART_FAST;
+               break;
+       case FIRST_SLOW_SAMPLE:
+               recstate = FAST_SAMPLE;
+               MM_TSTART_FAST;
+               break;
+       case FAST_WAIT_FOR_CLOCK:
+               recstate = FAST_SAMPLE;
+               MM_TSTART_FAST;
+               break;
+       case SLOW_WAIT_FOR_CLOCK:
+               recstate = SLOW_SAMPLE;
+               MM_TSTART_SLOW;
+               break;
+
+               /* Not expected */
+       case FIRST_FAST_SAMPLE:
+       case FAST_SAMPLE:
+       case SLOW_SAMPLE:
+       default:
+               break;
+       }
+}
+
+void __attribute__((weak))mm_switch_drive(uint8_t decoder, uint8_t function,
+                                         uint8_t command)
+{
+       while(1);
+}
+
+void __attribute__((weak))mm_switch_command(uint8_t address, uint8_t command)
+{
+}
+
+
+/******************************************************************************
+ *                        The end :-)
+ */
+
+
+