trennfix/sw: Added smokefix, further modularized the mm decoder
authorPhilipp Hachtmann <hachti@hachti.de>
Tue, 28 Feb 2017 16:59:18 +0000 (17:59 +0100)
committerPhilipp Hachtmann <hachti@hachti.de>
Tue, 18 Apr 2023 20:03:20 +0000 (22:03 +0200)
The mm decoder part will probably be renamed to "railway" or some
other more generic term when it also supports DCC.

Signed-off-by: Philipp Hachtmann <hachti@hachti.de>
12 files changed:
trennfix/sw/Makefile
trennfix/sw/config/trennfix_0.4.h
trennfix/sw/mk/hw/trennfix_0.4.mk [new file with mode: 0644]
trennfix/sw/mk/prog/smokefix.mk [new file with mode: 0644]
trennfix/sw/mk/prog/trennfix.mk [new file with mode: 0644]
trennfix/sw/mk/trennfix_0.4.mk [deleted file]
trennfix/sw/mm/include/mm/mm_decode.h [new file with mode: 0644]
trennfix/sw/mm/include/mm/mm_switch.h
trennfix/sw/mm/src/mm_switch.c
trennfix/sw/src/main.c [deleted file]
trennfix/sw/src/smokefix_main.c [new file with mode: 0644]
trennfix/sw/src/trennfix_main.c [new file with mode: 0644]

index f096339946b8f6ae2413ec2310a1c93b8c6c8303..0ad4b1b1ff8862a37fe0b070f72dc891e9b0293c 100644 (file)
-#----------------------------------------------------------------------------
-#
-# Makefile based on the public domain Makefile by Peter Fleury
-#
-#----------------------------------------------------------------------------
-# On command line:
-#
-# make all = Make software.
-#
-# make clean = Clean out built project files.
-#
-# make coff = Convert ELF to AVR COFF.
-#
-# make extcoff = Convert ELF to AVR Extended COFF.
-#
-# make program = Download the hex file to the device, using avrdude.
-#                Please customize the avrdude settings below first!
-#
-# make debug = Start either simulavr or avarice as specified for debugging,
-#              with avr-gdb or avr-insight as the front end for debugging.
-#
-# make filename.s = Just compile filename.c into the assembler code only.
-#
-# make filename.i = Create a preprocessed source file for use in submitting
-#                   bug reports to the GCC project.
-#
-# To rebuild project do "make clean" then "make all".
-#----------------------------------------------------------------------------
-
-CONFIG?=trennfix_0.4
-
-include mk/${CONFIG}.mk
-
-INCLUDES+=-I mm/include -I include
-SRC+= mm/src/mm_switch.c
-CFLAGS+=-D__HW_CONF_HEADER__="<config/${CONFIG}.h>"
-
-# MCU name
-#MCU ?= atmega328
-
-# Processor frequency.
-#     This will define a symbol, F_CPU, in all source code files equal to the
-#     processor frequency. You can then use this symbol in your source code to
-#     calculate timings. Do NOT tack on a 'UL' at the end, this will be done
-#     automatically to create a 32-bit value in your source code.
-#F_CPU ?= 20000000
-
-
-# Output format. (can be srec, ihex, binary)
-FORMAT = ihex
-
-
-# Target file name (without extension).
-TARGET = trennfix
-
-# List C source files here. (C dependencies are automatically generated.)
-#SRC ?= main.c
-
-
-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.)
-OPT = s
-
-
-# Debugging format.
-#     Native formats for AVR-GCC's -g are dwarf-2 [default] or stabs.
-#     AVR Studio 4.10 requires dwarf-2.
-#     AVR [Extended] COFF format requires stabs, plus an avr-objcopy run.
-DEBUG = dwarf-2
 
+#######################################
 
-# List any extra directories to look for include files here.
-#     Each directory must be seperated by a space.
-#     Use forward slashes for directory separators.
-#     For a directory that has spaces, enclose it in quotes.
-EXTRAINCDIRS =
+HW?=trennfix_0.4
+PROG?=smokefix
 
+#######################################
 
-# Compiler flag to set the C Standard level.
-#     c89   = "ANSI" C
-#     gnu89 = c89 plus GCC extensions
-#     c99   = ISO C99 standard (not yet fully implemented)
-#     gnu99 = c99 plus GCC extensions
-CSTANDARD = -std=gnu99
-
-
-# Place -D or -U options here
-CDEFS = -DF_CPU=$(F_CPU)UL  -DBOOT_START=${BOOT_START} -DBOOT_PAGES=${BOOT_PAGES}
 
+#######################################
+# VERBOSITY CONTROL
+ifeq ($(V),)
+Q=@
+else
+Q=
+endif
 
