Hallo,
ich habe in einem anderen Thread angekündigt, ein DCC Weichendecoder entwickeln zu wollen (dieser und folgende Beiträge).
Dass es die zahlreich zu kaufen gibt, ist mir klar. Ich mache das aus Spaß am Entwickeln und Programmieren. Es geht auch nicht um's Geld sparen, zumindest nicht in erster Linie.
Am Ende des Projekts möchte ich einen Weichendecoder im "Inselbetrieb" haben - Decoder versorgt sich mit Energie und Steuersignalen einzig aus dem Gleis alleine.
Stand ist jetzt, dass ich mir einen Optokoppler ONsemi H11L1M besorgt habe und - zusammen mit Bauteilen, die ich schon hier hatte, zu einer Schaltung zusammengelötet habe:

Die Schaltung mit Arduino Uno kann ich gleichzeitig an die Zentrale und an den PC anschließen (der Arduino wird über USB-Kabel vom PC stromversorgt, und mit Hilfe des Bootloaders geflasht).
Die LEDs dienen als Ersatz für die "Leistungsstufe" der Weichenmagnete. Es geht erstmal nur ums Decodieren des DCC-Signals, die LEDs dienen zur "Erfolgskontrolle".
Ich habe auch einen Schalter eingelötet, da mein Decoder auch eine Adresslernfunktion haben soll, die mit diesem Schalter gestartet wird.
Für das Erkennen von Nullen und Einsen habe ich folgenden Ansatz:
Eine Art Zustandsautomat mit drei Zuständen,
1) "Grundzustand",
2) "0er Halbwelle erkannt, warte auf zweite Halbwelle",
3) "1er Halbwelle erkannt, warte auf zweite Halbwelle".
Zeitmessung mit abwärts zählendem Timer, dessen "Startwert" bei Betreten eines Zustands stets dem Zustand entsprechenden Wert gesetzt wird (z.B Zustand 2: auf 64us). Tritt bei 0 der Zähler-Unterlauf auf, kam die nachfolgende Signalflanke zu spät und mein Automat fällt in den Grundzustand zurück. Wird in Zustand 2/3 ein erfolgreiches Bit erkannt, wird dies dem Rest des Programms signalisiert, und der Automat fällt wieder in den Zustand 1 zurück.
Die Zustandsänderungen werden durch Interrupts, ausgelöst durch den Opto einerseits, und dem Timer-Unterlauf andererseits, verwirklicht.
Ich habe eine erste Programm-Version bereits in Arduino-C implementiert, scheint soweit auch prinzipiell zu funktionieren. Allerdings glaube ich, dass die micros() Funktion, mit der ich die Zeitmessung implementiert habe, zu unpräzise ist. Zugriff auf Timer-Interrupts habe ich in Arduino-C auch nicht. Damit werde ich nicht weit kommen (habe ich auch nicht wirklich erwartet). Mit Standard-C, der avr-libc und einer geeigneten Toolchain abseits von der Arduino-IDE muss ich noch beschäftigen.
Das Erkennen des relevanten DCC-Befehls stelle ich mir ungefähr so vor:
- Im Speicher ist der relevante DCC-Befehl einfach nur als Bitmuster hinterlegt.
- Warten bis genügend 1en hintereinander empfangen wurden, sodass die Bedingung für die Präambel erfüllt ist.
- Warten auf die 0, die die Präambel abschließt.
- Dann die nachfolgenden Bits zählen, bis die Anzahl der des hinterlegten Bitmusters entspricht.
- Empfangenes Bitmuster mit dem hinterlegten vergleichen.
- Wenn Übereinstimmung: Nutzlast des Befehls extrahieren, entsprechende Aktion ausführen.
- XOR-Byte? Müsste noch mal schauen, ob es reicht, es als Muster zu hinterlegen, oder ob man es stets neu berechnen muss.
Ich habe mir die relevanten Standards der NMRA mal angesehen.
Zu S-9.1 Electrical Standards... Kapitel "One Bit Timing", hätte ich Fragen:
- die Bedingungen für das Eins-Bit sind klar formuliert: eine empfangene Halbwelle zwischen 52 und 64 us, zweite Halbwelle darf sich von erster höchstens um 6 us unterscheiden.
- die Bedingungen für das Null-Bit habe ich nicht so richtig verstanden: die zwei Halbwellen sollen (normalerweise(?)) gleich lang sein, sie dürfen zwischen 90 und 10000 us lang sein, sollen aber zusammen nicht länger, als 12000 us sein? Wieso darf eine 0-Halbwelle 10000us lang sein, beide zusammen nur 12000us? Das widerspricht sich doch? Was ist mit "Stretched-0-Bit" gemeint?
Mich treibt noch eine andere Sorge um: In der finalen Schaltung möchte ich einen wesentlich kleineren Atmel AVR, als den 328 im Arduino Uno einsetzen, und zwar mit internem Taktgeber. Da wäre wohl höchstens 1MHz zu erwarten, was hieße, dass im Extremfall 1MHz * 52us = 52 Takte zur Verfügung stünden, um den aktuellen Interrupt abzuarbeiten, bis die nächste Signalflanke (heißt: Interrupt) kommt. Da wäre ich gespannt, ob das reicht. Eventuell muss ich mit Assembler an die Sache ran...
Danke im Voraus und Gruß, Philipp
