Funktionsgenerator oder wie erzeugt man DCC Signale zum Test von Modellbahn Decodern

Hallo zusammen,

also mir sind da gleich 2 Punkte aufgefallen ...

1. Der Optokoppler. ... Einerseits entkoppelt der den Steuerkreis wunderbar vom Versorgungskreis. Andererseits zieht der auf der LED-Seite auch mächtig Strom. So wunderbar schnell sind die mit den SlewRates auch nicht grade. Vor allem bei 250kHz Datentakt (wenn ich das richtig in Erinnerung habe). Das was die saugen, summiert sich bei ner großen Anlage auch kräftig zusammen. Man müßte einen einsetzen, der einen guten Kopplungsfaktor hat, wenig LED-Strom benötigt aber trotzdem hohe Frequenzen und Flankensteilheiten schaft. Wobei, wenn ich das mal kurz überschlage, komme ich bei 18V Versorgung auf etwa 5mA durch den OK. Ist eigentlich vertretbar.

Wie man das ohne Optokoppler lösen könnte ... tja ... Fällt mir nur ne Kondensatorkopplung ein. Also sozusagen 2 Kondensatoren die den Steuereingang gleichspannungsfrei an eine Eingangsschaltung koppeln. Es werden also sozusagen nur die Flanken durchgelassen. Man müßte dann aber auch kurze Spikes rausfiltern. Das würde aber auf jeden Fall sehr wenig Strom für den Steuereingang fressen. Macht die Eingangsschaltung aber wieder komplizierter. Bei 5mA (nach meinem Überschlag) würde das aber wohl den Rahmen sprengen.

2. Die Abtastung nach 1,5 facher Bitzeit. Das ist nicht grade fehlertolerant. Hört sich nach Entprellroutine an, nur das es bei so einer Modellbahn dauernd prellt. Würde ich eventuell mit einem "Oversampling" lösen und dann nach Mehrheitsbeschluß lösen. Also 4x oder 8x abtasten und dann danach entscheiden wie oft 0 oder 1 vorhanden war. (per Schieberegister und Bits zählen). Wäre dann nur unerheblich komplizierter aber wesentlich fehlertoleranter.

Die Steuersignale würde ich evtl erst einmal stumpf mit nem GateTreiber für MOSFETs verstärken. Die haben doch meiner Meinung nach ne Gegentaktendstufe und können 5V auf ne höhere Spannung umsetzen. Sonst ganz simpel A4 an eine höhere Spannung (+12 oder so) und dann A3 per Transistor nach GND durchschalten. Wenn man da nun noch an der Schaltung mit parallelem Widerstand zum R7 rum macht ... hmm ... würde ich eher Pin1 des OK als Testpin herausführen.

Zu meinem Modellbahnwissen ... hab hier eine MB im Haus die mein Vater für meinen Neffen zusammengebastelt hat. Wegen älterem Semester und Problemen beim Verständnis der digitalen Zusammenhänge (früher war das alles nur Gleichstrom) mußte ich mich zwangsläufig ein wenig damit befassen. Ist aber ne Märklin mit entsprechendem Protokoll.

Gruß
Dino
 
Die Abtastung nach 1,5 facher Bitzeit. Das ist nicht grade fehlertolerant
Hehe, gut erkannt ;)
Ich habe lange überlegt ob ich mehr Abtastungen durchführen soll. Die Option ist vorgesehen. Da ich wegen der fixen Verkabelung eher nur mäßige Störungen erwarte, hab ich mich mal für die einfachere Variante entschieden. Die Praxis wird zeigen ob es reicht. Wenn nicht, muss ich den Programmteil für die Signalerkennung erweitern.

Strenggenommen invertiert der OK das Signal, aber das interessiert wegen der Symmetrie des Signals nicht.
Du hast Recht.
Wie ich schon weiter oben geschrieben habe, bietet der ATtiny die Möglichkeit seine Ein- und Ausgänge zu invertieren. Damit hebt sich das dann wieder auf.

