ATMega644P und UART

    Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

    Aufgrund technischer Veränderungen ist der Mailverkehr innerhalb des Forums (Private Nachrichten) nur noch eingeschränkt möglich. Die Einschränkung ist notwendig, um zusätzliche Betriebskosten für das Forum zu vermeiden. Näheres zu den Hintergründen im Thread "Aktuelles zum Forum".Wir bitten um Verständnis.

    Hinweis kann nach Kenntnisnahme deaktiviert werden!

    • ATMega644P und UART

      Hallo Zusammen

      Ich möchte über den UART0 des ATMega 644P einen langen String mit Input einlesen.
      Funktioniert leider nicht besonders gut.
      Der String enthält Text, Zahlen und Steuerzeichen.
      Erkanntes Problem: Der Empfang wird nach jedem CR abgebrochen
      Wie bringe ich dem ATMega 644P das Stringende bei?
      Config Input eher ungeeignet

      Hintergrund:
      Ich programmiere eine Steuersoftware für meine PV-Anlage
      Hier:
      Abfrage der Batterie (2x US3000C von Pylontech) mit "pwr";Chr(13) funktioniert auch

      Die Batterie antwortet auch, nur den String bekomme ich nicht vollständig aufgefangen

      Eine gute Idee wäre hilfreich

      Danke

      Anton
      Dateien
      • 644 Seriell.bas

        (2,86 kB, 23 mal heruntergeladen, zuletzt: )
    • Beginnt die Antwort mit "pwr"? Oder ist das noch die Frage und die Antwort beginnt mit "@" ?
      Wenn der Beginn eindeutig ist kann er einen String mit 480 Zeichen füllen und den Rest ignorieren bis zum nächsten Start.
      49kV ? Nicht dran packen :D
      Obwohl bei 24tausend Grad geht man sowieso nicht so nahe ran a_38_b45e201d

      Wird nur die Zeile mit der 1 vorn benötigt oder auch die mit der 2?
      Die 1 ist der ideale Beginn.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Pluto25 ()

    • Hallo Anton

      Ich habe mir deinen Quellcode und auch die Log-Datei angesehen.
      Da fallen mir mehrere Dinge auf.

      Der Empfang ist mit 115200 Baud recht sportlich. Bedenkt man, dass dein Systemtakt nur 3,6864 MHz ist, bleiben dem System nur 32 Takte je Zeichen zur Verarbeitung.
      Es gibt auch schnellere Baud-Quarze. Ich empfehle dir das 14,7456 MHz Quarz. Es gibt auch noch eins mit ca. 18,ungrad MHz. Das müsste ich dann nochmal schauen.

      Wenn ich den Log so ansehe, fallen 16 Datensätze auf. Das sind die Zeilen, die mit der "1" bis "16" beginnen. Jede Zeile hat einen Abschluss, darauf komme ich noch.
      Scheinbar eingerahmt ist das alles in ein Startzeichen "@" und endet mit "$$".

      Das Telegramm besteht also aus einzelnen Strings, die nacheinander gesendet werden. Man könnte den Eindruck gewinnen, einfach mit Input einzulesen. Aber:

      Wenn man die Datei mit dem Hexeditor ansieht, erkennt man, der Abschluss der Zeilen ist nicht konform. Mal ist es LFCR, dann mal CRCRLF. Vielleicht noch andere Varianten.

      Wenn man nun mit dem Input-Befehl einen String einliest, wird je nach Konfiguration am Ende ein CR oder ein LF oder CRLF erwartet. Je nach Konstellation führt das dazu, dass diese Steuerzeichen im String landen, wo sie nicht gebraucht werden.

      Hier muss man also irgendwie diese Zeichen loswerden. Entweder im eingelesenen String ein Replace machen oder beim Empfang solche Zeichen bereits eleminieren.

      Ich empfehle dir einen Empfangsbuffer zu nehmen, der groß genug ist, um das Telegramm aufzunehmen. Dann gehen dir keine Zeichen verlohren und du kannst mit Input und Replace arbeiten. (LF ersetzen durch "")

      Hast du deine Zeilen verarbeitet, löschst du den Empfangsbuffer, bevor du eine neue Anfrage aussendest.

      Auf diese Weise hast du vorgesorgt, falls da mal Batterien dazu kommen, oder die Reihenfolge mal nicht durchgehend ab 1 ist.

      Wenn du nun so eine Zeile in einen String eingelesen hast, ist der ca. 160 Zeichen lang. $BigString ist also nicht notwendig.
      Dann kannst du mit Split den String in seine Bestandteile zerlegen. Und hast so die Einzelwerte oder Überschriften in den Indexen des Ergebnis-Arrays von Split.

      Das dürfte in etwa ein relativ einfacher Lösungsweg sein, der auch funktionieren sollte.

      Ein anderer Weg wäre, eine eigene ISR-Routine zu schreiben, die die Zeichen empfängt und auch gleich die LF verwirft.
      Problem dabei ist, dass die Zeichen sehr schnell reinkommen. Daher müsste das in Assembler gemacht werden. Die Bufferung ist da ja dann auch nicht möglich (Config SerialIn=Buffered).

      Somit müsste das alles entweder Life verarbeitet werden, was Zeitkritisch ist, oder man muss einen eigenen Fifo proggen, der dann als Empfangsbuffer dient. Dafür bräuchte es dann aber auch Routinen zum einen String auslesen.
      Ist also komplizierter.

      Ich empfehle dir der 1. Lösungsweg.

      Vielleicht gibts noch andere Ansätze?
    • Hallo Anton,
      ich würde es tatsächlich anders machen.
      Du hast einen Controller, der genügend Speicher für die 2500 Zeichen hat. Und du weißt genau, wann wieder Zeichen zu erwarten sind, da du das ja selber auslöst.
      Also würde ich eine ISR anlegen, die nichts anderes macht, als die eingfetroffenen Zeichen in ein großes Array abzulegen.
      Anders als Mitch meint, hat der Controller dafür etwa 320 Takte Zeit (der komplette Empfang eines Bytes), das reicht locker ohne Tricks und ASM.
      Die ISR würde dann z.B. so aussehen:

      BASCOM-Quellcode

      1. On Urxc Urxc_isr
      2. Enable Urxc
      3. Enable Interrupts
      4. Urxc_isr:
      5. B = Udr 'ein Byte einlesen
      6. Tcnt0 = 0 'timer zurücksetzen
      7. If Cnt < 2600 Then 'Zeichen abspeichern, falls bisher weniger als 2600 Zeichen eingelesen wurden
      8. B_arr(cnt) = B
      9. Incr Cnt 'Zeichen Zähler erhöhen
      10. End If
      11. Return
      Alles anzeigen
      Um das Ende der Übertragung zu erkennen, kannst du einen Timer aufsetzen, der z.B. nach 1/15 s überläuft. Jedesmal, wenn ein Zeichen empfangen wird, wird der Timer zurückgesetzt. So erkennst du sicher das Ende der Übertragung.

      Die Timer ISR sieht dann z.B. so aus:

      BASCOM-Quellcode

      1. Config Timer0 = Timer , Prescale = 1024
      2. On Ovf0 Ovf0_isr
      3. Enable Ovf0
      4. Ovf0_isr: 'wenn dieser Timer überläuft, wurde etwa 1/15s kein Zeichen empfangen
      5. If Cnt > 0 Then 'falls schon etwas empfangen wurde
      6. Rcvd_flag = 1 'Auswertung freigeben
      7. B_arr(cnt) = 0 'hinter das letzte empfangene Zeichen den String korrekt beenden
      8. End If
      9. Return
      In der Hauptschleife kannst du dann in aller Ruhe das Array auswerten. Das geht recht einfach, wenn du dir einen String darüber legst und für die einzelnen Werte auch wieder kleinere Strings.
      Die Dims sehen dann z.B. so aus:

      Quellcode

      1. Config Base = 0
      2. Dim B As Byte
      3. Dim B_arr(2600 + 1) As Byte , B_str As String * 2600 At B_arr(1) Overlay
      4. Dim B_volt1 As String * 7 At B_arr(174) Overlay
      5. Dim L_volt1 As Long
      6. Dim Rcvd_flag As Bit
      7. Dim Cnt As Word
      B_volt1 ist ein Teilstring von B_str, der beim 174. Zeichen startet.
      Mit
      L_volt1 = Val(B_volt1)
      kannst du dir den Anfang dieses Strings in eine Variable abspeichern.
      Für die anderen Werte dann entsprechend.

      Ist meiner meinung nach die einfachere und sichere Variante.
    • Hallo allerseits
      Danke für euren Aufwand

      1. an dem eingehenden String kann ich zunächst garnichts ändern. Unglücklicherweise reagiert die Batterie sehr schnell und zudem auch gleich mit einem Echo "pwr"

      2. Ich vermisse einfach einen Befehl wie Config input as String oder besser noch bigstring.
      Der sollte einfach nur den String empfangen und alle darin enthaltenen Steuerzeichen ignoriren (nicht entfernen)

      Es wäre auch denkbar
      Remarks
      INPUT
      Use INPUT or INPUT1 for COM1, INPUT2 for COM2, INPUT3 for COM3, etc.
      Term
      A parameter with one of the following values :
      CR - Carriage Return (default)
      LF - Line Feed
      CRLF - Carriage Return followed by a Line Feed
      LFCR - Line Feed followed by a Carriage Return



      Erweiterung Term um "define" (Endzeichen(string) oder auch Zeichenzahl wäre eine gute Lösung


      Wofür gibt es bigstring, wenn ich keine langen Strings mit einfachen Mittel empfangen kann?



      Ich habe das problem mittlerweise so gelößt (Holzhammermethode, bitte nicht lachen!)

      Print "pwr";chr (13) 'Braucht die Batterie
      Input Empfang
      Waitms xy Hier springe ich auf den Datenzug auf (kurz vor meinen Daten, aber nach dem letzten CR)

      Und schon sind meine Daten da
      Nur noch aussortieren mit midstring (....

      Bitte um Kommentare, auch schräge

      Anton
    • Gut erkannt Pluto25

      Etwas verfeinert schaut es so aus

      Print "pwr 1";chr(13); 'Batterieabfrage Bat 1
      waitms 18 'liefert einen Teilstring für Coulomb Bat1 ab oulomb bis CR
      Input Empf1 ' Empfang von der Batterie
      waitms 100 'nur für Test
      LEmpfStr1 =len (Empf1) ' zur Kontrolle
      print#1, Empf1
      print#1, "Laenge 1 =";LEmpfStr1
      Empf1=""

      Meine Methode liefert einen Teilstring vom Einsprung bis zum nächsten CR
      Für meine Zwecke vollkommen ausreichend
      War halt ziemlich viel Probierarbeit
      Ausgabe auf Print#1 nur zur Kontrolle!

      PS Weshalb benützt Ihr CR? Nahezu immer ist LF das letzte Zeichen
      pps antwortet die Batterie nicht auf Print "pwr" ?
      Die Batterie braucht das CR sonst macht sie garnichts

      Anton
    • Das ist wirklich krass :thumbsup:
      Wenn die Batt mal ein bisschen trödelt kommt nur Quatsch an ;(
      Auch ist es viel Arbeit die richtige mid-Position zu finden?
      Vielleicht doch die Lf zählen?
      Dann braucht man erst gar kein mid ;)
      Jedoch wenns läuft Ziel erreicht a_64_3a718cae

      Ps Hinter jedem Print (ohne ";") ist automatisch ein CrLf und das reichte nicht?


      Anton70 schrieb:

      waitms 100 'nur für Test
      Auch nach der Testphase muß Deine Main lange genug zu tun haben oder warten bis die Batt ausgesprochen hat sonst ergibt die nächste Abfrage sicher Müll
      (bei meinen Tests an anderen Modulen kam dann "ERRORR" =O )

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Pluto25 ()

    • ich habe gute Erfahrungen mit einem Wait hinter dem Iskeywaiting Ding, wenn das in die programmstruktur irgendwie reinpasst:

      --->Main Loop---

      Do
      ...
      ...
      neue_Zeichen=ischarwaiting(#1)
      if neue_Zeichen=1 then gosub Read_String
      ---
      ...
      Loop

      --->Subs---

      Read_String:
      String = inkey(#1)
      waitms 1
      neue_Zeichen=ischarwaiting(#1)
      if neue_Zeichen=1 then goto Read_string
      ...
      ...
      return

      Da kann man die Übertragung ein wenig verzögern (hier 1ms, kann man ja anpassen auf die eigenen Bedürfnisse).
      Ja, der Empfang ist dann ein wenig langsamer, aber man holt alles aus dem Buffer.
      Wenn nichts mehr in der (trägen) Zeit kommt, dann ist der empfangene String zuende.
      Läuft bei mir in vielen Programmen sehr robust, ohne all die Abfragen mit ISR Buzzwords usw.
      Das geht oft mit Anlauf in die Hose, wenn das Sendeprotokoll nicht so ganz klar ist oder sogar unbekannt.
    • Peer Gehrmann schrieb:

      Da kann man die Übertragung ein wenig verzögern
      Das geht aber nur wenn Handshakes genutzt werden. Ohne spricht der Sender ungebremst weiter, egal was der Empfänger mitbekommt.

      Peer Gehrmann schrieb:

      aber man holt alles aus dem Buffer.
      Na ja, alles was nicht überschrieben wurde (in 1ms sind 10 Zeichen durch = 90% Verlust)) Jedoch hilft das wenig da nur das letzte Zeichen im String gespeichert wird. 99,96% Verlust ;)
    • Hallo zusammen,

      die Batterie hat leider nur Rx und Tx

      Da die Batterie sehr präzise und schnell antwortet, funktioniert meine Küppelmehtode inzwischen fehlerfrei.

      Trotzdem Danke für eure Unterstützung :thumbsup:

      Anton

      PS Aus den verschiedenen Lösungsvorschägen entnehme ich, daß mein Problem garnicht so trivial ist wie es zunächtst ausschaut.
    • Pluto, ich nehme dafür natürlich einen gebufferten seriellen Anschluss. Sonst ist klar, geht alles verloren.
      Mit Buffer eben nicht.
      Vor allem nicht, wenn das Protokoll eigentlich klein ist und man genug Zeit für Übertragungen lässt.
      Wenn Dinge komplexer werden, verwende ich auch gern Master/Slave- Konstruktionen mit mehreren Ardus...kosten ja nichts.
      Dann kann man sich einen nur für die Übertragung und das Parsen hernehmen. Aber mit hoher Übertragungsrate geht das auch super schnell, eigentlch arbeite ich nur noch mit 115200 Baud.
      Da geht das sehr schön.
      Die Pause braucht auch überhaupt nicht so lang sein, die soll nur evtl. Übertragungspausen im Protokoll überbrücken, da reichen wohl auch ein paar ns.
      Da muss man ein wenig optimieren, wenn gewünscht.
    • ahhhh, neeee, habe einen Fehler im Code gemacht, hier nochmal richtig, sonst wird ja nur ein Zeichen gefangen und immer wieder überschrieben:

      --->Main Loop---


      Do
      ...
      ...
      neue_Zeichen=ischarwaiting(#1)
      if neue_Zeichen=1 then gosub sub_char
      ---
      ...
      Loop


      --->Subs---

      Sub_Char:
      String=""

      Read_String:
      String = String + inkey(#1)
      waitms 1
      neue_Zeichen=ischarwaiting(#1)
      if neue_Zeichen=1 then goto Read_string
      return

      Jetzt passt es, sorry...
    • Peer Gehrmann schrieb:

      da reichen wohl auch ein paar ns.
      Vermutlich ms? Meine Kerlchen haben mehr als 1000ns für einen Takt nötig, ein Ardu 63? ;)

      Anton70 schrieb:

      verschiedenen Lösungsvorschägen
      Es führen viele Wege nach Rom. Hauptsache es funktioniert. Den einen Königsweg gibt es nicht.

      PS Bei der Verwendung von Input einen Timeout berücksichtigen damit die Main nicht stehen bleibt falls mal keine Antwort kommt.
      Ist das in Bascom irgendwie vorgesehen? Oder muß dann der Watchdog "zubeißen"
    • Hallo!

      Mich würden noch folgende Dinge interessieren:

      - hast Du genauere Informationen über die Kommunikation und das BMS? Kannst Du die Infos hier einstellen oder verlinken?
      - gehst Du über RS232, RS485 oder CAN an die Batterie?
      - was genau steuerst Du mit Deinen abgefragten Werten? Was willst Du damit in der PV-Anlage optimieren?

      Viele Grüße und danke für die Antworten!

      Andy
    • Hallo allerseits

      "da reichen wohl auch ein paar ns."
      1. Bei mir sind es so um die 12 ms
      Ich kann verschiedene Befehle an die Batterie schicken.
      Die Zeit ist davon abhängig, welche Daten ich haben will.
      (Ladezustand, Strom, Spannung u.s.w.

      2.$timeout = 1000000
      Gibt´s in BASCOM
      Problem dabei: Mir fehlt der Bezug zwischen der Zahl und der Echtzeit

      Mechanic:
      Kommunikation zur Batterie über RS232
      Meine Steuerung regelt den kompletten Lastfluß (Inselanlage).
      Wenig PV-wenig Last.
      Stellgröße ist hierbei der Batteriestrom.
      Der Ladezustand wird ebenfalls mit berücksichtigt.
      Infos zur Batteriekommunikation im Internet im Überfluß

      Anton