+++ /dev/null
-/******************************************************************************
- *
- * Trennfix firmware - main.c
- *
- * Copyright (C) 2017 Philipp Hachtmann
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- *****************************************************************************/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <avr/io.h>
-#include <avr/eeprom.h>
-#include <avr/interrupt.h>
-#include <avr/pgmspace.h>
-
-#include <util/delay.h>
-#include <stdint.h>
-#include <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 :-)
- */