ASM UART Interrupt Problem

  • Hallo,
    ich bin zur Zeit dabei einen ATMega328P in Assembler zu programmieren, der anschließend in der Lage sein soll WS2811 Pixel anzusteuern. Die Daten soll er später von Glediator bekommen.


    Die eigentliche Datenausgabe an die Pixel funktioniert auch soweit korrekt. Probleme bereitet mir nun der Datenempfang.


    Zu Testzwecken habe ich jetzt ein neuen Codeblock geschrieben, welcher einfach eine LED beim Empfangen eines bestimmten Zeichens (hier "d") einschaltet. Wird irgendein anderes Zeichen gesendet so soll die LED wieder ausgehen.


    Packe ich das ganze in die Main-Loop passiert folgendes:
    Der µC reagiert, so wie ich das sehe immer auf das vorletzte Zeichen was gesendet wurde, also sende ich per Terminalprog z.B. ein "s" & "d" so passiert erstmal nichts. Erst wenn ich ein weiteres Zeichen sende geht die LED an, abhängig davon, ob dieses Zeichen ein "d" war, bleibt die LED bei einem weiteren Zeichen an oder geht aus.


    Aber eigentlich soll der Datenempfang bzw deren Verarbeitung per Interrupt ablaufen. Wenn ich nun die Routine aus der Main- Loop lösche und nur bei einem Rx- Complete-Interupt anspringe, ist es so, dass die LED quasi die ganze Zeit aus ist. Erst wenn ich eine Taste gedrückt halte, also Daten ununterbrochen gesendet werden, so flackert die LED. Dabei scheint es auch nicht zu interessieren, ob ich "d" oder eine andere Taste gedrückt halte.
    Daraus schließe ich schonmal, dass die ISR immerhin angespringen wird bzw der Interrupt auslöst


    Hier erstmal mein Code um das ganze etwas zu veranschaulichen:
    Die Block sieht folgendermaßen aus:

    Code
    Main:
    cpi temp0,100			;ist temp0="d"?
    brne ledaus				;nein,also springen
    sbi LED_Port,Test_Pin	;led an
    rjmp Main
    ledaus:
    cbi LED_Port,Test_Pin	;led aus
    
    
    rjmp Main


    Die ISR sieht dan folgendermaßen aus:


    Es wäre nett wenn mich jemand aufklären könnte was ich falsch mache. Denn wenn selbst sowas simples nicht funktioniert, brauche ich mir über komplexere Sachen gar keine Gedanken machen. Falls noch weitere Dinge zur Aufklärung benötigt werden liefer ich die gerne nach.


    Ach so zu sagen wäre noch, dass der µC mit 16MHz läuft und UART für den Moment auf 9600 Baud, 8Bit, 1 Stoppbit, No Parity eingestellt ist.


    Vielen lieben Dnak schonmal

  • also wie jetzt, wenn ich das richtig sehe, wertest Du das empfangene Byte 2x aus... ?(


    oder ist der eine Codeschnipsel, wie es vorher war, und der zweite, wie es jetzt ist...?!


    darf ich aufgrund des Schriftsatzes richtig vermuten, dass der Teil:



    aus irgendner SW von mir kopiert ist...? ;)


    Frage: ist das nur kopiert, oder weißt Du, was das macht....?


    ist erst mal richtig, die in der ISR verwendeten Register und das SREG werden gesichert - nur, dann passiert folgendes: Du hast Dein empfangenes Byte in temp0, anschließend holst Du per pop wieder vom Stack den Wert, der *vorher* in temp0 war, überschreibst also Dein empfangenes Byte!


    also kann das so nicht funktionieren...


    bei asm ist es wichtig, den Überblick zu behalten, was wann wie passiert - ist aber gar nicht schwer, wenn man ein paar einfache Regeln beherzigt...


    z.B., bei Routinen, die Daten übergeben müssen, benutzt Du ein extra Register (oder auch ne RAM-Zelle), das/die dann ideal in der Subroutine vermerkt ist zum nachsehen...


    also hier könntest Du z.B. temp 1 für das empfangene Byte benutzen, das wird ja nicht gesichert - ist dann aber später blöd, wenn Du die SW erweiterst, irgendwo anders auch temp1 benutzt, und dann die ISR Dir das zerschiesst... also entweder extra Register zur Datenübergabe, oder irgendwo im RAM nen Puffer anlegen (sts/lds).


    ich habe neulich nen guten Tipp gelesen: man hat ja doch recht viele Register, am Besten benutzt man alle als Arbeitsregister, machtisch dann ein Set für "normal", und eins für ISRs...


    also z.B. in der Mainloop, oder Subroutinen, die Du selbst aufrufst, und weißt, welche Register du verwendest, benutzt Du temp0, temp1, temp2, temp3 - und in der ISR dann itemp0, itemp1, itemp2, itemp3


    Vorteil: Du sparst Dir ggfs. ne Menge push und pop (= weniger Laufzeit, insb. bei WS2811 sehr wichtig, die sind etwas heikel), und kannst dann Werte gleich in diesen Registern übergeben - z.B. das empfangene Byte in itemp0, das musst Du vorher nicht sichern, weil's ausserhalb der ISR nicht verwendet wird, und das Byte bleibt dann in itemp0, weil Du das am Ende der ISR ja auch nicht wiederherstellen musst...


    so hast Du mit diesem Registersatz schon mal die Möglichkeit, ganz easy 4 Bytes Daten zu übergeben - das reicht oft.


    für das SREG ein Register "unten" (z.B. R3) reservieren, und das SREG einfach da rein sichern mit mov, spart noch einmal push und pop (immerhin 4 Takte) - da kann auch nix passieren, wenn Du das nur in ISRs verwendest, ausser Du machst "Tricks" wie IRG-Nesting, aber die sind eh' nix für Anfänger... ;)


    Übrigens, guck' doch mal hier, bis zu 1.024 Pixel per tpm2 aus Glediator empfangen und an WS2801 ausgeben - da müsstest Du nur die Ausgaberoutine für WS2811 umbauen, und da gehen nicht mehr so viele, wegen der Timing-Sache...

    It's only light - but we like it!


    Da es sich in letzter Zeit häuft: Ich beantworte keine PNs mit Fragen, die sich auch im Forum beantworten lassen!
    Insbesondere solche von Mitgliedern mit 0 Beiträgen, die dann meist auch noch Sachen fragen, die bereits im entsprechenden Thread beantwortet wurden.
    Ich bin keine private Bastler-Hotline, technische Tipps etc. sollen möglichst vielen Lesern im Forum helfen!

  • Moin erstmal,

    Zitat

    also wie jetzt, wenn ich das richtig sehe, wertest Du das empfangene Byte 2x aus... ?(

    Nein, das mache ich nicht. Der Einfachheit halber habe ich es erstmal ohne Interrupt versucht. Also besteht mein Programm im Prinzip nur aus meinem ersten eingefügten Codeblock (+ Register- Def. usw). Dies führt, wie erklärt zur verzögerten Ausgabe der empfangenen Daten.


    Anschließend habe ich den Teil aus der Main-Loop ausgeschnitten und in eine Sub gepackt, die von der ISR angesprungen wird.


    Zitat

    darf ich aufgrund des Schriftsatzes richtig vermuten, dass der Teil aus irgendner SW von mir kopiert ist...? ;)

    Ja und nein. Ich muss dazu sagen, dass das mein erstes richtiges Projekt in Assembler ist. (Hatte aber letztes Semester ein Labor an der Uni in welchem die Grundlagen gelehrt, gelernt und angewendet wurden) Daher habe mich erstmal umgeschaut und bin dann ,wie du ja schon richtig vermutet hast, auf deine SW gestoßen. Daraus habe ich aber nichts sinnlos kopiert, sondern nur den "Stil" übernommen, da ich ihn für sehr übersichtlich und nachvollziehbar halte (was bei Assembler meiner Meinung nach recht schwierig ist).


    Zitat

    Frage: ist das nur kopiert, oder weißt Du, was das macht....?

    Siehe oben und ich denke zumindest, dass ich weiß, was ich da tue ;)
    Wobei ich glaube, dass ich in diesem einfachen Fall auch darauf verzichten könnte die Register zu sichern, da das Programm im Moment nur aus einer leeren Main-Loop und dem Rx- Complete- Interrupt (aka SUB_Byte_received) besteht, also die Register woanders gar nicht verwendet werden.

    Zitat

    ... nur, dann passiert folgendes: Du hast Dein empfangenes Byte in temp0,
    anschließend holst Du per pop wieder vom Stack den Wert, der *vorher* in
    temp0 war, überschreibst also Dein empfangenes Byte!

    Grundsätzlich richtig ABER bevor ich temp0 wieder vom Stack hole, werte ich es ja in der ISR direkt aus, von daher sehe ich da eigentlich kein Problem.


    Und die von dir verlinkte SW kenne ich schon (mit DMX-, wie auch tmp2- Empfang)- wie gesagt sind die Teil meines Wissensfundaments, auf das ich versuche aufzubauen. Sofern ich das bestehende Problem gelöst bekomme, sieht mein nächster Schritt erstmal so aus, dass ich versuchen werde das "Glediator-Protokoll" zu implementieren. Das ist dann doch einfacher als tmp2 (wenn auch Fehleranfälliger, denke ich). Mir geht es halt nicht primär um das Endergebnis, sondern darum was zu lernen/ Gelerntes anzuwenden.


    btw der Vorschlag mit den "2- Klassen- Registern" ist sehr gut, den werde ich sicherlich, wenn es etwas komplexer wird, beherzigen.


    Schönen Sonntag noch
    drs

  • Also besteht mein Programm im Prinzip nur aus meinem ersten eingefügten Codeblock (+ Register- Def. usw). Dies führt, wie erklärt zur verzögerten Ausgabe der empfangenen Daten.


    Anschließend habe ich den Teil aus der Main-Loop ausgeschnitten und in eine Sub gepackt, die von der ISR angesprungen wird.

    Das war eben nicht klar - ob das nun zwei völlig verschiedene Sachen sind, oder zusammen gehören...


    also bitte genauer kennzeichnen, z.B. "Zuerst habe ich es so versucht (Code) und dann so (Code)"


    das kann doch irgendwie auch nixcht alles gewesen sein:


    Code
    Main:
    cpi temp0,100			;ist temp0="d"?
    brne ledaus				;nein,also springen
    sbi LED_Port,Test_Pin	;led an
    rjmp Main
    ledaus:
    cbi LED_Port,Test_Pin	;led aus
    
    
    rjmp Main


    Du fragst dauernd temp0 ab, aber nirgends wird da was rein geschrieben... ?( - wo wird denn der Usart abgefragt...? - doch auch in ner ISR, wo dann evtl. temp0 wieder überschrieben wird...?


    Am Besten immer den *kompletten* Code posten (wenn zu lang, dann als .txt anhängen), sonst kann keiner nachvollziehen, was da alles wie passiert - der Teil, wo der USART konfiguriert wird, fehlt auch, evtl. ist dort ja auch ein Problem...


    Wobei ich glaube, dass ich in diesem einfachen Fall auch darauf verzichten könnte die Register zu sichern, da das Programm im Moment nur aus einer leeren Main-Loop und dem Rx- Complete- Interrupt (aka SUB_Byte_received) besteht, also die Register woanders gar nicht verwendet werden.


    (...)


    btw der Vorschlag mit den "2- Klassen- Registern" ist sehr gut, den werde ich sicherlich, wenn es etwas komplexer wird, beherzigen.

    ich würde mir gleich angewöhnen, das alles "richtig" zu machen - spreche aus Erfahrung, hatte schon oft Stunden nach irgendnem doofen Fehler gesucht, der dann darin bestand, dass irgendeine Routine ein in einer anderen Routine verwendetes Register überschrieben hat - deswegen sichere ich nun z.B. auch in allen SUBs pauschal alle verwendeten Register, auch wenn das evtl. gar nicht nötig wäre...


    dann ist erst mal alles auf "Nummer sicher" und solche nervigen Fehler ausgeschlossen - man kann dann immer noch optimieren, also wenn es wegen den paar push und pop auf Laufzeit oder Codegröße ankommt, im konkreten Fall die Sicherung wieder aus der Routine raus löschen, bewusst und gut überlegt, ob's dann acuh noch geht...


    also gleich das so aufteilen, temp normal und itemp in ISRs, sonst kommst Du irgendwann durcheinander - oder es gibt Probleme, wenn Du früher geschriebene Codeteile (ohne Registersicherung bzw. eigene Register) mit neueren kombinierst, wo dann was überschrieben wird...

    It's only light - but we like it!


    Da es sich in letzter Zeit häuft: Ich beantworte keine PNs mit Fragen, die sich auch im Forum beantworten lassen!
    Insbesondere solche von Mitgliedern mit 0 Beiträgen, die dann meist auch noch Sachen fragen, die bereits im entsprechenden Thread beantwortet wurden.
    Ich bin keine private Bastler-Hotline, technische Tipps etc. sollen möglichst vielen Lesern im Forum helfen!

  • Ok, dann nochmal alles auf Anfang:


    Also ersteinmal Variante A mit UART- Abfrage in der Main-Loop. Diese Variante entspricht der angehängten Datei mit der Ausnahme, das die "SUB_Byte_received" leer ist. Brenne ich dieses Programm nun auf den Chip passiert das oben schon beschriebene:

    Zitat

    Der µC reagiert, so wie ich das sehe immer auf das vorletzte Zeichen was
    gesendet wurde, also sende ich per Terminalprog z.B. ein "s" & "d"
    so passiert erstmal nichts. Erst wenn ich ein weiteres Zeichen sende
    geht die LED an, abhängig davon, ob dieses Zeichen ein "d" war, bleibt
    die LED bei einem weiteren Zeichen an oder geht aus.

    So und weiter mit Variante B. Hier habe ich folgende Veränderungen vorgenommen:
    - in der "Interrupt.inc" das zu Rx-Complete gehörende "reti" durch "rjmp SUB_Byte_received" ersetzt
    - der Inhalt der Main- Loop besteht nur noch aus einem nop- Befehl
    - "SUB_Byte_received" sieht nun so aus, wie in der .txt


    Bei dieser Variante tut sich nun gar nichts mehr... Die LED bleibt dauerhaft aus.