Liebe Z21-Bastler!
Nach einem Monat mit umfangreichen Tests und Softwareanpassungen möchte ich nun mal mit einer Zsammenfassung beginnen. Ursache für diese Forschungen war ein habwegs stabiler Betrieb der DR5088RC an der Z21PG mit RailCom-Kanal 1. Beim Kanal 2 zappelte es jedoch ganz schön gewaltig.
Vorab noch einige Worte während im Hintergrund noch einige Langzeittests laufen:
Ziel war es "mal eben schnell" zu Testen ob die PG21 im Zusammenspiel mit RocRail und den Belegtmelder DR5088RC (keinen Loconet-Funktionsdecoder !!!) hinsichtlich der Loknummernerkennung via RC funktioniert. Insgesamt hat das dann doch ca. 120 h Arbeitszeit und ein paar Nerven gekostet. Die Freude am Lernen und das es jetzt "funzt" gleicht das jedoch aus.
Warum ich einen Kauf-Belegtmelder genommen und nicht alles selber bauen ? Nun, Ideen sowas umzusetzten hätte ich schon, nur ... Ich glaube fest daran, dass ich sowas könnte und spare mir Zeit und Geld - OK Außerdem ist es keine Kunst zwei Eigenbaugeräte aufeinander abzustimmen. Reizvoller war es schon, mit einer vorhandenen Blackbox, auf die man keinen Einfluss hat, zurechtzukommen.
Doch, was geht nun ? Ich versuch mal das Pferd von hinten aufzuzäumen (chronologisch erfolgten Forschungen/Erkenntnisse/Änderungen in ungekehrter Reihenfolge, also von unten nach oben).
Zur Info:
Beim Ermitteln der RC-Adresse über Kanal 1 wird die eine Rückmeldung des Dekoders (ACK) ausgewertet wenn dieser mit seiner eigenen Adresse Angesprochen wird. D. h. es sollte im Normalfall nur eine Lok antworten. Hierzu ist es allerdinges erforderlich, dass alle Loks in einem bestimmten Zeitfenster zyklisch angesprochen werden. Dabei wird ein Ringspeicher verwendet in dem alle während der Laufzeit der Zentrale gesendeten DCC-Pakete abgelegt werden. Geänderte Pakete (z.B. Funktion wieder aus, andere Geschwindigkeit) werden überschrieben. Damit alle Loks in einem minimalen Zeitfenster einmal bedient werden, habe ich die Software angepasst (siehe unten). Ohne diese Anpassung werden u. U. alle Pakete einer Lok hintereinander und dann alle anderen Loks "angepingt". Der Abstand dieser Rückmeldungen ist dann bei vielen Loks mit vielen aktiven Funktionen einfach zu groß und der Detektor meldet die Lok "abwesend".
Ein Rechenbeispiel:
Pro Lok können neben einem Paket für die Geschwindigkeit bis zu 5 Pakete mit Funktionsausgängen gesendet werden. Diese werden im DCC-Refresh-Ringspeicher zyklisch wiederholt. Der Ringspeicher kann eine maximale Größe von 255 Paketen haben. Ein Paket benötigt ca. 8 ms für die Übertragung. Das bedeutet - im Extremfall max 255 Loks = 2 Sekunden für die Wiederholung. So viel Geduld hat die DR5088 nicht - max. 1200 ms = 150 Loks. Dabei darf es jedoch zu keinerlei Übertragungsstörungen kommen. Das ist praktisch ausgeschlossen. Die folgenden Werte habe ich in Langzeittests mit simulierten Loks ermittelt.
Getestet habe ich die PG21-MEGA, Rocrail und DR5088RC mit den Werkseinstellungen:
1
2
3
4
5
Belegungdetectorzähler: 15
Polaritätszähler: 2
Adressendetektionswartezeit: 250 ms
Multiplexer Scan-Zeit: 33 ms
Detektor (1-17) Eigenschaften - Aus Wartezeit: 750 ms
Mit meiner aktuellen Softwareadaption (2019-02-0x) und der DR5088-Version 1.4.1 (Werkseinstellungen) geht:
- die Erkennung mittels Railcom Kanal 1 (CV #28 = 3), max eine Lok pro Abschnitt
- die Erkennung mittels Railcom Kanal 2 (CV #28 = 2), Haken DR5088 "Verwende Kanal 2 ..."
- max. vier Loks pro Abschnitt
- max. 32 aktive Loks, gesamt max. 172 DCC-Pakete, Werkseinstellungen in DR5088
- max. 33 aktive Loks, 178 DCC-Pakete, DR5088-Multiplexer Scan-Zeit: 34 ms
- max. 37 aktive Loks, 207 DCC-Pakete, DR5088-Multiplexer Scan-Zeit: 35 ms, neue Befehle führen zu Aussetzern
- max. 38 aktive Loks, 213 DCC-Pakete, DR5088-Multiplexer Scan-Zeit: 36 ms
- max. 40 aktive Loks, 225 DCC-Pakete, DR5088-Multiplexer Scan-Zeit: 38 ms, XpressNet blockiert !!!
- max. 40 aktive Loks, 151 DCC-Pakete, DR5088-Multiplexer Scan-Zeit: 38 ms
- max. 44 aktive Loks, 167 DCC-Pakete, DR5088-Multiplexer Scan-Zeit: 38 ms
- max. 50 aktive Loks, 191 DCC-Pakete, DR5088-Multiplexer Scan-Zeit: 38 ms, 2h fehlerfrei, XpressNet OK
- max. 60 aktive Loks, 231 DCC-Pakete, DR5088-Multiplexer Scan-Zeit: 38 ms, 1h fehlerfrei, XpressNet OK
- max. 70 aktive Loks, 204 DCC-Pakete, DR5088-Multiplexer Scan-Zeit: 38 ms, instabil, XpressNet blockiert !!!
- max. 70 aktive Loks, 136 DCC-Pakete, DR5088-Multiplexer Scan-Zeit: 38 ms, instabil
- max. 64 aktive Loks, 126 DCC-Pakete, Refresh-Zyklus 430 ms, DR5088 angepassten Einstellungen (s.u.): Stunden-Test OK
Diese Extremwertbetrachtungen sind jedoch nicht ohne weiteres auf den Praxisbetrieb übertragbar. Neue Kommandos drängeln sich dazwischen und führen zwangsläufig zu kurzen Unterbrechungen der Belegtmeldung. Praktisch scheinen 25 Loks (mit DR 5088 Werkseinstellungen) und 45 mit folgenden angepassten Einstellungen praktikabel:
1
2
3
4
5
Belegungdetectorzähler: 5
Polaritätszähler: 2
Adressendetektionswartezeit: 15 ms
Multiplexer Scan-Zeit: 38 ms
Detektor (1-17) Eigenschaften - Aus Wartezeit: 1200 ms
Das Ganze ist nun etwas träge aber wenn es sein muss ...
Welche Wirkung der Multiplexer hat ist mir nicht ganz klar. Ich denke jedoch, dass die DR5088 hardwareseitig für den Kanal 1 entwickelt wurde (da braucht man entweder viele serielle Schnittstellen, einen Multiplexer oder eine Mischung aus beiden). Kanal 2 wurde wohl softwareseitig nachgeschoben. Ein Multiplexer ist grundsätzlich für Kanal 2 nicht nötig, da ja nur eine Lok antworten sollte.
Doch wie kommen die Loks überhaupt in diesen Refreshspeicher?
1. ALLE Loks manuell z. B. über Multimaus oder RocRail ansprechen (F0 an/aus, V=0) - unbequem!!!
2. selber in die PG-Software feste Loks reinprogramieren - ungünstig, nur zum Testen
3. in der PG-Software die Loks im EEPROM ablegen - müssten wie einen Programmierer einstellen
4. im RocRail - Automatik - "Bei Gleisspannung EIN Geschwindigkeits-Befehl Null senden" aktivieren.
Dabei werden nach dem Einschalten von RR alle parametrierten Loks einmal angesprochen. Rob hat die Funktion ab Version 14900 gängig gemacht und ich konnte es am 24.01.2019 mit 14916 erfolgreich testen.
Bei richtig vielen Loks die auch nur gelegentlich verwendet werden müsste der Refreshspeicher noch mit einer Funktion des Vergessens versehen werden. Ziel pro Lok nur noch ein Paket. Unbenutzte (Funktionen aus, V=0), überzählige Pakete pro Lok raus. Weiterhin könnte man "stehende" Loks (V=0) anders priorisieren und die Abmeldungen stehender Loks im LocoNet abfangen. Da fehlt aber ein universelles Gesamtkonzept, Zeit und die Zahl der fahrfähigen Loks Loks bauenhat Vorfahrt!
Die Frage ist weiterhin ob das bei der MEGA-Zentrale überhaupt sinnvoll ist (Speicher, Prozessorleistung).
--------------------- Achtung ! Ab hier kommen langweilige technische Details. -----------------------------------
Hinweis: Die folgenden Änderungen wurden so eingebaut, dass sie mit #define in den ersten Zeilen der jeweiligen Dateien wieder rückgängig gemacht werden können (Auskommentieren mit "//" vor dem jeweiligen #define). Das soll das Testen für mich und die nicht so programmieraffinen unter Euch erleichtern.
1.
Um alle Loks mit einer ausreichenden Regelmäßigkeit ansprechen zu können musste ein hinsichtlich Lesbarkeit, Speicher und Rechenleistung günstiger Algorithmus gefunden werden. In der Refresh-Queue werden jetzt die jeweiligen Lok-/Adresspakete beieinander gehalten. Das erste Paket der Lok wird am DCC ausgegeben, alle weiteren dieser Lok wieder hinten angefügt, und zuguterletzt das eben ausgegebene als letztes hinten angefügt. Dann kommt die nächste Lok/Adresse. Somit hat ein Zyklus maximal die Länge von 8 ms * Anzahl Loks. Kommt ein neues Paket aus der Repeat-Queue in die Refresh-Queue wird es nur hinten angefügt und ein Flag gesetzt ("packet->setRepeat(0x3F);"). Ist die Lok/Adresse das nächste mal dran, werden alle Pakete dieser Adresse in der Queue zusammengesammelt und als Block hinten angefügt. Rechenleistungsbedarf ist OK, da das pro eingefügten Packet nur einmal erfolgen muss.
1
2
3
librariesDCCInterfaceMasterDCCPacketQueue.cpp
#define RC_CH2_OPTIMIZED // Optimiert für Belegtmeldung mir RailCom-Kanal-2, sendet Pakete aller Loks/Adressen gleichverteilt, unabhängig wieviele Paketarten/Funktionen aktiviert
2.
Bei jedem eingefügten Packet wird die gesamte Queue durchsucht ob Adresse und Funktionsart ("Kind") schon enthalten sind. Wenn ja wird es überschrieben, sonst angefügt. Allerdings erfolgt im ursprünglichen Code dieser Exist-Check immer. Wurde das Paket eben entnommen ist diese Prüfung jedoch überflüssig. Der Exist-Check wird nun nur noch bei neu eingefügten Paketen durchgeführt.
1
2
3
4
librariesDCCInterfaceMasterDCCPacketQueue.cpp
#define CHECKEXIST false // false fügt gerade gelesene Pakete am Ende der Queue ein ohne nochmal auf Vorhandensein zu prüfen, true ist Unsinn, da Pakete bereits geprüft --> Performance und Laufzeitkritisch
3.
Leider führt eine zu lange Rechenzeit für das Umsortieren der Queue dazu, dass deas Aussenden des DCC-Packetes schon beginnt, bevor die Daten hierfür bereitstehen. Dauert die Berechnung länger als bis zum Ende der Präambel, werden im DCC-Bitstrom lauter "1" eingefügt was zu einem ungültigen Packet und CHK führt. Änderung durch Entkoppelung der Prozesse insofern, das die neuen Daten vorab bereitgestellt werden und anschließend umsortiert werden.
1
2
3
4
librariesDCCInterfaceMasterDCCPacketSheduler.cpp
#define ENTKOPPLESHEDULER //UH 2019-01-30, ermöglicht DCC-Ausgabe entkoppelt, von der refresh_queue-Umsortierung
Der untere Kanal zeigt die ob die Funktion readPacket() mit dem Umsortieren des Ringspeichers beschäftigt ist (Debug-Pin 23 wird am Anfang der Funktion gesetzt und am Ende wieder auf 0 zurückgesetzt).
4.
Zur Erzeugung des DCC-Signals wird ein Interrupt (TIMER2) verwendet der immer hzur rechten Zeit die ISR (Interrupt Service Routine) aufruft und den Flankenwechsel veranlasst. Ist gerade ein anderer Interrupt aktiv sind standardmäßig alle anderen Interrupts verboten. Das bedeutet die DCC-Flanke wird um eine gewisse Zeit verzögert. Da der Xpressnet-Interrupt ca. 15 us benötigt kommt es zu Flankenverschiebungen von 15 us (siehe Bilder) - zulässig sind 3 oder 6 us ... Abhilfe schafft jetzt die Möglichkeit den Xpressnet-Interrupt durch einen anderen Interrupt zu unterbrechen.
Die roten Kreuze und Punkte im DCC-Signal zeigen die verjitterten Stellen/Flankenwechsel. Unten ist dann alles gut
1
2
3
4
5
6
//UH 2019-01-24 verursacht DCC-Jitter, 2019-01-25 Anpassung in librariesXpressNetMasterxpressnetmaster.cpp
#define ENABLE_IE_RX //Erlaubt Unterbrechng der XpressNet-RX-Interrupte, alleine nicht ausreichend, gemeinsam praktisch 100%-tig
#define ENABLE_IE_TX //Erlaubt Unterbrechng der XpressNet-TX-Interrupte, Minimum zur Jitterunterdrückung, geringer Jitter, Kaum Enfluss auf XpressNet-MM
#define DISABLE_IE_TIMER0 //Verbietet Unterbrechng der XpressNet-Interrupte durch Timer0 (ca. 5 us Ausführungszeit)
5.
Der arduinoeigene Timer0 dient u. A. delay(), millis() und somit dem Zählen der Millisekunden. Der Aufruf erfolgt aller 1 ms und hinterlässt recht regelmäßig im DCC-Signal einen weiteren Jitter von ca. 5 us.
Auch dieser stört das DCC-Signal. Es ist dem aber nicht so einfach beizukommen. Es sind Änderungen an der Datei wiring.c im Programme-Pfad erforderlich (Admin-Rechte!!!). Diese gilt dann für alle Arduino-Projekte!!! Wer das nicht möchte - für 5 bis 8 Loks scheint es zu gehen (ausreichend Zyklen im Prüfzeitraum der DR508 für mehr Loks sollte man die Anpassung vornehemen. Ähnlich dem XpressNet wird hier anderen Interrupts (und so auch DCC) erlaubt hier einzuspringen und zu unterbrechen.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// import aus C:Program FilesArduinohardwarearduinoavrcoresarduinowiring.c
// UH 2018-01-27 Enable the Global Interrupt Enable flag so that other interrupts can be processed
// #define ENABLE_IE == Änderung um kleinen DCC-Jitter auszuschalten (ca. 5 us)
#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
// Enable the Global Interrupt Enable flag so that other interrupts can be processed
#define ENABLE_IE
#ifdef ENABLE_IE
char oldTIMSK0;
oldTIMSK0 = TIMSK0;
TIMSK0 = 0;
sei(); // Enable the Global Interrupt Enable flag so that interrupts can be processed
#endif
...
#ifdef ENABLE_IE
cli(); //disable interrupts
TIMSK0 = oldTIMSK0;
#endif
}
Die angepasste Funktion ist nochmal am Ende der *.ino als Kommentar angefügt.
6.
Um nun noch genug Speicher für die ganzen Datenpakete zu haben, müssen die Queuegrößen noch angepasst werden (nur beim MEGA).
1
2
3
librariesDCCInterfaceMasterDCCPacketSheduler.cpp
#define MAX_REFRESH_QUEUE //UH 2019-01-26 gosser DCC-Repeat- und maqximaler DCC-Refrsh-Puffer
7.
RailCom-CutOut: Die Datenübertragung von der Zentrale zum Decoder wird nicht ununterbrochen durchgeführt, sondern es wird ein Zeitmultiplex eingeführt, welches Zeitschlitze für die Übertragung zum Decoder und für den Rückweg vorsieht. Der Zeitschlitz für die Rückübertragung (=cutout, grau dargestellt) ist wie folgt definiert:
Quelle: https://www.opendcc.de/info/railcom/railcom.html
Cutout der PG ohne (oben) und mit Anpassung (unten)
Ich habe festgestellt, dass die z21PG einen etwas anderen Zeitschlitz erzeugt. Die Vermutung, dass dies Ursache der spontanen Unterbrechnungen der Belegtmelders ist hat sich zwar nicht bestätigt - es hat offensichtlich keinen Einfluss. Die Anpassung ist im Code verblieben jedoch aus Performancegründen auskommentiert. Sollte es pingelige Decoder oder Besetztmelder geben, besteht die Möglichkeit es wieder reinzunehmen.
1
2
3
librariesDCCInterfaceMasterDCCHardware.c
//#define ISRDIVRC //UH 2019-01-13, RCN-konformes RC-Cutout
8.
Übrigens, ein einfacher "8 Kanal Logik Analyser, CH 8 Channel USB Logic Analyzer" mit 24MHz ist sehr hilfreich. Die Saleae-Logic-Software ist super, kann von Haus aus das Async-Serial des RailCom-Signals in der Rohform (vor Decodierungstabelle) auslesen. Als Plug-In gibt es auch einen DCC-Decoder vom freundlichen Dähnen:
https://www.ejberg.dk/portfolio/saleae-dcc-decoder/
9.
Mischbetrieb von Loks mit und ohne Kanal 1 geht zwar prinzipiell. Die Auswirkunden auf das Steuerprogramm können jedoch von sehr unterschiedlicher Natur sein. Es empfiehlt sich per POM-Write auf Lok 0* CV #28= 2 zusetzen und Kanal 2 im DR5088 zu setzen oder an alle CV #28= 3 und Kanal 2 zu deaktivieren -> Kanal 1 zu nutzen.
* Ja, das geht jetzt bei der PG im Gegensatz zum Original - zumindest bei Zimo-Decodern. Bei meinem ESU gehts nicht.
10.
Das Steuerprogramm (z. B. RocRail) muss nach der Z21PG gestartet werden, da die Informationen (z. B. LocoNet-Belegtmeldungen) bei der PG abonniert werden müssen. Nach jedem Neustart der Z21PG muss (im Gegensatz zum Original) RocRail o. Ä. auch neugestartet werden.
Ich werde in den nächsten Stunden den Quellcode noch etwas aufräumen und dann an der bekannten Stelle (siehe Signatur) hochladen. Quellcode mit der NANO-Variante ist getestet.
@Andre: War schön mal zu swatzen. Aktualisiere mal Deine neue Z21-App. Bei mir gehts jetzt.
So, Schaffenspause
Viele Grüße
Uwe