-# Place -I options here
-CINCS =
+include mk/hw/${HW}.mk
+include mk/prog/*.mk
 
-# Bootloader LDFLAGS
-#BOOT_LDFLAGS=-Wl,-Ttext=${BOOT_START}
+INCLUDES+=-I mm/include -I include -I.
+CFLAGS+=-D__HW_CONF_HEADER__="<config/${CONFIG}.h>"
 
-#---------------- Compiler Options ----------------
-#  -g*:          generate debugging information
-#  -O*:          optimization level
-#  -f...:        tuning, see GCC manual and avr-libc documentation
-#  -Wall...:     warning level
-#  -Wa,...:      tell GCC to pass this to the assembler.
-#    -adhlns...: create assembler listing
-CFLAGS += -g$(DEBUG)
-CFLAGS += $(CDEFS) $(CINCS)
-CFLAGS += -O$(OPT)
+CFLAGS += -mmcu=$(MCU) -DF_CPU=$(F_CPU)UL
+CFLAGS += -std=gnu99
 CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
 CFLAGS += -Wall -Wstrict-prototypes
+CFLAGS += -Os -gdwarf-2
 CFLAGS += -Wa,-adhlns=$(<:.c=.lst)
-CFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS))
-CFLAGS += $(CSTANDARD)
+CFLAGS += -MD $(@:.c:.d)
 CFLAGS += $(INCLUDES)
 
-
-#---------------- Assembler Options ----------------
-#  -Wa,...:   tell GCC to pass this to the assembler.
-#  -ahlms:    create listing
-#  -gstabs:   have the assembler create line number information; note that
-#             for use in COFF files, additional information about filenames
-#             and function names needs to be present in the assembler source
-#             files -- see avr-libc docs [FIXME: not yet described there]
-ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs
-
+ASFLAGS += -Wa,-adhlns=$(<:.S=.lst),-gstabs -mmcu=$(MCU)
+ASFLAGS += -x assembler-with-cpp
 
 #---------------- Library Options ----------------
 # Minimalistic printf version
@@ -137,7 +45,6 @@ PRINTF_LIB =
 #PRINTF_LIB = $(PRINTF_LIB_MIN)
 #PRINTF_LIB = $(PRINTF_LIB_FLOAT)
 
-
 # Minimalistic scanf version
 SCANF_LIB_MIN = -Wl,-u,vfscanf -lscanf_min
 
@@ -149,33 +56,8 @@ SCANF_LIB =
 #SCANF_LIB = $(SCANF_LIB_MIN)
 #SCANF_LIB = $(SCANF_LIB_FLOAT)
 
-
-MATH_LIB = -lm
-
-
-#---------------- External Memory Options ----------------
-
-# 64 KB of external RAM, starting after internal RAM (ATmega128!),
-# used for variables (.data/.bss) and heap (malloc()).
-#EXTMEMOPTS = -Wl,-Tdata=0x801100,--defsym=__heap_end=0x80ffff
-
-# 64 KB of external RAM, starting after internal RAM (ATmega128!),
-# only used for heap (malloc()).
-#EXTMEMOPTS = -Wl,--defsym=__heap_start=0x801100,--defsym=__heap_end=0x80ffff
-
-EXTMEMOPTS =
-
-
-
-#---------------- Linker Options ----------------
-#  -Wl,...:     tell GCC to pass this to linker.
-#    -Map:      create map file
-#    --cref:    add cross reference to  map file
-LDFLAGS =  $(BOOT_LDFLAGS) -Wl,-Map=$(TARGET).map,--cref
-LDFLAGS += $(EXTMEMOPTS)
-LDFLAGS += $(PRINTF_LIB) $(SCANF_LIB) $(MATH_LIB)
-
-
+LDFLAGS =  -Wl,-Map=$(@:.elf=.map),--cref
+LIBS += $(PRINTF_LIB) $(SCANF_LIB) $(MATH_LIB)
 
 #---------------- Programming Options (avrdude) ----------------
 
@@ -185,16 +67,11 @@ LDFLAGS += $(PRINTF_LIB) $(SCANF_LIB) $(MATH_LIB)
 # Type: avrdude -c ?
 # to get a full listing.
 #
-
 AVRDUDE_PROGRAMMER ?= avrisp2
 
-
 # com1 = serial port. Use lpt1 to connect to parallel port.
 AVRDUDE_PORT ?= usb
 
-AVRDUDE_WRITE_FLASH =  -U flash:w:$(TARGET).hex
-#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep
-
 AVRDUDE_WRITE_FUSES = -U lfuse:w:${LFUSE}:m -U hfuse:w:${HFUSE}:m \
         -U efuse:w:${EFUSE}:m
 
@@ -216,40 +93,7 @@ AVRDUDE_FLAGS = -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER)
 AVRDUDE_FLAGS += $(AVRDUDE_NO_VERIFY)
 AVRDUDE_FLAGS += $(AVRDUDE_VERBOSE)
 AVRDUDE_FLAGS += $(AVRDUDE_ERASE_COUNTER)
-AVRDUDE_FLAGS += -B 6
-
-
-#---------------- Debugging Options ----------------
-
-# For simulavr only - target MCU frequency.
-DEBUG_MFREQ = $(F_CPU)
-
-# Set the DEBUG_UI to either gdb or insight.
-# DEBUG_UI = gdb
-DEBUG_UI = insight
-
-# Set the debugging back-end to either avarice, simulavr.
-DEBUG_BACKEND = avarice
-#DEBUG_BACKEND = simulavr
-
-# GDB Init Filename.
-GDBINIT_FILE = __avr_gdbinit
-
-# When using avarice settings for the JTAG
-JTAG_DEV = /dev/com1
-
-# Debugging port used to communicate between GDB / avarice / simulavr.
-DEBUG_PORT = 4242
-
-# Debugging host used to communicate between GDB / avarice / simulavr, normally
-#     just set to localhost unless doing some sort of crazy debugging when
-#     avarice is running on a different computer.
-DEBUG_HOST = localhost
-
-
-
-#============================================================================
-
+AVRDUDE_FLAGS += -B 5
 
 # Define programs and commands.
 SHELL = sh
@@ -259,235 +103,130 @@ OBJDUMP = avr-objdump
 SIZE = avr-size
 NM = avr-nm
 AVRDUDE = avrdude
-REMOVE = rm -rf
-COPY = cp
-WINSHELL = cmd
-
-
-# Define Messages
-# English
-MSG_ERRORS_NONE = Errors: none
-MSG_BEGIN = -------- begin --------
-MSG_END = --------  end  --------
-MSG_SIZE_BEFORE = Size before:
-MSG_SIZE_AFTER = Size after:
-MSG_COFF = Converting to AVR COFF:
-MSG_EXTENDED_COFF = Converting to AVR Extended COFF:
-MSG_FLASH = Creating load file for Flash:
-MSG_EEPROM = Creating load file for EEPROM:
-MSG_EXTENDED_LISTING = Creating Extended Listing:
-MSG_SYMBOL_TABLE = Creating Symbol Table:
-MSG_LINKING = Linking:
-MSG_COMPILING = Compiling:
-MSG_ASSEMBLING = Assembling:
-MSG_CLEANING = Cleaning project:
-
-
-# Define all object files.
-OBJ = $(SRC:.c=.o) $(ASRC:.S=.o)
-
-# Define all listing files.
-LST = $(SRC:.c=.lst) $(ASRC:.S=.lst)
-
-
-# Compiler flags to generate dependency files.
-GENDEPFLAGS = -MD -MP -MF .dep/$(@F).d
-
-
-# Combine all necessary flags and optional flags.
-# Add target processor to flags.
-ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)
-ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)
-
-
-# Default target.
-all: begin gccversion sizebefore build sizeafter end
-
-build: elf hex eep lss sym
-
-elf: $(TARGET).elf
-hex: $(TARGET).hex
-eep: $(TARGET).eep
-lss: $(TARGET).lss
-sym: $(TARGET).sym
-
-
-# Eye candy.
-# AVR Studio 3.x does not check make's exit code but relies on
-# the following magic strings to be generated by the compile job.
-begin:
-       @echo
-       @echo $(MSG_BEGIN)
-
-end:
-       @echo $(MSG_END)
-       @echo
-
+RM = rm -rf
 
-# Display size of file.
-HEXSIZE = $(SIZE) --target=$(FORMAT) $(TARGET).hex
-ELFSIZE = $(SIZE) -A $(TARGET).elf
-AVRMEM = avr-mem.sh $(TARGET).elf $(MCU)
+define SELECT_DEFAULT_template =
+elf: $(1).elf
+hex: $(1).hex
+eep: $(1).eep
+lss: $(1).lss
+sym: $(1).sym
+clean: $(1)_clean
+build: $(1)_build
+program: $(1)_program
+fuses: $(1)_fuses
+endef
 
-sizebefore:
-       @if test -f $(TARGET).elf; then echo; echo $(MSG_SIZE_BEFORE); $(ELFSIZE); \
-       $(AVRMEM) 2>/dev/null; echo; fi
+all: build
 
-sizeafter:
-       @if test -f $(TARGET).elf; then echo; echo $(MSG_SIZE_AFTER); $(ELFSIZE); \
-       $(AVRMEM) 2>/dev/null; echo; fi
-
-
-
-# Display compiler version information.
-gccversion :
-       @$(CC) --version
-
-
-
-# Program the device.
-program: $(TARGET).hex $(TARGET).eep
-       $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) $(AVRDUDE_WRITE_EEPROM)
-
-fuses:
-       $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FUSES)
-
-# Generate avr-gdb config/init file which does the following:
-#     define the reset signal, load the target file, connect to target, and set
-#     a breakpoint at main().
-gdb-config:
-       @$(REMOVE) $(GDBINIT_FILE)
-       @echo define reset >> $(GDBINIT_FILE)
-       @echo SIGNAL SIGHUP >> $(GDBINIT_FILE)
-       @echo end >> $(GDBINIT_FILE)
-       @echo file $(TARGET).elf >> $(GDBINIT_FILE)
-       @echo target remote $(DEBUG_HOST):$(DEBUG_PORT)  >> $(GDBINIT_FILE)
-ifeq ($(DEBUG_BACKEND),simulavr)
-       @echo load  >> $(GDBINIT_FILE)
-endif
-       @echo break main >> $(GDBINIT_FILE)
-
-debug: gdb-config $(TARGET).elf
-ifeq ($(DEBUG_BACKEND), avarice)
-       @echo Starting AVaRICE - Press enter when "waiting to connect" message displays.
-       @$(WINSHELL) /c start avarice --jtag $(JTAG_DEV) --erase --program --file \
-       $(TARGET).elf $(DEBUG_HOST):$(DEBUG_PORT)
-       @$(WINSHELL) /c pause
-
-else
-       @$(WINSHELL) /c start simulavr --gdbserver --device $(MCU) --clock-freq \
-       $(DEBUG_MFREQ) --port $(DEBUG_PORT)
-endif
-       @$(WINSHELL) /c start avr-$(DEBUG_UI) --command=$(GDBINIT_FILE)
-
-
-# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB.
-COFFCONVERT=$(OBJCOPY) --debugging \
---change-section-address .data-0x800000 \
---change-section-address .bss-0x800000 \
---change-section-address .noinit-0x800000 \
---change-section-address .eeprom-0x810000
-
-
-coff: $(TARGET).elf
-       @echo
-       @echo $(MSG_COFF) $(TARGET).cof
-       $(COFFCONVERT) -O coff-avr $< $(TARGET).cof
-
-
-extcoff: $(TARGET).elf
-       @echo
-       @echo $(MSG_EXTENDED_COFF) $(TARGET).cof
-       $(COFFCONVERT) -O coff-ext-avr $< $(TARGET).cof
+cleanall: $(foreach prog, $(PROGRAMS), $(prog)_clean)
+buildall: $(foreach prog, $(PROGRAMS), $(prog)_build)
 
+.PHONY: clean build program cleanall buildall all
 
+$(eval $(call SELECT_DEFAULT_template, $(PROG)))
 
 # Create final output files (.hex, .eep) from ELF output file.
 %.hex: %.elf
-       @echo
-       @echo $(MSG_FLASH) $@
-       $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@
+       @echo HEX $@
+       $(Q)$(OBJCOPY) -O ihex -R .fuse -R .eeprom $< $@
 
 %.eep: %.elf
-       @echo
-       @echo $(MSG_EEPROM) $@
-       -$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \
-       --change-section-lma .eeprom=0 -O $(FORMAT) $< $@
+       @echo EEP $@
+       $(Q)$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \
+       --change-section-lma .eeprom=0 -O ihex $< $@
 
 # Create extended listing file from ELF output file.
 %.lss: %.elf
-       @echo
-       @echo $(MSG_EXTENDED_LISTING) $@
-       $(OBJDUMP) -h -S $< > $@
+       @echo LSS $@
+       $(Q)$(OBJDUMP) -h -S $< > $@
 
 # Create a symbol table from ELF output file.
 %.sym: %.elf
-       @echo
-       @echo $(MSG_SYMBOL_TABLE) $@
-       $(NM) -n $< > $@
-
-
+       @echo SYM $@
+       $(Q)$(NM) -n $< > $@
 
 # Link: create ELF output file from object files.
-.SECONDARY : $(TARGET).elf
-.PRECIOUS : $(OBJ)
 %.elf: $(OBJ)
-       @echo
-       @echo $(MSG_LINKING) $@
-       $(CC) $(ALL_CFLAGS) $^ --output $@ $(LDFLAGS)
-
+       @echo LD $@
+       $(Q)$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
 
 # Compile: create object files from C source files.
 %.o : %.c Makefile
-       @echo
-       @echo $(MSG_COMPILING) $<
-       $(CC) -c $(ALL_CFLAGS) $< -o $@
-
+       @echo CC $@
+       $(Q)$(CC) -c $(CFLAGS) $< -o $@
 
 # Compile: create assembler files from C source files.
 %.s : %.c Makefile
-       $(CC) -S $(ALL_CFLAGS) $< -o $@
-
+       @echo CC-ASM $<
+       $(Q)$(CC) -S $(CFLAGS) $< -o $@
 
 # Assemble: create object files from assembler source files.
 %.o : %.S
-       @echo
-       @echo $(MSG_ASSEMBLING) $<
-       $(CC) -c $(ALL_ASFLAGS) $< -o $@
+       @echo ASM $@
+       $(Q)$(CC) -c $(ASFLAGS) $< -o $@
 
-# Create preprocessed source for use in sending a bug report.
-%.i : %.c
-       $(CC) -E -mmcu=$(MCU) -I. $(CFLAGS) $< -o $@
+########################### The big template for each program ##################
+define PROGRAM_template= 
 
+# Determine program's object files
+$(1)_OBJS=$$($(1)_SRC:.c=.o)
+$(1)_OBJS+=$$($(1)_ASRC:.S=.o)
 
-# Target: clean project.
-clean: begin clean_list end
+# Collect all listing files - for cleanup
+$(1)_LST += $$($(1)_SRC:.c=.lst) $$($(1)_ASRC:.S=.lst)
 
-clean_list :
-       @echo
-       @echo $(MSG_CLEANING)
-       $(REMOVE) $(TARGET).hex
-       $(REMOVE) $(TARGET).eep
-       $(REMOVE) $(TARGET).cof
-       $(REMOVE) $(TARGET).elf
-       $(REMOVE) $(TARGET).map
-       $(REMOVE) $(TARGET).sym
-       $(REMOVE) $(TARGET).lss
-       $(REMOVE) $(OBJ)
-       $(REMOVE) $(LST)
-       $(REMOVE) $(SRC:.c=.s)
-       $(REMOVE) $(SRC:.c=.d)
-       $(REMOVE) .dep
-       $(REMOVE) *~
+$(1): $(1).elf
+$(1).elf: $$($(1)_OBJS)
 
+$(1)_program : $(1).hex $(1).eep
+       @echo PROG $(1)
+       $(Q)$(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:w:$(1).hex -U eeprom:w:$(1).eep
 
+$(1)_unfug:
+       @echo UNFUG $$@
 
-# Include the dependency files.
--include $(shell mkdir .dep 2>/dev/null) $(wildcard .dep/*)
+$(1)_fuses.bin: $(1).elf
+       @echo OBJCOPY $$@
+       $(Q)$(OBJCOPY) -j .fuse $(1).elf -O binary $$@
+       $(Q)chmod -x $$@
 
+$(1)_fuses: $(1)_fuses.bin
+       @echo FUSES $$@
+       $(Q)$(AVRDUDE) $(AVRDUDE_FLAGS)\
+       -U lfuse:w:0x$$$$(hd $(1)_fuses.bin| awk '{print $$$$2}'):m \
+       -U hfuse:w:0x$$$$(hd $(1)_fuses.bin| awk '{print $$$$3}'):m \
+       -U efuse:w:0x$$$$(hd $(1)_fuses.bin| head -n1 |awk '{print $$$$4}'):m
 
-# Listing of phony targets.
-.PHONY : all begin finish end sizebefore sizeafter gccversion \
-build elf hex eep lss sym coff extcoff \
-clean clean_list program debug gdb-config
+$(1)_size: $(1).elf
+       @echo; echo
+       $(Q) $(SIZE) -A $(1).elf
 
+# Target: clean project.
+$(1)_clean:
+       @echo CLEAN $(1)
+       $(Q)$(RM) $(1).hex
+       $(Q)$(RM) $(1).eep
+       $(Q)$(RM) $(1).cof
+       $(Q)$(RM) $(1).elf
+       $(Q)$(RM) $(1).map
+       $(Q)$(RM) $(1).sym
+       $(Q)$(RM) $(1).lss
+       $(Q)$(RM) $$($(1)_OBJS)
+       $(Q)$(RM) $$($(1)_LST)
+       $(Q)$(RM) $$($(1)_SRC:.c=.s)
+       $(Q)$(RM) $$($(1)_SRC:.c=.d)
+       @$(Q)$(RM) $(1)_fuses.bin
+
+$(1)_build: $(1).hex $(1)_size
+
+-include $$($(1)_OBJS:.o=.d)
+
+.PRECIOUS : $$($(1)_OBJS)
+.PHONY: $(1) $(1)_clean $(1)_size $(1)_build
+
+#############
+endef
+################################################################################
+
+$(foreach prog,$(PROGRAMS),$(eval $(call PROGRAM_template,$(prog))))
index dbce67fb740ba9d30d922ca3ecd6fa4fc8f476b1..ffa98a9dd3b8e67caf03ee13005251987636f72b 100644 (file)
@@ -12,7 +12,7 @@
 
 #define STOP_TIMER1 ({TCCR1 = 0; TCNT1 = 1;})
 
-#define PWM_CYCLE 21
+#define PWM_CYCLE 14
 
 static inline void setup_hw(void)
 {
@@ -20,16 +20,15 @@ static inline void setup_hw(void)
        CLKPR = (1 << CLKPCE);
        CLKPR = 0;
 
+       setpin(PIN_LED, 0);
+       setpin(PIN_TRIGGER, 0);
+       setpin(PIN_BTN, 1);   /* Need pullup              */
        INPUT_PIN(PIN_SENSE);
        INPUT_PIN(PIN_BTN);
        OUTPUT_PIN(PIN_DRIVE);
        OUTPUT_PIN(PIN_LED);
        OUTPUT_PIN(PIN_TRIGGER);