komme ich bei 18V Versorgung auf etwa 5mA durch den OK
Exakt so ist er dimensioniert. Bei 7V (untere Grenze der Norm) wird er etwa 2mA benötigen. Messungen haben aber ergeben, dass er bereits bei 1,6mA (Datenblatt) zuverlässig gut schaltet. Somit wird in der Endvariante der R7 noch eine Spur höher werden. Etwa 3K80.

LED-Seite auch mächtig Strom
schnell sind die mit den SlewRates auch nicht grade
Grundsätzlich könntest Du auch den Ausgang eines Mikrocontrollers auf A4 legen und A3 auf Gnd - ab welcher Spannung der OK über R7 allerdings durchschalten könnte, weiß ich nicht - scheint ja auf'ne Differenz von etwa 8,5V dimensioniert zu sein...
Ist denkbar.
Der erlaubte Spannungsbereich reicht laut Norm von 7V bis 27V. Der verwendete Optokoppler ist ein Hochgeschwindigkeitstype der im ns-Bereich schaltet. Ich will möglichst keine Signalverzerrung oder Streckung udg. Er funkt laut Oszi super gut und schaltet bereits bei If=1,6mA und kann bis If=10mA belastet werden. Er ist für OK-Verhältnisse ein super kleines Ding :)

1598469171426.png1598469703648.png

Ist aber ne Märklin mit entsprechendem Protokoll
Ja, so ähnlich hab ich auch angefangen. Anno 1967 als Fünfjähriger gab's am 24.12. eine Märklin Anlage. Seit dem hat mich das Fieber nicht mehr losgelassen. Naja, über 40 Jahre Pause waren dazwischen. :rolleyes:
 
Zuletzt bearbeitet:
Der erlaubte Spannungsbereich reicht laut Norm von 7V bis 27V. Der verwendete Optokoppler ist ein Hochgeschwindigkeitstype der im ns-Bereich schaltet. Ich will möglichst keine Signalverzerrung oder Streckung udg. Er funkt laut Oszi super gut und schaltet bereits bei If=1,6mA und kann bis If=10mA belastet werden. Er ist für OK-Verhältnisse ein super kleines Ding :)

Anhang anzeigen 8267

Das Blockschaltbild vom Optokoppler sagt schon alles. Ist nen digitaler mit Photodiode. Der sollte eigentlich von der Slewrate keine Probleme machen. Bei so einem kann man sich alternative Lösungen normalerweise sparen ;)

Gruß
Dino
 
Hallo zusammen,
ich hatte mal einen simplen DCC Generator programmiert, weil ein Kumpel auch seine Decoder testen wollte.
Falls es hier also nicht nur um die Treiberstufe geht, sondern auch um ein solches Programm, dann kannst du dies ja vielleicht als Ausgangspunkt für deine spezielle Anforderung nehmen.
Im Moment wird dort eine an- und abschwellende PWM ausgegeben.



CodeBox BascomAVR
'(
Tool, zur Ausgabe von DCC Protokollen nach OpenDCC Beschreibung
Format eines Telegramms ist
kurz: 111111111111110AAAAAAAA0BBBBBBBB0CCCCCCCC
lang: 111111111111110AAAAAAAA0BBBBBBBB0EEEEEEEE0CCCCCCCC
Anzahl Praeambel Bits (immer 1) kann scheinbar zwischen 10 und 16 variieren (ist daher unten als Konstante angegeben)
Trennbits zwischen Adresse (A), Befehl (B), Erweiterung (E) und Checksumme (C) sind immer 0
eine 1 ist 58µs high und 58µs low, eine 0 ist 116µs high und 116µs low
Zwischen den einzelnen Telegrammen ist eine Pause 1000µs (siehe Konstante Zwischen_pause)
Nachdem alle Telegramme übertragen wurden, kommt eine Pause von 10000µs  (siehe Konstante Ende_pause)

