static uint8_t shift_function;
static uint8_t shift_address;
static enum mm_recstate recstate = MM_IDLE;
+static uint8_t sense_last = 23;
+uint8_t time_h = 0;
+uint8_t time_l = 0;
+uint8_t bit_val = 23;
#endif
};
#define lookup_nibble(nibble) nibble_table[nibble & 0xf]
-static uint8_t lookup_decoder(uint8_t mm_byte)
+static uint8_t __attribute__((unused)) lookup_decoder(uint8_t mm_byte)
{
uint8_t low;
uint8_t high;
return 9 * high + low;
}
-static uint8_t lookup_command(uint8_t mm_command)
+static uint8_t __attribute__((unused)) lookup_command(uint8_t mm_command)
{
uint8_t res;
/*
return res;
}
-
/* We will shift from right to left.
* XXXXXXXX XX XXXXXXXX
* shift_address shift_function shift_command
static volatile uint8_t mm_rec_tolerated_timeouts;
+#define MM_SLOW 0
+#define MM_FAST 1
-ISR(MM_TIMER_INT_VECT) {
+static uint8_t style;
+void mm_feed_bit(uint8_t bit, uint8_t seen_style)
+{
static volatile uint8_t shift_command_first;
static volatile uint8_t shift_function_first;
static volatile uint8_t shift_address_first;
static uint8_t function_last = 0xff;
static uint8_t command_last = 0xff;
#endif
- MM_TSTART;
-
- switch(recstate) {
- case MM_FIRST_FAST_SAMPLE:
- recstate = MM_FIRST_SLOW_SAMPLE;
- break;
-
- case MM_FIRST_SLOW_SAMPLE:
- bitno = 0;
-
- case MM_SLOW_SAMPLE:
- recstate = MM_SLOW_WAIT_FOR_CLOCK_DELAY;
- break;
-
- case MM_SLOW_WAIT_FOR_CLOCK_DELAY:
- recstate = MM_SLOW_WAIT_FOR_CLOCK;
- return;
-
- case MM_FAST_SAMPLE:
- recstate = MM_FAST_WAIT_FOR_CLOCK;
- break;
-
- case MM_FAST_WAIT_FOR_CLOCK: /* A timeout! */
- if (mm_rec_tolerated_timeouts)
- mm_rec_tolerated_timeouts--;
- else
- recstate = MM_IDLE;
- return;
-
- case MM_SLOW_SAMPLE_DELAY:
- recstate = MM_SLOW_SAMPLE;
- return;
-
- case MM_SLOW_WAIT_FOR_CLOCK:
- if (mm_rec_tolerated_timeouts) {
- mm_rec_tolerated_timeouts--;
- recstate = MM_SLOW_WAIT_FOR_CLOCK_DELAY;
+
+ if (bitno == 0)
+ style = seen_style;
+ else
+ if (seen_style != style) {
+ bitno = 0;
return;
}
- default:
- recstate = MM_IDLE;
- case MM_IDLE:
- return;
- }
-
- shift(MM_SENSE);
+
+ shift(bit);
bitno++;
-
if (bitno == 18) { /* Save first received word */
shift_address_first = shift_address;
shift_function_first = shift_function;
shift_command_first = shift_command;
mm_rec_tolerated_timeouts = 18;
-
}
if (bitno == 36) {
+
if ((shift_command == shift_command_first) &&
(shift_address == shift_address_first) &&
(shift_function == shift_function_first)) {
if ((shift_address != address_last) || (shift_command != command_last) ||
shift_function != function_last) {
#endif
- address = lookup_decoder(shift_address);
- if (recstate == MM_SLOW_WAIT_FOR_CLOCK_DELAY) {
+ if (style == MM_SLOW) {
+ address = lookup_decoder(shift_address);
mm_switch_drive(address, shift_function, shift_command);
- } else if (recstate == MM_FAST_WAIT_FOR_CLOCK) {
+ } else {
+ trigger();
+
command = lookup_command(shift_command);
mm_switch_command(address, command);
- }
- recstate = MM_IDLE;
+ }
+ bitno = 0;
#ifdef MM_FILTER_REPEATED
}
address_last = shift_address;
}
}
-//void __attribute((weak)) mm_switch_drive(uint8_t address, uint8_t function, uint8_t command);
+ISR(__vector_timer_extra) {
+ //trigger();
+}
-ISR(BADISR_vect)
+/*
+ * The timeout interrupt vector does nothing else
+ * than incrementing the time_h round counter.
+ *
+ * It is written in naked assembly because we want to avoid pushing
+ * and popping of all upper registers.
+ */
+void __attribute__((naked)) MM_TIMER_INT_VECT(void)
{
- 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);
- */
- }
+ asm("push r0 ; save r0 \n\t"
+ "in r0, __SREG__ \n\t"
+#ifdef MM_USE_REGISTER_VARS
+ "inc %[th] \n\t"
+#else
+ "push r1 \n\t"
+ "lds r1, time_h \n\t"
+ "inc r1 \n\t"
+ "sts time_h, r1 \n\t"
+ "pop r1 \n\t"
+#endif
+ "out __SREG__, r0 \n\t"
+ "pop r0 \n\t"
+ // "rjmp __vector_timer_extra \n\t"
+ "reti \n\t"
+#ifdef MM_USE_REGISTER_VARS
+ :: [th] "r" (time_h)
+#endif
+ );
}
-ISR(TIM0_OVF_vect)
+/*
+ * Another naked interrupt trampoline
+ *
+ * Here we first save the timer value as fast as possible, then we jump (!)
+ * into the "official" interrupt handler with all its decorations.
+ */
+void __attribute__((naked)) PCINT0_vect(void)
{
- return;
- while(1) {
- setpin(PIN_LED, 1);
- _delay_ms(30);
- setpin(PIN_LED, 0);
- _delay_ms(300);
+#ifdef MM_USE_REGISTER_VARS
+ asm("in %[tl], %[tmr] \n\t"
+ "rjmp __vector_pinchange \n\t"
+ :: [tl] "r" (time_l), [tmr] "I" (_SFR_IO_ADDR(TCNT0))
+ );
+#else
+ asm("push r0 \n\t"
+ "in r0, %[tmr] \n\t"
+ "sts time_l, r0 \n\t"
+ "pop r0 \n\t"
+ "rjmp __vector_pinchange \n\t"
+ :: [tmr] "I" (_SFR_IO_ADDR(TCNT0))
+ );
+#endif
+}
+
+/* Pin change interrupt vector, here we have a bit more time */
+ISR(__vector_pinchange){
+ /* First kill off that timer */
+
+ MM_TSTART; /* Restart timer */
+
+ /* Account for not yet handled timer overflow */
+ if (TIFR & _BV(TOV0)) {
+ time_h++;
+ TIFR |= _BV(TOV0);
}
-}
+ bit_val = !MM_SENSE;
+
+ uint16_t duration = time_h << 8;
+ duration += time_l;
+ time_h = 0;
+
+ /*
+ * The nominal length of a bit cycle is 208 us.
+ * This consists of 8 parts, each 26 us:
+ * 1 d d d d d d 0
+ *
+ * That means that the 1 pulse is 7 * 26 = 182us
+ * and the short pulse is 1 * 26 = 26us.
+ *
+ * Reality seems to look not as that exact. I measure
+ * 26us for the clock pulse, but 196 for the long pulse.
+ *
+ */
+
+#define D_MATCH(d, v) ((duration > (d * 2 - 2 * v)) && (duration < (d * 2 + 2 * v)))
+
+ static uint8_t mm_positive = 1;
+ static uint8_t last_was_short = 0;
-uint8_t register sense_last asm("r9");
+ if (D_MATCH(26, 4)) {
+ if (last_was_short)
+ mm_positive = bit_val;
+ last_was_short = 1;
+ if (bit_val == mm_positive)
+ mm_feed_bit(0, MM_SLOW);
+ } else {
+ last_was_short = 0;
+ }
-/* Pin change interrupt vector */
-ISR(PCINT0_vect){
- static uint8_t sense_last;
- if (MM_SENSE && !sense_last) {
- MM_TSTART;
- switch(recstate) {
- case MM_IDLE:
+ if (style == MM_SLOW) {
+ if (duration > 4000) { /* Maerklin inter package timeout 2ms */
bitno = 0;
- recstate = MM_FIRST_FAST_SAMPLE;
- break;
-
- case MM_FIRST_SLOW_SAMPLE:
- recstate = MM_FAST_SAMPLE;
- break;
-
- case MM_FAST_WAIT_FOR_CLOCK:
- recstate = MM_FAST_SAMPLE;
- mm_rec_tolerated_timeouts = 0;
- break;
-
- case MM_SLOW_WAIT_FOR_CLOCK_DELAY: /* If clock comes early */
- recstate = MM_SLOW_SAMPLE_DELAY;
- break;
-
- case MM_SLOW_WAIT_FOR_CLOCK:
- recstate = MM_SLOW_SAMPLE_DELAY;
- mm_rec_tolerated_timeouts = 0;
- break;
-
- case MM_SLOW_SAMPLE_DELAY:
- recstate = MM_SLOW_SAMPLE;
- break;
-
- /* Not expected */
- case MM_FIRST_FAST_SAMPLE:
- case MM_FAST_SAMPLE:
- case MM_SLOW_SAMPLE:
- recstate = MM_IDLE;
- default:
- break;
+ goto done;
+ }
+ } else {
+ if (duration > 2000) { /* Maerklin inter package timeout 1ms */
+ bitno = 0;
+ goto done;
}
}
- sense_last = MM_SENSE;
+
+ if (bit_val != mm_positive)
+ goto done;
+
+ if (D_MATCH(182, 25)) mm_feed_bit(1, MM_SLOW);
+
+ if (D_MATCH(91, 17)) {mm_feed_bit(1, MM_FAST);}
+ if (D_MATCH(13, 4)) {mm_feed_bit(0, MM_FAST);}
+
+ done:
mm_switch_pinchange_callback();
}
}
+
/******************************************************************************
* The end :-)
*/
-
-
-