-       setpin(PIN_LED, 0);
-       setpin(PIN_TRIGGER, 0);
-               
-       setpin(PIN_BTN, 1);   /* Need pullup              */
-       
+
        GIMSK |= _BV(PCIE);     /* Enable pin change interrupt for sense port */
        PCMSK |= _BV(PCINT4);    /* PB4, Rail sense input */
 
@@ -39,12 +38,11 @@ static inline void setup_hw(void)
        /* Setup timer 0, used for mm_switch */
         TCCR0A = 0; /* Normal mode */
        TCCR0B = 2; /* Prescaler 8 */
-       //      TIMSK |= _BV(OCIE0A);   /* Get a match interrupt */
+
        TIMSK |= _BV(TOIE0);
-       OCR0A = 100; 
        TCCR0B = 2;
 
-#ifdef WITH_PWM        
+#ifdef WITH_PWM
        /* Timer 1 as PWM */
        OCR1C = PWM_CYCLE;
        OCR1B = PWM_CYCLE;
@@ -67,7 +65,6 @@ static inline void __attribute__((unused)) trigger(void)
        setpin(PIN_TRIGGER, 0);
 }
 
-
 static inline void __attribute__((unused)) trigger_on(void)
 {
        setpin(PIN_TRIGGER, 1);
@@ -91,7 +88,9 @@ static inline void __attribute__((unused)) trigger_off(void)
        }
 
 #define MM_RESETFLAG  TIFR   |= _BV(OVF0)
-
+//#define MM_USE_QUEUE
+#define MM_QUEUE_DEPTH 8
+#define MM_USE_CALLBACK
 
 #ifdef USE_REGISTER_VARS
 uint8_t register drive_on asm("r12");
diff --git a/trennfix/sw/mk/hw/trennfix_0.4.mk b/trennfix/sw/mk/hw/trennfix_0.4.mk
new file mode 100644 (file)
index 0000000..1e14a25
--- /dev/null
@@ -0,0 +1,16 @@
+AVRDUDE_PROGRAMMER = avrisp2
+MCU?=attiny25
+F_CPU = 16000000
+
+CFLAGS+=-D__trennfix_0_4__ 
+
+CFLAGS+= -DDEBUG_DRIVE_LED
+CFLAGS+= -DUSE_REGISTER_VARS
+CFLAGS+= -DMM_USE_REGISTER_VARS
+
+CFLAGS+= -DWITH_EEPROM_UPDATE
+CFLAGS+= -DWITH_PWM
+CFLAGS+= -DWITH_SOUND
+CFLAGS+= -DWITH_INITIAL_PULSE
+
+
diff --git a/trennfix/sw/mk/prog/smokefix.mk b/trennfix/sw/mk/prog/smokefix.mk
new file mode 100644 (file)
index 0000000..1e1d516
--- /dev/null
@@ -0,0 +1,3 @@
+
+PROGRAMS+=smokefix
+smokefix_SRC=src/smokefix_main.c mm/src/mm_switch.c
diff --git a/trennfix/sw/mk/prog/trennfix.mk b/trennfix/sw/mk/prog/trennfix.mk
new file mode 100644 (file)
index 0000000..c5888b4
--- /dev/null
@@ -0,0 +1,3 @@
+
+PROGRAMS+=trennfix
+trennfix_SRC=src/trennfix_main.c mm/src/mm_switch.c
diff --git a/trennfix/sw/mk/trennfix_0.4.mk b/trennfix/sw/mk/trennfix_0.4.mk
deleted file mode 100644 (file)
index b3558ce..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-
-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
-
-ifneq ($(KEEP_EEPROM),)
-
-HFUSE=0xd4
-
-else
-
-HFUSE=0xdc
-
-endif
-
-LFUSE=0x61
-EFUSE=0xff
-
-CFLAGS+= -DUSE_REGISTER_VARS
-CFLAGS+= -DMM_USE_REGISTER_VARS
-CFLAGS+= -DWITH_EEPROM_UPDATE
-#CFLAGS+= -DWITH_PWM
-CFLAGS+= -DWITH_SOUND
-CFLAGS+= -DWITH_INITIAL_PULSE
-
diff --git a/trennfix/sw/mm/include/mm/mm_decode.h b/trennfix/sw/mm/include/mm/mm_decode.h
new file mode 100644 (file)
index 0000000..5b5b7b2
--- /dev/null
@@ -0,0 +1,124 @@
+#ifndef __MM_DECODE_H
+#define __MM_DECODE_H
+
+/*
+ * 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 __mm_nibble_table[16]={
+       [0x0] = 0,
+       [0xc] = 1,
+       [0x8] = 2,
+       [0x3] = 3,
+       [0xf] = 4,
+       [0xb] = 5,
+       [0x2] = 6,
+       [0xe] = 7,
+       [0xa] = 8
+};
+#define __mm_lookup_nibble(nibble) __mm_nibble_table[nibble & 0xf]
+
+static uint8_t __attribute__((unused)) mm_lookup_decoder(uint8_t mm_byte)
+{
+       uint8_t low;
+       uint8_t high;
+       uint8_t retval;
+
+       if (mm_byte == 0)
+               return 80;
+       low  = __mm_lookup_nibble(mm_byte >> 4);
+       high = __mm_lookup_nibble(mm_byte & 0xf);
+       if (!low)
+               return 0;
+
+       /*      retval = 9 * high + low; */
+       retval = high << 3;
+       retval += high;
+       retval += low;
+       return retval;
+}
+
+static uint8_t __attribute__((unused)) mm_lookup_key(uint8_t mm_command)
+{
+       uint8_t res;
+       /*
+        * Check for aabbccdd condition
+        *
+        *        a  a  b  b  c  c  d  d   mm_command
+        *   XOR  a  b  b  c  c  d  d  0   mm_command << 1
+        * Mask   1  0  1  0  1  0  1  0   0xaa
+        *
+        * Must be zero!
+        *
+        */
+
+       if ((mm_command ^ (mm_command << 1)) & 0xaa)
+               return 0;
+       /*
+        * Protocol differences:
+        * =====================
+        *
+        * I have an old "central control" 6022 and a "control unit" 6021
+        * for measurements and test. It is assumed that the 6022 outputs
+        * old MM1 format while the 6021 definitively outputs MM2 telegrams.
+        *
+        * In MM1, switch commands are different from MM2 with respect what
+        * happens if you release a button.
+        *
+        * When you press a button, both protocols send
+        *
+        * <aaaaaaaa><00><aabbcc11>
+        *
+        * where a = 1, b = 2, c = 4 and the keys are numerated from 0 to 7
+        * in the order 1 red, 1 green, 2 red, 2 green and so on.
+        *
+        * The last two bits correspond to "on" state of the button/coil.
+        *
+        * When a key is released under MM1 protocol, the sequence sent is
+        * analogue to the button down sequence:
+        *
+        * <aaaaaaaa><00><aabbcc00> where abc again represents the button's
+        * address and the last bits now signal "off".
+        *
+        * MM2 handles this differently:
+        * Whenever any key from the addressed decoder is released, the sequence
+        * <aaaaaaaa>00<00000000> is sent - not only for key 0, but for all
+        * keys!
+        *
+        * While MM1 presents the theoretical possibility to press several keys
+        * independently and simultaneously (which my keyboard does NOT
+        * support), MM2 supports only one key at a time (besides strange
+        * sequences like "one down, another down, all up"...
+        *
+        * A decoder that strictly adheres to the MM1 standard would not work
+        * properly with MM2 control units. As far as I know all K83/K84
+        * decoders always worked with MM2 control units. That means that
+        * they reduce the commands to the possibilities of MM2 from the
+        * beginning.
+        *
+        * Possible use cases for the old protocol button release commands:
+        *   - Determine if the protocol is MM1 or MM2
+        *   - Implement hidden evil features into the controller which can
+        *     only be summoned by old MM1 gear or selfmade control telegram
+        *     generators.
+        *
+        * What this code now actually does:
+        * =================================
+        *
+        * When key pressed (aabbcc11), it will send out the key number in the
+        * range 1-8 and 0 if it gets any key up command and therefore ignore
+        * the key number if it is transmitted with the key up command.
+        *
+        */
+       if (!(mm_command & 0x01))
+               res = 0;
+       else
+               res = (mm_command & 0x80) * 1 + (mm_command & 0x20) * 0x02
+                       + (mm_command & 0x08) * 0x04 + 1;
+       return res;
+}
+
+#endif
index 240bea9b886a21f71d79077c27f1eac6cf5e6825..fb6cccac063ce33a22a3af765448b53279cc69b9 100644 (file)
@@ -33,17 +33,17 @@ static void inline __attribute((unused)) mm_init(void)
 
 #endif
 
