Liebe Stumm
Heute möchte ich mein Arduino Library zum dekodieren von DCC Signale vorstellen: der AP_DCC_Library.
Ein neuer DCC Library? Brauchen wir das? ich wurde meinen nein, …… oder, vielleicht doch …?
Es gibt schon viele Jahre die NmraDcc Library. Diese Bibliothek funktioniert in die meiste Fälle hervorragend, und der Support im Internet ist ausgezeichnet. Es gibt hunderte von Sketches die mit diese Bibliothek gebaut worden sind, und die laufen auf traditionele Arduino Uno (und ähnliche Boards) aber auch auf neuere Platformen, wie zur Beispiel der ESP32. Warum also etwas neues?
Dazu erst etwas Hintergrund. Da ich meine Platinen gerne selber herstelle (oder besser: in China herstellen lasse), benütze ich auch gerne die neuere ATMega Processoren, wie zur Beispiel der 4809 und der AVR-DA und AVR-DB Prozessoren. Diese neue Prozessoren sind nicht nur “besser” als die alte AtMega 328 oder 2560, aber auch noch billiger. Für neuere Decoder Boards kann ich daher jeder empfehlen diese neuere Prozessoren ein zu setzen.
NmraDcc und neuere Prozessoren
Obwohl der NmraDcc-Bibliothek lauft auf die neue AtMega4809, ist deren Leistung etwas enttäuschend. Das hat mehrere Grunde, worauf ich später eingehen werde. Anpassung der NmraDcc-Bibliothek ist nicht einfach, denn es gibt ein zweites Problem: der Quellcode ist nicht sonderlich gut strukturiert und lesbar. Alles steht in eine grosse Datei, und separate Module die optimiert für bestimmte Processoren sind, sind nicht vorher gesehen. Selber neue Module bei zu steuern die optimiert für die neuere AtMega Processoren sind, ist daher kaum möglich. Wenn ich mich aber irre und jemanden Teile meiner Code in der NmraDcc-Bibliothek einfügen kann, würde ich mich aber freuen.
Die NmraDcc-Bibliothek verfolgt einen "brute-force"-Ansatz um das DCC-Signal zu dekodieren. Brute-force in dem Sinne, dass sie nur auf CPU-Geschwindigkeit setzt. Die NmraDcc-Bibliothek benützt keiner der spezielle Hardware-Peripherie Systeme, die auf neuere ATMega Prozessoren zu finden sind. Zur beispiel benützt die NmraDcc-Bibliothek keiner der extra Timers und auch nicht das Event System, die die neuere AtMega Prozessoren gerade so attraktiv machen.
Die Entwicklung von Mikroprozessoren geht in zwei Richtungen. Einerseits sehen wir schnellere Prozessoren (mehr Mhz) auf den Markt erscheinen. Beispiele sind der Raspberry Pi Pico (mit 125 MHz) und der ESP-32 (mit 160 MHz). Anderseits sehen wir eine Entwicklung bei der Prozessoren Leistungsfähigere Zusatz (Peripherie) Funktionen bieten, wie das Event-System, mehr und bessere Timers und Funktionen zur Störungsunterdrückung. Der NmraDcc-Bibliothek richtet sich in die erste Richtung, der AP-DCC-Bibliothek in die Zweite.
Bild entfernt (keine Rechte)
DCC Signal Decodierung
Da ein DCC Signal etwa jede 100 Mikrosekunden von Polarität wechselt, ist es wichtig innerhalb von wenige Mikrosekunde auf eine Änderung zu reagieren. Dafür benutzt man Interrupts. Traditionelle ATmega Prozessoren, wie dem 328P (für den Uno und Nano) und 2560 (für den Mega), haben eine begrenzte Anzahl von Interrupt Pins, die aber alle über ihre eigene Interrupt Vector verfügen und damit sofort ihre eigenen Interrupt Service Routine (ISR) auslösen können. Dies macht herkömmliche ATmega Prozessoren relativ schnell.
Neue ATmega Prozessoren
Die neue ATmega Prozessoren, wie der 4809 und der AVR-DA und AVR-DB Prozessoren, sind etwas anders und unterstützen Interrupts auf (fast) alle Eingänge. Das gibt zwar viele Vorteile, aber leider auch einige Nachteile. So teilen sich zur Beispiel alle Pins die zu einem bestimmten Port gehören, den gleichen ISR-Vektor und Routine. Das resultiert darin das nach ein Interrupt zusätzliche Code ausgeführt werden muss, um zu bestimmen, zu welchem Pin der Interrupt gehört / welche Routine aufgerufen werden muss, um den benutzerspezifischen Teil der Interrupt-Routine aus zu führen. Das Resultat ist das der Anruf von traditionele Interrupt Code (welche auf `attachInterrupt’ basiert) auf neue ATmega Prozessoren etwa zwei mal zu viel Zeit kostet. Zum Glück kann man, statt traditionele Interrupt Code, auch das neue Event system einsetzen um ein Interrupt aus zu lösen wenn das DCC Signal von Polarität wechselt. Dan wird das ganze plötzlich viel schneller.
Performance von den NmraDcc Bibliothek auf 4809 Prozessoren
Im vergleich zu Traditionele ATMega Prozessoren, ist der NmraDcc Bibliothek wegen die andere Interrupt Struktur der neuere ATMega Prozessoren viel langsamer. Ein zusätzliches Problem ist das auch der Arduino `micros()’ Anruf auf neuere ATMega Prozessoren deutlich langsamer ist.
Beide Probleme sind in der folgenden Abbildung dargestellt, die einige NmraDcc-Bibliothek Messungen zeigen. Mann sieht dass auf ein UNO (328) etwa 9 Mikrosekunden benötigt werden um ein Wechsel des DCC-Eingangssignal zu detektieren (attachInterrupt + micros() + digitalRead()), während auf ein Nano Every (4809) etwa 17 Mikrosekunden benötigt werden.
Bild entfernt (keine Rechte)
Wie es besser geht
Obwohl AttachInterrupt() und Micros() auf neue ATMega Prozessoren erheblich langsamer sind, enthalten diese neuen Prozessoren auch eine Reihe neue Peripherie Funktionen, die eine viel schnellere und weitaus präzisere DCC-Decodierung ermöglichen. Det wichtigste neue Peripherie Funktion ist das Event (Ereignis)system, das es ermöglicht den DCC-Eingangs-Pin direkt mit einem Timer zu verbinden. Immer wenn das DCC-Eingangssignal von Polarität ändert, wird der Timer sofort (neu) gestartet, ohne Beteiligung der CPU (oder ein ISR). Darüber hinaus verfügen die verbesserten Timer über neuartige Funktionen wie den „Capture Frequency Measurement Mode“. In diesem Mode aktiviert ein Event (Ereignis) den Timer, um die Zeit seit dem vorherigen Ereignis sehr präzise zu erfassen und in einem speziellen Register zu speichern. Erst danach wird der Timer ISR angerufen, der dann dieses Register in alle ruhe auslesen kann, und beurteilen ob eine 0 oder eine 1 empfangen würde.
Dank des Ereignissystem und dem „Capture Frequency Measurement Mode“, kann der AP_DCC_Library das DCC-Signal sehr präzise erfassen, so das die Bit-Timing-Werte, die vom RCN-210 vorgegeben sind, genau eingehalten werden. Das ist mit der NmraDcc Bibliothek unmöglich. Das DCC Signal wird daher besser erkannt, und Störungen haben weniger Effekt.
Ein zweiter Vorteil ist das der CPU wenige belast wird, weil das Event System und den Timer die meiste Arbeit verrichten. Die folgende Abbildung zeigt dies bei einem (24-MHz) AVR 128DA48 Prozessor, worauf der Main-loop bei jedem Durchgang der TP4 Ausgang ein- und wieder ausschaltet. Nur wenn ein Interrupt Service Routine den Main-loop unterbricht, kann TP4 temporär nicht ein- und ausgeschaltet werden. Die Figur zeigt das, nachdem das DCC seine Polarität gewechselt hat, der Interrupt Service Routine nur noch 3 bis 4 Mikrosekunden benötigt um das DCC Signal zu erfassen und dekodieren. Das ist, verglichen mit der NmraDcc-Bibliothek, eine klare Verbesserung. Statt 15 Mikrosekunden auf ein UNO (328), oder sogar 22 Mikrosekunden auf der Nano Every (4809), wird jetzt nur noch (zwei mal) 3 à 4 Mikrosekunden benötigt (jedes mal wenn die Polarität wechselt).
Bild entfernt (keine Rechte)
Ich hoffe das es Stummis gibt die mit diese Bibliothek etwas anfangen können. Auch möchte ich das OpenDCC Forum bedanken, für die wertvolle Unterstützung die ich während die Entwicklung diese Bibliothek empfangen dürfte.
Aiko
https://github.com/aikopras/AP_DCC_library