Telegramme können per UART an das Programm übergeben werden.
Format ist hierbei
kurz: {A,B}
lang: {A,B,E}
A,B und E dürfen maximal den Wert 255 haben.
Es wird keine Kontrolle auf Korrektheit nach dem DCC Protokoll durchgeführt.
Dies muss vom übergebenden Programm erfolgen
Wenn ein korrektes Telegramm erkannt wurde, wird es auf dem UART ausgeben.
Format ist hierbei
kurz: A,B,C
lang: A,B,E,C
C wird aus A XOR B bzw A XOR B XOR E berechnet
Falls ein Wert > 255 ist, wird ein Fehler zurückgegeben

Erweiterte Programmierbefehle des OpenDCC Protokolls werden zur Zeit nicht unterstützt
')

$regfile = "m1284pdef.dat"
$crystal = 20000000                                         'Minimum ist etwa 1MHz
$hwstack = 40
$swstack = 40
$framesize = 100
$baud = 115200                                              'kann auch kleiner sein
Config Timer1 = Timer , Prescale = 1024 , Compare_a = Toggle , Clear_timer = 1

Const _print_out = 0
Const _use_timer = 1                                        'Timer1 oder Timer2 kann verwendet werden (Timer muss Fast-PWM mit OCRnA als TOP Wert können)
Const _mit_rauschen = 0
Config Timer2 = Pwm , Prescale = 64 , Compare_b_pwm = Clear_up
Config Timer0 = Timer , Prescale = 256
#if _use_timer = 1
   Const Timer_prescale = 1
   Tccr1a = Bits(wgm11 , Wgm10)                             'fast_PWM (Mode 15), Ausgang = B, Prescale = 1)
   Tccr1b = Bits(cs10 , Wgm13 , Wgm12)                      'Top = OCR1A, Compare = OCR1B
#elseif _use_timer = 2
   #if _xtal > 8000000                                      'da timer2 nur 8-bit hat, müssen Werte größer 255 durch geeignete Wahl des Prescalers vermieden werden
      Const Timer_prescale = 32
      Tccr2a = Bits(wgm21 , Wgm20)                          'fast_PWM (mode 7), Ausgang = B, Prescale = 32)
      Tccr2b = Bits(cs21 , Cs20 , Wgm22)                    'Top = OCR2A, Compare = OCR2B
   #else
      Const Timer_prescale = 8
      Tccr2a = Bits(wgm21 , Wgm20)                          'fast_PWM mode 7), Ausgang = B, Prescale = 8)
      Tccr2b = Bits(cs21 , Wgm22)                           'Top = OCR2A, Compare = OCR2B
   #endif
#endif

Const Eins_high = 58
Const Null_high = 116
Const Eins_gesamt = Int(_xtal * Eins_high * 2 / Timer_prescale / 1000000) - 1       '58
Const Eins = Int(eins_gesamt / 2 )
Const Null_gesamt = Int(_xtal * Null_high * 2 / Timer_prescale / 1000000) - 1
Const Null = Int(null_gesamt / 2 )
Const Zwischen_pause = 1000                                 '1000µs Pause zwischen zwei Telegrammen
Const Ende_pause = 2000                                     '10000µs Pause nach Übertragung aller Telegramme, danach wieder von vorne
Const Praeambel_bits = 16                                   'Festlegung der Anzahl der Synchronisierungsbits
Const Praeambel_start = 1                                   'Dies sind die Bitnummern in Abhängigkeit von der Anzahl Praeambel Bits und evtl. Erweiterungsbyte
Const Praeambel_end = Praeambel_bits
Const Trenn_null_1 = Praeambel_end + 1
Const Adress_start = Trenn_null_1 + 1
Const Adress_end = Adress_start + 7
Const Trenn_null_2 = Adress_end + 1
Const Befehl_start = Trenn_null_2 + 1
Const Befehl_end = Befehl_start + 7
Const Trenn_null_3 = Befehl_end + 1
Const Erweiterung_start = Trenn_null_3 + 1
Const Erweiterung_end = Erweiterung_start + 7
Const Trenn_null_4 = Erweiterung_end + 1
Const Checksumme_start = Trenn_null_4 + 1
Const Checksumme_end = Checksumme_start + 7
Const Telegram_end1 = Erweiterung_end + 2
Const Telegram_end2 = Checksumme_end + 2
Const Max_telegram_count = 100                              'max 100 Telegramme können erzeugt werden