-
+#ifdef MM_USE_CALLBACK
 
 /*
- * mm_switch_command - Callback function!
+ * mm_key_cb - 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
@@ -52,17 +52,33 @@ static void inline __attribute((unused)) mm_init(void)
  *   4 - key2 red pressed
  *   ...
  *   0 - all keys up
- * 
+ *
  */
-void mm_switch_command(uint8_t decoder, uint8_t key);
+void mm_key_cb(uint8_t decoder, uint8_t function, uint8_t key);
+void mm_drive_cb(uint8_t decoder, uint8_t function, uint8_t command);
 
-/*
- *  empty comment :-)
- */
-void mm_switch_drive_cb(uint8_t address, uint8_t speed, uint8_t functions, uint8_t flags);
+#endif
+
+void mm_pinchange_callback(void);
+
+enum mm_recmode {
+       __MM_INIT,
+       __MM_ARMED,
+       MM_SLOW,
+       MM_FAST,
+};
+
+#ifdef MM_USE_QUEUE
+
+struct mm_command {
+       enum mm_recmode recmode; /* Only MM_FAST or MM_SLOW!       */
+       uint8_t address;         /* Undecoded loco/decoder address */
+       uint8_t function;        /* The two function bits          */
+       uint8_t command;         /* Button/Speed/Functions         */
+};
 
-void mm_switch_drive(uint8_t decoder, uint8_t function, uint8_t command);
+struct mm_command *mm_get(void);
 
-void mm_switch_pinchange_callback(void);
+#endif /* MM_USE_QUEUE */
 
 #endif
index 35016fcd4769d6826847d12ef697b992bdfcd7e0..a960355298ba8aac8f1354f862eebf1e34d70144 100644 (file)
@@ -35,7 +35,7 @@
 
 #include <config/hardware.h>
 #include <mm/mm_switch.h>
-
+#include <mm/mm_decode.h>
 
 /*
  *
 #error Missing needed MM_... macro!
 #endif
 
-/*
- *    Private global variables
- */
+#ifndef MM_QUEUE_DEPTH
+#define MM_QUEUE_DEPTH 8
+#endif
 
-enum recmode {
-       INIT,
-       ARMED,
-       MM_SLOW,
-       MM_FAST,
-       DCC,
-};
-enum recmode recmode = INIT;
+
+       /*
+        * 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.
+        *
+        * Keep in mind: Time is counted in 500ns steps.
+        *
+        */
+#define IN_RANGE(duration, lower, upper)               \
+       ((duration >= (2 * lower)                       \
+         && (duration <= (2 * upper))))
+
+#define MM_FAST_SHORT(duration) IN_RANGE(duration,   7,  18)
+#define MM_FAST_LONG(duration)  IN_RANGE(duration,  70, 130)
+#define MM_FAST_GAP(duration)   IN_RANGE(duration, 500, 1100)
+
+#define MM_SLOW_SHORT(duration) IN_RANGE(duration,   18,   35)
+#define MM_SLOW_LONG(duration)  IN_RANGE(duration,  150,  210)
+#define MM_SLOW_GAP(duration)   IN_RANGE(duration, 1000, 2000)
+
+#define MM_SUFFICIENT_IDLE(duration) (duration > 800)
+
+
+
+static enum mm_recmode recmode = __MM_INIT;
+
+#ifdef MM_USE_QUEUE
+static uint8_t queue_wpos = 0;
+static uint8_t queue_rpos = 0;
+static struct mm_command queue[MM_QUEUE_DEPTH];
+#endif
 
 #ifndef MM_USE_REGISTER_VARS
 
@@ -73,126 +103,6 @@ static uint8_t mm_polarity = 1;
 
 #endif
 
-/*
- * 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 __attribute__((unused)) lookup_decoder(uint8_t mm_byte)
-{
-       uint8_t low;
-       uint8_t high;
-       uint8_t retval;
-
-       if (mm_byte == 0)
-               return 80;
-       low = lookup_nibble(mm_byte >> 4);
-       high = lookup_nibble(mm_byte & 0xf);
-       if (!low)
-               return 0;
-
-       /*      retval = 9 * high + low; */
-       retval = high << 3;
-       retval += high;
-       retval += low;
-       return retval;
-}
-
-static uint8_t __attribute__((unused)) lookup_command(uint8_t mm_command)
-{
-       uint8_t res;
-       /*
-        * Check for aabbccdd condition
-        * 
-        *        a  a  b  b  c  c  d  d   mm_command
-        *   XOR  a  b  b  c  c  d  d  0   mm_command << 1
-        * Mask   1  0  1  0  1  0  1  0   0xaa
-        *
-        * Must be zero!
-        *
-        */
-
-       if ((mm_command ^ (mm_command << 1)) & 0xaa)
-               return 0;
-       /*
-        * Protocol differences:
-        * =====================
-        *
-        * I have an old "central control" 6022 and a "control unit" 6021
-        * for measurements and test. It is assumed that the 6022 outputs
-        * old MM1 format while the 6021 definitively outputs MM2 telegrams.
-        * 
-        * In MM1, switch commands are different from MM2 with respect what
-        * happens if you release a button.
-        *
-        * When you press a button, both protocols send
-        *
-        * <aaaaaaaa><00><aabbcc11>
-        *
-        * where a = 1, b = 2, c = 4 and the keys are numerated from 0 to 7
-        * in the order 1 red, 1 green, 2 red, 2 green and so on.
-        * 
-        * The last two bits correspond to "on" state of the button/coil.
-        *
-        * When a key is released under MM1 protocol, the sequence sent is
-        * analogue to the button down sequence:
-        * 
-        * <aaaaaaaa><00><aabbcc00> where abc again represents the button's
-        * address and the last bits now signal "off".
-        *
-        * MM2 handles this differently:
-        * Whenever any key from the addressed decoder is released, the sequence
-        * <aaaaaaaa>00<00000000> is sent - not only for key 0, but for all
-        * keys!
-        *
-        * While MM1 presents the theoretical possibility to press several keys
-        * independently and simultaneously (which my keyboard does NOT
-        * support), MM2 supports only one key at a time (besides strange
-        * sequences like "one down, another down, all up"...
-        * 
-        * A decoder that strictly adheres to the MM1 standard would not work
-        * properly with MM2 control units. As far as I know all K83/K84
-        * decoders always worked with MM2 control units. That means that
-        * they reduce the commands to the possibilities of MM2 from the
-        * beginning.
-        *
-        * Possible use cases for the old protocol button release commands:
-        *   - Determine if the protocol is MM1 or MM2
-        *   - Implement hidden evil features into the controller which can
-        *     only be summoned by old MM1 gear or selfmade control telegram
-        *     generators.
-        *
-        * What this code now actually does:
-        * =================================
-        *
-        * When key pressed (aabbcc11), it will send out the key number in the
-        * range 1-8 and 0 if it gets any key up command and therefore ignore
-        * the key number if it is transmitted with the key up command.
-        *
-        */
-       if (!(mm_command & 0x01))
-               res = 0;
-       else
-               res = (mm_command & 0x80) * 1 + (mm_command & 0x20) * 0x02
-                       + (mm_command & 0x08) * 0x04 + 1;
-       return res;
-}
-
 /* We will shift from right to left.
  * XXXXXXXX        XX          XXXXXXXX
  * shift_address   shift_function  shift_command
@@ -205,7 +115,7 @@ static uint8_t __attribute__((unused)) lookup_command(uint8_t mm_command)
  * The real shift function is written in assembly and saves many instructions.
  */
 #if 0
-void shift(uint8_t value)
+static void shift(uint8_t value)
 {
        shift_address <<= 1;
        if (shift_function & 2)
@@ -219,7 +129,7 @@ void shift(uint8_t value)
 }
 #else
 
