--- /dev/null
+/******************************************************************************
+ *
+ * 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 :-)
+ */
+
+
+