Dim Adresse(max_telegram_count) As Byte
Dim Befehl(max_telegram_count) As Byte
Dim Erweiterung(max_telegram_count) As Byte
Dim Neue_adresse As Word
Dim Neuer_befehl As Word
Dim Neue_erweiterung As Word
Dim Neue_checksumme As Byte
Dim Checksumme As Byte
Dim Dcc_bit_no As Byte
Dim Bit_no As Byte
Dim Next_bit As Byte
Dim Current_bit As Byte
Dim Invld_flag As Byte
Dim Telegram_no As Byte
Dim Telegram_count As Byte
Dim Byte_cnt As Byte
Dim Byte_rcvd As Byte
Dim Peak As Byte
Dim Pwm_up As Bit
Dim Pwm_0 As Byte

Telegram_count = 1
Adresse(1) = 99
Befehl(1) = 100
Erweiterung(1) = 6
Adresse(2) = 3
Befehl(2) = 5
'Adresse(3) = 2
'Befehl(3) = 7
'Erweiterung(3) = 5
'Adresse(4) = 2
'Befehl(4) = 7

Config Portd.5 = Output
#if _mit_rauschen = 1
   Const Max_rnd = 2000                                     'dies ist das absolute maximum, kleinere Werte erzeugen mehr peaks
   Config Timer3 = Timer , Prescale = 1 , Clear_timer = 1
   Ocr3a = Rnd(max_rnd) + 500
   On Compare3a Cmp3_isr
   Enable Compare3a
#endif
#if _use_timer = 1
   On Compare1b Cmp_isr Nosave                              'dadurch ist die ISR nur 45 Takte lang, damit geht auch 1MHz Clock
   Enable Compare1b                                         'beim Compare Match werden die neuen Werte für OCRnA und OCRnB entsprechend des Bits geladen.
   Ocr1a = Eins_gesamt                                      'Beim Erreichen des Top Wertes werden sie dann in die Register übernommen
   Ocr1b = Eins - 1
   #if _chip = 62                                           'M88P
      Config Portb.2 = Output                               'Ausgabe Pin für Timer1 Kanal B
   #elseif _chip = 103                                      'M1284P
      Config Portd.4 = Output                               'Ausgabe Pin für Timer1 Kanal B
   #endif

#elseif _use_timer = 2
   On Compare2b Cmp_isr Nosave
   Enable Compare2b
   Ocr2a = Eins_gesamt
   Ocr2b = Eins - 1
   #if _chip = 62
      Config Portd.3 = Output                               'Ausgabe Pin für Timer2 Kanal B
   #elseif _chip = 103
      Config Portd.6 = Output                               'Ausgabe Pin für Timer2 Kanal B
   #endif
#endif
On Urxc Urxc_isr
Enable Urxc

Enable Interrupts

