Hallo Leute, es gibt für fast alles zig Codeexemplare, außer für DCC. Da ich viel "ernten" konnte aus dem Forum, möchte ich nun mal was zurückgeben. Oft habe ich gelesen, dass nach sowas die Suche ist, viel habe ich nur in Assembler gefunden. Ich habe (für mich) für das DCC-Protokoll eine kleine Codesammlung geschrieben. Die 2 Dateien können das DCC-Signal encodieren und auch codieren, also man kann bspw. eine Zentrale oder einen intelligenten Bremsgenerator bauen (das habe ich auch gemacht). Dort wird in Echtzeit das DCC-Signal vom Hauptgleis auf das Bremsgleis weitergeleitet, und die Fahrinformation gegen STOP ausgetauscht (optional wäre auch Langsamfahrt, etc...) Mir ging es beim Programmieren darum, einen möglichst komplexen "alleskönner" zu programmieren. Die Implementierung ist super einfach, gibt viele Macro's und Funktionen die einem die gesamte Arbeit abnehmen. Zwar alles sehr konfortabel aber dafür auch nicht gerade klein (4kb ist der Encoder mit allen Möglcihkeiten), aber die µC kosten ja nix :). Man braucht aber auch nicht unbedingt alles, kann man kürzen dann wird es viel kleiner. Ich extrahiere direkt alle Daten und man kann Fahrinformation, Richtung, etc.. abrufen. Der Decoder & Soft-PWM läuft ohne Interrupt nur in der loop. Wenn man andere Taktraten nehmen will, dann muss man in der dcc_init den Wert nach der Timerinitialisierung anpassen. Formel lautet: 77 / ((FCPU/TIMER_PRESCALER)^-1 * (10^6)) Den Wert dann einfach da eintragen und dann klappt es (decoder.dcc.dcc_time_logic_one dort eintragen in der dcc_init_and_sei) Damit kann man sich jeden denkbaren Decoder/Zentrale/Modul selber bauen für wenig Geld. Ich hoffe es hilft jmd! Das DCC-Signal einfach 1 Leitung vom Gleis vor dem Gleichrichter über 10K und 5,1V Z-Diode an einen beliebigen Pin. Das reicht. Kurze Zusammenfassung: - DCC encodieren/decodieren - CV Programmierung POM, Register, CV read/write - Breites Spektrum an Sonderfunktioen (Neon, Gaslampe, TV, Flops, etc..) - NMRA-Test bestanden (ich habe mal ein Gerät von mir eingeschickt und es prüfen lassen). Man kann Softwareversion einfach angeben (ist dann in CV7 gespeichert). Meine ID (Manufacture ID) darf NICHT genutzt werden. Hier die freie ID eintragen (glaube ist 13, kann man nachlesen auf NMRA.org). Meine ID ist nur für mich von NMRA zugeteilt! EEPROM gibt es ein struct, dort einfach einfügen welche CV's man haben will und fertig. Sieht man aber alles in der .h. Bei Fragen oder Problemen einfach melden. Hier noch andere Codesammlungen von mir: https://www.dropbox.com/sh/l121q3iv151kgrq/AABhNLPHT_ch2xuF_rqRPOL0a?dl=0
Das ist eine sehr interessante Bibliothek. Ich habe den Code durchsucht, aber es ist nicht klar, wie die Funktionen verwendet werden. Gibt es auch Dokumentation?
Frysk schrieb: > Das ist eine sehr interessante Bibliothek. Ich habe den Code durchsucht, > aber es ist nicht klar, wie die Funktionen verwendet werden. Gibt es > auch Dokumentation? KURZER EDIT: Ich habe noch vergessen zu sagen, dass für den ACK ein idR. 100 Ohm Widerstand zwischen + und - geschaltet werden sollte (über einen BSS123 bspw.). So ca. 200mAmps sollten fließen, sonst erkennen manche Zentralen den ACK nicht. Ich habe den als seperates Bauteil hinzugefügt, da einem so das lästige Anschließen einer Last erspart bleibt. So jetzt zur Frage: Wahrscheinlich wäre das wohl bei der Komplexität angebracht, für mich aber glasklar, daher gibt es keine. Ich helfe dir aber gerne, und dann kann man das als Doku ansehen (hoffentlich) (kleine "Anleitung" zur Bedienung befindet sich oben in der dcc.h): 1. Am besten in einem Editor á la AtmelStudio öffnen, dass man etwas Highlighting bekommt, sonst blickt keiner durch. 2. Die Soft-PWM ist ganz einfach gestrickt, Timer-Counter Register angeben und fertig, dann kann an jedem Pin PWM erzeugt werden, und das invers oder normal. Das sollte klar sein. Darf natürlich dann keine Delays im Programm geben (bei guter Programmierung eigentlich auch nicht vorhanden). Der Timer von der PWM kann als System-Timer genutzt werden (bspw. Systemtakt, wird auch in der ddc.c initialisiert für den Analogwechsel etc... (Takt 1ms/ovf ==> Pwm läuft dementsprechend mit 1khz) 3. dcc_init_and_sei aufrufen BEVOR die mainloop kommt. Dort wird der ack und dcc pin/port angegeben und der timer initialisert 4. dcc_detect_dcc convertiert und wandelt das dcc signal an angegebenen pin (muss in die loop). 5. Dann haben wir rund 4kb belegt (encoder + alle Programmiermöglichkeiten + Resetfunktionen etc... (siehe dazu unbedingt auch die dcc_servce Funktionen) und ab dann kann die eigene Logik kommen Hier ist es jetzt wichtig was du haben willst (accesory, Lok-/Zubehördecoder oder was auch immer). Das definierst du im groben in der h, damit stellst du dein Layout im groben ein. Dann kannst du in der EEPROM-Struct alle deine benötigten Einstellungen hinzufügen (achte hierzu mal auf die darüberliegenden structs wie bspw. cv_recommenend, cv_f_functs etc...). In der EEProm Struct sind auch nochmals Beispiele aufgeführt (dort ist ein Weichendecoder mit 2 Weichenausgängen und 2 F-Ausgängen initialisiert, da sind dann die benötigten CV-Werte + Startwerte alle angegeben). Da kannst du dann hinzufügen was du willst (einfach neue Instanzen der bspw- Funktionsausgänge Strukturen erstellen (bspw. ist ein drüber mit F1-F7). Das was dahinter steht in {} musst du dann nach dem cv_xxxxx EEMEM = .... einfügen, da das ja ins EEPROM als Startwert soll. Resetbereiche passt du in der dcc.c in den ServiceMode Funktionen an. Klingt schwer, klingt kompliziert, ist es aber nicht. Einfach mal in Ruhe durchlesen, vorallem auch was in der .h steht. Und dann kommt die Logik. bspw. (in der loop, also bspw. direkt nach der dcc_detect_dcc)
1 | //Das wäre die normale digitale funktion (if (decoder.dcc.state.systemBits.operation_dcc) ==> digitalbetrieb, sonst analogbetrieb (kann sich natürlich ändern bei fahrzeugen (blockstellen, was weiß ich)
|
2 | |
3 | if (dcc_is_detection_complete()) //got one correct data packet |
4 | {
|
5 | if (dcc_ReceivedMyNonAccesoirAddr()) //lokadresse angekommen? UND ZWAR MEINE??? (Automatische Erkennung zwischen CV1 oder CV 17/18 (lange Adresse). Also hier testet er immer, ob eine Lokadresse gesendet wurde, die zu meiner in der CV1 bzw. CV17/18 passt (das hängt ja von CV29 bit5 ab (lange ode kurze Adresse, in meiner Dropbox liegen unter dem Ordner NMRA DCC die Dokumente mit der Beschreibung) |
6 | {
|
7 | //Hier wird gefragt, ob es eine änderung zum schalten für F1 gibt und ob F1 überhaupt gesendet wurde, der rest stammt aus meiner internen Logic die ich hinzugebastelt habe (man kann bei mir in cv49 umschalten, ob die F-Ausgänge mit F-Taste und Lokadresse oder per Weichenadresse schaltbar sind).
|
8 | if (DCC_IS_F_SWITCHED_TO_SWITCH_RECEIVED(decoder.system.f1.function) && decoder.MD_config_cv49.bits.light_outputs_with_F_Button & (1<<0)) |
9 | dcc_setFOutput(&decoder.system.f1, &decoder.f1, f1_spec_timer, FALSE, DCC_GET_ACT_F_STATE(decoder.system.f1.function), FALSE, &F1_PORT, F1_PIN); //schalte den Ausgangspin entsprechend dem gesendetem Befehl, timer sind ggf. für programmierbare sonderfunktionen (siehe ebenfalls .h der strukturen) |
10 | }
|
11 | |
12 | |
13 | |
14 | if (dcc_isMY_accessoirAddr_received(TRUE, DCC_16BIT_ADDRESS(decoder.system.switch1.switch_addr_hi, decoder.system.switch1.switch_addr_lo), TRUE)) //accesor (weichenadresse gesendet?) und enspricht meiner? (berechnung passiert im hintergrund über macros etc... einfach mal die Funktionen rückwärtsverfolgen) |
15 | {
|
16 | //weiche schalten (h-brücke für EPL-Antriebe mit Spulenantrieb). Hier müssen div. Timer übergeben werden. Das Timerhandling passiert in der ISR in der dcc.c jede ms einen OVF INT generiert (Systemzeit). Kann kann hier jetzt
|
17 | //bspw. in CV118 angeben ob invers oder normal geschaltet werden soll, DCC_IS_ACESSOR_SignalGO_SwitchSTRAIGHT das gibt einen den Schaltstatus an, automatisches zurückschalten etc.. ist halt auch möglich (via timer)
|
18 | dcc_setSwitchAccesoir(&decoder.system.switch1, &decoder.switch1, switch1_on_timer, switch1_switchback_timer, DCC_HANDLE_WITH_INVERS(DCC_IS_ACESSOR_SignalGO_SwitchSTRAIGHT, decoder.system.extended.cv118_SwitchInverse & (1<<0)), FALSE, FALSE, SW1_SET_R_FKT); |
19 | }
|
20 | |
21 | |
22 | dcc_get_new_data(); //after ready, get new data |
23 | }
|
24 | |
25 | |
26 | |
27 | ///hier dann bspw. die Timer-Funktion IN der mainloop
|
28 | //wenn timer der weiche abgelaufen ==> ausgang ausschalten (schaltzeit lässt sich in einer cv einstellen, siehe hierzu dcc.h)
|
29 | if (!decoder.timer.timer_univ[switch1_on_timer] && decoder.switch1.bit.timerON_active) dcc_setSwitchAccesoir(&decoder.system.switch1, &decoder.switch1, switch1_on_timer, switch1_switchback_timer, OLD_DIR_MASK(decoder.switch1.bit.Old_State), TRUE, TRUE, SW1_SET_R_FKT); |
30 | |
31 | |
32 | |
33 | |
34 | //und das klatsche ich dir jetzt einfach mal aus meinem Projekt rein, ist für automatisches zurückschalten in Gegenrichtung von Weichen
|
35 | //timer for switches (SWITCH BACK ONLY)
|
36 | if (!decoder.timer.timer_univ[switch1_switchback_timer] && decoder.switch1.bit.timerSB_active) |
37 | {
|
38 | //if encoupler for switch, then turn on when on and off when off, the E-Lamp stay on while entcoupler is above
|
39 | if (decoder.MD_config_cv49.bits.f1_outp_encoupler) dcc_setFOutput(&decoder.system.f1, &decoder.f1, f1_spec_timer, FALSE, OLD_DIR_MASK(decoder.switch1.bit.Old_State), TRUE, &F1_PORT, F1_PIN); |
40 | |
41 | dcc_setSwitchAccesoir(&decoder.system.switch1, &decoder.switch1, switch1_on_timer, switch1_switchback_timer, OLD_DIR_MASK(decoder.switch1.bit.Old_State), FALSE, TRUE, SW1_SET_R_FKT); |
42 | |
43 | //if 3-way, check now the short one switch to old position
|
44 | if (decoder.MD_config_cv49.bits.threewayswitch_active && decoder.switch1.bit.mode_type_async == SWITCH_ODD_STOP && decoder.switch2.bit.Dummy_mode_type_async) |
45 | {
|
46 | dcc_setSwitchAccesoir(&decoder.system.switch2, &decoder.switch2, switch2_on_timer, switch2_switchback_timer, SWITCH_ODD_STOP, FALSE, TRUE, SW2_SET_R_FKT); |
47 | decoder.switch2.bit.SB_DONE = TRUE; |
48 | }
|
49 | |
50 | decoder.switch2.bit.Dummy_mode_type_async = FALSE; |
51 | }
|
Das war jetzt mehr für Weichen, für Lok/Funktionsausgänge gibt es noch andere Funktionen (siehe dcc.c) die auch die ganzen Sonderfunktionen (Neon, Gaslampen, TV, Feuer, etc...) beinhalten. Eigentlich beschränkt sich das Programm auf vll. 5 Funktionen, der Rest ist alles ineinander verschachtelt, um die Bedienung so einfach wie möglich zu machen und wird intern genutzt. Da das DCC Protokoll mit den Jahren gewachsen ist, ist es halt sehr "komplex", man muss zig Fälle unterscheiden jenachdem was wie aktiv ist etc... Um da keinen Kollaps zu bekommen habe ich diese vielen Funktionen gemacht die Schrittweise die Sachen im Hintergrund erledigen, man selber fragt ganz "dumm" nur ab (ist Lokadresse angekommen? Dann weiß man gleich das eine angekommen ist und es meiner entspricht und dabei ist es egal ob die lange oder die kurze, das wird alles im Hintergrund gemanaged).
Hallo Marius, Danke für die Erklärung. Ich werde sehen, ob ich den Code verstehen könnte. Ihr Beispiel wird sicherlich dazu beitragen.
Frysk schrieb: > Hallo Marius, > > Danke für die Erklärung. Ich werde sehen, ob ich den Code verstehen > könnte. Ihr Beispiel wird sicherlich dazu beitragen. Wenn noch fragen da sind, einfach mal melden. Im code ist auch ein Beispiel (vor der ISR). Du solltest sie aber aus meiner Dropbox runterladen, manchmal gibt's ne Aktualisierung. Ich habe jetzt bspw. noch eingebaut, dass man Ausgänge (gerade bei Weichen) auch invers schalten kann (siehe DCC_HANDLE_INVERS) aus der Dropbox. Ansonsten gutes gelingen!
Es ist die Tage ein neues Update der Lib raus gekommen. Darin wurde u.a. Funktionen für normale Decoder hinzugefügt (Wechselblinker) und eine Funktion kombinierte Ausgänge logisch zu verknüpfen. Außerdem wurde ein kleiner Bug beim Fotographblitz beseitigt und ein Bug in der Bereitstellung der Richtung bei 128 Fahrstufen. Hinzugefügt wurde noch eine Funktion, welche das Handling für Conditionen übernimmt. Conditionen sind (siehe .h) nur bei Vorwärtsfahrt, nur im Stand, bei Fahrt, etc..... Dieses Handling fügt man so ein:
1 | if (dcc_is_detection_complete()) //got one correct data packet |
2 | {
|
3 | if (dcc_ReceivedMyNonAccesoirAddr()) //lokadresse angekommen? UND ZWAR MEINE??? (Automatische Erkennung zwischen CV1 oder CV 17/18 (lange Adresse). Also hier testet er immer, ob eine Lokadresse gesendet wurde, die zu meiner in der CV1 bzw. CV17/18 passt (das hängt ja von CV29 bit5 ab (lange ode kurze Adresse, in meiner Dropbox liegen unter dem Ordner NMRA DCC die Dokumente mit der Beschreibung) |
4 | {
|
5 | //Hier wird gefragt, ob es eine änderung zum schalten für F1 gibt und ob F1 überhaupt gesendet wurde, der rest stammt aus meiner internen Logic die ich hinzugebastelt habe (man kann bei mir in cv49 umschalten, ob die F-Ausgänge mit F-Taste und Lokadresse oder per Weichenadresse schaltbar sind).
|
6 | if (DCC_IS_F_SWITCHED_TO_SWITCH_RECEIVED(decoder.system.f1.function) && decoder.MD_config_cv49.bits.light_outputs_with_F_Button & (1<<0)) |
7 | dcc_setFOutput(&decoder.system.f1, &decoder.f1, f1_spec_timer, FALSE, DCC_GET_ACT_F_STATE(decoder.system.f1.function), FALSE, &F1_PORT, F1_PIN); //schalte den Ausgangspin entsprechend dem gesendetem Befehl, timer sind ggf. für programmierbare sonderfunktionen (siehe ebenfalls .h der strukturen) |
8 | }
|
9 | |
10 | |
11 | |
12 | if (dcc_isMY_accessoirAddr_received(TRUE, DCC_16BIT_ADDRESS(decoder.system.switch1.switch_addr_hi, decoder.system.switch1.switch_addr_lo), TRUE)) //accesor (weichenadresse gesendet?) und enspricht meiner? (berechnung passiert im hintergrund über macros etc... einfach mal die Funktionen rückwärtsverfolgen) |
13 | {
|
14 | //weiche schalten (h-brücke für EPL-Antriebe mit Spulenantrieb). Hier müssen div. Timer übergeben werden. Das Timerhandling passiert in der ISR in der dcc.c jede ms einen OVF INT generiert (Systemzeit). Kann kann hier jetzt
|
15 | //bspw. in CV118 angeben ob invers oder normal geschaltet werden soll, DCC_IS_ACESSOR_SignalGO_SwitchSTRAIGHT das gibt einen den Schaltstatus an, automatisches zurückschalten etc.. ist halt auch möglich (via timer)
|
16 | dcc_setSwitchAccesoir(&decoder.system.switch1, &decoder.switch1, switch1_on_timer, switch1_switchback_timer, DCC_HANDLE_WITH_INVERS(DCC_IS_ACESSOR_SignalGO_SwitchSTRAIGHT, decoder.system.extended.cv118_SwitchInverse & (1<<0)), FALSE, FALSE, SW1_SET_R_FKT); |
17 | }
|
18 | |
19 | |
20 | |
21 | //hier jetzt die Funktionen vom Handling einfügen, sind nur interessant bei normalen Decoder, also keinen Weichendecodern bzw. bei Decodern die
|
22 | //die conditionen je funktionsausgang können.
|
23 | |
24 | |
25 | dcc_get_new_data(); //after ready, get new data |
26 | }
|
Außerdem habe ich mal eine Anleitung angehängt, wie solche Funktionsausgänge zu konfigurieren wären, bzw. wie das dann aufgebaut ist (hilft denke ich noch mehr den Code zu verstehen). http://www.md-electronics.de/sitecake-content/e9d37180-c9c1-11e6-9230-000000000000-1.pdf
Hallo Marius, das klingt alles recht vielversprechend. Ich werde mich wohl mal wieder mit dem Thema befassen, wenn andere Projekte fertig sind. Vielen Dank.
UPDATE: Analogerkennung verbessert Analoge Richtungserkennung eingefügt US-Lights (Marslight, Strobes (double) hinzugefügt Auf/Abblenden (bit7 des Dimmwerts) hinzugefügt (neue Funktion zum Setzen der Dimmspannung) Auf/Abdimmen hinzugefügt in der Soft-PWM .h
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.