trennfix/sw: More versatile file structure
[eisenbahn.git] / trennfix / sw / src / main.c
diff --git a/trennfix/sw/src/main.c b/trennfix/sw/src/main.c
new file mode 100644 (file)
index 0000000..7d5c10a
--- /dev/null
@@ -0,0 +1,469 @@
+/******************************************************************************
+ *
+ *  Trennfix firmware - main.c
+ *
+ *  Copyright (C) 2017 Philipp Hachtmann
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <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_END,            /* Only a label                                */
+};             
+
+struct config {
+       uint8_t magic; /* Magic value */
+       enum op_mode op_mode;
+       uint8_t decoder_on;
+       uint8_t key_on;
+       uint8_t decoder_off;
+       uint8_t key_off;
+       uint8_t initial_pulse; /* Lenghth of initial pulse in 10ms steps   */
+       uint8_t on_duty_cycle; /* Duty cycle for on. 0-10                  */
+        volatile enum learn_mode  learn_mode;
+};
+
+static struct EEMEM config ee_config;
+static volatile struct config config;
+static volatile uint8_t drive_on = 0;
+
+static void load_config(void)
+{
+       eeprom_read_block((uint8_t *)&config, &ee_config, sizeof(config));
+}
+
+static void save_config(void)
+{
+#ifdef USE_EEPROM_UPDATE
+       eeprom_update_block((uint8_t *)&config, &ee_config, sizeof(config));
+#else
+       eeprom_write_block((uint8_t *)&config, &ee_config, sizeof(config));
+#endif
+}
+
+
+
+ISR(PCINT0_vect){
+       static uint8_t btn_last = 0;
+
+       mm_pinchange_handler();
+
+       if (BTN_PRESSED && !btn_last) {
+               config.learn_mode++;
+               config.learn_mode %= LM_END;
+       }
+       btn_last = BTN_PRESSED;
+}
+
+static uint8_t get_speed(uint8_t command)
+{
+       uint8_t b1,  b3, b5, b7;
+               
+       b1 = ((command & 0x80) != 0);
+       //      b2 = ((command & 0x40) != 0);
+       b3 = ((command & 0x20) != 0);
+       //      b4 = ((command & 0x10) != 0);
+       b5 = ((command & 0x8) != 0);
+       //      b6 = ((command & 0x4) != 0);
+       b7 = ((command & 0x2) != 0);
+       //      b8 = ((command & 0x1) != 0);
+       
+       //if ((b1 != b2) || (b2 != b3) || (b3 != b4) || (b5 != b6) || (b7 != b8))
+       //      return 0xff;
+       
+       return (b1 + b3*2 + b5*4 +b7*8);
+}
+
+#ifdef INTERPRET_DRIVE_COMMANDS
+
+void mm_switch_drive(uint8_t decoder, uint8_t function, uint8_t command)
+{
+       static uint8_t seen_before = 0;
+       uint8_t speed;
+
+       if (!seen_before) {
+               if ((decoder == 80) && (function == 0) && (command == 0xc0)) {
+                       config.learn_mode = 1;
+                       save_config();
+               }
+       }
+       seen_before = 1;
+       
+       speed = get_speed(command);
+       static uint8_t itsme = 0;
+
+       if (config.learn_mode) {
+               if (decoder == 70) {
+                       if (speed == 1) {
+                               itsme = 1;
+                       drive_on = 1;
+                       }else {
+                               if (itsme) {
+                                       drive_on = 0;
+                                       itsme = 0;
+                               }
+                       }
+               }
+               
+               if (decoder == 33) {
+                       if (speed != 0xff) {
+                               if (speed >= 1) speed -= 1;
+                               if (speed <= 14)
+                                       config.on_duty_cycle = speed;
+                               else
+                                       config.on_duty_cycle = 14;
+                               
+                       }
+               }
+               
+               if (decoder == 32) {
+                       if (speed != 0xff) {
+                               config.initial_pulse = speed;
+                       }
+               }
+               
+               if (decoder == 31 && command == 0xc0)
+                       save_config();
+       }
+}
+
+#else
+#ifdef INTERPRET_DRIVE_SIMPLE
+
+static volatile uint8_t current_loco;
+
+void mm_switch_drive(uint8_t decoder, uint8_t function, uint8_t command)
+{
+       uint8_t speed;
+       speed = get_speed(command);
+       current_loco = decoder;
+       if (speed != 0xff) {
+               if (decoder == 50) {
+                       setpin(PIN_LED, drive_on = function & 3);
+                       config.initial_pulse = speed;
+               } else if (decoder == 51) {
+                       if (speed >= 1) speed -= 1;
+                       if (speed <= 14)
+                               config.on_duty_cycle = speed;
+                       else
+                               config.on_duty_cycle = 14;
+               } else if (decoder == 52) {
+                       static uint8_t last_speed;
+                       if (speed == 1 && last_speed != 1)
+                               save_config();
+                       last_speed = speed;
+                       
+               }
+       }
+}
+
+#endif
+#endif
+static volatile uint8_t switch_on = 0;
+
+void mm_switch_command(uint8_t decoder, uint8_t command)
+{
+       static uint8_t toggle_lock = 0;
+       switch(config.learn_mode) {
+
+       case LM_OFF:
+       default:
+               if ((decoder == config.decoder_on) &&
+                   (command == config.key_on)) { /* Primary key pressed */
+                       switch(config.op_mode) {
+                       case OM_MOMENTARY:
+                       case OM_DOUBLE:
+                               if (!switch_on && current_loco == 52)
+                                       save_config();
+                               switch_on = 1;
+                               break;
+                       case OM_TOGGLE:
+                               if (!toggle_lock) {
+                                       drive_on = !drive_on;
+                                       toggle_lock = 1;
+                               }
+                               break;
+                       default:
+                               break;
+                       }
+               }
+
+               if ((decoder == config.decoder_on) &&
+                   (command == 0)) { /* Primary key released */
+                       switch(config.op_mode) {
+                       case OM_MOMENTARY:
+                               switch_on = 0;
+                               break;
+                       case OM_TOGGLE:
+                               toggle_lock = 0;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+#ifdef HANDLE_OFF_KEY
+
+               if ((decoder == config.decoder_off) &&
+                   (command == config.key_off)) { /* Secondary "off" key pressed */
+                       switch(config.op_mode) {
+                       case OM_DOUBLE:
+                               drive_on = 0;
+                               break;
+                       case OM_TOGGLE:
+                       case OM_MOMENTARY:
+                       default:
+                               break;
+                       }
+               }
+#endif
+               break;
+               
+       case LM_LEARN_ON_KEY:
+               if (command) {
+                       config.decoder_on = decoder;
+                       config.key_on     = command;
+                       if (config.op_mode == OM_DOUBLE)
+                               config.learn_mode = LM_LEARN_OFF_KEY;
+                       else
+                               config.learn_mode = LM_OFF;
+                       save_config();
+               }
+               break;
+#ifdef LEARN_ADVANCED
+       case LM_LEARN_OFF_KEY:
+               if (command) {
+                       config.decoder_off = decoder;
+                       config.key_off     = command;
+                       config.learn_mode = LM_OFF;
+                       save_config();
+               }
+               break;
+               
+       case LM_LEARN_INITIAL:
+               if (drive_on) {
+                       if (command == 0)
+                               drive_on = 0;
+                       
+               } else {
+                       switch(command) {
+                       case 1:
+                               if (config.initial_pulse >= 10)
+                                       config.initial_pulse -= 10;
+                               else 
+                                       config.initial_pulse = 0;
+                               save_config();
+                               drive_on = 1;
+                               break;
+                       case 2:
+                               if (config.initial_pulse <= 245)
+                                       config.initial_pulse += 10;
+                               else
+                                       config.initial_pulse = 255;
+                               save_config();
+                               drive_on = 1;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+               break;
+
+       case LM_LEARN_DUTY:
+               if (drive_on) {
+                       if (command == 0)
+                               drive_on = 0;
+               } else {
+                       switch(command) {
+                       case 1:
+                               if (config.on_duty_cycle > 0)
+                                       config.on_duty_cycle -= 1;
+                               save_config();
+                               drive_on = 1;
+                               break;
+                       case 2:
+                               if (config.on_duty_cycle < 10)
+                                       config.on_duty_cycle += 1;
+                               save_config();
+                               drive_on = 1;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+               break;
+               
+       case LM_LEARN_OP_MODE:
+               switch(command) {
+               case 1:
+                       config.op_mode = OM_MOMENTARY;
+                       save_config();
+                       config.learn_mode = LM_OFF;
+                       break;
+               case 3:
+                       config.op_mode = OM_DOUBLE;
+                       save_config();
+                       config.learn_mode = LM_OFF;
+                       break;
+               case 5:
+                       config.op_mode = OM_TOGGLE;
+                       save_config();
+                       config.learn_mode = LM_OFF;
+                       break;
+               default:
+                       break;
+               }
+               break;
+#endif
+       }
+}
+
+
+/******************************************************************************
+ *
+ * main() - The main routine
+ *
+ */
+
+int main(void) {
+       uint8_t learn_mode_off;
+       uint8_t drive_last = 0;
+       uint8_t drive_slope = 0;
+       uint8_t i;
+       uint8_t output_on;
+
+       load_config();
+       setup_hw();
+       
+       if ((config.op_mode == OM_ERASED) || (config.magic != EE_MAGIC)) {
+               config.magic = EE_MAGIC;
+               config.op_mode = OM_MOMENTARY;
+               config.decoder_on = 1;
+               config.key_on = 1;
+               config.decoder_off = 1;
+               config.key_off = 2;
+               config.initial_pulse = 10;
+               config.on_duty_cycle = 5;
+               config.learn_mode = LM_LEARN_ON_KEY;
+       }
+       drive_on = 0;
+       sei();
+       setpin(PIN_DRIVE, 1);   
+       _delay_ms(50);
+       setpin(PIN_DRIVE, 0);   
+
+       //setpin(PIN_LED,1 );
+       //_delay_ms(400);
+       //setpin(PIN_LED,0 );
+       while (1) {
+               
+       drive_start:
+
+               output_on = drive_on || switch_on;
+               cli();
+               if (!drive_last && output_on)
+                       drive_slope = 1;
+               else
+                       drive_slope = 0;
+               drive_last = output_on;
+               sei();
+
+               if (output_on) {
+                       if (drive_slope) {
+                               for (i = 0; i < config.initial_pulse; i++) {
+                                       setpin(PIN_DRIVE, 1);   
+                                       _delay_ms(20);
+                               }
+                       }
+
+                       for (i = 0; i < config.on_duty_cycle; i++) {
+                               setpin(PIN_DRIVE, output_on);
+                               _delay_us(3);
+                       }
+                       for (i = 0; i < (28 - config.on_duty_cycle); i++) {
+                               setpin(PIN_DRIVE, 0);
+                               _delay_us(3);
+                       }
+
+               } else {
+                       setpin(PIN_DRIVE, 0);
+                       if (!config.learn_mode)
+                               continue;
+
+                       learn_mode_off = !config.learn_mode;
+                       
+                       if (output_on || (config.learn_mode && learn_mode_off))
+                               goto drive_start;
+
+                       for (i = 0; i < config.learn_mode; i++) {
+                               setpin(PIN_LED, 1);
+                               _delay_ms(10);
+                               //if (output_on || (config.learn_mode && learn_mode_off))
+                               //      goto drive_start;
+                               setpin(PIN_LED, 0);
+                               _delay_ms(135);
+                               //if (output_on || (config.learn_mode && learn_mode_off))
+                               //      goto drive_start;
+
+                               //if (output_on || (config.learn_mode && learn_mode_off))
+                               //      goto drive_start;
+                       }
+                       for (i = 0; i < 15 - config.learn_mode; i++) {
+                               //if (output_on || (config.learn_mode && learn_mode_off))
+                               //      goto drive_start;
+                               setpin(PIN_LED, 0);
+                               _delay_ms(70);
+                               //if (output_on || (config.learn_mode && learn_mode_off))
+                               //      goto drive_start;
+                               // _delay_ms(40);
+                       }
+               }
+       }
+       return 0;
+}
+
+
+/******************************************************************************
+ *                        The end :-)
+ */