Do
   If Tifr0.tov0 = 1 Then
      Tifr0.tov0 = 1
      If Pwm_up = 1 Then
         If Pwm_0 < 255 Then
            Incr Pwm_0
         Else
            Pwm_up = 0
         End If
      Else
         If Pwm_0 > 0 Then
            Decr Pwm_0
         Else
            Pwm_up = 1
         End If
      End If
   End If
   If Dcc_bit_no < Befehl_start Then
      Befehl(1) = Pwm_0
      Ocr2b = Pwm_0
   End If
   If Telegram_count > 0 Then
      If Next_bit = 1 Then
         Next_bit = 0
         Incr Dcc_bit_no
         If Dcc_bit_no = Telegram_end1 And Erweiterung(telegram_no ) = 0 Or Dcc_bit_no = Telegram_end2 Then
            Dcc_bit_no = 0
            Incr Telegram_no
            #if _use_timer = 1
               Reset Tccr1a.com1b1                          'Ausgang B ausschalten für die Pausen
            #elseif _use_timer = 2
               Reset Tccr2a.com2b1                          'Ausgang B ausschalten
            #endif
            Reset Portd.5
            If Telegram_no > Telegram_count Then
               Telegram_no = 1
               Waitus Ende_pause
            Else
               Waitus Zwischen_pause
            End If
            Set Portd.5
            #if _use_timer = 1
               Tcnt1 = 0                                    'Ausgang B ausschalten
            #elseif _use_timer = 2
               Tcnt2 = 0                                    'Ausgang B ausschalten
            #endif
            If Telegram_count > 0 Then
               #if _use_timer = 1
                  Set Tccr1a.com1b1                         'Ausgang B ausschalten
               #elseif _use_timer = 2
                  Set Tccr2a.com2b1                         'Ausgang B ausschalten
               #endif
            End If
         Else
            Select Case Dcc_bit_no
               Case Praeambel_start To Praeambel_end:       'Praeambel Bits erzeugen
                  Current_bit = 1
               Case Trenn_null_1:                           'erste 0 nach Praeambel
                  Current_bit = 0
               Case Adress_start To Adress_end              'Adress-Bits erzeugen
                  Bit_no = Adress_end - Dcc_bit_no
                  Current_bit = Adresse(telegram_no).bit_no
               Case Trenn_null_2:                           'zweite 0 nach Adress
                  Current_bit = 0
               Case Befehl_start To Befehl_end              'Befehl-Bits erzeugen
                  Bit_no = Befehl_end - Dcc_bit_no
                  Current_bit = Befehl(telegram_no).bit_no
               Case Trenn_null_3:                           'dritte 0 nach Befehl
                  Current_bit = 0
               Case Erweiterung_start To Erweiterung_end:   'Erweiterung-Bits erzeugen falls notwendig
                  Bit_no = Erweiterung_end - Dcc_bit_no
                  If Erweiterung(telegram_no) = 0 Then
                     Checksumme = Adresse(telegram_no) Xor Befehl(telegram_no )
                     Current_bit = Checksumme.bit_no
                  Else
                     Current_bit = Erweiterung(telegram_no).bit_no
                  End If
               Case Trenn_null_4:                           'vierte 0 nach Erweiterung falls notwendig
                  Current_bit = 0
               Case Checksumme_start To Checksumme_end:     'Checksummen Bits erzeugen
                  Bit_no = Checksumme_end - Dcc_bit_no
                  Checksumme = Adresse(telegram_no) Xor Befehl(telegram_no )
                  Checksumme = Checksumme Xor Erweiterung(telegram_no )
                  Current_bit = Checksumme.bit_no
            End Select
         End If
      End If
   End If
Loop

Cmp_isr:                                                    'beim Match (also mitten im Bit) werden die neuen Werte fürs nächste Bit gesetzt.
   !PUSH R24
   !IN r24, SREG
   !PUSH R24
   !PUSH R25
   !PUSH R16
   #if _use_timer = 1
      If Current_bit = 0 Then
         Ocr1a = Null_gesamt
         Ocr1b = Null - 1
      Else
         Ocr1a = Eins_gesamt
         Ocr1b = Eins - 1
      End If
   #elseif _use_timer = 2
      If Current_bit = 0 Then
         Ocr2a = Null_gesamt
         Ocr2b = Null - 1
      Else
         Ocr2a = Eins_gesamt
         Ocr2b = Eins - 1
      End If
   #endif
   Next_bit = 1                                             'jetzt kann die MainLoop das nächste Bit holen
   !POP R16
   !POP R25
   !POP R24
   !Out Sreg , R24
   !POP R24
Return

