--- /dev/null
+/******************************************************************************
+ *
+ * Trennfix firmware - main.c
+ *
+ * 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 "pin_magic.h"
+
+#define LED _PIN(PORTB, PORTB2)
+#define SENSE _PIN(PORTB, PORTB4)
+#define DRIVE _PIN(PORTB, PORTB3)
+#define BTN _PIN(PORTB, PB1)
+
+#define SIGIN (!PINVAL(SENSE))
+
+#define BTN_PRESSED (PINVAL(BTN))
+
+// #define DEBUG_TIMEOUT
+
+#ifdef DEBUG_TIMEOUT
+#define MON_TIMEOUT(val) setpin(DRIVE, val)
+#else
+#define MON_TIMEOUT(xx) ({0;})
+#endif
+
+
+
+/*
+ * Lookup decoder number.
+ *
+ */
+
+static uint8_t get_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 get_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 setup_hw(void)
+{
+ /* Get rid of the 1/8 clock prescaler */
+ CLKPR = (1 << CLKPCE);
+ CLKPR = 0;
+
+ INPUT_PIN(SENSE);
+ INPUT_PIN(BTN);
+ OUTPUT_PIN(DRIVE);
+ OUTPUT_PIN(LED);
+ setpin(BTN, 1); /* Need pullup */
+ _delay_ms(1); /* Let the line rise */
+
+
+ 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 uint8_t ctr = 0;
+
+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.
+ */
+
+#define STOP_TIMER0 (TCCR0B = 0)
+
+volatile uint8_t bitno = 0;
+
+
+static void zero(void)
+{
+ setpin(LED, 1);
+ _delay_ms(20);
+ setpin(LED, 0);
+ _delay_ms(980);
+ GIFR |= _BV(PCIF);
+}
+
+
+static void one(void)
+{
+ setpin(LED, 1);
+ _delay_ms(20);
+ setpin(LED, 0);
+ _delay_ms(160);
+ setpin(LED, 1);
+ _delay_ms(20);
+ setpin(LED, 0);
+ _delay_ms(800);
+ GIFR |= _BV(PCIF);
+}
+
+static void trigger(void)
+{
+ setpin(DRIVE, 1);
+ _delay_us(20);
+ setpin(DRIVE, 0);
+}
+
+static volatile uint8_t our_decoder = 3;
+static volatile uint8_t our_key = 5;
+static volatile uint8_t learn_mode = 0;
+
+ISR(TIMER0_COMPA_vect) {
+
+ STOP_TIMER0;
+
+ shift_decoder <<= 1;
+ if (shift_zero & 2)
+ shift_decoder |= 1;
+ shift_zero <<= 1;
+ if (shift_sub & 0x80)
+ shift_zero |= 1;
+ shift_sub <<= 1;
+ if (SIGIN)
+ shift_sub |= 1;
+
+ bitno++;
+
+ if (bitno == 18) { /* Save first received word */
+ shift_sub_first = shift_sub;
+ shift_decoder_first = shift_decoder;
+ } else if (bitno == 36) {
+
+ if ((shift_sub == shift_sub_first) &&
+ (shift_decoder == shift_decoder_first)) {
+ uint8_t decoder = get_decoder(shift_decoder);
+ uint8_t command = get_command(shift_sub);
+ if (decoder) {
+ /* Congratulations, we have a valid command */
+ trigger();
+
+ if (learn_mode) {
+ if (command) {
+ our_decoder = decoder;
+ our_key = command;
+ learn_mode = 0;
+ }
+ } else {
+
+ if (decoder == our_decoder) {
+ if (command == our_key)
+ setpin(LED, 1);
+ if (command == 0)
+ setpin(LED, 0);
+ }
+ }
+ }
+ }
+ }
+}
+
+#define START_TIMER0 ({TCNT0 = 0; TCCR0B = 2;})
+#define START_TIMER1 ({TCNT1 = 1; TCCR1 = 7; GTCCR |= 2;})
+#define STOP_TIMER1 ({TCCR1 = 0; TCNT1 = 1;})
+
+ISR(TIM1_OVF_vect)
+{
+ STOP_TIMER1;
+ // if (!SIGIN)
+ // setpin(LED, 1);
+ bitno = 0;
+ MON_TIMEOUT(1);
+}
+
+ISR(TIM0_OVF_vect)
+{
+ while(1) {
+ setpin(LED, 1);
+ _delay_ms(30);
+ setpin(LED, 1);
+ _delay_ms(30);
+ }
+
+}
+
+/* Shortest element seen 184 clocks */
+/* Officially 208 clocks */
+/* (1 + 7) * 208 clocks */
+
+/* Pin change interrupt vector */
+ISR(PCINT0_vect)
+{
+ START_TIMER1;
+ MON_TIMEOUT(0);
+
+ if (SIGIN) {
+ START_TIMER0;
+ }
+}
+
+
+
+/******************************************************************************
+ *
+ * main() - The main routine
+ *
+ */
+
+int main(void) {
+ setup_hw();
+ sei();
+ // while(PINVAL(BTN));
+
+ while (1) {
+ if (!learn_mode) {
+ if (!PINVAL(BTN))
+ learn_mode = 1;
+ } else {
+ setpin(LED, 1);
+ _delay_ms(30);
+ setpin(LED, 0);
+ _delay_ms(600);
+ }
+
+/* Main loop here */
+ }
+ return 0;
+}
+
+/******************************************************************************
+ * The end :-)
+ */