Hallo zusammen,
ich empfange über die USART-Schnittstelle Daten und möchte diese als
Übung im SRAM speichern und danach wieder ausgeben. Falls ein Byte über
die USART-Schnittstelle ankommt, wird ein Interrupt ausgelöst. Danach
wird folgender Code ausgeführt, der auch soweit funktioniert:
1
uart:
2
3
in temp, sreg
4
in data, UDR ;empfangenes Byte aus UDR lesen
5
st y+, data ;Byte im SRAM speichern und Zeiger erhöhen
Zum einlesen der Daten nutze ich einen Barcode Scanner der das Ende
eines Datenstroms stets mit einem Carriage Return absclhießt, den ich
auch abfrage (0xD). Danach erfolgt die Ausgabe durch Sprung zu
"sram_out". Bis hierher läuft alles problemlos.
Nun möchte ich aber auch Daten einlesen können die nicht mit einem
Carriage Return abgeschlossen werden, sondern ein beliebiges Zeichen am
Ende haben. Hier war meine Idee das RXC0-Bit abzufragen.
1
uart:
2
3
in temp, sreg
4
in data,UDR
5
st y+, data
6
in temp2, UCSR0A
7
andi temp2, 0b10000000
8
breq sram_out
9
out sreg, temp
10
reti
Der Sprung zu "sram_out" erfolgt wunschgemäß, aber bei der Ausgabe
erscheint nur Datenmüll(0x0,0x40) in einer Endlosschleife. Ich habe auch
schon mehrere Varianten ausprobiert, die alle nicht funktionieren. Hat
jemand eine Idee, Tipp oder Anregung wo der Fehler liegen könnte. Bin
ziemlich ratlos, da ich noch wenig Erfahrung mit der Materie habe.
Danke schon mal für Eure Hilfe und sorry für die lange Ausführung!
Gruß
sharth
Du sicherst zwar sreg in temp, aber sonst nichts. Nicht einmal temp.
Du kannst also davon ausgehen, dass im restlichen Programm der Inhalt
aller Register, die Du in diesem Interrupt verwendest, unzuverlässig ist
und zu annähernd beliebigen Zeiten sich ändern kann.
Zur Ausgabe und wie die zustande kommt, kann Dir keiner was sagen, da
der Code fehlt. Etwas merkwürdig finde ich allerdings, dass Du direkt
aus dem Empfangsinterrupt in die Senderoutine springst.
sharth schrieb:
> Nun möchte ich aber auch Daten einlesen können die nicht mit einem> Carriage Return abgeschlossen werden, sondern ein beliebiges Zeichen am> Ende haben. Hier war meine Idee das RXC0-Bit abzufragen.
Ich raffs nicht.
Was haben das RXC0 Flag im UCSR0A und die Tatsache, dass du das
Ende-of-data Byte anders haben willst, miteinander zu tun?
Das RXC Bit zeigt an, dass ein Zeichen komplett empfangen wurde. Das
weißt du aber sowieso, denn genau aus diesem Grunde befindet sich das
Programm momentan in der ISR. Wäre das RXC0 nicht gesetzt gewesen, wärst
du gar nicht an dieser Stelle im Programm.
PS: Du achtest hoffentlch peinlich genau darauf, dass dein sram_out das
sreg aus temp wiederherstellt und danach einen reti macht. Insgesamt ist
dieser branch dorthin eine Stolperfalle ersten Grades und keine so
wahnsinnig gute Idee
Hallo,
wie gesagt, ich stehe noch am Anfang meiner Assembler-Karriere. Aber
jetzt zum Thema:
Karl heinz Buchegger schrieb:
> Ich raffs nicht.> Was haben das RXC0 Flag im UCSR0A und die Tatsache, dass du das> Ende-of-data Byte anders haben willst, miteinander zu tun?
Ich möchte nicht das End-of-data-Byte anders haben, sondern ich möchte
das mein Programm erkennt, wenn das Ende einer Datenübertragung erreicht
ist, unabhängig davon, wie das letzte Byte aussieht.
Und weil das RXC0 Flag nicht mehr gesetzt wird, wenn keine weiteren
Bytes ankommen, dachte ich, dieses benutzen zu können. Aber scheinbar
ist das wohl nicht so sinnvoll? Kann mir jemand sagen warum?
> PS: Du achtest hoffentlch peinlich genau darauf, dass dein sram_out das> sreg aus temp wiederherstellt und danach einen reti macht. Insgesamt ist> dieser branch dorthin eine Stolperfalle ersten Grades und keine so> wahnsinnig gute Idee
Ja, ich habe das sreg wieder hergestellt. Allerdings weiß ich nicht wie
ich diese Stolperfalle mit dem sram_out Aufruf umgehen kann und warum es
überhaupt eine Stolperfalle ist. Sollte die Ausgabe von der
Hauptschleife aufgerufen werden?
mizch schrieb:
> Du sicherst zwar sreg in temp, aber sonst nichts.
Ob ich alle Register gesichert habe werde ich nochmal überprüfen.
Danke für eure Hilfe
sharth
Tschuldigung für den Doppelpost aber "Bearbeiten" hat nicht
funktioniert.
mizch schrieb:
> Du sicherst zwar sreg in temp, aber sonst nichts.> Du kannst also davon ausgehen, dass im restlichen Programm der Inhalt> aller Register, die Du in diesem Interrupt verwendest, unzuverlässig ist> und zu annähernd beliebigen Zeiten sich ändern kann.
Das ist ein Punkt den ich noch nicht verstanden habe. Wozu muss ich ein
Register sichern, wenn ich es im folgenden sowieso überschreibe, bevor
ich es wieder benutze. Ich hoffe es ist klar was gemeint ist.
Danke für eure Hilfe
sharth
Chris p. Chicken schrieb:
>> Ich raffs nicht.>> Was haben das RXC0 Flag im UCSR0A und die Tatsache, dass du das>> Ende-of-data Byte anders haben willst, miteinander zu tun?>> Ich möchte nicht das End-of-data-Byte anders haben, sondern ich möchte> das mein Programm erkennt, wenn das Ende einer Datenübertragung erreicht> ist, unabhängig davon, wie das letzte Byte aussieht.
Wie soll das gehen?
Ich bin Sender, du bist Empfänger
Ich sende dir mal was
01237885531241243 // ich mach mal eine kurze Pause, ist
// das jetzt schon das Ende?
// Nein, natürlich nicht, da kommt noch was
87963451764787687347 // was jetzt, jetzt schon zu Ende?
// Nö
36257453124548734
und so können wir das Spielchen immer weiter treiben. Solange wir nicht
vereinbaren, wann die Übertragung zu Ende ist, kannst du nicht
feststellen ob ich schon fertig bin oder nicht.
Und wenn du das nicht kannst, dann kann es dein µC auch nicht. Der ist
ja kein Hellseher.
Und das RXC Flag hat absolut nichts damit zu tun. Das zeigt nur ein,
dass 1 Zeichen komplett empfangen wurde. Nicht mehr.
Alles darüberhinausgehende musst du per Vereinbarung regeln!
> Ja, ich habe das sreg wieder hergestellt. Allerdings weiß ich nicht wie> ich diese Stolperfalle mit dem sram_out Aufruf umgehen kann und warum es> überhaupt eine Stolperfalle ist.
Die Stolperfalle besteht darin, dass du plötzlich eine Abhängigkeit
hast. Verbrauchst du in der ISR ein Register mehr, dann darfst du nicht
vergessen, in sram_out dieses Register wiederherzustellen. Was aber,
wenn sram_out gar nicht von der ISR aufgerufen wird, sondern von ganz
woanders?
> Sollte die Ausgabe von der> Hauptschleife aufgerufen werden?
zumindest sollte es ein call sein, damit sram_out wieder dorthin
zurückkehrt, von wo es hergekommen ist. Und der Aufrufer, in diesem
Falle die ISR räumt ihren Dreck selber weg und restauriert wieder alles
was sie angetascht hat.
Chris p. Chicken schrieb:
> mizch schrieb:>> Du sicherst zwar sreg in temp, aber sonst nichts.>> Du kannst also davon ausgehen, dass im restlichen Programm der Inhalt>> aller Register, die Du in diesem Interrupt verwendest, unzuverlässig ist>> und zu annähernd beliebigen Zeiten sich ändern kann.>> Das ist ein Punkt den ich noch nicht verstanden habe. Wozu muss ich ein> Register sichern, wenn ich es im folgenden sowieso überschreibe, bevor> ich es wieder benutze. Ich hoffe es ist klar was gemeint ist.
Der Schlüsselbegriff ist "im restlichen Programm", sprich: außerhalb des
Interrupts. Da ein Interrupt an jeder Stelle unterbrechen kann, ist
danach jedes Register "kaputt", das der Interrupt verändert.
Zu RXC: das ist gesetzt, sobald ein neues Zeichen verfügbar ist und zwar
nur solange, bis es von der CPU abgeholt wird. Auch bei
ununterbrochener Sendung auf der asynchronen Leitung dauert aber das
Eintrudeln eines Zeichens aus CPU-Sicht annähernd Ewigkeiten. In denen
ist das RXC vom letzten Zeichen längst gelöscht (du holst ja im
Interrupt ab, also praktisch sofort) und das nächste Zeichen noch lange
nicht komplett.
Hallo Karl heinz,
Karl heinz Buchegger schrieb:
> Wie soll das gehen?>> Ich bin Sender, du bist Empfänger> Ich sende dir mal was>> 01237885531241243 // ich mach mal eine kurze Pause, ist> // das jetzt schon das Ende?>> // Nein, natürlich nicht, da kommt noch was>> 87963451764787687347 // was jetzt, jetzt schon zu Ende?>> // Nö>> 36257453124548734>> und so können wir das Spielchen immer weiter treiben. Solange wir nicht> vereinbaren, wann die Übertragung zu Ende ist, kannst du nicht> feststellen ob ich schon fertig bin oder nicht.
Im Grunde verstehe ich was du meinst.
Aber richtig ist doch auch, das ich mittels RXC-Bit testen kann ob im
UDR daten vorhanden sind. Wenn ich mittels JTAG debugge, kann ich
erkennen, dass das RXC-Bit solange gesetzt bleibt, bis ich das letzte
empfangene Byte aus dem UDR gelesen habe. Danach wird RXC 0.
Jetzt weiß ich natürlich nicht ob der Sender mir noch mehr Daten senden
möchte. Das heißt aber auch, das der Sender mir ein eindeutiges Zeichen
geben muss, wann ein Datenstrom abgeschlossen ist.
Da ich aber erreichen wollte, das bei einer Übertragungs-Pause die
Ausgabe erfolgt habe ich das RXC-Bit verwendet.
In meinem Beispiel wusste ich ja, dass immer nur 8 Bytes reinkommen und
darauf eine Pause eintritt.
Mal ein anderer Ansatz
ich weiß nicht wieviele Bytes reinkommen, und auch nicht wie das letzte
Byte aussieht. Lässt sich das dann besser zeitlich regeln.
Z.B. wenn nach einer Wartezeit von 50ms kein weiteres Byte anliegt nehme
ich an, dass die aktuelle Übertragung abgeschlossen ist?
Ich hoffe es ist einigermaßen klar worauf ich hinaus will!
Viele Grüße,
sharth
Hc Zimmerer schrieb:
> Der Schlüsselbegriff ist "im restlichen Programm", sprich: außerhalb des> Interrupts. Da ein Interrupt an jeder Stelle unterbrechen kann, ist> danach jedes Register "kaputt", das der Interrupt verändert.>> Zu RXC: das ist gesetzt, sobald ein neues Zeichen verfügbar ist und zwar> nur solange, bis es von der CPU abgeholt wird. Auch bei> ununterbrochener Sendung auf der asynchronen Leitung dauert aber das> Eintrudeln eines Zeichens aus CPU-Sicht annähernd Ewigkeiten. In denen> ist das RXC vom letzten Zeichen längst gelöscht (du holst ja im> Interrupt ab, also praktisch sofort) und das nächste Zeichen noch lange> nicht komplett.
Okay, das ist jetzt klar geworden. Ich werde versuchen mein Programm
entsprechend zu ändern.
Danke für eure schnelle Hilfe
sharth
Chris p. Chicken schrieb:
> Im Grunde verstehe ich was du meinst.> Aber richtig ist doch auch, das ich mittels RXC-Bit testen kann ob im> UDR daten vorhanden sind. Wenn ich mittels JTAG debugge, kann ich> erkennen, dass das RXC-Bit solange gesetzt bleibt, bis ich das letzte> empfangene Byte aus dem UDR gelesen habe.
Ja.
Aus Sicht deines Programms passiert dieses RXC ist gesetzt nur alle
heiligen Zeiten!
Ich schick dir am Vormittag was, dann 3 Stunden später, dann 2.5 Stunden
später. Die ganze Nacht über nichts, mogen am Vormittag 3 Zeichen usw.
> Jetzt weiß ich natürlich nicht ob der Sender mir noch mehr Daten senden> möchte. Das heißt aber auch, das der Sender mir ein eindeutiges Zeichen> geben muss, wann ein Datenstrom abgeschlossen ist.
Ganz genau
> Da ich aber erreichen wollte, das bei einer Übertragungs-Pause die> Ausgabe erfolgt habe ich das RXC-Bit verwendet.
Das hilft dir aber nicht viel
Du bist im Interrupt, eben weil jetzt gerade ein Zeichen vollständig da
ist. Selbst wenn die Übertragung ununterbrochen läuft, könntest du an
dieser Stelle nicht feststellen, ob noch was kommt oder nicht. Du siehst
ganz einfach viel zu früh nach!
>Z.B. wenn nach einer Wartezeit von 50ms kein weiteres Byte anliegt nehme>ich an, dass die aktuelle Übertragung abgeschlossen ist?
"nehme ich an" ist mist. weißt Du aber, das der Sender, wenn 50ms nichts
kam auch eine große Pause macht, dann ist es gut.
Guten Morgen,
leider habe ich mein Problem noch immer nicht so richtig lösen können.
Programmablauf:
Ich möchte eine undefinierte Anzahl an Bytes von einem Sender empfangen
und im SRAM speichern. Wenn der Sender eine gewisse Zeitspanne nichts
gesendet hat
möchte ich die Daten über die USART Schnittstelle wieder ausgeben.
Bei der Ausgabe erhalte ich jedoch nur Datenmüll in einer
Endlosschleife.
Folgendermaßen sieht mein Code bisher aus:
Nun ist wieder das Problem, dass in der Interrupt Routine ein "call"
ausgeführt wird. Ich weiß das, das vermieden werden sollte. Aber bin
noch blutiger Anfänger und weiß nicht, wie ich das sonst umsetzen soll.
Für einige Tipps wäre ich sehr dankbar.
Viele Grüße,
sharth
Chris p. Chicken schrieb:
> Nun ist wieder das Problem, dass in der Interrupt Routine ein "call"> ausgeführt wird. Ich weiß das, das vermieden werden sollte.
Das ist hier gar nicht sooo schlimm.
Schalte aber wenigstens den Timer ab. Der tickt während der ganzen ISR
weiter und wenn die ISR zu lange braucht, kommt schon der nächste
Overflow und die ganze Ausgabe beginnt wieder von vorne.
ldi temp, (1<<CS02)
out TCCR0, temp ;Timer starten
Hier musst du den Timer im TCNT0 wieder auf 0 zurücksetzen. Du willst ja
haben, dass eine längere Pause, NACHDEM ein paar Zeichen eingetrudelt
sind, die Ausgabe triggert. Bei jedem Zeichen, welches reinkommt, muss
daher die 'Stoppuhr' erneut bei 0 anfangen zu laufen.
cpi data, 0xD
brne serout
Das würde ich nicht machen.
Du hast keine Garantie, dass ein 0x0D im Speicher abgelegt wurde. Alles
was du weißt ist, wo die abgelegten Daten enden (durch den Wert im
Y-Pointer bei betreten der Routine). An diesen Wert würde ich mich
halten.
> Das ist hier gar nicht sooo schlimm.
Das heißt aber wohl auch, das es keine besonders elegante Lösung ist.
Gibt es eine gute Alternative, die auf mein Problem anwendbar ist?
> Schalte aber wenigstens den Timer ab.
Okay,habe den Timer jetzt während des Timer_Interrupt gestoppt.
> Hier musst du den Timer im TCNT0 wieder auf 0 zurücksetzen.
Danke und nochmals danke! Genau da lag der Fehler. Hatte nämlich gerade
getestet ab welcher Byte-Anzahl der Fehler auftritt. Immer ab ca. 10
übertragenen Bytes kam eine Endlosschleife an Bytes.
> Du hast keine Garantie, dass ein 0x0D im Speicher abgelegt wurde. Alles> was du weißt ist, wo die abgelegten Daten enden (durch den Wert im> Y-Pointer bei betreten der Routine). An diesen Wert würde ich mich> halten.
Das hab ich noch nicht ganz verstanden. Bei betrteten der "sram_out"
Routine die Adresse auf die der y-Zeiger gerichtet ist auslesen? Da
sollte ja dann der letzte eingelesene Wert drinstehen. So Richtig?
Danke für deine schnelle Hilfe
Gruß,
sharth
Chris p. Chicken schrieb:
>> Das ist hier gar nicht sooo schlimm.>> Das heißt aber wohl auch, das es keine besonders elegante Lösung ist.> Gibt es eine gute Alternative, die auf mein Problem anwendbar ist?
Die allgemeine Empfehlung lautet:
In einer ISR nur das absolut notwendige tun, und dann so schnell wie
möglich wieder aus der ISR raus.
Nur: Wei bei jeder Regel, ist das eine Faustregel. Im Einzelfall kann
man die auch schon mal links liegen lassen.
Die elegantere, allerdings aufwändigere Lösung, sieht so aus: Das
Auslesen in die Hauptschleife verfrachten und in der ISR nur ein Flag
setzen, andem die Hautpschleife den 'Arbeitsauftrag' erkennt.
Bei dir, in diesem konkreten Beispiel, ist das aber ein nicht
unbeträchtlicher Aufwand. Du musst dich dann plötzlich um Race
Conditions kümmern, etc (Das musst du sowieso irgendwann tun, denn was
soll passieren wenn während der Ausgabe der Sender wieder zu senden
anfängt)
Aber hier ging es um ein anderes Problem, drum wollte ich nicht zuviele
Änderungen auf einmal anregen.
>> Du hast keine Garantie, dass ein 0x0D im Speicher abgelegt wurde. Alles>> was du weißt ist, wo die abgelegten Daten enden (durch den Wert im>> Y-Pointer bei betreten der Routine). An diesen Wert würde ich mich>> halten.>> Das hab ich noch nicht ganz verstanden. Bei betrteten der "sram_out"> Routine die Adresse auf die der y-Zeiger gerichtet ist auslesen?
Nö.
Den Y-Zeiger selber merken -> Endwert
Und dann in der Schleife nur solange ausgeben, bis Y wieder den vorher
gemerkten Endwert erreicht hat.
Damit gibst du dann nur das aus, was auch vorher in den SRAM
hineingeschrieben wurde.
Je mehr Annahmen du darüber triffst, was in den Daten überhaupt drinnen
steht, desto mehr bindest du die Funktion an diesen einen speziellen
Fall. Im Idealfall ist es egal, was du empfangen hast. Du gibst nur das
aus was du auch empfangen hast. Damit können es aber nicht die
empfangenen Datenbytes, bzw deren Werte, sein, die die Ausgabe steuern.
holger schrieb:
> Da fehlt doch ein reti am Ende?
Das ist beim kopieren abhanden gekommen. Hatte ein bisschen mit der
Formatierung hier zu kämpfen. Habe vorher alle Kommentare rausgenommen,
weil sonst die Zeilen zu lang waren. Dabei ist das "reti" wohl auch
verloren gegangen.
Würde es gerne oben noch ändern, aber das ist scheinbar nicht mehr
möglich.
Trotzdem, danke!
sharth
Karl heinz Buchegger schrieb:
> Du musst dich dann plötzlich um Race> Conditions kümmern, etc (Das musst du sowieso irgendwann tun, denn was> soll passieren wenn während der Ausgabe der Sender wieder zu senden> anfängt)
Das hatte ich mich auch schon gefragt. Wenn es soweit ist, melde ich
mich bestimmt wieder;-) Oder weißt du eine gute Quelle, oder 'nen
anderen Thread, wo man sich soetwas nachlesen kann?
Karl heinz Buchegger schrieb:
> Nö.> Den Y-Zeiger selber merken -> Endwert
Ja, das ist wohl etwas sinnvoller, weil allgemeiner. Aber ich war
nah dran:-)
Gruß,
sharth
Chris p. Chicken schrieb:
> Karl heinz Buchegger schrieb:>> Du musst dich dann plötzlich um Race>> Conditions kümmern, etc (Das musst du sowieso irgendwann tun, denn was>> soll passieren wenn während der Ausgabe der Sender wieder zu senden>> anfängt)>> Das hatte ich mich auch schon gefragt. Wenn es soweit ist, melde ich> mich bestimmt wieder;-) Oder weißt du eine gute Quelle, oder 'nen> anderen Thread, wo man sich soetwas nachlesen kann?
Was würdest du im realen Leben machen?
Folgende Situation:
Dein Kumpel schreibt dir Dinge auf einen Zettel auf.
Wenn eine gewisse kritische Masse beisammen ist (oder er 10 Sekunden
nichts aufschreibt), nimmst du den Zettel, gehst nach hinten ins Lager
und holst die Teile die er aufgeschrieben hat.
In der Zeit, in der du im Lager bist, kann dein Kumpel klarerweise
nichts auf den Zettel, den du ja mit hast, aufschreiben.
Ihm fallen aber noch mehr Dinge ein, die er braucht. Und ermuss die
niederschreiben.
Wie würdest du dieses Problem im realen Leben lösen?
Wenn du dafür eine Lösung hast, dann kannst du diese Lösung, der Idee
nach, so ziemlich 1:1 auch in deinen Programmcode übernehmen :-)
Erstaunlich viele Programmierprobleme haben Entsprechungen in unserem
realen Leben, wo wir diese Probleme mit links lösen. Und diese
Lösungsideen wiederrum sind oft gut in Programme übernehmbar