Urxc_isr:
   Byte_rcvd = Udr
   Select Case Byte_rcvd
      Case "{":
         If Byte_cnt = 0 Then Byte_cnt = 1 Else Byte_cnt = 0
      Case ",":
         If Byte_cnt = 2 Or Byte_cnt = 4 Then Incr Byte_cnt Else Byte_cnt = 0
      Case "}":
         If Byte_cnt = 4 Or Byte_cnt = 6 Then
            If Telegram_count < Max_telegram_count Then
               Invld_flag = 0
               If Neue_adresse > 255 Then
                  Print "Adr nio"
                  Invld_flag = 1
               End If
               If Neuer_befehl > 255 Then
                  Print "Bef nio"
                  Invld_flag = 1
               End If
               If Neue_erweiterung > 255 Then
                  Print "Erw nio"
                  Invld_flag = 1
               End If
               If Invld_flag = 0 Then
                  Adresse(telegram_count + 1) = Neue_adresse
                  Befehl(telegram_count + 1) = Neuer_befehl
                  Neue_checksumme = Neue_adresse Xor Neuer_befehl
                  Print Neue_adresse ; "," ; Neuer_befehl ;
                  If Byte_cnt = 6 Then
                     Erweiterung(telegram_count + 1) = Neue_erweiterung
                     Neue_checksumme = Neue_checksumme Xor Neue_erweiterung
                     Print "," ; Neue_erweiterung ;
                  End If
                  Print "," ; Neue_checksumme
                  Incr Telegram_count
               End If
            End If
         End If
         Byte_cnt = 0
      Case 48 To 57:                                        'es sind nur Ziffern erlaubt
         Byte_rcvd = Byte_rcvd - 48                         'ASCII Wert der 0 abziehen
         Select Case Byte_cnt
            Case 1:                                         'Start des Adressbyte
               Neue_adresse = Byte_rcvd
               Byte_cnt = 2
            Case 2:                                         'Fortsetzung des Adressbyte
               Neue_adresse = Neue_adresse * 10
               Neue_adresse = Neue_adresse + Byte_rcvd
            Case 3:                                         'Start des Befehlsbyte
               Neuer_befehl = Byte_rcvd
               Byte_cnt = 4
            Case 4:                                         'Fortsetzung des Befehlsbyte
               Neuer_befehl = Neuer_befehl * 10
               Neuer_befehl = Neuer_befehl + Byte_rcvd
            Case 5:                                         'Start des Erweiterungsbyte
               Neue_erweiterung = Byte_rcvd
               Byte_cnt = 6
            Case 6:                                         'Fortsetzung des Erweiterungsbyte
               Neue_erweiterung = Neue_erweiterung * 10
               Neue_erweiterung = Neue_erweiterung + Byte_rcvd
         End Select
   End Select
Return

#if _mit_rauschen = 1
Cmp3_isr:
  Ocr3a = Rnd(max_rnd) + 500
  Toggle Tccr1a.com1b1
  Peak = Rnd(10) + 1
  Waitus Peak
  Toggle Tccr1a.com1b1
  Return
#endif
 
Das ist ziemlich genau das, was ich bereits die ganze Zeit vorgeschlagen habe:
Empfang der zu sendenden Daten über UART (ob man da jetzt ASCII mit Start-/Ende-Klammern und Trenn-Kommata oder reine Binär-Bytes mit vorangestellter Anzahl nimmt ist letztlich egal),
Erzeugung des eigentlichen Signals mittels Fast-PWM mit 50% Duty, wobei KanalA die Frequenz, also die Periodenlänge jedes Bits festlegt,
Vorbereitung des jeweils nächsten Bits in der ISR des KanalB-Compare-Matches.

Ich hätte die Präambel auf zwei Bytes, also 16 festgenagelt (jeder Decoder muß damit klarkommen, und getriggert wird auf die folgende zwischen-null).
Die große Sate-Machine im Hauptprogramm hätte ich teilweise in die Timer-ISR ausgelagert, und statt des großen If/Case-Konstruktes (Er zählt letztlich über alles Bits und vergleicht mittels Compare) Schleifen, eine die nach acht Bits das Ende eines Bytes signalisiert (Carry oder Zero-Flag), eine analog für die 2/3Bytes...

Wenn das ganze auf die zwei möglichen Fälle zwei ODER drei Bytes plus Checksumme beschränkt ist, kannst Du quickNdirty dafür auch Rechenregister reservieren.
Vier RR für die zu sendenden Bytes (inklusive Checksumme), großzügige zwei für die Zähler/Statemachine.
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)