Forum: Projekte & Code FULL NMRA DCC Library, Encoder, Generator, Bremsmodul


von MOBA 2. (Gast)


Angehängte Dateien:

Lesenswert?

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

von Frysk (Gast)


Lesenswert?

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?

von MOBA 2. (Gast)


Lesenswert?

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).

von Frysk (Gast)


Lesenswert?

Hallo Marius,

Danke für die Erklärung. Ich werde sehen, ob ich den Code verstehen 
könnte. Ihr Beispiel wird sicherlich dazu beitragen.

von MOBA 2. (Gast)


Lesenswert?

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!

von MOBA 2. (Gast)


Lesenswert?

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

von Michael L. (michaelx)


Lesenswert?

Hallo Marius,

das klingt alles recht vielversprechend. Ich werde mich wohl mal wieder 
mit dem Thema befassen, wenn andere Projekte fertig sind.

Vielen Dank.

von MOBA 2. (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.