You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1778 lines
35 KiB
1778 lines
35 KiB
/* Ardurail 1.0 |
|
|
|
Copyright (C) 2012 Jan Weller jan.weller@gmx.de Rewritten |
|
and reorganized 2013 by Holger Wirtz <dcoredump@googlemail.com> |
|
|
|
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. |
|
|
|
*/ |
|
|
|
#include "Ardurail.h" |
|
#include <avr/pgmspace.h> |
|
|
|
#define SERIALPORT Serial |
|
|
|
// Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734 |
|
#undef PROGMEM |
|
#define PROGMEM __attribute__((section(".progmem.data"))) |
|
|
|
void _short_irq() |
|
{ |
|
Ardurail::handle_short_interrupt(); |
|
} |
|
|
|
// |
|
// ######################### GLOBAL VARS ############################# |
|
// |
|
Ardurail *Ardurail::_active_object = 0; |
|
unsigned long _switch_time; |
|
// |
|
// ######################### PUBLIC METHODS ############################# |
|
// |
|
|
|
Ardurail::Ardurail(void) |
|
{ |
|
_active_object = 0; |
|
_power = false; |
|
_halt = false; |
|
_short = false; |
|
_refresh_pointer = 0; |
|
_refresh_max = 0; |
|
_updated_loco = -1; |
|
_timer = TIMER_MM_LOCO; |
|
_break_t1 = false; |
|
_break_t2 = false; |
|
_break_t3 = false; |
|
_step = 0; |
|
_bit_counter = 0; |
|
_packet_counter = 0; |
|
_msg_counter = 0; |
|
_waiting_track_commands = 0; |
|
_shortcut_signal_pin = -1; // default: not used |
|
_go_signal_pin = -1; // default: not used |
|
} |
|
|
|
const uint8_t extended_MM_addr[] PROGMEM = |
|
{ |
|
0b00000000, 0b01000000, 0b01100000, 0b10010111, 0b01110000, |
|
0b01001000, 0b01101000, 0b01011000, 0b01111000, 0b01000100, |
|
0b01100100, 0b01010100, 0b01110100, 0b01001100, 0b01101100, |
|
0b01011100, 0b01111100, 0b01000010, 0b01100010, 0b01010010, |
|
0b01110010, 0b01001010, 0b01101010, 0b01011010, 0b01111010, |
|
0b01000110, 0b01100110, 0b01010110, 0b01110110, 0b01001110, |
|
0b01101110, 0b01011110, 0b01111110, 0b01000001, 0b01100001, |
|
0b01010001, 0b01110001, 0b01001001, 0b01101001, 0b01011001, |
|
0b01111001, 0b01000101, 0b01100101, 0b10011111, 0b01110101, |
|
0b01001101, 0b01101101, 0b01011101, 0b01111101, 0b01000011, |
|
0b01100011, 0b01010011, 0b01110011, 0b01001011, 0b01101011, |
|
0b01011011, 0b01111011, 0b01000111, 0b01100111, 0b01010111, |
|
0b01110111, 0b01001111, 0b01101111, 0b01011111, 0b01111111, |
|
0b00010000, 0b00011000, 0b00010100, 0b00011100, 0b00010010, |
|
0b00011010, 0b00010110, 0b00011110, 0b00010001, 0b00011001, |
|
0b00010101, 0b00011101, 0b00010011, 0b00011011, 0b00010111, |
|
0b00011111, 0b11010000, 0b11011000, 0b11010100, 0b11011100, |
|
0b11010010, 0b11011010, 0b11010110, 0b11011110, 0b11010001, |
|
0b11011001, 0b11010101, 0b11011101, 0b11010011, 0b11011011, |
|
0b11010111, 0b11011111, 0b10010000, 0b10011000, 0b10010100, |
|
0b10011100, 0b10010010, 0b10011010, 0b10010110, 0b10011110, |
|
0b10010001, 0b10011001, 0b10010101, 0b10011101, 0b10010011, |
|
0b10011011, 0b01010000, 0b01010101, 0b00000100, 0b00000110, |
|
0b00000101, 0b00000111, 0b11000100, 0b11000110, 0b11000101, |
|
0b11000111, 0b10000100, 0b10000110, 0b10000101, 0b10000111, |
|
0b00110100, 0b00110110, 0b00110101, 0b00110111, 0b11110100, |
|
0b11110110, 0b11110101, 0b11110111, 0b10110100, 0b10110110, |
|
0b10110101, 0b10110111, 0b00100100, 0b00100110, 0b00100101, |
|
0b00100111, 0b11100100, 0b11100110, 0b11100101, 0b11100111, |
|
0b10100100, 0b10100110, 0b10100101, 0b10100111, 0b00000001, |
|
0b11000001, 0b10000001, 0b00110001, 0b11110001, 0b10110001, |
|
0b00100001, 0b11100001, 0b10100001, 0b00001101, 0b11001101, |
|
0b10001101, 0b00111101, 0b11111101, 0b10111101, 0b00101101, |
|
0b11101101, 0b10101101, 0b00001001, 0b11001001, 0b10001001, |
|
0b00111001, 0b11111001, 0b10111001, 0b00101001, 0b11101001, |
|
0b10101001 |
|
}; |
|
|
|
void Ardurail::init(byte pin) |
|
{ |
|
_active_object = this; |
|
|
|
#ifdef __LED13__ |
|
pinMode(13,OUTPUT); |
|
__led13__(true); |
|
#endif |
|
|
|
// Pin->Port |
|
pinMode(pin, OUTPUT); |
|
_digital_signal = digitalPinToBitMask(pin); |
|
_digital_reg = portOutputRegister(digitalPinToPort(pin)); |
|
|
|
// Portsetup for S88-Decoder |
|
#ifdef USE_S88 |
|
pinMode(S88_Data, INPUT); |
|
pinMode(S88_Clock, OUTPUT); |
|
digitalWrite(S88_Clock, LOW); |
|
pinMode(S88_PS, OUTPUT); |
|
digitalWrite(S88_PS, HIGH); |
|
pinMode(S88_Reset, OUTPUT); |
|
digitalWrite(S88_Reset, LOW); |
|
#endif |
|
|
|
// if we have no loco, fill with Idlepackets in MM Loco Mode |
|
for (uint8_t i = 0; i < MAXMSG; i++) |
|
{ |
|
_write_mm_output_register(i, 0b10101010, 0b00, 0b00000000); |
|
} |
|
|
|
// dcc idle packets too |
|
for (uint8_t i = 0; i < MAXMSG; i++) |
|
{ |
|
_msg_dcc[i].byte1 = 0xff; |
|
_msg_dcc[i].byte2 = 0; |
|
_msg_dcc[i].byte3 = 0xff; |
|
_msg_dcc[i].byte4 = 0; |
|
_msg_dcc[i].byte5 = 0; |
|
_msg_dcc[i].byte6 = 0; |
|
_msg_dcc[i].used_bytes = 3; |
|
} |
|
|
|
_mode = MM_LOCO; |
|
_timer = TIMER_MM_LOCO; |
|
|
|
// Timerstart |
|
TCCR2A = 0x00; // Timer2 arbeitet als normaler Timer |
|
TCCR2B = (1 << CS21); // Timer2 Control Reg B: Timer Prescaler 8 |
|
TIMSK2 = (1 << TOIE2); // Timer2 Overflow Interrupt Enable |
|
|
|
sei(); // enable all Interrupts |
|
delay(50); |
|
|
|
#ifdef __LED13__ |
|
pinMode(13,OUTPUT); |
|
__led13__(false); |
|
#endif |
|
} |
|
|
|
void Ardurail::init(byte digital_signal_pin,byte shortcut_pin,byte go_signal_pin) |
|
{ |
|
_shortcut_signal_pin = shortcut_pin; |
|
_go_signal_pin = go_signal_pin; |
|
|
|
if (shortcut_pin == 2) |
|
{ |
|
attachInterrupt(1, _short_irq, FALLING); |
|
} |
|
pinMode(_shortcut_signal_pin, INPUT); |
|
digitalWrite(_shortcut_signal_pin, HIGH); // Pullup |
|
pinMode(_go_signal_pin, OUTPUT); |
|
digitalWrite(_go_signal_pin, LOW); |
|
init(digital_signal_pin); |
|
_s88_max_s88 = MAX_S88; |
|
} |
|
|
|
void Ardurail::S88(void) |
|
{ |
|
#ifdef USE_S88 |
|
uint8_t S88value = 0; |
|
|
|
if (_s88modulcycleaddr == 0) |
|
{ |
|
digitalWrite(S88_Clock, HIGH); |
|
delayMicroseconds(15); |
|
digitalWrite(S88_Clock, LOW); |
|
delayMicroseconds(15); |
|
|
|
digitalWrite(S88_Reset, HIGH); |
|
delayMicroseconds(15); |
|
digitalWrite(S88_Reset, LOW); |
|
} |
|
|
|
digitalWrite(S88_PS, LOW); |
|
delayMicroseconds(15); |
|
|
|
// 2 Bytes auslesen und weiterschieben |
|
for (uint8_t byte_nr = 0; byte_nr <= 1; byte_nr++) |
|
{ |
|
|
|
for (uint8_t clock = 0; clock <= 7; clock++) |
|
{ |
|
S88value |= (digitalRead(S88_Data) << (7 - clock)); |
|
|
|
digitalWrite(S88_Clock, HIGH); |
|
delayMicroseconds(10); |
|
digitalWrite(S88_Clock, LOW); |
|
delayMicroseconds(10); |
|
} |
|
_s88_state[_s88modulcycleaddr * 2 + byte_nr] = S88value; |
|
S88value = 0; |
|
} |
|
|
|
_s88modulcycleaddr++; |
|
if (_s88modulcycleaddr == _s88_max_s88) |
|
{ |
|
_s88modulcycleaddr = 0; |
|
digitalWrite(S88_PS, HIGH); |
|
} |
|
#else |
|
; |
|
#endif |
|
} |
|
|
|
byte Ardurail::get_S88(byte modul, byte byte_nr) |
|
{ |
|
#ifdef USE_S88 |
|
return ((byte)_s88_state[modul * 2 + byte_nr]); |
|
#else |
|
return ((byte)0x00); |
|
#endif |
|
} |
|
|
|
boolean Ardurail::get_power(void) |
|
{ |
|
return (_power); |
|
} |
|
|
|
void Ardurail::set_power(boolean power) |
|
{ |
|
_power = power; |
|
if(power) |
|
{ |
|
set_go(); |
|
_halt = false; |
|
_short = false; |
|
} |
|
else |
|
set_stop(); |
|
} |
|
|
|
boolean Ardurail::get_halt(void) |
|
{ |
|
return(_halt); |
|
} |
|
|
|
void Ardurail::set_halt(void) |
|
// not ready |
|
{ |
|
_halt = true; |
|
for (int i = 0; i <= _refresh_max; i++) |
|
{ |
|
emergency_stop(_refresh[i].address); |
|
} |
|
} |
|
|
|
void Ardurail::set_go(void) |
|
{ |
|
if((_go_signal_pin >= 0) && (get_shortcut() == false)) |
|
digitalWrite(_go_signal_pin, HIGH); |
|
} |
|
|
|
void Ardurail::set_stop(void) |
|
{ |
|
if(_go_signal_pin >= 0) |
|
digitalWrite(_go_signal_pin, LOW); |
|
} |
|
|
|
boolean Ardurail::get_shortcut(void) |
|
{ |
|
if(_shortcut_signal_pin >= 0) |
|
{ |
|
if (digitalRead(_shortcut_signal_pin) == HIGH) |
|
{ |
|
_short = true; |
|
} |
|
else |
|
{ |
|
_short = false; |
|
} |
|
} |
|
return (_short); |
|
} |
|
|
|
void Ardurail::add_loco(byte addr) |
|
{ |
|
add_loco(addr,M2); |
|
} |
|
|
|
void Ardurail::add_loco(byte addr, byte protocol) |
|
{ |
|
if(_find_refresh(addr)<0) |
|
{ |
|
int8_t refresh_position = _create_refresh(addr); |
|
_refresh[refresh_position].type = protocol; |
|
_refresh[refresh_position].speed = 0; |
|
_refresh[refresh_position].dir = true; |
|
|
|
byte i=0; |
|
for(i=0;i<5;i++) |
|
{ |
|
set_function(addr,i,false); |
|
} |
|
} |
|
} |
|
|
|
byte Ardurail::get_loco(byte addr) |
|
{ |
|
int8_t refresh_position = (-1); |
|
refresh_position = _find_refresh(addr); |
|
|
|
if (refresh_position >= 0) |
|
{ |
|
return(_refresh[refresh_position].type); |
|
} |
|
else |
|
{ |
|
return(-1); |
|
} |
|
} |
|
|
|
byte Ardurail::get_speed(byte addr) |
|
{ |
|
int8_t refresh_position = (-1); |
|
refresh_position = _find_refresh(addr); |
|
|
|
if (refresh_position >= 0) |
|
{ |
|
return (_refresh[refresh_position].speed); |
|
} |
|
else |
|
{ |
|
return (-1); |
|
} |
|
} |
|
|
|
byte Ardurail::get_speedsteps(byte addr) |
|
{ |
|
switch(_refresh[_find_refresh(addr)].type) |
|
{ |
|
case M1: |
|
case M2: |
|
case M2_14_256A: |
|
return(14); |
|
case M2_28_80A: |
|
case M2_28_256A: |
|
return(28); |
|
default: |
|
return(-1); |
|
} |
|
} |
|
|
|
void Ardurail::set_speed(byte addr, byte speed) |
|
{ |
|
int8_t refresh_position = -1; |
|
|
|
if (_halt) |
|
return; |
|
|
|
refresh_position = _create_refresh(addr); |
|
if (refresh_position >= 0) |
|
{ |
|
_refresh[refresh_position].ready = false; |
|
_refresh[refresh_position].speed = speed; |
|
} |
|
} |
|
|
|
boolean Ardurail::get_dir(byte addr) |
|
{ |
|
int8_t refresh_position = -1; |
|
|
|
refresh_position = _find_refresh(addr); |
|
if (refresh_position >= 0) |
|
{ |
|
return(_refresh[refresh_position].dir); |
|
} |
|
else |
|
{ |
|
return (-1); |
|
} |
|
} |
|
|
|
void Ardurail::set_dir(byte addr, boolean direction) |
|
{ |
|
int8_t refresh_position = -1; |
|
|
|
refresh_position = _find_refresh(addr); |
|
if (refresh_position >= 0) |
|
{ |
|
_refresh[refresh_position].ready = false; |
|
if (_refresh[refresh_position].dir != direction) |
|
{ |
|
_refresh[refresh_position].ready = false; |
|
_refresh[refresh_position].changedir = true; |
|
_refresh[refresh_position].dir = direction; |
|
} |
|
} |
|
} |
|
|
|
void Ardurail::change_dir(byte addr) |
|
{ |
|
int8_t refresh_position = -1; |
|
|
|
refresh_position = _find_refresh(addr); |
|
if (refresh_position >= 0) |
|
{ |
|
_refresh[refresh_position].ready = false; |
|
_refresh[refresh_position].changedir = true; |
|
_refresh[refresh_position].dir = ~(_refresh[refresh_position].dir); |
|
} |
|
} |
|
|
|
boolean Ardurail::get_function(byte addr, byte function_nr) |
|
{ |
|
int8_t refresh_position = _find_refresh(addr); |
|
boolean function_value = false; |
|
|
|
if (refresh_position >= 0) |
|
{ |
|
switch (function_nr) |
|
{ |
|
case 0: |
|
function_value = (boolean)(_refresh[refresh_position].function); |
|
break; |
|
case 1: |
|
function_value = (boolean)(_refresh[refresh_position].f1); |
|
break; |
|
case 2: |
|
function_value = (boolean)(_refresh[refresh_position].f2); |
|
break; |
|
case 3: |
|
function_value = (boolean)(_refresh[refresh_position].f3); |
|
break; |
|
case 4: |
|
function_value = (boolean)(_refresh[refresh_position].f4); |
|
break; |
|
case 5: |
|
function_value = (boolean)(_refresh[refresh_position].f5); |
|
break; |
|
case 6: |
|
function_value = (boolean)(_refresh[refresh_position].f6); |
|
break; |
|
case 7: |
|
function_value = (boolean)(_refresh[refresh_position].f7); |
|
break; |
|
case 8: |
|
function_value = (boolean)(_refresh[refresh_position].f8); |
|
break; |
|
} |
|
} |
|
return(function_value); |
|
} |
|
|
|
void Ardurail::set_function(byte addr, byte function, boolean state) |
|
{ |
|
int8_t refresh_position = -1; |
|
|
|
if ((refresh_position = _find_refresh(addr)) >= 0) |
|
{ |
|
_refresh[refresh_position].ready = false; |
|
switch (function) |
|
{ |
|
case 0: |
|
_refresh[refresh_position].function = state; |
|
break; |
|
case 1: |
|
_refresh[refresh_position].f1 = state; |
|
break; |
|
case 2: |
|
_refresh[refresh_position].f2 = state; |
|
break; |
|
case 3: |
|
_refresh[refresh_position].f3 = state; |
|
break; |
|
case 4: |
|
_refresh[refresh_position].f4 = state; |
|
break; |
|
case 5: |
|
_refresh[refresh_position].f5 = state; |
|
break; |
|
case 6: |
|
_refresh[refresh_position].f6 = state; |
|
break; |
|
case 7: |
|
_refresh[refresh_position].f7 = state; |
|
break; |
|
case 8: |
|
_refresh[refresh_position].f8 = state; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
void Ardurail::emergency_stop(byte addr) |
|
{ |
|
set_speed(addr,15); // für alte Decoder |
|
commit(addr); |
|
set_dir(addr,!get_dir(addr)); |
|
set_speed(addr,0); |
|
commit(addr); |
|
set_dir(addr,!get_dir(addr)); |
|
commit(addr); |
|
} |
|
|
|
// The following function is not ready yet... HW |
|
void Ardurail::reset_locos(void) |
|
{ |
|
byte i=0; |
|
byte n=0; |
|
uint8_t r=_refresh_max; |
|
|
|
for(i=0;i<MAX_REFRESH;i++) |
|
{ |
|
set_speed(i,0); |
|
set_dir(i,true); |
|
for(n=0;n<=8;n++) |
|
set_function(i,n,0); |
|
commit(i); |
|
} |
|
|
|
_refresh_max=r; |
|
} |
|
|
|
void Ardurail::commit(void) |
|
{ |
|
byte i=0; |
|
|
|
for(i=0;i<=_refresh_max;i++) |
|
{ |
|
_commit(i); |
|
} |
|
} |
|
|
|
void Ardurail::commit(byte addr) |
|
{ |
|
_commit(_find_refresh(addr)); |
|
} |
|
|
|
void Ardurail::set_track(byte addr, byte sub_addr, boolean state) |
|
{ |
|
// we have one new command |
|
_waiting_track_commands++; |
|
|
|
if (_waiting_track_commands > 1) |
|
{ |
|
// move all commands |
|
for (uint8_t i = _waiting_track_commands; i > 0; i--) |
|
{ |
|
tswitch[i] = tswitch[i-1]; |
|
} |
|
} |
|
|
|
// insert the new command |
|
tswitch[0].address = addr; |
|
tswitch[0].subaddress = sub_addr; |
|
tswitch[0].state = state; |
|
tswitch[0].ready = true; |
|
} |
|
|
|
// |
|
// ######################### PRIVATE METHODS ############################# |
|
// |
|
|
|
void Ardurail::_commit(byte index) |
|
{ |
|
_refresh[index].ready = true; |
|
_updated_loco = index; |
|
} |
|
|
|
|
|
int8_t Ardurail::_create_refresh(byte address) |
|
{ |
|
int8_t refresh_position = -1; |
|
|
|
if (_refresh_max >= MAX_REFRESH) |
|
{ |
|
return (-1); // maximum refresh position reached! |
|
} |
|
|
|
if ((refresh_position = _find_refresh(address)) < 0) |
|
{ |
|
refresh_position = ++_refresh_max; |
|
_refresh[refresh_position].address = address; |
|
_refresh[refresh_position].ready = false; |
|
} |
|
|
|
return (refresh_position); |
|
} |
|
|
|
int8_t Ardurail::_find_refresh(byte address) |
|
{ |
|
int8_t i = 0; |
|
|
|
for (i = 0; i <= _refresh_max; i++) |
|
{ |
|
if (_refresh[i].address == address) |
|
{ |
|
return(i); |
|
} |
|
} |
|
|
|
return (-1); |
|
} |
|
|
|
uint8_t Ardurail::_lsb_msb_replacement(uint8_t inbyte) |
|
{ |
|
uint8_t outbyte = |
|
((inbyte & 1) << 7) | |
|
((inbyte & 2) << 5) | |
|
((inbyte & 4) << 3) | |
|
((inbyte & 8) << 1) | |
|
((inbyte & 16) >> 1) | |
|
((inbyte & 32) >> 3) | |
|
((inbyte & 64) >> 5) | |
|
((inbyte & 128) >> 7); |
|
|
|
return(outbyte); |
|
} |
|
|
|
void Ardurail::_write_mm_output_register(uint8_t message, uint8_t address, uint8_t function, uint8_t data) |
|
{ |
|
uint8_t value1 = _lsb_msb_replacement(address); |
|
uint16_t value2 = (_lsb_msb_replacement(function) & 0b11000000) >> 6; |
|
uint32_t value3 = _lsb_msb_replacement(data); |
|
|
|
_msg[message] = value1 | (value2 << 8) | (value3 << 10); |
|
} |
|
|
|
byte Ardurail::_calc_trinary_address(byte address) |
|
{ |
|
uint8_t divident = address; |
|
uint8_t new_address = 0; |
|
uint8_t i=0; |
|
|
|
for (i = 0; i < 4; i++) |
|
{ |
|
uint8_t stellenwert = divident % 3; |
|
|
|
if (stellenwert == 1) |
|
{ |
|
new_address |= (0b11 << (6 - 2 * i)); |
|
} |
|
if (stellenwert == 2) |
|
{ |
|
new_address |= (0b10 << (6 - 2 * i)); |
|
} |
|
divident = divident / 3; |
|
} |
|
return (new_address); |
|
} |
|
|
|
uint8_t Ardurail::_calc_dcc_address(uint8_t address) |
|
{ |
|
return (address); |
|
} |
|
|
|
uint8_t Ardurail::_calc_dcc128_speed(uint8_t speed) |
|
{ |
|
if (speed > 0) |
|
{ |
|
speed++; |
|
} |
|
return(speed); |
|
} |
|
|
|
uint8_t Ardurail::_calc_dcc128_speed_and_direction(uint8_t speed, uint8_t dir) |
|
{ |
|
uint8_t out = _calc_dcc128_speed(speed); |
|
|
|
// 1 = forward |
|
// 0 = backward |
|
|
|
if (dir == 1) |
|
{ |
|
out |= 0b10000000; |
|
} |
|
|
|
return(out); |
|
} |
|
|
|
byte Ardurail::_calc_speed(byte speed) |
|
{ |
|
if (speed > 0) |
|
{ |
|
speed++; |
|
} |
|
|
|
byte out = |
|
((speed & 1) << 7) | |
|
((speed & 2) << 4) | |
|
((speed & 4) << 1) | |
|
((speed & 8) >> 2); |
|
|
|
return out; |
|
} |
|
|
|
|
|
byte Ardurail::_calc_speed_and_direction(byte speed, boolean dir) |
|
{ |
|
byte out = _calc_speed(speed); |
|
|
|
if (dir == false) |
|
{ |
|
if (speed >= 7) |
|
{ |
|
out |= 0b01000100; |
|
} |
|
else |
|
{ |
|
out |= 0b01000101; |
|
} |
|
} |
|
else |
|
{ |
|
if (speed <= 6) |
|
{ |
|
out |= 0b00010001; |
|
} |
|
else |
|
{ |
|
out |= 0b00010000; |
|
} |
|
} |
|
return out; |
|
} |
|
|
|
uint8_t Ardurail::_calc_dcc28_speed_and_direction(uint8_t speed, uint8_t dir) |
|
{ |
|
// http://www.nmra.org/sites/default/files/s-92-2004-07.pdf |
|
|
|
uint8_t out; |
|
|
|
if (speed > 0) |
|
{ |
|
speed += 3; |
|
} |
|
|
|
out = ((speed & 0b1) << 4) | ((speed & 0b11110) >> 1); |
|
|
|
if (dir == 1) |
|
{ |
|
// 1 = forward |
|
out |= 0x20; |
|
} |
|
|
|
// opcode |
|
out |= 0x40; |
|
|
|
return out; |
|
} |
|
|
|
byte Ardurail::_calc_function_f1(byte speed, boolean function) |
|
{ |
|
byte out = _calc_speed(speed); |
|
|
|
if ((speed == 2) && (function == false)) |
|
{ // 1. Ausnahme |
|
out |= 0b01000100; |
|
} |
|
else |
|
{ |
|
if ((speed == 10) && (function == true)) |
|
{ // 2. Ausnahme |
|
out |= 0b00010001; |
|
} |
|
else |
|
{ // keine Ausnahme |
|
if (function == true) |
|
{ |
|
out |= 0b01010001; |
|
} |
|
else |
|
{ |
|
out |= 0b01010000; |
|
} |
|
} |
|
} |
|
return out; |
|
} |
|
|
|
|
|
byte Ardurail::_calc_function_f2(byte speed, boolean function) |
|
{ |
|
byte out = _calc_speed(speed); |
|
|
|
if ((speed == 3) && (function == false)) |
|
{ // 1. Ausnahme |
|
out |= 0b01000100; |
|
} |
|
else |
|
{ |
|
if ((speed == 11) && (function == true)) |
|
{ // 2. Ausnahme |
|
out |= 0b00010001; |
|
} |
|
else |
|
{ // keine Ausnahme |
|
if (function == true) |
|
{ |
|
out |= 0b00000101; |
|
} |
|
else |
|
{ |
|
out |= 0b00000100; |
|
} |
|
} |
|
} |
|
return out; |
|
} |
|
|
|
|
|
byte Ardurail::_calc_function_f3(byte speed, boolean function) |
|
{ |
|
byte out = _calc_speed(speed); |
|
|
|
if ((speed == 5) && (function == false)) |
|
{ // 1. Ausnahme |
|
out |= 0b01000100; |
|
} |
|
else |
|
{ |
|
if ((speed == 13) && (function == true)) |
|
{ // 2. Ausnahme |
|
out |= 0b00010001; |
|
} |
|
else |
|
{ // keine Ausnahme |
|
if (function == true) |
|
{ |
|
out |= 0b00010101; |
|
} |
|
else |
|
{ |
|
out |= 0b00010100; |
|
} |
|
} |
|
} |
|
return out; |
|
} |
|
|
|
|
|
byte Ardurail::_calc_function_f4(byte speed, boolean function) |
|
{ |
|
byte out = _calc_speed(speed); |
|
|
|
if ((speed == 6) && (function == false)) |
|
{ // 1. Ausnahme |
|
out |= 0b01000100; |
|
} |
|
else |
|
{ |
|
if ((speed == 14) && (function == true)) |
|
{ // 2. Ausnahme |
|
out |= 0b00010001; |
|
} |
|
else |
|
{ // keine Ausnahme |
|
if (function == true) |
|
{ |
|
out |= 0b01010101; |
|
} |
|
else |
|
{ |
|
out |= 0b01010100; |
|
} |
|
} |
|
} |
|
return out; |
|
} |
|
|
|
void Ardurail::_generate(byte index) |
|
{ |
|
switch(_refresh[index].type) |
|
{ |
|
case M1: |
|
_generate_m1(index); |
|
break; |
|
|
|
case DEF: |
|
case M2: |
|
_generate_m2(index); |
|
break; |
|
|
|
case M2_28_80A: |
|
_generate_m3(index); |
|
break; |
|
|
|
case M2_14_256A: |
|
_generate_m4(index); |
|
break; |
|
|
|
case M2_28_256A: |
|
_generate_m5(index); |
|
break; |
|
|
|
case DCC_28: |
|
_generate_dcc_28(index); |
|
break; |
|
|
|
case DCC_128: |
|
_generate_dcc_128(index); |
|
break; |
|
} |
|
} |
|
|
|
void Ardurail::_generate_m1(byte index) |
|
{ |
|
// if no commit |
|
if(_refresh[index].ready == false) |
|
{ |
|
return; |
|
} |
|
|
|
// just for counting |
|
byte i = 0; |
|
|
|
// get all informations of loco |
|
byte address = _refresh[index].address; |
|
byte speed = _refresh[index].speed; |
|
boolean changedir = _refresh[index].changedir; |
|
boolean function = _refresh[index].function; |
|
|
|
// timing for loco |
|
_mode = MM_LOCO; |
|
_timer = TIMER_MM_LOCO; |
|
|
|
// calculate data |
|
byte tri_address = _calc_trinary_address(address); |
|
byte f0 = (function & 1) | ((function & 1) << 1); |
|
byte data; |
|
|
|
// KEIN Fahrtrichtungswechsel |
|
if (changedir == false) |
|
{ |
|
data = _calc_speed(speed); // Speedinformation A0B0C0D0 |
|
data |= ((data & 0b101010) >> 1); // MM1 AABBCCDD |
|
} |
|
// Fahrtrichtungswechsel |
|
else |
|
{ |
|
data = 0b11000000; |
|
_refresh[index].changedir = false; |
|
} |
|
|
|
for(i=0;i<8;i++) |
|
{ |
|
_write_mm_output_register(i, tri_address, f0, data); |
|
} |
|
} |
|
|
|
void Ardurail::_generate_m2(byte index) |
|
{ |
|
// if no commit |
|
if(_refresh[index].ready == false) |
|
{ |
|
return; |
|
} |
|
|
|
// get all informations of loco |
|
uint8_t address = _refresh[index].address; |
|
uint8_t speed = _refresh[index].speed; |
|
uint8_t dir = _refresh[index].dir; |
|
uint8_t changedir = _refresh[index].changedir; |
|
uint8_t function = _refresh[index].function; |
|
uint8_t f1 = _refresh[index].f1; |
|
uint8_t f2 = _refresh[index].f2; |
|
uint8_t f3 = _refresh[index].f3; |
|
uint8_t f4 = _refresh[index].f4; |
|
|
|
// timing for loco |
|
_mode = MM_LOCO; |
|
_timer = TIMER_MM_LOCO; |
|
|
|
// Adresse und Funktion berechnen |
|
uint8_t tri_address = _calc_trinary_address(address); |
|
uint8_t f0 = ((function & 1) << 1) | (function & 1); |
|
|
|
// KEIN Fahrtrichtungswechsel |
|
if (changedir == false) |
|
{ |
|
// Die Pakete 0,2,4 und 6 fuer Geschwindigkeit und Richtung |
|
uint8_t data = _calc_speed_and_direction(speed, dir); |
|
|
|
for (uint8_t i = 0; i < 8; i += 2) |
|
{ |
|
_write_mm_output_register(i, tri_address, f0, data); |
|
} |
|
|
|
// Die Pakete 1,3,5 und 7 fuer Geschwindigkeit und functionen (f1 - f4) |
|
data = _calc_function_f1(speed, f1); |
|
_write_mm_output_register(1, tri_address, f0, data); |
|
|
|
data =_calc_function_f2(speed, f2); |
|
_write_mm_output_register(3, tri_address, f0, data); |
|
|
|
data =_calc_function_f3(speed, f3); |
|
_write_mm_output_register(5, tri_address, f0, data); |
|
|
|
data =_calc_function_f4(speed, f4); |
|
_write_mm_output_register(7, tri_address, f0, data); |
|
} |
|
// Fahrtrichtungswechsel |
|
else |
|
{ |
|
_refresh[index].changedir = false; |
|
for (uint8_t i = 0; i < 8; i++) |
|
{ |
|
_write_mm_output_register(i, tri_address, 0b00, 0b11000000); |
|
} |
|
} |
|
} |
|
|
|
void Ardurail::_generate_m3(byte index) |
|
{ |
|
// if no commit |
|
if(_refresh[index].ready == false) |
|
{ |
|
return; |
|
} |
|
|
|
// just for counting |
|
uint8_t i = 0; |
|
|
|
// get all informations of loco |
|
uint8_t address = _refresh[index].address; |
|
boolean dir = _refresh[index].dir; |
|
boolean changedir = _refresh[index].changedir; |
|
boolean function = _refresh[index].function; |
|
boolean f1 = _refresh[index].f1; |
|
boolean f2 = _refresh[index].f2; |
|
boolean f3 = _refresh[index].f3; |
|
boolean f4 = _refresh[index].f4; |
|
|
|
uint8_t speed = (_refresh[index].speed + 1) / 2; |
|
boolean zfs = false; |
|
|
|
if (speed != 0) |
|
{ |
|
zfs = (boolean)(_refresh[index].speed + 1) % 2; |
|
} |
|
|
|
// timing for loco |
|
_mode = MM_LOCO; |
|
_timer = TIMER_MM_LOCO; |
|
|
|
// Alle Pakete mit der richtigen Adresse und Funktion versehen |
|
uint8_t tri_address = _calc_trinary_address(address); |
|
uint8_t f0; |
|
|
|
if (zfs == false) // Zwischenfahrstufe :) |
|
{ |
|
f0 = (function & 1) | ((function & 1) << 1); |
|
} else |
|
{ |
|
if (function == true) |
|
{ |
|
f0 = 0b10; |
|
} else |
|
{ |
|
f0 = 0b01; |
|
} |
|
} |
|
|
|
// kein Fahrtrichtungswechsel |
|
if (changedir == false) |
|
{ |
|
// Die Pakete 0,2,4 und 6 fuer Geschwindigkeit und Richtung |
|
uint8_t data = _calc_speed_and_direction(speed, dir); |
|
for (i = 0; i < 8; i += 2) |
|
{ |
|
_write_mm_output_register(i, tri_address, f0, data); |
|
} |
|
|
|
// Die Pakete 1,3,5 und 7 fuer Geschwindigkeit und functionen (f1 - f4) |
|
data = _calc_function_f1(speed, f1); |
|
_write_mm_output_register(1, tri_address, f0, data); |
|
data = _calc_function_f2(speed, f2); |
|
_write_mm_output_register(3, tri_address, f0, data); |
|
data = _calc_function_f3(speed, f3); |
|
_write_mm_output_register(5, tri_address, f0, data); |
|
data = _calc_function_f4(speed, f4); |
|
_write_mm_output_register(7, tri_address, f0, data); |
|
} |
|
|
|
// Fahrtrichtungswechsel |
|
else |
|
{ |
|
_refresh[index].changedir = false; |
|
|
|
for (i = 0; i < 8; i++) |
|
{ |
|
_write_mm_output_register(i, tri_address, f0, 0b11000000); |
|
} |
|
} |
|
} |
|
|
|
void Ardurail::_generate_m4(byte index) |
|
{ |
|
// if no commit |
|
if(_refresh[index].ready == false) |
|
{ |
|
return; |
|
} |
|
|
|
// just for counting |
|
uint8_t i = 0; |
|
|
|
// get all informations of loco |
|
uint8_t address = _refresh[index].address; |
|
uint8_t speed = _refresh[index].speed; |
|
boolean dir = _refresh[index].dir; |
|
boolean changedir = _refresh[index].changedir; |
|
boolean function = _refresh[index].function; |
|
boolean f1 = _refresh[index].f1; |
|
boolean f2 = _refresh[index].f2; |
|
boolean f3 = _refresh[index].f3; |
|
boolean f4 = _refresh[index].f4; |
|
|
|
// timing for loco |
|
_mode = MM_LOCO; |
|
_timer = TIMER_MM_LOCO; |
|
|
|
uint8_t tri_address; |
|
|
|
// if we have address >= 80 this is an extended address (M4) |
|
if (address < 80) |
|
{ |
|
tri_address = _calc_trinary_address(address); |
|
} else |
|
{ |
|
tri_address = extended_MM_addr[address-80]; |
|
} |
|
|
|
uint8_t f0 = (function & 1) | ((function & 1) << 1); |
|
|
|
// KEIN Fahrtrichtungswechsel |
|
if (changedir == false) |
|
{ |
|
// Die Pakete 0,2,4 und 6 fuer Geschwindigkeit und Richtung |
|
uint8_t data = _calc_speed_and_direction(speed, dir); |
|
for (i = 0; i < 8; i += 2) |
|
{ |
|
_write_mm_output_register(1, tri_address, f0, data); |
|
} |
|
|
|
// Die Pakete 1,3,5 und 7 fuer Geschwindigkeit und functionen (f1 - f4) |
|
data = _calc_function_f1(speed, f1); |
|
_write_mm_output_register(1, tri_address, f0, data); |
|
data = _calc_function_f2(speed, f2); |
|
_write_mm_output_register(3, tri_address, f0, data); |
|
data = _calc_function_f3(speed, f3); |
|
_write_mm_output_register(5, tri_address, f0, data); |
|
data = _calc_function_f4(speed, f4); |
|
_write_mm_output_register(7, tri_address, f0, data); |
|
} |
|
|
|
// Fahrtrichtungswechsel |
|
else |
|
{ |
|
_refresh[index].changedir = false; |
|
for (i = 0; i < 8; i++) |
|
{ |
|
_write_mm_output_register(i, tri_address, f0, 0b11000000); |
|
} |
|
} |
|
} |
|
|
|
void Ardurail::_generate_m5(byte index) |
|
{ |
|
// if no commit |
|
if(_refresh[index].ready == false) |
|
{ |
|
return; |
|
} |
|
|
|
// just for counting |
|
uint8_t i = 0; |
|
|
|
// get all informations of loco |
|
uint8_t address = _refresh[index].address; |
|
boolean dir = _refresh[index].dir; |
|
boolean changedir = _refresh[index].changedir; |
|
boolean function = _refresh[index].function; |
|
boolean f1 = _refresh[index].f1; |
|
boolean f2 = _refresh[index].f2; |
|
boolean f3 = _refresh[index].f3; |
|
boolean f4 = _refresh[index].f4; |
|
|
|
uint8_t speed = (_refresh[index].speed + 1) / 2; |
|
boolean zfs = false; |
|
|
|
if (speed != 0) |
|
{ |
|
zfs = (boolean)(_refresh[index].speed + 1) % 2; |
|
} |
|
|
|
// timing for loco |
|
_mode = MM_LOCO; |
|
_timer = TIMER_MM_LOCO; |
|
|
|
uint8_t tri_address; |
|
uint8_t f0; |
|
|
|
// if we have address >= 80 this is an extended address (M4) |
|
if (address < 80) |
|
{ |
|
tri_address = _calc_trinary_address(address); |
|
} else |
|
{ |
|
tri_address = extended_MM_addr[address-80]; |
|
} |
|
|
|
if (zfs == false) |
|
{ |
|
f0 = (function & 1) | ((function & 1) << 1); |
|
} else |
|
{ |
|
if (function == true) |
|
{ |
|
f0 = 0b10; |
|
} else |
|
{ |
|
f0 = 0b01; |
|
} |
|
} |
|
|
|
// kein Fahrtrichtungswechsel |
|
if (changedir == false) |
|
{ |
|
// Die Pakete 0,2,4 und 6 fuer Geschwindigkeit und Richtung |
|
uint8_t data = _calc_speed_and_direction(speed, dir); |
|
for (i = 0; i < 8; i += 2) |
|
{ |
|
_write_mm_output_register(i, tri_address, f0, data); |
|
} |
|
|
|
// Die Pakete 1,3,5 und 7 fuer Geschwindigkeit und functionen (f1 - f4) |
|
data = _calc_function_f1(speed, f1); |
|
_write_mm_output_register(1, tri_address, f0, data); |
|
data = _calc_function_f2(speed, f2); |
|
_write_mm_output_register(3, tri_address, f0, data); |
|
data = _calc_function_f3(speed, f3); |
|
_write_mm_output_register(5, tri_address, f0, data); |
|
data = _calc_function_f4(speed, f4); |
|
_write_mm_output_register(7, tri_address, f0, data); |
|
} |
|
|
|
// Fahrtrichtungswechsel |
|
else |
|
{ |
|
_refresh[index].changedir = false; |
|
for (i = 0; i < 8; i++) |
|
{ |
|
_write_mm_output_register(i, tri_address, f0, 0b11000000); |
|
} |
|
} |
|
} |
|
|
|
void Ardurail::_generate_dcc_28(uint8_t index) |
|
{ |
|
// if no commit |
|
if(_refresh[index].ready == false) |
|
{ |
|
return; |
|
} |
|
|
|
// get all informations of loco |
|
uint8_t address = _calc_dcc_address(_refresh[index].address); |
|
uint8_t speed = _refresh[index].speed; |
|
uint8_t dir = _refresh[index].dir; |
|
|
|
uint8_t speed_and_dir = _calc_dcc28_speed_and_direction(speed, dir); |
|
|
|
/* |
|
boolean changedir = _refresh[index].changedir; |
|
boolean function = _refresh[index].function; |
|
boolean f1 = _refresh[index].f1; |
|
boolean f2 = _refresh[index].f2; |
|
boolean f3 = _refresh[index].f3; |
|
boolean f4 = _refresh[index].f4; |
|
*/ |
|
|
|
_msg_dcc[0].used_bytes = 3; |
|
_msg_dcc[0].byte1 = address; |
|
_msg_dcc[0].byte2 = speed_and_dir; |
|
_msg_dcc[0].byte3 = address ^ speed_and_dir; |
|
_bit_counter = 0; |
|
_dcc_state = PREAMBLE; |
|
|
|
// DCC-Frequency |
|
_mode = DCC; |
|
_timer = TIMER_DCC; |
|
} |
|
|
|
void Ardurail::_generate_dcc_128(uint8_t index) |
|
{ |
|
// if no commit |
|
if(_refresh[index].ready == false) |
|
{ |
|
return; |
|
} |
|
|
|
uint8_t address = _calc_dcc_address(_refresh[index].address); |
|
uint8_t speed_and_dir = _calc_dcc28_speed_and_direction(_refresh[index].speed, _refresh[index].dir); |
|
|
|
/* |
|
boolean changedir = _refresh[index].changedir; |
|
*/ |
|
|
|
_msg_dcc[0].used_bytes = 4; |
|
_msg_dcc[0].byte1 = address; |
|
_msg_dcc[0].byte2 = 0b00111111; // extended command:001 128 speedsteps: 11111 |
|
_msg_dcc[0].byte3 = speed_and_dir; |
|
_msg_dcc[0].byte4 = address ^ 0b00111111 ^ speed_and_dir; |
|
|
|
uint8_t functions = |
|
(0b100 << 5) | // functiongroup 1 |
|
(_refresh[index].function << 4) | |
|
(_refresh[index].f4 << 3) | |
|
(_refresh[index].f3 << 2) | |
|
(_refresh[index].f2 << 1) | |
|
_refresh[index].f1; |
|
|
|
_msg_dcc[1].used_bytes = 3; |
|
_msg_dcc[1].byte1 = address; |
|
_msg_dcc[1].byte2 = functions; |
|
_msg_dcc[1].byte3 = address ^ functions; |
|
|
|
functions = |
|
(0b101 << 5) | // functiongroup 2 |
|
(1 << 4 ) | // S = 1 |
|
(_refresh[index].f8 << 3) | |
|
(_refresh[index].f7 << 2) | |
|
(_refresh[index].f6 << 1) | |
|
_refresh[index].f5; |
|
|
|
_msg_dcc[2].used_bytes = 3; |
|
_msg_dcc[2].byte1 = address; |
|
_msg_dcc[2].byte2 = functions; |
|
_msg_dcc[2].byte3 = address ^ functions; |
|
|
|
functions = |
|
(0b101 << 5) | // functiongroup 2 |
|
// S = 0 |
|
(_refresh[index].f12 << 3) | |
|
(_refresh[index].f11 << 2) | |
|
(_refresh[index].f10 << 1) | |
|
_refresh[index].f9; |
|
|
|
_msg_dcc[3].used_bytes = 3; |
|
_msg_dcc[3].byte1 = address; |
|
_msg_dcc[3].byte2 = functions; |
|
_msg_dcc[3].byte3 = address ^ functions; |
|
|
|
_bit_counter = 0; |
|
_step = 0; |
|
_dcc_state = PREAMBLE; |
|
|
|
// DCC-Frequency |
|
_mode = DCC; |
|
_timer = TIMER_DCC; |
|
|
|
} |
|
|
|
// erzeugt 1 Doppelpaket mit dem Weichen-Befehl |
|
void Ardurail::_generate_trackswitch(byte address, byte subaddress, boolean state) |
|
{ |
|
// Weichenfrequenz und Mode setzen |
|
_mode = MM_DEVICE; |
|
_timer = TIMER_MM_DEVICE; |
|
|
|
uint8_t tri_address = _calc_trinary_address(address); |
|
uint8_t data = |
|
((subaddress & 1) << 7) | ((subaddress & 1) << 6) | |
|
((subaddress & 2) << 4) | ((subaddress & 2) << 3) | |
|
((subaddress & 4) << 1) | (subaddress & 4) | |
|
((state & 1) << 1) | (state & 1); |
|
|
|
for (uint8_t i=0; i<1; i++) { |
|
_write_mm_output_register(i, tri_address, 0b00, data); |
|
} |
|
} |
|
|
|
void Ardurail::_do_timer2_irq_MM(void) |
|
{ |
|
TCNT2 = _timer; |
|
|
|
if (_mode == DCC) |
|
{ |
|
_do_timer2_irq_DCC(); |
|
} |
|
else |
|
{ |
|
|
|
if ((_break_t1 + _break_t2 + _break_t3) == 0) |
|
{ |
|
// rising edge |
|
if (_step == 0) |
|
{ |
|
// high |
|
*_digital_reg |= _digital_signal; |
|
|
|
_outbit = (_msg[_msg_counter] >> _bit_counter) & 1; |
|
} |
|
|
|
// falling edge |
|
else if (((_step == 1) && (_outbit == 0)) || ((_step == 6) && (_outbit == 1))) |
|
{ |
|
// low |
|
*_digital_reg &= ~_digital_signal; |
|
} |
|
|
|
// 8. step is the next step 0 |
|
if (++_step == 8) |
|
{ |
|
_step = 0; |
|
_bit_counter++; |
|
|
|
if (_bit_counter == 18) |
|
{ |
|
_bit_counter = 0; |
|
_packet_counter++; |
|
|
|
if ((_packet_counter == 1) || (_packet_counter == 3)) |
|
{ |
|
_break_t1 = true; |
|
} |
|
|
|
else if (_packet_counter == 2) |
|
{ |
|
_break_t2 = true; |
|
} |
|
|
|
else if (_packet_counter == 4) |
|
{ |
|
_break_t3 = true; |
|
_packet_counter = 0; |
|
_msg_counter++; |
|
} |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
_step++; |
|
|
|
if ((_step >= TIME_T1) && (_break_t1 == true)) |
|
{ |
|
_break_t1 = false; |
|
_step = 0; |
|
} |
|
|
|
else if ((_step >= TIME_T2) && (_break_t2 == true)) |
|
{ |
|
_break_t2 = false; |
|
_step = 0; |
|
} |
|
|
|
else if ((_step >= TIME_T3) && (_break_t3 == true)) |
|
{ |
|
// this is the end of breake t3 |
|
_break_t3 = false; |
|
_step = 0; |
|
|
|
// the 8. loco msg and 1 devise msg is number 0 |
|
if ((_msg_counter == MAXMSG) || ((_msg_counter == 2) && (_mode == MM_DEVICE))) |
|
{ |
|
_msg_counter = 0; |
|
_bit_counter = 0; |
|
// we can load a new set of commands |
|
_do_update(); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
void Ardurail::_do_timer2_irq_DCC(void) |
|
{ |
|
// next state |
|
switch (_dcc_state) |
|
{ |
|
case PREAMBLE: |
|
// Preamble: 15 1-bits |
|
_outbit = 1; |
|
|
|
// end of Preamble |
|
if (_bit_counter == 15) |
|
{ |
|
_bit_counter = 0; |
|
_dcc_state = SEPERATOR; |
|
} |
|
break; |
|
|
|
case SEPERATOR: |
|
// one 0-bit |
|
_outbit = 0; |
|
|
|
if (_bit_counter == 1) |
|
{ |
|
_bit_counter = 0; |
|
_dcc_state = SENDBYTE; |
|
} |
|
break; |
|
|
|
case SENDBYTE: |
|
|
|
if (_bit_counter < 8) |
|
{ |
|
if (_step == 0) |
|
{ |
|
// byte to send |
|
uint8_t sendout = 0; |
|
|
|
switch (_packet_counter) |
|
{ |
|
case 0: |
|
sendout = _msg_dcc[_msg_counter].byte1; |
|
break; |
|
|
|
case 1: |
|
sendout = _msg_dcc[_msg_counter].byte2; |
|
break; |
|
|
|
case 2: |
|
sendout = _msg_dcc[_msg_counter].byte3; |
|
break; |
|
|
|
case 3: |
|
sendout = _msg_dcc[_msg_counter].byte4; |
|
break; |
|
|
|
case 4: |
|
sendout = _msg_dcc[_msg_counter].byte5; |
|
break; |
|
|
|
case 5: |
|
sendout = _msg_dcc[_msg_counter].byte6; |
|
break; |
|
} |
|
|
|
_outbit = (sendout >> (7 - _bit_counter)) & 1; |
|
} |
|
} |
|
else // _bit_counter = 8 |
|
{ |
|
_bit_counter = 0; |
|
_step = 0; |
|
_packet_counter++; |
|
|
|
if (_packet_counter == _msg_dcc[_msg_counter].used_bytes) |
|
{ |
|
_dcc_state = TERMINATOR; |
|
_packet_counter = 0; |
|
} |
|
else |
|
{ |
|
_dcc_state = SEPERATOR; |
|
} |
|
} |
|
break; |
|
|
|
case TERMINATOR: |
|
// one 1-bit |
|
_outbit = 1; |
|
|
|
if (_bit_counter == 1) |
|
{ |
|
_dcc_state = BREAK; |
|
_bit_counter = 0; |
|
_step = 0; |
|
} |
|
break; |
|
|
|
case BREAK: |
|
_step++; |
|
|
|
if (_step == TIME_DCC_BREAK) |
|
{ |
|
_dcc_state = PREAMBLE; |
|
_step = 0; |
|
_msg_counter++; |
|
if (_msg_counter == MAXMSG) |
|
{ |
|
_msg_counter = 0; |
|
_do_update(); |
|
} |
|
} |
|
break; |
|
} |
|
|
|
if (_dcc_state != BREAK) |
|
{ |
|
if (_outbit == 1) |
|
{ |
|
if ((_step == 0) || (_step == 1)) |
|
{ |
|
*_digital_reg ^= _digital_signal; |
|
} |
|
_step++; |
|
if (_step == 2) |
|
{ |
|
_step = 0; |
|
_bit_counter++; |
|
} |
|
} |
|
else |
|
{ |
|
if ((_step == 0) || (_step == 2)) |
|
{ |
|
*_digital_reg ^= _digital_signal; |
|
} |
|
_step++; |
|
if (_step == 4) |
|
{ |
|
_step = 0; |
|
_bit_counter++; |
|
} |
|
} |
|
} |
|
} |
|
|
|
void Ardurail::_do_update(void) |
|
{ |
|
#ifdef __LED13__ |
|
__led13__(HIGH); |
|
#endif |
|
|
|
if (_power == true) { |
|
|
|
// if we have an update |
|
if(_updated_loco >= 0) { |
|
_generate(_updated_loco); |
|
_updated_loco = -1; |
|
} |
|
else { |
|
// if we have a regular command for trackswitch |
|
if(_waiting_track_commands > 0) { |
|
_waiting_track_commands--; |
|
|
|
_generate_trackswitch( |
|
tswitch[_waiting_track_commands].address, |
|
tswitch[_waiting_track_commands].subaddress, |
|
tswitch[_waiting_track_commands].state); |
|
|
|
/* |
|
tswitch[_waiting_track_commands].address = 0; |
|
tswitch[_waiting_track_commands].subaddress = 0; |
|
tswitch[_waiting_track_commands].state = 0; |
|
tswitch[_waiting_track_commands].ready = false; |
|
*/ |
|
} |
|
else { |
|
if (_refresh_max == 0) { |
|
// if we have no loco, fill with Idlepackets |
|
for (uint8_t i = 0; i < MAXMSG; i++) { |
|
|
|
// MM |
|
_write_mm_output_register(i, 0b10101010, 0b00, 0b00000000); |
|
|
|
// DCC |
|
_msg_dcc[i].byte1 = 0xff; |
|
_msg_dcc[i].byte2 = 0; |
|
_msg_dcc[i].byte3 = 0xff; |
|
_msg_dcc[i].byte4 = 0; |
|
_msg_dcc[i].byte5 = 0; |
|
_msg_dcc[i].byte6 = 0; |
|
_msg_dcc[i].used_bytes = 3; |
|
} |
|
|
|
// timing for loco |
|
_mode = MM_LOCO; |
|
_timer = TIMER_MM_LOCO; |
|
} |
|
else { |
|
// the "regular" refresh |
|
_generate(_refresh_pointer++); |
|
|
|
if(_refresh_pointer > _refresh_max) { |
|
_refresh_pointer = 0; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
#ifdef __LED13__ |
|
__led13__(LOW); |
|
#endif |
|
} |
|
|
|
inline void Ardurail::handle_timer2_interrupt(void) |
|
{ |
|
if (_active_object) |
|
{ |
|
_active_object->_do_timer2_irq_MM(); |
|
} |
|
} |
|
|
|
inline void Ardurail::handle_short_interrupt(void) |
|
{ |
|
if (_active_object) |
|
{ |
|
boolean State = true; |
|
for (int counter = 0; counter < 5; counter++) |
|
{ |
|
//delay(1); |
|
State = (boolean)digitalRead(_active_object->_shortcut_signal_pin); |
|
if (State == false) |
|
{ |
|
_active_object->_short = false; |
|
break; |
|
} |
|
} |
|
|
|
if (State == true) |
|
{ |
|
_active_object->set_power(false); |
|
_active_object->_short = true; |
|
} |
|
} |
|
} |
|
|
|
|
|
// |
|
// ######################### FUNCTIONS ############################# |
|
// |
|
|
|
#ifdef __LED13__ |
|
inline void __led13__(boolean state) |
|
{ |
|
#ifdef __MEGA__ |
|
if (state == HIGH) |
|
{ |
|
PORTB |= 1 << 7; // high |
|
} |
|
else |
|
{ |
|
PORTB &= ~(1 << 7); // low |
|
} |
|
#else |
|
if (state == HIGH) |
|
{ |
|
PORTB |= 1 << 5; // high |
|
} |
|
else |
|
{ |
|
PORTB &= ~(1 << 5); // low |
|
} |
|
#endif |
|
} |
|
#endif |
|
|
|
// |
|
// Timer2 Interrupt function for generation of the signal |
|
// |
|
#ifdef USE_SHORT_INTERRUPT |
|
ISR(TIMER2_OVF_vect) |
|
{ |
|
Ardurail::handle_timer2_interrupt(); |
|
} |
|
#endif |
|
|
|
|
|
|
|
|