-void shift(uint8_t value)
+static void shift(uint8_t value)
 {
        asm("ror %[val]                 ; Shift value right into carry\n\t"
            "rol %[cmd]                 ; and shift to command reg\n\t"
@@ -242,7 +152,6 @@ static void mm_feed_bit(uint8_t bit)
        static volatile uint8_t shift_function_first;
        static volatile uint8_t shift_address_first;
        uint8_t address;
-       uint8_t command;
 
        shift(bit);
        mm_bitno++;
@@ -251,27 +160,48 @@ static void mm_feed_bit(uint8_t bit)
                shift_address_first  = shift_address;
                shift_function_first = shift_function;
                shift_command_first  = shift_command;
-       } 
+       }
 
        if (mm_bitno == 36) {
-
                if ((shift_command == shift_command_first) &&
                    (shift_address == shift_address_first) &&
                    (shift_function == shift_function_first)) {
-                       address = lookup_decoder(shift_address);
-                       if (recmode == MM_SLOW) {
-                               trigger();
 
-                               mm_switch_drive(address, shift_function,
-                                               shift_command);
+#ifdef MM_USE_CALLBACK
+                       address = mm_lookup_decoder(shift_address);
+                       if (recmode == MM_SLOW) {
+                               mm_drive_cb(address, shift_function,
+                                           shift_command);
                        } else {
-                               command = lookup_command(shift_command);
-                               mm_switch_command(address, command);
-                       }                               
+                               mm_key_cb(address, shift_function, shift_command);
+                       }
+#endif
+
+#ifdef MM_USE_QUEUE
+                       queue[queue_wpos].recmode  = recmode;
+                       queue[queue_wpos].address  = shift_address;
+                       queue[queue_wpos].function = shift_function;
+                       queue[queue_wpos].command  = shift_command;
+                       queue_wpos = (queue_wpos + 1) % MM_QUEUE_DEPTH;
+#endif
                }
-               
        }
 }
