From: Philipp Hachtmann Date: Thu, 9 Feb 2017 12:51:30 +0000 (+0100) Subject: trennfix/sw: More versatile file structure X-Git-Url: http://gitweb.hachti.de/?p=eisenbahn.git;a=commitdiff_plain;h=93cb14d45dd54a188bef116c5884d6bd1e410677 trennfix/sw: More versatile file structure A working state commit. Sources have been reordered. Signed-off-by: Philipp Hachtmann --- diff --git a/trennfix/sw/Makefile b/trennfix/sw/Makefile index 66135de..5f3774f 100644 --- a/trennfix/sw/Makefile +++ b/trennfix/sw/Makefile @@ -27,13 +27,13 @@ # To rebuild project do "make clean" then "make all". #---------------------------------------------------------------------------- -HW?=default +CONFIG?=trennfix_attiny25 -CONFIG ?= config.make -include ${CONFIG} +include config/${CONFIG}.mk -#test: -# echo ${BOOT_PAGES} +INCLUDES+=-I mm/include -I include +SRC+= mm/src/mm_switch.c +CFLAGS+=-D__HW_CONF_HEADER__="" # MCU name #MCU ?= atmega328 @@ -51,22 +51,14 @@ FORMAT = ihex # Target file name (without extension). -TARGET = weiche1 +TARGET = trennfix # List C source files here. (C dependencies are automatically generated.) #SRC ?= main.c -# List Assembler source files here. -# Make them always end in a capital .S. Files ending in a lowercase .s -# will not be considered source files but generated files (assembler -# output from the compiler), and will be deleted upon "make clean"! -# Even though the DOS/Win* filesystem matches both .s and .S the same, -# it will preserve the spelling of the filenames, and gcc itself does -# care about how the name is spelled on its command-line. ASRC = - # Optimization level, can be [0, 1, 2, 3, s]. # 0 = turn off optimization. s = optimize for size. # (Note: 3 is not always the best optimization level. See avr-libc FAQ.) @@ -120,6 +112,7 @@ CFLAGS += -Wall -Wstrict-prototypes CFLAGS += -Wa,-adhlns=$(<:.c=.lst) CFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS)) CFLAGS += $(CSTANDARD) +CFLAGS += $(INCLUDES) #---------------- Assembler Options ---------------- diff --git a/trennfix/sw/README b/trennfix/sw/README new file mode 100644 index 0000000..5bda705 --- /dev/null +++ b/trennfix/sw/README @@ -0,0 +1 @@ +This is trennfix. \ No newline at end of file diff --git a/trennfix/sw/config.make b/trennfix/sw/config.make deleted file mode 100644 index 17f249d..0000000 --- a/trennfix/sw/config.make +++ /dev/null @@ -1,24 +0,0 @@ - -SRC = main.c mm_switch.c - -################################################################################ -################################################################################ - -AVRDUDE_PROGRAMMER = avrisp2 -MCU=attiny25 -F_CPU = 16000000 - -# Fuse-Bits gibts praktisch bei http://www.engbedded.com/fusecalc ! -# 8 MHz interner RC-Oszillator usw. Brown out 4V -LFUSE=0x61 -HFUSE=0xdc -EFUSE=0xff - - - - - - - - - diff --git a/trennfix/sw/config/hardware.h b/trennfix/sw/config/hardware.h new file mode 100644 index 0000000..4f07b0b --- /dev/null +++ b/trennfix/sw/config/hardware.h @@ -0,0 +1,10 @@ +#ifndef __HARDWARE_H_GENERIC__ +#define __HARDWARE_H_GENERIC__ + + +#include __HW_CONF_HEADER__ + + + + +#endif diff --git a/trennfix/sw/config/trennfix_attiny25.h b/trennfix/sw/config/trennfix_attiny25.h new file mode 100644 index 0000000..a995437 --- /dev/null +++ b/trennfix/sw/config/trennfix_attiny25.h @@ -0,0 +1,86 @@ +#ifndef __HARDWARE_H +#define __HARDWARE_H + +#include + +#define PIN_LED _PIN(PORTB, PORTB2) +#define PIN_SENSE _PIN(PORTB, PORTB4) +#define PIN_DRIVE _PIN(PORTB, PORTB3) +#define PIN_BTN _PIN(PORTB, PB1) + +#define BTN_PRESSED (!PINVAL(PIN_BTN)) + +#define START_TIMER0 ({TCNT0 = 0; TCCR0B = 2;}) +#define START_TIMER1 ({TCNT1 = 1; TCCR1 = 7; GTCCR |= 2;}) +#define STOP_TIMER1 ({TCCR1 = 0; TCNT1 = 1;}) + + +/* + * Various sinks for valuable program memory + * + */ +// #define USE_EEPROM_UPDATE /* 14 bytes */ +//#define INTERPRET_DRIVE_COMMANDS +#define INTERPRET_DRIVE_SIMPLE + +static inline void setup_hw(void) +{ + /* Turn off the 1/8 clock prescaler - now running at 16MHz*/ + CLKPR = (1 << CLKPCE); + CLKPR = 0; + + INPUT_PIN(PIN_SENSE); + INPUT_PIN(PIN_BTN); + OUTPUT_PIN(PIN_DRIVE); + OUTPUT_PIN(PIN_LED); + setpin(PIN_BTN, 1); /* Need pullup */ + + GIMSK |= _BV(PCIE); /* Enable pin change interrupt for sense port */ + PCMSK |= _BV(PCINT4); /* PB4, Rail sense input */ + + /* Change interrupt for button */ + PCMSK |= _BV(PCINT1); /* PB1 */ + + /* Setup timer 0, used for mm_switch */ + TCCR0A = 0; /* Normal mode */ + TCCR0B = 0; /* Timer off */ + TIMSK |= _BV(OCIE0A); /* Get a match interrupt */ + + /* We need 13 + 45,5 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 */ +} + +/* + * Function to trigger an oscilloscope on the drive pin + * + * Can be altered to use the LED. + * + */ +static void __attribute__((unused)) trigger(void) +{ + setpin(PIN_DRIVE, 1); + _delay_us(4); + setpin(PIN_DRIVE, 0); + _delay_us(4); +} + +/* + * Configuration for mm receiver code. + */ + +#define MM_SENSE (!PINVAL(PIN_SENSE)) +#define MM_TSTART_FAST ({TCNT0 = 0; GTCCR |= 1; OCR0A = 110; TCCR0B = 2;}) +#define MM_TSTART_SLOW ({TCNT0 = 0; GTCCR |= 1; OCR0A = 220; TCCR0B = 2;}) +#define MM_TSTOP ({TCCR0B = 0;}) +#define MM_TIMER_INT_VECT TIMER0_COMPA_vect + +/* Costs 63 bytes program memory */ +#define MM_FILTER_REPEATED + +#define USE_EEPROM_UPDATE +#endif diff --git a/trennfix/sw/config/trennfix_attiny25.mk b/trennfix/sw/config/trennfix_attiny25.mk new file mode 100644 index 0000000..466277b --- /dev/null +++ b/trennfix/sw/config/trennfix_attiny25.mk @@ -0,0 +1,29 @@ + +SRC = src/main.c + +################################################################################ +################################################################################ + +AVRDUDE_PROGRAMMER = avrisp2 +MCU=attiny25 +F_CPU = 16000000 + +# Fuse-Bits gibts praktisch bei http://www.engbedded.com/fusecalc ! +# 8 MHz interner RC-Oszillator usw. Brown out 4V + +LFUSE=0x61 +#HFUSE=0xdc +HFUSE=0xd4 +EFUSE=0xff + +CFLAGS+=-DONLY_2K + + + + + + + + + + diff --git a/trennfix/sw/config/trennfix_attiny85.h b/trennfix/sw/config/trennfix_attiny85.h new file mode 100644 index 0000000..39c1592 --- /dev/null +++ b/trennfix/sw/config/trennfix_attiny85.h @@ -0,0 +1,85 @@ +#ifndef __HARDWARE_H +#define __HARDWARE_H + +#include + +#define PIN_LED _PIN(PORTB, PORTB2) +#define PIN_SENSE _PIN(PORTB, PORTB4) +#define PIN_DRIVE _PIN(PORTB, PORTB3) +#define PIN_BTN _PIN(PORTB, PB1) + +#define BTN_PRESSED (!PINVAL(PIN_BTN)) + +#define START_TIMER0 ({TCNT0 = 0; TCCR0B = 2;}) +#define START_TIMER1 ({TCNT1 = 1; TCCR1 = 7; GTCCR |= 2;}) +#define STOP_TIMER1 ({TCCR1 = 0; TCNT1 = 1;}) + + +/* + * Various sinks for valuable program memory + * + */ +// #define USE_EEPROM_UPDATE /* 14 bytes */ +//#define INTERPRET_DRIVE_COMMANDS +#define INTERPRET_DRIVE_SIMPLE + +static inline void setup_hw(void) +{ + /* Turn off the 1/8 clock prescaler - now running at 16MHz*/ + CLKPR = (1 << CLKPCE); + CLKPR = 0; + + INPUT_PIN(PIN_SENSE); + INPUT_PIN(PIN_BTN); + OUTPUT_PIN(PIN_DRIVE); + OUTPUT_PIN(PIN_LED); + setpin(PIN_BTN, 1); /* Need pullup */ + + GIMSK |= _BV(PCIE); /* Enable pin change interrupt for sense port */ + PCMSK |= _BV(PCINT4); /* PB4, Rail sense input */ + + /* Change interrupt for button */ + PCMSK |= _BV(PCINT1); /* PB1 */ + + /* Setup timer 0, used for mm_switch */ + TCCR0A = 0; /* Normal mode */ + TCCR0B = 0; /* Timer off */ + TIMSK |= _BV(OCIE0A); /* Get a match interrupt */ + + /* We need 13 + 45,5 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 */ +} + +/* + * Function to trigger an oscilloscope on the drive pin + * + * Can be altered to use the LED. + * + */ +static void __attribute__((unused)) trigger(void) +{ + setpin(PIN_DRIVE, 1); + _delay_us(4); + setpin(PIN_DRIVE, 0); + _delay_us(4); +} + +/* + * Configuration for mm receiver code. + */ + +#define MM_SENSE (!PINVAL(PIN_SENSE)) +#define MM_TSTART_FAST ({TCNT0 = 0; GTCCR |= 1; OCR0A = 110; TCCR0B = 2;}) +#define MM_TSTART_SLOW ({TCNT0 = 0; GTCCR |= 1; OCR0A = 220; TCCR0B = 2;}) +#define MM_TSTOP ({TCCR0B = 0;}) +#define MM_TIMER_INT_VECT TIMER0_COMPA_vect + +/* Costs 63 bytes program memory */ +#define MM_FILTER_REPEATED + +#endif diff --git a/trennfix/sw/config/trennfix_attiny85.mk b/trennfix/sw/config/trennfix_attiny85.mk new file mode 100644 index 0000000..896a008 --- /dev/null +++ b/trennfix/sw/config/trennfix_attiny85.mk @@ -0,0 +1,30 @@ + +SRC = src/main.c + +################################################################################ +################################################################################ + +AVRDUDE_PROGRAMMER = avrisp2 +MCU=attiny85 +F_CPU = 16000000 + +# Fuse-Bits gibts praktisch bei http://www.engbedded.com/fusecalc ! +# 8 MHz interner RC-Oszillator usw. Brown out 4V + +# Tiny 25 +#LFUSE=0x61 +#HFUSE=0xdc +#EFUSE=0xff + +LFUSE=0x61 +HFUSE=0xd4 +EFUSE=0xff + + + + + + + + + diff --git a/trennfix/sw/hardware.h b/trennfix/sw/hardware.h deleted file mode 100644 index edcd7ff..0000000 --- a/trennfix/sw/hardware.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef __HARDWARE_H -#define __HARDWARE_H - - -#include "pin_magic.h" - -#define PIN_LED _PIN(PORTB, PORTB2) -#define PIN_SENSE _PIN(PORTB, PORTB4) -#define PIN_DRIVE _PIN(PORTB, PORTB3) -#define PIN_BTN _PIN(PORTB, PB1) - -#define MM_SENSE (!PINVAL(PIN_SENSE)) -#define BTN_PRESSED (!PINVAL(PIN_BTN)) - -#define START_TIMER0 ({TCNT0 = 0; TCCR0B = 2;}) -#define START_TIMER1 ({TCNT1 = 1; TCCR1 = 7; GTCCR |= 2;}) -#define STOP_TIMER1 ({TCCR1 = 0; TCNT1 = 1;}) - - -static inline void setup_hw(void) -{ - /* Turn off the 1/8 clock prescaler - now running at 16MHz*/ - CLKPR = (1 << CLKPCE); - CLKPR = 0; - - INPUT_PIN(PIN_SENSE); - INPUT_PIN(PIN_BTN); - OUTPUT_PIN(PIN_DRIVE); - OUTPUT_PIN(PIN_LED); - setpin(PIN_BTN, 1); /* Need pullup */ - - GIMSK |= _BV(PCIE); /* Enable pin change interrupt for sense port */ - PCMSK |= _BV(PCINT4); /* PB4, Rail sense input */ - - /* Change interrupt for button */ - PCMSK |= _BV(PCINT1); /* PB1 */ - - /* Setup timer 0, used for mm_switch */ - TCCR0A = 0; /* Normal mode */ - TCCR0B = 0; /* Timer off */ - TIMSK |= _BV(OCIE0A); /* Get a match interrupt */ - - /* We need 13 + 45,5 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 */ -} - -/* deprecated */ -#define START_TIMER0 ({TCNT0 = 0; TCCR0B = 2;}) -#define START_TIMER1 ({TCNT1 = 1; TCCR1 = 7; GTCCR |= 2;}) -#define STOP_TIMER0 (TCCR0B = 0) -#define STOP_TIMER1 ({TCCR1 = 0; TCNT1 = 1;}) - -#define TSTART_CLK_TO_SAMPLE_FAST ({TCNT0 = 0; GTCCR |= 1; OCR0A = 110; TCCR0B = 2;}) -#define TSTART_CLK_TO_SAMPLE_SLOW ({TCNT0 = 0; GTCCR |= 1; OCR0A = 220; TCCR0B = 2;}) - -//#define TSTART_CLK_TO_SAMPLE_FAST ({TCNT0 = 0; GTCCR |= 1; OCR0A = 91; TCCR0B = 2;}) -//#define TSTART_CLK_TO_SAMPLE_SLOW ({TCNT0 = 0; GTCCR |= 1; OCR0A = 182; TCCR0B = 2;}) - -#define TSTOP ({TCCR0B = 0;}) - - - -#endif diff --git a/trennfix/sw/main.c b/trennfix/sw/main.c deleted file mode 100644 index 2437151..0000000 --- a/trennfix/sw/main.c +++ /dev/null @@ -1,429 +0,0 @@ -/****************************************************************************** - * - * 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 . - * - *****************************************************************************/ - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include "mm_switch.h" -#include "hardware.h" - -// #define DEBUG_TIMEOUT -#ifdef DEBUG_TIMEOUT -#define MON_TIMEOUT(val) setpin(DRIVE, val) -#else -#define MON_TIMEOUT(xx) ({0;}) -#endif - -#define EE_MAGIC 0xab - -static void trigger(void) -{ - setpin(PIN_DRIVE, 1); - _delay_us(10); - setpin(PIN_DRIVE, 0); -} - -enum op_mode { - OM_MOMENTARY, /* on as long as "key on" pressed */ - OM_DOUBLE, /* On off with "on" and "off" keys */ - OM_TOGGLE, /* toggle on "key on" pressed */ - OM_ERASED = 0xff /* EEPROM erased, need setup */ -}; - -enum learn_mode { - LM_OFF = 0, - LM_LEARN_ON_KEY, /* Learn primary key */ - LM_LEARN_OFF_KEY, /* Learn secondary key, relevant for OM_DOUBLE */ - LM_LEARN_INITIAL, /* Learn initial pulse length, 10ms steps */ - LM_LEARN_DUTY, /* Learn duty cycle 0-10 */ - LM_LEARN_OP_MODE, /* Learn operation mode */ - LM_END, /* Only a label */ -}; - -struct config { - uint8_t magic; /* Magic value */ - enum op_mode op_mode; - uint8_t decoder_on; - uint8_t key_on; - uint8_t decoder_off; - uint8_t key_off; - uint8_t initial_pulse; /* Lenghth of initial pulse in 10ms steps */ - uint8_t on_duty_cycle; /* Duty cycle for on. 0-10 */ -}; - -static struct EEMEM config ee_config; -static struct config config; -static volatile enum learn_mode learn_mode = LM_OFF; - -static void load_config(void) -{ - eeprom_read_block(&config, &ee_config, sizeof(config)); -} - -static void save_config(void) -{ - eeprom_update_block(&config, &ee_config, sizeof(config)); -} - -static volatile uint8_t drive_on = 0; - -ISR(PCINT0_vect){ - static uint8_t btn_last = 0; - - mm_pinchange_handler(); - - if (BTN_PRESSED && !btn_last) { - learn_mode++; - learn_mode %= LM_END; - } - btn_last = BTN_PRESSED; -} - -static uint8_t get_speed(uint8_t command) -{ - uint8_t b1, b2, b3, b4, b5, b6, b7, b8; - b1 = ((command & 0x80) != 0); - b2 = ((command & 0x40) != 0); - b3 = ((command & 0x20) != 0); - b4 = ((command & 0x10) != 0); - b5 = ((command & 0x8) != 0); - b6 = ((command & 0x4) != 0); - b7 = ((command & 0x2) != 0); - b8 = ((command & 0x1) != 0); - - //if ((b1 != b2) || (b2 != b3) || (b3 != b4) || (b5 != b6) || (b7 != b8)) - // return 0xff; - - return (b1 + b3*2 + b5*4 +b7*8); -} - -void mm_switch_drive(uint8_t decoder, uint8_t function, uint8_t command) -{ - static uint8_t seen_before = 0; - uint8_t speed; - - - if (!seen_before) { - // if (decoder == 100) - // - - if ((decoder == 100) && (function == 0) && (command == 0xc0)) { - config.magic = 0; - save_config(); - } - } - seen_before = 1; - speed = get_speed(command); - static uint8_t itsme = 0; - if (decoder == 10) { - trigger(); - - if (speed == 1) { - itsme = 1; - drive_on = 1; - }else { - if (itsme) { - drive_on = 0; - itsme = 0; - } - } - } - if (decoder == 33) { - // trigger(); - - speed = get_speed(command); - if (speed != 0xff) { - if (speed >= 1) speed -= 1; - if (speed <= 14) - config.on_duty_cycle = speed; - else - config.on_duty_cycle = 14; - } - } -} - -void mm_switch_command(uint8_t decoder, uint8_t command) -{ - static uint8_t toggle_lock = 0; - switch(learn_mode) { - - case LM_OFF: - default: - if ((decoder == config.decoder_on) && - (command == config.key_on)) { /* Primary key pressed */ - switch(config.op_mode) { - case OM_MOMENTARY: - case OM_DOUBLE: - drive_on = 1; - break; - case OM_TOGGLE: - if (!toggle_lock) { - drive_on = !drive_on; - toggle_lock = 1; - } - break; - default: - break; - } - } - - if ((decoder == config.decoder_on) && - (command == 0)) { /* Primary key released */ - switch(config.op_mode) { - case OM_MOMENTARY: - drive_on = 0; - break; - case OM_TOGGLE: - toggle_lock = 0; - break; - default: - break; - } - } - - if ((decoder == config.decoder_off) && - (command == config.key_off)) { /* Secondary "off" key pressed */ - switch(config.op_mode) { - case OM_DOUBLE: - drive_on = 0; - break; - case OM_TOGGLE: - case OM_MOMENTARY: - default: - break; - } - } - break; - - case LM_LEARN_ON_KEY: - if (command) { - config.decoder_on = decoder; - config.key_on = command; - save_config(); - if (config.op_mode == OM_DOUBLE) - learn_mode = LM_LEARN_OFF_KEY; - else - learn_mode = LM_OFF; - } - break; - -#ifdef LEARN_THE_STUFF - case LM_LEARN_OFF_KEY: - if (command) { - config.decoder_off = decoder; - config.key_off = command; - save_config(); - learn_mode = LM_OFF; - } - break; - - case LM_LEARN_INITIAL: - if (drive_on) { - if (command == 0) - drive_on = 0; - - } else { - switch(command) { - case 1: - if (config.initial_pulse >= 10) - config.initial_pulse -= 10; - else - config.initial_pulse = 0; - save_config(); - drive_on = 1; - break; - case 2: - if (config.initial_pulse <= 245) - config.initial_pulse += 10; - else - config.initial_pulse = 255; - save_config(); - drive_on = 1; - break; - default: - break; - } - } - break; - - case LM_LEARN_DUTY: - if (drive_on) { - if (command == 0) - drive_on = 0; - } else { - switch(command) { - case 1: - if (config.on_duty_cycle > 0) - config.on_duty_cycle -= 1; - save_config(); - drive_on = 1; - break; - case 2: - if (config.on_duty_cycle < 10) - config.on_duty_cycle += 1; - save_config(); - drive_on = 1; - break; - default: - break; - } - } - break; - - case LM_LEARN_OP_MODE: - switch(command) { - case 1: - config.op_mode = OM_MOMENTARY; - save_config(); - learn_mode = LM_OFF; - break; - case 3: - config.op_mode = OM_DOUBLE; - save_config(); - learn_mode = LM_OFF; - break; - case 5: - config.op_mode = OM_TOGGLE; - save_config(); - learn_mode = LM_OFF; - break; - default: - break; - } - break; -#endif - } -} - - -/****************************************************************************** - * - * main() - The main routine - * - */ - -int main(void) { - // uint8_t learn_mode_off; - uint8_t drive_last = 0; - uint8_t drive_slope = 0; - uint8_t i; - - load_config(); - setup_hw(); - - if ((config.op_mode == OM_ERASED) || (config.magic != EE_MAGIC)) { - config.magic = EE_MAGIC; - config.op_mode = OM_MOMENTARY; - config.decoder_on = 1; - config.key_on = 1; - config.decoder_off = 1; - config.key_off = 2; - config.initial_pulse = 10; - config.on_duty_cycle = 5; - learn_mode = LM_LEARN_ON_KEY; - } - sei(); - - while (1) { - - // drive_start: - - cli(); - if (!drive_last && drive_on) - drive_slope = 1; - else - drive_slope = 0; - drive_last = drive_on; - sei(); - - if (drive_on) { -#ifdef ROOM_FOR_KICK - if (drive_slope) { - for (i = 0; i < config.initial_pulse; i++) { - setpin(PIN_DRIVE, 1); - _delay_ms(10); - } - } -#endif - -#define ROOM_FOR_PWM -#ifdef ROOM_FOR_PWM - for (i = 0; i < config.on_duty_cycle; i++) { - setpin(PIN_DRIVE, drive_on); - _delay_us(1); - } - for (i = 0; i < (14 - config.on_duty_cycle); i++) { - setpin(PIN_DRIVE, 0); - _delay_us(1); - } - -#else - setpin(PIN_DRIVE, 1); -#endif - - } else { - setpin(PIN_DRIVE, 0); - if (!learn_mode) - continue; - - //learn_mode_off = !learn_mode; - - //if (drive_on || (learn_mode && learn_mode_off)) - // goto drive_start; - - for (i = 0; i < learn_mode; i++) { - setpin(PIN_LED, 1); - _delay_ms(30); - setpin(PIN_LED, 0); - _delay_ms(280); - - //if (drive_on || (learn_mode && learn_mode_off)) - // goto drive_start; - //if (drive_on || (learn_mode && learn_mode_off)) - // goto drive_start; - } - for (i = 0; i < 15 - learn_mode; i++) { - //if (drive_on || (learn_mode && learn_mode_off)) - // goto drive_start; - setpin(PIN_LED, 0); - _delay_ms(80); - //if (drive_on || (learn_mode && learn_mode_off)) - // goto drive_start; - // _delay_ms(40); - } - } - } - return 0; -} - - -/****************************************************************************** - * The end :-) - */ diff --git a/trennfix/sw/mm/include/mm/mm_switch.h b/trennfix/sw/mm/include/mm/mm_switch.h new file mode 100644 index 0000000..fb36036 --- /dev/null +++ b/trennfix/sw/mm/include/mm/mm_switch.h @@ -0,0 +1,38 @@ +#ifndef MM_SWITCH_H +#define MM_SWITCH_H + +#include + +/* + * mm_switch_command - Callback function! + * + * This function must be defined whenever the mm_switch module is used. + * It will be called from interrupt context whenever a new valid command + * has arrived. + * + * decoder is in the range from 1 to 25. Other values will not occur. + * + * key is in the range from 0 to 8: + * + * 1 - key1 green pressed + * 2 - key1 red pressed + * 3 - key2 green pressed + * 4 - key2 red pressed + * ... + * 0 - all keys up + * + */ +void mm_switch_command(uint8_t decoder, uint8_t key); + +/* + * empty comment :-) + */ +void mm_switch_drive_cb(uint8_t address, uint8_t speed, uint8_t functions, uint8_t flags); + +void mm_switch_drive(uint8_t decoder, uint8_t function, uint8_t command); + +void mm_pinchange_handler(void); + +void mm_init(void); + +#endif diff --git a/trennfix/sw/mm/src/mm_switch.c b/trennfix/sw/mm/src/mm_switch.c new file mode 100644 index 0000000..2b09e5c --- /dev/null +++ b/trennfix/sw/mm/src/mm_switch.c @@ -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 . + * + *****************************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +/* + * 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 :-) + */ + + + diff --git a/trennfix/sw/mm_switch.c b/trennfix/sw/mm_switch.c deleted file mode 100644 index cb7b4f7..0000000 --- a/trennfix/sw/mm_switch.c +++ /dev/null @@ -1,330 +0,0 @@ -/****************************************************************************** - * - * 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 . - * - *****************************************************************************/ - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#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_nibble(uint8_t mm_nibble) -{ - switch(mm_nibble) { - case 0x0: return 0; - case 0xc: return 1; - case 0x8: return 2; - case 0x3: return 3; - case 0xf: return 4; - case 0xb: return 5; - case 0x2: return 6; - case 0xe: return 7; - case 0xa: return 8; - default: return 0; - } - return 0; -} - -static uint8_t lookup_decoder(uint8_t mm_byte) -{ - uint8_t low; - uint8_t high; - if (mm_byte == 0) - return 100; - 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; - } -} - - -static __attribute__((unused)) 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_command; -static volatile uint8_t shift_function; -static volatile uint8_t shift_address; -static volatile uint8_t shift_command_first; -static volatile uint8_t shift_address_first; - -/* 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. - */ - -volatile uint8_t bitno = 0; - -static void __attribute__((unused)) trigger(void) -{ - setpin(PIN_DRIVE, 1); - _delay_us(4); - setpin(PIN_DRIVE, 0); - _delay_us(4); -} - -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_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(TIMER0_COMPA_vect) { - static uint8_t patience = 0; - static uint8_t address_last = 0xff; - static uint8_t function_last = 0xff; - static uint8_t command_last = 0xff; - - 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; - } - default: - TSTOP; - recstate = IDLE; - return; - } - - shift(MM_SENSE); - bitno++; - - if (bitno == 18) { /* Save first received word */ - shift_command_first = shift_command; - shift_address_first = shift_address; - patience = 18; - - } - - if (bitno == 36) { - if ((shift_command == shift_command_first) && - (shift_address == shift_address_first)) { - if ((shift_address != address_last) || (shift_command != command_last) || - shift_function != function_last) { - - uint8_t addr = lookup_decoder(shift_address); - - if (recstate == SLOW_WAIT_FOR_CLOCK) { - mm_switch_drive(addr, shift_function, shift_command); - } - - - if (recstate == FAST_WAIT_FOR_CLOCK && addr) { - uint8_t command = lookup_command(shift_command); - /* Congratulations, we have a valid command */ - mm_switch_command(addr, command); - } - } - address_last = shift_address; - function_last = shift_function; - command_last = shift_command; - } - - } -} - -//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; - 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 :-) - */ - - - diff --git a/trennfix/sw/mm_switch.h b/trennfix/sw/mm_switch.h deleted file mode 100644 index 7791f07..0000000 --- a/trennfix/sw/mm_switch.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef MM_SWITCH_H -#define MM_SWITCH_H - -#include - -/* - * mm_switch_command - Callback function! - * - * This function must be defined whenever the mm_switch module is used. - * It will be called from interrupt context whenever a new valid command - * has arrived. - * - * decoder is in the range from 1 to 25. Other values will not occur. - * - * key is in the range from 0 to 8: - * - * 1 - key1 green pressed - * 2 - key1 red pressed - * 3 - key2 green pressed - * 4 - key2 red pressed - * ... - * 0 - all keys up - * - */ -void mm_switch_command(uint8_t decoder, uint8_t key); - -/* - * empty comment :-) - */ -void mm_switch_drive_cb(uint8_t address, uint8_t speed, uint8_t functions, uint8_t flags); - -void mm_switch_drive(uint8_t decoder, uint8_t function, uint8_t command); - -void mm_pinchange_handler(void); - -void mm_init(void); - - - - - -#endif diff --git a/trennfix/sw/src/main.c b/trennfix/sw/src/main.c new file mode 100644 index 0000000..7d5c10a --- /dev/null +++ b/trennfix/sw/src/main.c @@ -0,0 +1,469 @@ +/****************************************************************************** + * + * 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 . + * + *****************************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + + +#define EE_MAGIC 0xab + +enum op_mode { + OM_MOMENTARY, /* on as long as "key on" pressed */ + OM_DOUBLE, /* On off with "on" and "off" keys */ + OM_TOGGLE, /* toggle on "key on" pressed */ + OM_ERASED = 0xff /* EEPROM erased, need setup */ +}; + +enum learn_mode { + LM_OFF = 0, + LM_LEARN_ON_KEY, /* Learn primary key */ + LM_LEARN_OFF_KEY, /* Learn secondary key, relevant for OM_DOUBLE */ + LM_LEARN_INITIAL, /* Learn initial pulse length, 10ms steps */ + LM_LEARN_DUTY, /* Learn duty cycle 0-10 */ + LM_LEARN_OP_MODE, /* Learn operation mode */ + LM_END, /* Only a label */ +}; + +struct config { + uint8_t magic; /* Magic value */ + enum op_mode op_mode; + uint8_t decoder_on; + uint8_t key_on; + uint8_t decoder_off; + uint8_t key_off; + uint8_t initial_pulse; /* Lenghth of initial pulse in 10ms steps */ + uint8_t on_duty_cycle; /* Duty cycle for on. 0-10 */ + volatile enum learn_mode learn_mode; +}; + +static struct EEMEM config ee_config; +static volatile struct config config; +static volatile uint8_t drive_on = 0; + +static void load_config(void) +{ + eeprom_read_block((uint8_t *)&config, &ee_config, sizeof(config)); +} + +static void save_config(void) +{ +#ifdef USE_EEPROM_UPDATE + eeprom_update_block((uint8_t *)&config, &ee_config, sizeof(config)); +#else + eeprom_write_block((uint8_t *)&config, &ee_config, sizeof(config)); +#endif +} + + + +ISR(PCINT0_vect){ + static uint8_t btn_last = 0; + + mm_pinchange_handler(); + + if (BTN_PRESSED && !btn_last) { + config.learn_mode++; + config.learn_mode %= LM_END; + } + btn_last = BTN_PRESSED; +} + +static uint8_t get_speed(uint8_t command) +{ + uint8_t b1, b3, b5, b7; + + b1 = ((command & 0x80) != 0); + // b2 = ((command & 0x40) != 0); + b3 = ((command & 0x20) != 0); + // b4 = ((command & 0x10) != 0); + b5 = ((command & 0x8) != 0); + // b6 = ((command & 0x4) != 0); + b7 = ((command & 0x2) != 0); + // b8 = ((command & 0x1) != 0); + + //if ((b1 != b2) || (b2 != b3) || (b3 != b4) || (b5 != b6) || (b7 != b8)) + // return 0xff; + + return (b1 + b3*2 + b5*4 +b7*8); +} + +#ifdef INTERPRET_DRIVE_COMMANDS + +void mm_switch_drive(uint8_t decoder, uint8_t function, uint8_t command) +{ + static uint8_t seen_before = 0; + uint8_t speed; + + if (!seen_before) { + if ((decoder == 80) && (function == 0) && (command == 0xc0)) { + config.learn_mode = 1; + save_config(); + } + } + seen_before = 1; + + speed = get_speed(command); + static uint8_t itsme = 0; + + if (config.learn_mode) { + if (decoder == 70) { + if (speed == 1) { + itsme = 1; + drive_on = 1; + }else { + if (itsme) { + drive_on = 0; + itsme = 0; + } + } + } + + if (decoder == 33) { + if (speed != 0xff) { + if (speed >= 1) speed -= 1; + if (speed <= 14) + config.on_duty_cycle = speed; + else + config.on_duty_cycle = 14; + + } + } + + if (decoder == 32) { + if (speed != 0xff) { + config.initial_pulse = speed; + } + } + + if (decoder == 31 && command == 0xc0) + save_config(); + } +} + +#else +#ifdef INTERPRET_DRIVE_SIMPLE + +static volatile uint8_t current_loco; + +void mm_switch_drive(uint8_t decoder, uint8_t function, uint8_t command) +{ + uint8_t speed; + speed = get_speed(command); + current_loco = decoder; + if (speed != 0xff) { + if (decoder == 50) { + setpin(PIN_LED, drive_on = function & 3); + config.initial_pulse = speed; + } else if (decoder == 51) { + if (speed >= 1) speed -= 1; + if (speed <= 14) + config.on_duty_cycle = speed; + else + config.on_duty_cycle = 14; + } else if (decoder == 52) { + static uint8_t last_speed; + if (speed == 1 && last_speed != 1) + save_config(); + last_speed = speed; + + } + } +} + +#endif +#endif +static volatile uint8_t switch_on = 0; + +void mm_switch_command(uint8_t decoder, uint8_t command) +{ + static uint8_t toggle_lock = 0; + switch(config.learn_mode) { + + case LM_OFF: + default: + if ((decoder == config.decoder_on) && + (command == config.key_on)) { /* Primary key pressed */ + switch(config.op_mode) { + case OM_MOMENTARY: + case OM_DOUBLE: + if (!switch_on && current_loco == 52) + save_config(); + switch_on = 1; + break; + case OM_TOGGLE: + if (!toggle_lock) { + drive_on = !drive_on; + toggle_lock = 1; + } + break; + default: + break; + } + } + + if ((decoder == config.decoder_on) && + (command == 0)) { /* Primary key released */ + switch(config.op_mode) { + case OM_MOMENTARY: + switch_on = 0; + break; + case OM_TOGGLE: + toggle_lock = 0; + break; + default: + break; + } + } +#ifdef HANDLE_OFF_KEY + + if ((decoder == config.decoder_off) && + (command == config.key_off)) { /* Secondary "off" key pressed */ + switch(config.op_mode) { + case OM_DOUBLE: + drive_on = 0; + break; + case OM_TOGGLE: + case OM_MOMENTARY: + default: + break; + } + } +#endif + break; + + case LM_LEARN_ON_KEY: + if (command) { + config.decoder_on = decoder; + config.key_on = command; + if (config.op_mode == OM_DOUBLE) + config.learn_mode = LM_LEARN_OFF_KEY; + else + config.learn_mode = LM_OFF; + save_config(); + } + break; +#ifdef LEARN_ADVANCED + case LM_LEARN_OFF_KEY: + if (command) { + config.decoder_off = decoder; + config.key_off = command; + config.learn_mode = LM_OFF; + save_config(); + } + break; + + case LM_LEARN_INITIAL: + if (drive_on) { + if (command == 0) + drive_on = 0; + + } else { + switch(command) { + case 1: + if (config.initial_pulse >= 10) + config.initial_pulse -= 10; + else + config.initial_pulse = 0; + save_config(); + drive_on = 1; + break; + case 2: + if (config.initial_pulse <= 245) + config.initial_pulse += 10; + else + config.initial_pulse = 255; + save_config(); + drive_on = 1; + break; + default: + break; + } + } + break; + + case LM_LEARN_DUTY: + if (drive_on) { + if (command == 0) + drive_on = 0; + } else { + switch(command) { + case 1: + if (config.on_duty_cycle > 0) + config.on_duty_cycle -= 1; + save_config(); + drive_on = 1; + break; + case 2: + if (config.on_duty_cycle < 10) + config.on_duty_cycle += 1; + save_config(); + drive_on = 1; + break; + default: + break; + } + } + break; + + case LM_LEARN_OP_MODE: + switch(command) { + case 1: + config.op_mode = OM_MOMENTARY; + save_config(); + config.learn_mode = LM_OFF; + break; + case 3: + config.op_mode = OM_DOUBLE; + save_config(); + config.learn_mode = LM_OFF; + break; + case 5: + config.op_mode = OM_TOGGLE; + save_config(); + config.learn_mode = LM_OFF; + break; + default: + break; + } + break; +#endif + } +} + + +/****************************************************************************** + * + * main() - The main routine + * + */ + +int main(void) { + uint8_t learn_mode_off; + uint8_t drive_last = 0; + uint8_t drive_slope = 0; + uint8_t i; + uint8_t output_on; + + load_config(); + setup_hw(); + + if ((config.op_mode == OM_ERASED) || (config.magic != EE_MAGIC)) { + config.magic = EE_MAGIC; + config.op_mode = OM_MOMENTARY; + config.decoder_on = 1; + config.key_on = 1; + config.decoder_off = 1; + config.key_off = 2; + config.initial_pulse = 10; + config.on_duty_cycle = 5; + config.learn_mode = LM_LEARN_ON_KEY; + } + drive_on = 0; + sei(); + setpin(PIN_DRIVE, 1); + _delay_ms(50); + setpin(PIN_DRIVE, 0); + + //setpin(PIN_LED,1 ); + //_delay_ms(400); + //setpin(PIN_LED,0 ); + while (1) { + + drive_start: + + output_on = drive_on || switch_on; + cli(); + if (!drive_last && output_on) + drive_slope = 1; + else + drive_slope = 0; + drive_last = output_on; + sei(); + + if (output_on) { + if (drive_slope) { + for (i = 0; i < config.initial_pulse; i++) { + setpin(PIN_DRIVE, 1); + _delay_ms(20); + } + } + + for (i = 0; i < config.on_duty_cycle; i++) { + setpin(PIN_DRIVE, output_on); + _delay_us(3); + } + for (i = 0; i < (28 - config.on_duty_cycle); i++) { + setpin(PIN_DRIVE, 0); + _delay_us(3); + } + + } else { + setpin(PIN_DRIVE, 0); + if (!config.learn_mode) + continue; + + learn_mode_off = !config.learn_mode; + + if (output_on || (config.learn_mode && learn_mode_off)) + goto drive_start; + + for (i = 0; i < config.learn_mode; i++) { + setpin(PIN_LED, 1); + _delay_ms(10); + //if (output_on || (config.learn_mode && learn_mode_off)) + // goto drive_start; + setpin(PIN_LED, 0); + _delay_ms(135); + //if (output_on || (config.learn_mode && learn_mode_off)) + // goto drive_start; + + //if (output_on || (config.learn_mode && learn_mode_off)) + // goto drive_start; + } + for (i = 0; i < 15 - config.learn_mode; i++) { + //if (output_on || (config.learn_mode && learn_mode_off)) + // goto drive_start; + setpin(PIN_LED, 0); + _delay_ms(70); + //if (output_on || (config.learn_mode && learn_mode_off)) + // goto drive_start; + // _delay_ms(40); + } + } + } + return 0; +} + + +/****************************************************************************** + * The end :-) + */