+#ifdef MM_USE_QUEUE
+
+struct mm_command *mm_get(void)
+{
+       struct mm_command *result = NULL;
+       uint8_t sreg_bak = SREG;
+       cli();
+       if (queue_rpos != queue_wpos) {
+               result = &queue[queue_rpos];
+               queue_rpos = (queue_rpos + 1) % MM_QUEUE_DEPTH;
+       }
+       SREG = sreg_bak;
+       return result;
+}
+#endif
 
 /*
  *  The timeout interrupt vector does nothing else
@@ -331,14 +261,14 @@ void __attribute__((naked)) PCINT0_vect(void)
            );
 #endif
 }
-       
+
 /* Pin change interrupt vector, here we have a bit more time */
 ISR(__vector_pinchange){
        uint16_t duration;
 
        /* First kill off that timer */
        MM_TSTART;  /* Restart timer */
-       
+
        /* Account for not yet handled timer overflow */
        TIFR |= _BV(TOV0);
 
@@ -347,131 +277,81 @@ ISR(__vector_pinchange){
        mm_bit_val = MM_SENSE;
        mm_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.
-        *
-        * Keep in mind: Time is counted in 500ns steps.
-        *
-        */
-#define D_MATCH(d, v) ((duration > (d * 2 - 2 * v)) && (duration < (d * 2 + 2 * v)))
-
-       switch(recmode) {
-       case INIT:
-       default:
-               break;
-       case ARMED:
-               /* Maerklin only interested when signal is 1 */
-               if (mm_bit_val == mm_polarity) {
-                       /* Fast short MM pulse (logical 0) */
-                       if (D_MATCH(13, 4)){
-                               recmode = MM_FAST;
+       if (recmode != MM_SLOW) {
+               /* Fast short MM pulse */
+               if (MM_FAST_SHORT(duration)){
+                       recmode = MM_FAST;
+                       if (mm_bit_val == mm_polarity)
                                mm_feed_bit(0);
-                               goto done;
-                       }
-                       
-                       /* Slow short MM pulse (logical 0) */
-                       if (D_MATCH(26, 4)) {
-                               recmode = MM_SLOW;
-                               mm_feed_bit(0);
-                               goto done;
-                       }
-
-                       /* Fast long MM pulse (logical 1) */
-                       if (D_MATCH(91, 17)) {
-                               recmode = MM_FAST;
-                               mm_feed_bit(1);
-                               goto done;
-                       }
-
-                       /* Slow long MM pulse (logical 1) */
-                       if (D_MATCH(182, 25)) {
-                               recmode = MM_SLOW;
+                       goto done;
+               }
+               /* Fast long MM pulse */
+               if (MM_FAST_LONG(duration)) {
+                       recmode = MM_FAST;
+                       if (mm_bit_val == mm_polarity)
                                mm_feed_bit(1);
+                       goto done;
+               }
+       } else {
+               /* Accepted slow inter package gap */
+               if (MM_SLOW_GAP(duration))
+                       if (mm_bit_val != mm_polarity)
                                goto done;
-                       }
-               } 
-               break;
+       }
 
-       case MM_FAST:
-               if (mm_bit_val == mm_polarity) {
+       if (recmode != MM_FAST) {
 
-                       /* Fast short MM pulse (logical 0) */
-                       if (D_MATCH(13, 4)){
+               /* Slow short MM pulse */
+               if (MM_SLOW_SHORT(duration)) {
+                       recmode = MM_SLOW;
+                       if (mm_bit_val == mm_polarity)
                                mm_feed_bit(0);
-                               goto done;
-                       }
-                       /* Fast long MM pulse (logical 1) */
-                       if (D_MATCH(91, 17)) {
-                               mm_feed_bit(1);
-                               goto done;
-                       }
-               } else {
-                       if (D_MATCH(13, 4))
-                               goto done;
-                       if (D_MATCH(91, 17))
-                               goto done;
-                       
-                       if (D_MATCH(700, 200))
-                               goto done;
+                       goto done;
                }
-               break;
-                       
-       case MM_SLOW:
-               if (mm_bit_val == mm_polarity) {
-                       /* Slow short MM pulse (logical 0) */
-                       if (D_MATCH(26, 4)) {
-                               mm_feed_bit(0);
-                               goto done;
-                       }
-                       /* Slow long MM pulse (logical 1) */
-                       if (D_MATCH(182, 40)) {
+               /* Slow long MM pulse */
+               if (MM_SLOW_LONG(duration)) {
+                       recmode = MM_SLOW;
+                       if (mm_bit_val == mm_polarity)
                                mm_feed_bit(1);
-                               goto done;
-                       }
-               } else {
-                       if (D_MATCH(26, 4))
-                               goto done;
-                       if (D_MATCH(182, 40))
-                               goto done;
-                       
-                       /* Accepted inter package gap */
-                       if (D_MATCH(1400, 400))
+                       goto done;
+               }
+       } else {
+               /* Accepted fast interpackage gap */
+               if (MM_FAST_GAP(duration)) {
+                       if (mm_bit_val != mm_polarity)
                                goto done;
                }
-               break;
        }
 
-       /* 
+       /*
         * If we have reached here, our pulse comes in somehow unexpected.
         * We kill of everything by re-arming the state machine.
         */
        /* Start over receiver */
        mm_bitno = 0;
-       mm_polarity = !mm_bit_val;
-       recmode = ARMED;
+
+       if (MM_SUFFICIENT_IDLE(duration)) {
+               recmode = __MM_ARMED;
+               mm_polarity = !mm_bit_val;
+       } else {
+               recmode = __MM_INIT;
+       }
  done:
-       mm_switch_pinchange_callback();
+       mm_pinchange_callback();
  }
 
 
-void __attribute__((weak))mm_switch_pinchange_callback(void)
+void __attribute__((weak))mm_pinchange_callback(void)
 {
 }
 
-void __attribute__((weak))mm_switch_drive(uint8_t decoder, uint8_t function,
+void __attribute__((weak))mm_drive_cb(uint8_t decoder, uint8_t function,
                                          uint8_t command)
 {
 }
 
-void __attribute__((weak))mm_switch_command(uint8_t address, uint8_t command)
+void __attribute__((weak))mm_key_cb(uint8_t address,  uint8_t function,
+                                   uint8_t command)
 {
 }
 
diff --git a/trennfix/sw/src/main.c b/trennfix/sw/src/main.c
deleted file mode 100644 (file)
index 3e7d265..0000000
+++ /dev/null
@@ -1,514 +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 <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 <mm/mm_switch.h>
-#include <config/hardware.h>
-
-#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_EASY_WAIT_PRESS,  /* Wait for key press to enter easy mode       */
-       LM_EASY_WAIT_UP,     /* Wait for end of key press                   */
-       LM_EASY_WAIT_TURN,   /* Wait for loco 80 turn end                   */
-       LM_EASY_MODE,        /* Easy config mode for PWM and initial kick   */
-       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 easy_mode = 0;
-static volatile uint8_t easy_mode_possible = 0;
-
-#ifndef USE_REGISTER_VARS
-static volatile uint8_t drive_on = 0;
-#endif
-
-/******************************************************************************
- *
- * Some nice sounds to play on your coil
- *
- */
-
-#define G 180
-#define sekunde(g)(quinte((quinte(g)))*2)
-#define terz(g) ((g) * 4 / 5)
-#define kleine_terz(g) ((g) * 5 / 6)
-#define quarte(g) ((g) * 3 / 4)
-#define quinte(g) ((g) * 2 / 3)
-#define tt(g) ((g) * 32 / 45)
-#define septime(g) ((g) * 15 / 8)
-
-#if defined(WITH_SOUND) && defined(WITH_PWM)
-void play_tone(uint8_t divisor, uint8_t duration, uint8_t pause)
-{
-       uint16_t c;
-       TCCR1 = 0x8;
-       OCR1C = divisor;
-       c = (divisor * 2) / 3;
-       OCR1B = c;
-       for (c = 0; c < duration - pause; c++)
-               _delay_ms(2);
-       TCCR1 = 0x6;
-       OCR1C = PWM_CYCLE;
-       OCR1B = 12;
-       for (c = 0; c < pause; c++)
-               _delay_ms(2);
-       TCCR1 = 0x6;
-       OCR1C = PWM_CYCLE;
-       OCR1B = PWM_CYCLE;
-}
-
-static void tone_enter(void)
-{
-       play_tone((G), 70, 20);
-       play_tone(terz(G), 70, 20);
-       play_tone(quinte(G), 70, 20);
-       play_tone(G/2, 100, 0);
-}
-
-static void tone_good(void)
-{
-       play_tone(G, 150, 120);
-       play_tone(terz(G), 100, 70);
-       play_tone(tt(G), 100, 50);
-       play_tone(quarte(terz((G))), 50, 0);
-       play_tone(quinte(G), 150, 0);
-       play_tone(terz(G), 100, 50);            
-}
-
-static void snd_on(void)
-{
-       TCCR1 = 0x8;
-       OCR1C = 120;
-       OCR1B = 60;
-}
-
-static void snd_off(void)
-{
-       TCCR1 = 0x6;
-       OCR1C = PWM_CYCLE;
-       OCR1B = PWM_CYCLE;
-}
-
-#else
-#define play_tone(...)
-#define tone_enter(...)
-#define tone_good(...)
-#define snd_on(...)
-#define snd_off(...)
-#endif
-
-static void load_config(void)
-{
-       eeprom_read_block((uint8_t *)&config, &ee_config, sizeof(config));
-}
-
-static void save_config(void)
-{
-#ifdef WITH_EEPROM_UPDATE
-       eeprom_update_block((uint8_t *)&config, &ee_config, sizeof(config));
-#else
-       eeprom_write_block((uint8_t *)&config, &ee_config, sizeof(config));
-#endif
-}
-
-void mm_switch_pinchange_callback(void)
-{
-       static uint8_t btn_last = 0;
-
-       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);
-       b3 = ((command & 0x20) != 0);
-       b5 = ((command & 0x8) != 0);
-       b7 = ((command & 0x2) != 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 loco, uint8_t function, uint8_t command)
-{
-       uint8_t speed;
-       speed = get_speed(command);
-       static uint8_t alert_last = 0;
-
-       switch(loco) {
-       case 80:
-               switch (config.learn_mode) {
-               case LM_OFF:
-                       if (speed == 1)
-                               config.learn_mode = LM_EASY_WAIT_PRESS;
-                       break;
-               case LM_EASY_MODE:
-                       if ((speed == 1) && (alert_last == 0)) {
-                               config.learn_mode = LM_OFF;
-                               save_config();
-                               tone_good();
-                       }
-                       break;
-
-               case LM_EASY_WAIT_PRESS:
-                       if (speed != 1)
-                           config.learn_mode = LM_OFF;
-                       break;
-
-               default:
-                       break;
-               }
-               alert_last = (speed == 1);
-               break;
-
-       case 50:
-               if (speed)
-                       speed = speed - 1;
-
-               if (config.learn_mode == LM_EASY_MODE) {
-                       config.initial_pulse = speed;
-               }
-               break;
-       case 51:
-               if (speed)
-                       speed = speed - 1;
-
-               if (config.learn_mode == LM_EASY_MODE)
-                       config.on_duty_cycle = speed;
-               break;
-       }
-       
-}
-
-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:
-                               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;
-                       }
-               }
-               break;
-
-       case LM_EASY_WAIT_PRESS:
-               if ((decoder == config.decoder_on) &&
-                   (command == config.key_on))
-                       config.learn_mode = LM_EASY_WAIT_UP;
-               return;
-
-       case LM_EASY_WAIT_UP:
-               if ((decoder == config.decoder_on) &&
-                   (command == 0)) {
-                       config.learn_mode = LM_EASY_MODE;
-                       tone_enter();
-               }
-               return;
-       
-#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
- *
- */
-void shift(uint8_t mu);
-
-#ifdef WITH_PWM
-#define DRIVE_OFF  {OCR1B = PWM_CYCLE;}
-#define DRIVE_ON   {OCR1B = PWM_CYCLE - config.on_duty_cycle;}
-#define DRIVE_FULL {OCR1B = 0;}
-#else
-#define DRIVE_OFF  {setpin(PIN_DRIVE, 0); setpin(PIN_LED, 0);}
-#define DRIVE_ON   {setpin(PIN_DRIVE, 1); setpin(PIN_LED, 1);}
-#define DRIVE_FULL {setpin(PIN_DRIVE, 1); setpin(PIN_LED, 1);}
-#endif
-
-int main(void) {
-       uint16_t i;
-
-#ifdef WITH_INITIAL_PULSE
-       uint8_t drive_last = 0;
-       uint8_t drive_slope = 0;
-#endif
-
-#ifdef USE_REGISTER_VARS
-       drive_on = 0;
-#endif
-       mm_init();
-       load_config();
-       setup_hw();
-
-       if (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;
-       }
-       sei();
-       while (1) {
-       drive_start:
-
-#ifdef WITH_INITIAL_PULSE
-               cli();
-               if (drive_on && !drive_last)
-                       drive_slope = 1;
-               else
-                       drive_slope = 0;
-               drive_last = drive_on;
-               sei();
-#endif
-               if (drive_on) {
-
-#ifdef WITH_INITIAL_PULSE
-                       if (drive_slope) {
-                               DRIVE_FULL;
-                               for (i = 0; i < config.initial_pulse; i++) {
-                                       _delay_ms(5);
-                               }
-                       }
-#endif
-                       DRIVE_ON;
-               
-               } else {
-                       DRIVE_OFF;
-
-                       if (!config.learn_mode ||
-                           config.learn_mode > LM_LEARN_OP_MODE)
-                               continue;
-
-                       for (i = 0; i < config.learn_mode; i++) {
-                               setpin(PIN_LED, 1);
-                               snd_on();
-                               _delay_ms(10);
-                               setpin(PIN_LED, 0);
-                               snd_off();
-                               if (drive_on) goto drive_start;
-                               _delay_ms(135);
-                               if (drive_on) goto drive_start;
-                       }
-                       for (i = 0; i < 15 - config.learn_mode; i++) {
-                               if (drive_on) goto drive_start;
-                               _delay_ms(70);
-                       }
-               }
-       }
-       return 0;
-}
-
-/******************************************************************************
- *                        The end :-)
- */
diff --git a/trennfix/sw/src/smokefix_main.c b/trennfix/sw/src/smokefix_main.c
new file mode 100644 (file)
index 0000000..594f77f
--- /dev/null
@@ -0,0 +1,731 @@
+/******************************************************************************
+ *
+ *  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 <mm/mm_switch.h>
+#include <mm/mm_decode.h>
+#include <config/hardware.h>
+
+uint8_t eeFooByte EEMEM = 123;
+
+#define EE_MAGIC 0xab
+
+FUSES = {
+       .low = FUSE_CKDIV8 & FUSE_SUT0 &\
+       FUSE_CKSEL3 & FUSE_CKSEL2 & FUSE_CKSEL1,
+       .high = FUSE_SPIEN & FUSE_BODLEVEL1 & FUSE_BODLEVEL0 & FUSE_EESAVE,
+       .extended = EFUSE_DEFAULT,
+};
+
+void mm_key_cb(uint8_t decoder, uint8_t function, uint8_t key);
+void mm_drive_cb(uint8_t decoder, uint8_t function, uint8_t command);
+
+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_EASY_WAIT_PRESS,  /* Wait for key press to enter easy mode       */
+       LM_EASY_WAIT_UP,     /* Wait for end of key press                   */
+       LM_EASY_WAIT_TURN,   /* Wait for loco 80 turn end                   */
+       LM_EASY_MODE,        /* Easy config mode for PWM and initial kick   */
+       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[15]; /* Duty cycle for on. 0-10                  */
+        volatile enum learn_mode  learn_mode;
+};
+
+static uint8_t main_speed = 0;
+static struct EEMEM config ee_config;
+static volatile struct config config;
+
+static volatile uint8_t easy_mode = 0;
+static volatile uint8_t easy_mode_possible = 0;
+
+#ifndef USE_REGISTER_VARS
+static volatile uint8_t drive_on = 0;
+#endif
+
+/******************************************************************************
+ *
+ * Some nice sounds to play on your coil
+ *
+ */
+
+#define G 180
+#define sekunde(g)(quinte((quinte(g)))*2)
+#define terz(g) ((g) * 4 / 5)
+#define kleine_terz(g) ((g) * 5 / 6)
+#define quarte(g) ((g) * 3 / 4)
+#define quinte(g) ((g) * 2 / 3)
+#define tt(g) ((g) * 32 / 45)
+#define septime(g) ((g) * 15 / 8)
+
+#if defined(WITH_SOUND) && defined(WITH_PWM)
+void play_tone(uint8_t divisor, uint8_t duration, uint8_t pause)
+{
+       uint16_t c;
+       TCCR1 = 0x8;
+       OCR1C = divisor;
+       c = (divisor * 2) / 3;
+       OCR1B = c;
+       for (c = 0; c < duration - pause; c++)
+               _delay_ms(2);
+       TCCR1 = 0x6;
+       OCR1C = PWM_CYCLE;
+       OCR1B = 12;
+       for (c = 0; c < pause; c++)
+               _delay_ms(2);
+       TCCR1 = 0x6;
+       OCR1C = PWM_CYCLE;
+       OCR1B = PWM_CYCLE;
+}
+
+static void tone_enter(void)
+{
+       play_tone((G), 70, 20);
+       play_tone(terz(G), 70, 20);
+       play_tone(quinte(G), 70, 20);
+       play_tone(G/2, 100, 0);
+}
+
+static void tone_good(void)
+{
+       play_tone(G, 150, 120);
+       play_tone(terz(G), 100, 70);
+       play_tone(tt(G), 100, 50);
+       play_tone(quarte(terz((G))), 50, 0);
+       play_tone(quinte(G), 150, 0);
+       play_tone(terz(G), 100, 50);
+}
+
+static void snd_on(void)
+{
+       TCCR1 = 0x8;
+       OCR1C = 120;
+       OCR1B = 60;
+}
+
+static void snd_off(void)
+{
+       TCCR1 = 0x6;
+       OCR1C = PWM_CYCLE;
+       OCR1B = PWM_CYCLE;
+}
+
+#else
+#define play_tone(...)
+#define tone_enter(...) {setpin(PIN_LED, 1); _delay_ms(500); setpin(PIN_LED, 0);}
+#define tone_good(...)
+#define snd_on(...)
+#define snd_off(...)
+#endif
+
+static void load_config(void)
+{
+       eeprom_read_block((uint8_t *)&config, &ee_config, sizeof(config));
+}
+
+static void save_config(void)
+{
+#ifdef WITH_EEPROM_UPDATE
+       eeprom_update_block((uint8_t *)&config, &ee_config, sizeof(config));
+#else
+       eeprom_write_block((uint8_t *)&config, &ee_config, sizeof(config));
+#endif
+}
+
+void mm_pinchange_callback(void)
+{
+       static uint8_t btn_last = 0;
+
+       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 b0, b2, b4, b6;
+
+       b0 = ((command & 0x80) != 0);
+       b2 = ((command & 0x20) != 0);
+       b4 = ((command & 0x8) != 0);
+       b6 = ((command & 0x2) != 0);
+
+       //if ((b9!= b2) || (b2 != b3) || (b3 != b4) || (b5 != b6) || (b7 != b8))
+       //      return 0xff;
+       return (b0+ b2 * 2 + b4 * 4 +b6 * 8);
+}
+
+enum mm2_command {
+       CF_MM1 ,
+       CF_REVERSE,
+       CF_FORWARD,
+       CF_F1_ON,
+       CF_F1_OFF,
+       CF_F2_ON,
+       CF_F2_OFF,
+       CF_F3_ON,
+       CF_F3_OFF,
+       CF_F4_ON,
+       CF_F4_OFF,
+};
+
+#ifdef MM_USE_QUEUE
+static void dequeue_commands(void)
+{
+
+       return;
+
+       struct mm_command *cmd = mm_get();
+       if (!cmd)
+               return;
+       if (cmd->recmode != MM_FAST)
+               return;
+       if ((cmd->function & 0x3) != 0x3)
+               return;
+       //      uint8_t loco = mm_lookup_decoder(cmd->address);
+
+}
+
+#endif
+
+void mm_drive_cb(uint8_t loco, uint8_t func_enc, uint8_t cmd_enc)
+{
+       uint8_t b1, b3, b5, b7;
+       uint8_t fcode;
+
+       /* Those three are decoded from fun and command */
+       uint8_t speed;
+       uint8_t function;
+       enum mm2_command mm2_command = CF_MM1;
+       static uint8_t alert_last = 0;
+       speed = get_speed(cmd_enc);
+
+#ifdef MUELL
+       goto mm2_done; // FIXME
+
+       b1 = ((cmd_enc & 0x4) != 0);
+       b3 = ((cmd_enc & 0x1) != 0);
+       b5 = ((cmd_enc & 0x4) != 0);
+       b7 = ((cmd_enc & 0x1) != 0);
+
+       fcode = b1 * 8 + b3 * 4 + b5 *2 + b7;
+
+       /* The ugly speed section */
+       if (speed < 8) {
+               if (fcode == 0xb) {
+                       mm2_command = CF_REVERSE;
+                       goto mm2_done;
+               }
+               if (fcode == 0x5) {
+                       mm2_command = CF_FORWARD;
+                       goto mm2_done;
+               }
+       } else {
+               if (fcode == 0xa) {
+                       mm2_command = CF_REVERSE;
+                       goto mm2_done;
+               }
+               if (fcode == 0x4) {
+                       mm2_command = CF_FORWARD;
+                       goto mm2_done;
+               }
+       }
+
+       /* Special cases for f1-f4  commands */
+       if (fcode == 0xa) {
+               switch(speed) {
+               case 3:
+                       mm2_command = CF_F1_ON;
+                       goto mm2_done;
+               case 4:
+                       mm2_command = CF_F2_ON;
+                       goto mm2_done;
+               case 6:
+                       mm2_command = CF_F3_ON;
+                       goto mm2_done;
+               case 7:
+                       mm2_command = CF_F4_ON;
+                       goto mm2_done;
+               default:
+                       goto mm2_done;
+               }
+       } else if (fcode == 0x5) {
+               switch(speed) {
+                       case 11:
+                               mm2_command = CF_F1_OFF;
+                               goto mm2_done;
+                       case 12:
+                               mm2_command = CF_F2_OFF;
+                               goto mm2_done;
+                       case 14:
+                               mm2_command = CF_F3_OFF;
+                               goto mm2_done;
+                       case 15:
+                               mm2_command = CF_F4_OFF;
+                               goto mm2_done;
+               }
+       }
+
+       switch(fcode) {
+       case 0xc:
+               mm2_command = CF_F1_OFF;
+               goto mm2_done;
+       case 0xd:
+               mm2_command = CF_F1_ON;
+               goto mm2_done;
+
+       case 0x2:
+               mm2_command = CF_F2_OFF;
+               goto mm2_done;
+       case 0x3:
+               mm2_command = CF_F2_ON;
+               goto mm2_done;
+
+       case 0x6:
+               mm2_command = CF_F3_OFF;
+               goto mm2_done;
+
+       case 0x7:
+               mm2_command = CF_F3_ON;
+               goto mm2_done;
+
+       case 0xe:
+               mm2_command = CF_F4_OFF;
+               goto mm2_done;
+
+       case 0xf:
+               mm2_command = CF_F4_ON;
+               goto mm2_done;
+       default:
+               break;
+       }
+ mm2_done:
+       if (loco == 34) {
+               switch(mm2_command) {
+               case CF_F3_ON:
+                       drive_on = 1;
+                       break;
+               case CF_F3_OFF:
+                       drive_on = 0;
+                       break;
+               default:
+                       break;
+               }
+       }
+#endif
+       if (loco == config.decoder_on) {
+               if (speed)
+                       speed = speed - 1;
+               main_speed = speed;
+               return;
+       }
+
+       switch(loco) {
+       case 80:
+               switch (config.learn_mode) {
+               case LM_OFF:
+                       if (speed == 1)
+                               config.learn_mode = LM_EASY_WAIT_PRESS;
+                       break;
+               case LM_EASY_MODE:
+                       if ((speed == 1) && (alert_last == 0)) {
+                               config.learn_mode = LM_OFF;
+                               save_config();
+                               tone_good();
+                       }
+                       break;
+
+               case LM_EASY_WAIT_PRESS:
+                       if (speed != 1)
+                           config.learn_mode = LM_OFF;
+                       break;
+
+               default:
+                       break;
+               }
+               alert_last = (speed == 1);
+               break;
+
+       case 50:
+               if (speed)
+                       speed = speed - 1;
+
+               if (config.learn_mode == LM_EASY_MODE) {
+                       config.initial_pulse = speed;
+               }
+               break;
+       case 51:
+               if (speed)
+                       speed = speed - 1;
+
+               if (config.learn_mode == LM_EASY_MODE)
+                       config.on_duty_cycle[main_speed] = speed;
+               break;
+       }
+}
+
+void mm_key_cb(uint8_t decoder, uint8_t function, uint8_t raw_command)
+{
+       static uint8_t toggle_lock = 0;
+       uint8_t command;
+
+       static uint8_t last_raw = 0;
+       static uint8_t last_fkey = 0;
+
+       if ((function & 3) == 3) { /* F key hack! */
+               uint8_t diff;
+               diff =  last_raw ^ raw_command;
+               if (diff) {
+                       if (diff & 0x80)
+                               command = 4;
+                       else if (diff & 0x20)
+                               command = 3;
+                       else if (diff & 0x8)
+                               command = 2;
+                       else
+                               command = 1;
+                       command |= 0x80;
+                       if ((command == config.key_on)
+                           && (last_raw & ~raw_command))
+                               command = 0;
+                       last_raw = raw_command;
+               } else {
+                       return;
+               }
+       } else {
+               command = mm_lookup_key(raw_command);
+       }
+
+       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:
+                               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;
+                       }
+               }
+               break;
+
+       case LM_EASY_WAIT_PRESS:
+               if ((decoder == config.decoder_on) &&
+                   (command == config.key_on))
+                       config.learn_mode = LM_EASY_WAIT_UP;
+               return;
+
+       case LM_EASY_WAIT_UP:
+               if ((decoder == config.decoder_on) &&
+                   (command == 0)) {
+                       config.learn_mode = LM_EASY_MODE;
+                       tone_enter();
+               }
+               return;
+
+#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
+ *
+ */
+void shift(uint8_t mu);
+
+#ifdef DEBUG_DRIVE_LED
+#define MON_LED(val) setpin(PIN_LED, val)
+#else
+#define MON_LED(val)
+#endif
+
+#ifdef WITH_PWM
+#define DRIVE_OFF  {OCR1B = PWM_CYCLE; MON_LED(0);}
+#define DRIVE_ON   {OCR1B = PWM_CYCLE - config.on_duty_cycle[main_speed]; \
+               MON_LED(1);}
+#define DRIVE_FULL {OCR1B = 0; MON_LED(1);}
+#else
+#define DRIVE_OFF  {setpin(PIN_DRIVE, 0); MON_LED(0);}
+#define DRIVE_ON   {setpin(PIN_DRIVE, 1); MON_LED(1);}
+#define DRIVE_FULL {setpin(PIN_DRIVE, 1); MON_LED(1);}
+#endif
+
+int main(void) {
+       uint16_t i;
+
+#ifdef WITH_INITIAL_PULSE
+       uint8_t drive_last = 0;
+       uint8_t drive_slope = 0;
+#endif
+
+#ifdef USE_REGISTER_VARS
+       drive_on = 0;
+#endif
+       mm_init();
+       load_config();
+       setup_hw();
+
+       while(0) {
+               setpin(PIN_LED, 1);
+               _delay_ms(2);
+               _delay_ms(2);
+               trigger();
+               sei();
+       }
+       if (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[0] = 5;
+               config.learn_mode = LM_LEARN_ON_KEY;
+       }
+       sei();
+       while (1) {
+#ifdef MM_USE_QUEUE
+               dequeue_commands();
+#endif
+       drive_start:
+
+#ifdef WITH_INITIAL_PULSE
+               cli();
+               if (drive_on && !drive_last)
+                       drive_slope = 1;
+               else
+                       drive_slope = 0;
+               drive_last = drive_on;
+               sei();
+#endif
+               if (drive_on) {
+
+#ifdef WITH_INITIAL_PULSE
+                       if (drive_slope) {
+                               DRIVE_FULL;
+                               for (i = 0; i < config.initial_pulse; i++) {
+                                       _delay_ms(5);
+                               }
+                       }
+#endif
+                       DRIVE_ON;
+
+               } else {
+                       DRIVE_OFF;
+
+                       if (!config.learn_mode ||
+                           config.learn_mode > LM_LEARN_OP_MODE)
+                               continue;
+
+                       for (i = 0; i < config.learn_mode; i++) {
+                               setpin(PIN_LED, 1);
+                               snd_on();
+                               _delay_ms(10);
+                               setpin(PIN_LED, 0);
+                               snd_off();
+                               if (drive_on) goto drive_start;
+                               _delay_ms(135);
+                               if (drive_on) goto drive_start;
+                       }
+                       for (i = 0; i < 15 - config.learn_mode; i++) {
+                               if (drive_on) goto drive_start;
+                               _delay_ms(70);
+                       }
+               }
+               //              MCUCR |= _BV(SE);
+               //      sleep();
+       }
+       return 0;
+}
+
+/******************************************************************************
+ *                        The end :-)
+ */
diff --git a/trennfix/sw/src/trennfix_main.c b/trennfix/sw/src/trennfix_main.c
new file mode 100644 (file)
index 0000000..9baf6c2
--- /dev/null
@@ -0,0 +1,535 @@
+/******************************************************************************
+ *
+ *  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 <mm/mm_switch.h>
+#include <config/hardware.h>
+
+#define EE_MAGIC 0xab
+
+FUSES = {
+       .low = FUSE_CKDIV8 & FUSE_SUT0 &\
+       FUSE_CKSEL3 & FUSE_CKSEL2 & FUSE_CKSEL1,
+       .high = FUSE_SPIEN & FUSE_BODLEVEL1 & FUSE_BODLEVEL0,
+       .extended = EFUSE_DEFAULT,
+};
+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_EASY_WAIT_PRESS,  /* Wait for key press to enter easy mode       */
+       LM_EASY_WAIT_UP,     /* Wait for end of key press                   */
+       LM_EASY_WAIT_TURN,   /* Wait for loco 80 turn end                   */
+       LM_EASY_MODE,        /* Easy config mode for PWM and initial kick   */
+       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 easy_mode = 0;
+static volatile uint8_t easy_mode_possible = 0;
+
+#ifndef USE_REGISTER_VARS
+static volatile uint8_t drive_on = 0;
+#endif
+
+/******************************************************************************
+ *
+ * Some nice sounds to play on your coil
+ *
+ */
+
+#define G 180
+#define sekunde(g)(quinte((quinte(g)))*2)
+#define terz(g) ((g) * 4 / 5)
+#define kleine_terz(g) ((g) * 5 / 6)
+#define quarte(g) ((g) * 3 / 4)
+#define quinte(g) ((g) * 2 / 3)
+#define tt(g) ((g) * 32 / 45)
+#define septime(g) ((g) * 15 / 8)
+
+#if defined(WITH_SOUND) && defined(WITH_PWM)
+void play_tone(uint8_t divisor, uint8_t duration, uint8_t pause)
+{
+       uint16_t c;
+       TCCR1 = 0x8;
+       OCR1C = divisor;
+       c = (divisor * 2) / 3;
+       OCR1B = c;
+       for (c = 0; c < duration - pause; c++)
+               _delay_ms(2);
+       TCCR1 = 0x6;
+       OCR1C = PWM_CYCLE;
+       OCR1B = 12;
+       for (c = 0; c < pause; c++)
+               _delay_ms(2);
+       TCCR1 = 0x6;
+       OCR1C = PWM_CYCLE;
+       OCR1B = PWM_CYCLE;
+}
+
+static void tone_enter(void)
+{
+       play_tone((G), 70, 20);
+       play_tone(terz(G), 70, 20);
+       play_tone(quinte(G), 70, 20);
+       play_tone(G/2, 100, 0);
+}
+
+static void tone_good(void)
+{
+       play_tone(G, 150, 120);
+       play_tone(terz(G), 100, 70);
+       play_tone(tt(G), 100, 50);
+       play_tone(quarte(terz((G))), 50, 0);
+       play_tone(quinte(G), 150, 0);
+       play_tone(terz(G), 100, 50);
+}
+
+static void snd_on(void)
+{
+       TCCR1 = 0x8;
+       OCR1C = 120;
+       OCR1B = 60;
+}
+
+static void snd_off(void)
+{
+       TCCR1 = 0x6;
+       OCR1C = PWM_CYCLE;
+       OCR1B = PWM_CYCLE;
+}
+
+#else
+#define play_tone(...)
+#define tone_enter(...)
+#define tone_good(...)
+#define snd_on(...)
+#define snd_off(...)
+#endif
+
+static void load_config(void)
+{
+       eeprom_read_block((uint8_t *)&config, &ee_config, sizeof(config));
+}
+
+static void save_config(void)
+{
+#ifdef WITH_EEPROM_UPDATE
+       eeprom_update_block((uint8_t *)&config, &ee_config, sizeof(config));
+#else
+       eeprom_write_block((uint8_t *)&config, &ee_config, sizeof(config));
+#endif
+}
+
+void mm_pinchange_callback(void)
+{
+       static uint8_t btn_last = 0;
+
+       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);
+       b3 = ((command & 0x20) != 0);
+       b5 = ((command & 0x8) != 0);
+       b7 = ((command & 0x2) != 0);
+
+       //if ((b1 != b2) || (b2 != b3) || (b3 != b4) || (b5 != b6) || (b7 != b8))
+       //      return 0xff;
+       return (b1 + b3 * 2 + b5 * 4 +b7 * 8);
+}
+
+
+void mm_drive_cb(uint8_t loco, uint8_t function, uint8_t command)
+{
+       uint8_t speed;
+       speed = get_speed(command);
+       static uint8_t alert_last = 0;
+
+       switch(loco) {
+       case 10:
+               switch (config.learn_mode) {
+               case LM_OFF:
+                       if (speed == 1)
+                               config.learn_mode = LM_EASY_WAIT_PRESS;
+                       break;
+               case LM_EASY_MODE:
+                       if ((speed == 1) && (alert_last == 0)) {
+                               config.learn_mode = LM_OFF;
+                               save_config();
+                               tone_good();
+                       }
+                       break;
+
+               case LM_EASY_WAIT_PRESS:
+                       if (speed != 1)
+                           config.learn_mode = LM_OFF;
+                       break;
+
+               default:
+                       break;
+               }
+               alert_last = (speed == 1);
+               break;
+
+       case 50:
+               if (speed)
+                       speed = speed - 1;
+
+               if (config.learn_mode == LM_EASY_MODE) {
+                       config.initial_pulse = speed;
+               }
+               break;
+       case 51:
+               if (speed)
+                       speed = speed - 1;
+
+               if (config.learn_mode == LM_EASY_MODE)
+                       config.on_duty_cycle = speed;
+               break;
+       }
+}
+
+void mm_key_cb(uint8_t decoder, uint8_t function, uint8_t command)
+{
+       static uint8_t toggle_lock = 0;
+       
+       /* We don't listen to F1-F4 secret function codes */
+       if (function & 0x3)
+           return;
+    
+       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:
+                               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;
+                       }
+               }
+               break;
+
+       case LM_EASY_WAIT_PRESS:
+               if ((decoder == config.decoder_on) &&
+                   (command == config.key_on))
+                       config.learn_mode = LM_EASY_WAIT_UP;
+               return;
+
+       case LM_EASY_WAIT_UP:
+               if ((decoder == config.decoder_on) &&
+                   (command == 0)) {
+                       config.learn_mode = LM_EASY_MODE;
+                       tone_enter();
+               }
+               return;
+
+#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
+ *
+ */
+void shift(uint8_t mu);
+
+#ifdef DEBUG_DRIVE_LED
+#define MON_LED(val) setpin(PIN_LED, val)
+#else
+#define MON_LED(val)
+#endif
+
+#ifdef WITH_PWM
+#define DRIVE_OFF  {OCR1B = PWM_CYCLE; MON_LED(0);}
+#define DRIVE_ON   {OCR1B = PWM_CYCLE - config.on_duty_cycle; MON_LED(1);}
+#define DRIVE_FULL {OCR1B = 0; MON_LED(1);}
+#else
+#define DRIVE_OFF  {setpin(PIN_DRIVE, 0); MON_LED(0);}
+#define DRIVE_ON   {setpin(PIN_DRIVE, 1); MON_LED(1);}
+#define DRIVE_FULL {setpin(PIN_DRIVE, 1); MON_LED(1);}
+#endif
+
+int main(void) {
+       uint16_t i;
+
+#ifdef WITH_INITIAL_PULSE
+       uint8_t drive_last = 0;
+       uint8_t drive_slope = 0;
+#endif
+
+#ifdef USE_REGISTER_VARS
+       drive_on = 0;
+#endif
+       mm_init();
+       load_config();
+       setup_hw();
+
+       while(0) {
+               setpin(PIN_LED, 1);
+               _delay_ms(2);
+               _delay_ms(2);
+               trigger();
+               sei();
+       }
+       if (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;
+       }
+       sei();
+       while (1) {
+       drive_start:
+
+#ifdef WITH_INITIAL_PULSE
+               cli();
+               if (drive_on && !drive_last)
+                       drive_slope = 1;
+               else
+                       drive_slope = 0;
+               drive_last = drive_on;
+               sei();
+#endif
+               if (drive_on) {
+
+#ifdef WITH_INITIAL_PULSE
+                       if (drive_slope) {
+                               DRIVE_FULL;
+                               for (i = 0; i < config.initial_pulse; i++) {
+                                       _delay_ms(5);
+                               }
+                       }
+#endif
+                       DRIVE_ON;
+
+               } else {
+                       DRIVE_OFF;
+
+                       if (!config.learn_mode ||
+                           config.learn_mode > LM_LEARN_OP_MODE)
+                               continue;
+
+                       for (i = 0; i < config.learn_mode; i++) {
+                               setpin(PIN_LED, 1);
+                               snd_on();
+                               _delay_ms(10);
+                               setpin(PIN_LED, 0);
+                               snd_off();
+                               if (drive_on) goto drive_start;
+                               _delay_ms(135);
+                               if (drive_on) goto drive_start;
+                       }
+                       for (i = 0; i < 15 - config.learn_mode; i++) {
+                               if (drive_on) goto drive_start;
+                               _delay_ms(70);
+                       }
+               }
+       }
+       return 0;
+}
+
+/******************************************************************************
+ *                        The end :-)
+ */