Hallo an Alle!
Ich habe einen Atmega 2560. Wenn ich über UART0 sende kann ich die Daten
über serielle Schnittstelle mit einem hyperterminal empfangen.
Das klappt allerdings nur, wenn ich die Daten direkt in das udre0
schreibe mittels
sts UDR0, r16 zum Beispiel in einem Timer.
Jetzt wollte ich die Daten testweise indirekt über das
DataRegisterEmptyInterrupt senden.
Also (1<<udrie0) DataRegisterEmptyInterruptEnable in ucsr0b setzen, dann
wird das Interrupt anschließend auch ausgelöst und dann im Interrupt
selber sts UDR0, r16 ausführen und (0<<udrie0) das
DataRegisterEmptyInterruptEnable wieder zurücksetzen.
Im nächsten Timer wird das ganze wiederholt. Timerintervall 1 Sekunde.
Klappt leider nicht. Es kommen keine Daten an am Pc an.
(1<<rxen0)|(1<<txen0)|(1<<rxcie0)|(1<<txcie0) sind gesetzt.
Kann mir jemand sagen, wo mein Denkfehler ist??
Danke!!!!!!
PCB schrieb:> Kann mir jemand sagen, wo mein Denkfehler ist??
Du hast nicht begriffen, dass das UDR-Interruptflag in deinem Fall
IMMER gesetzt ist.
Das wird erst gelöscht, wenn du kurz hintereinander zwei Bytes in die
Hardware geschrieben hast.
Die Software muss das wissen und entsprechend agieren.
Senden ist was anderes als Empfangen. Dementsprechend funktionieren die
Interrupts vollkommen anders.
Da bedeutet in deinem Fall (wenn man nur alle Jubeljahre mal ein
verschissenes Byte loswerden will): Die Nutzung des UDRE-Interrupts ist
vollkommen hyperfluid...
Hallo c-hater,
danke für deine Antwort.
Das hilft schon mal weiter.
Wie müsste ich dann bei einer interruptgesteuerten Datenübertragung
vorgehen ???
Danke
PCB schrieb:> Wie müsste ich dann bei einer interruptgesteuerten Datenübertragung> vorgehen ???
Einfach nur: richtig. Der Interrupt darf erst "scharf geschaltet"
werden, wenn die synchrone Befriedigung der Ausgabe-Anforderung nicht
mehr möglich ist.
Es läuft darauf hinaus, eine synchrone Ausgaberoutine zu bauen, die bei
entsprechender Last automatisch asynchron wird. Hört sich vielleicht
kompliziert an, ist aber in Wirklichkeit mit wenigen Zeilen Code
abgegessen. Jedenfalls, wenn man erstmal eine Ausgabe-Queue hat. Das ist
die Grundlage, genau wie bei einer interruptgesteuerten Eingabe. Nur das
Handling der Queue weicht ab.
Ich bin aber ziemlich sicher, dass in den Annalen dieses Forums auch die
interruptgesteuerte Ausgabe schonmal irgendwo vorkam. Wie wäre es also,
wenn du einfach mal selber suchen würdest?
Vorschlag, so ganz grob:
Ringpuffer - Ausgaberoutine - UDR-ISR
Die Ausgaberoutine schreibt das Zeichen (ein Byte) in den Ringpuffer;
ist der UDR-Interrupt gesperrt, wird er freigegeben.
Die UDR-ISR holt ein Byte aus dem Ringpuffer und übergibt es der
Hardware (schreibt es in UDR). War es das letzte Byte im Ringpuffer,
d.h. ist dieser leergelaufen, wird der UDR-Interrupt gesperrt.
S. Landolt schrieb:> Vorschlag, so ganz grob:> Ringpuffer - Ausgaberoutine - UDR-ISR>> Die Ausgaberoutine schreibt das Zeichen (ein Byte) in den Ringpuffer;> ist der UDR-Interrupt gesperrt, wird er freigegeben.
Das Konzept funktioniert auch, ist allerdings (relativ geringfügig)
suboptimal.
> Die UDR-ISR holt ein Byte aus dem Ringpuffer und übergibt es der> Hardware (schreibt es in UDR). War es das letzte Byte im Ringpuffer,> d.h. ist dieser leergelaufen, wird der UDR-Interrupt gesperrt.
Hier wäre noch die TXC-Geschichte zu ergänzen, um die Sache wirklich
"rund" zu machen.
Mein Problem scheint ja zu sein, dass das UDR-Interruptflag in meinem
Fall
IMMER gesetzt ist und ich das DataReggoster in der
DataRegisterEmptyInterrupt-Routine daher nicht beschreiben kann oder??
Ja schon, aber zu Beginn ist der UDR-Interrupt ja nicht freigegeben. Die
Freigabe erfolgt beim Aufruf der Ausgaberoutine mit dem Schreiben in den
Ringpuffer.
an c-hater:
Dann können Sie ja einen besseren Vorschlag machen, möglichst konkret.
Was mich betrifft, so halte ich es mit E.C. Bliss: The pursuit of
excellence is gratifying and healthy. The pursuit of perfection is
neurotic, frustrating and a terrible waste of time.
Damit verabschiede ich mich.
PCB schrieb:> Mein Problem scheint ja zu sein, dass das UDR-Interruptflag in meinem> Fall> IMMER gesetzt ist
Ja.
> und ich das DataReggoster in der> DataRegisterEmptyInterrupt-Routine daher nicht beschreiben kann oder??
Nein. Solange das Flag gesetzt ist,kannst du das Register natürlich
beschreiben. Egal ob aus einer ISR oder aus main(). Genau den
Sachverhalt, dass dieses Register (sinnvoll) beschreibbar ist, zeigt ja
das gesetzte UDRE-Flag an.
PCB schrieb:> Und warum werden die Daten dann nicht automatisch gesendet, wenn ich das> Register beschreiben kann ??
Werden sie doch, sobald du was in das Register schreibst. Zumindest so
bald als möglich. Genau das ist, was die Hardware tut, wofür sie
geschaffen wurde.
Offenbar hat der c-hater dein Problem nicht verstanden. Wundert mich
nicht, bei der unklaren Problembeschreibung. Ich traue mich auch nicht,
dir zu antworten ohne vorher den Quelltext zu sehen.
Hallo und danke für die Nachfrage.
Wenn ich wie folgt vorgehe:
[......]
sei ; interrupts global zulassen
reset_loop:; endlosschleife
sts UDR0, r16
sts UDR0, r16
rjmp reset_loop
Dann kann ich beobachten im Simulator, dass das UDRE0 auf 0 gesetzt
wird.
Wenn ich das Gleiche aber im DataRegisterEmptyInterrupt mache passiert
nix:
uartdre0:
sts UDR0, r16
sts UDR0, r16
reti
Das scheint der Kausus knacktus zu sein
Schreibe mal ein (im Debugger) ausführbares Testprogramm, damit man das
nachvollziehen kann.
Weil: Den von dir beschriebenen Effekt kenne ich so nicht. Ich schätze
da passiert noch etwas anderes relevantes in Code, den du nicht gezeugt
hast.
Stefan ⛄ F. schrieb:> Offenbar hat der c-hater dein Problem nicht verstanden.
Naja, hier geht es auch einigermaßen wirr zu.
OK, mit den AVR befasse ich mich nicht, aber bei den üblichen
U(S)ART-Cores ist es im Algemeinen so:
Die Sendeseite:
Vor dem Schieberegister, wo das zu sendende Byte herausgeschoben wird,
sitzt ein weiteres Register, in das man schreiben kann und wo (wenn das
Schieberegister leer ist) das Byte sogleich ins Schieberegiter
durchgewinkt wird. Ist grad noch was im Schieberegister, dann wird das
Byte erst mal in o.g. Register zwischengespeichert, bis das
Schieberegister leer ist.
So, und nun hat man zumeist 2 Interruptbits: eines was beim Leersein des
Schieberegisters gesetzt ist und eines, was immer dann gesetzt ist, wenn
Platz frei ist (im Schieberegister oder dem Register davor). Kann aber
auch sein, daß bei den beiden eines oder beide invertiert sind, macht
aber vom Prinzip nix.
Was also muß der Lowlevel-Treiber tun?
a) Wenn es etwas zu senden gibt und Platz frei ist (s.o.), dann soll er
das nächste Byte in den Sender (also die Kombi aus Schieberegister und
dem Register davor) schreiben. Normalerweise verschwindet dann das o.g.
Interruptbit (das für "es ist noch Platz frei") und die zugehörige
Interrupt-Anforderung. Kann aber auch sein, daß beim AVR so etwas
flankenabhängig ist, also die o.g. Bedingung des Leerseins bzw. Platz
leer nicht statisch die Bits steuert, sondern das Leerwerden oder das
Platz leer Werden zum Setzen der Bits führt. Das liest man besser im
Manual nach.
b) wenn es nix zu senden gibt, muß man die Interrupt-Anforderung
sperren, denn ohne dies würde der U(S)ART ständig nach Nachschub
brüllen. Logischerweise sollte man immer dann, wenn ein Byte gesendet
werden soll und deshalb zuerst in den treiberinternen Ringpuffer
gesteckt worden ist, diese Interrupt-Sperre aufheben. Dann kommt ein
Interrupt, der das Byte abholt und in den Sender steckt. Wenn
anschließend nix zu senden ist, dann siehe a)
Bei einigen Cortexen (LPC...) hab ich allerdings schon gesehen, daß dort
der UART erst dann tatsächlich zu senden und Interrups anzufordern
beginnt, wenn wenigstens 2 Bytes ab Initialisierung in den Sender
gestopft wurden. Aber bei den AVR würde ich so eine Sonderlocke nicht
erwarten.
W.S.
Der Kommentar stimmt nicht mit dem überein, was du da wirklich machst.
Und zwar erlaubst du Interrupts, aber zugleich schaltest zu alle anderen
Bits in udrie0 aus.
Im Simulator bleibt TXEN0 aber trotzdem auf 1, das wundert mich, ist
vielleicht ein Bug im Simulator (?).
Gleicher Fehler in der ISR:
1
ldi r16, (0<<udrie0) ; stop transmittion
2
sts ucsr0b, r16
Da stoppst du nicht nur die Übertragung sondern alles. Du setzt alle
Bits auf 0.
Außerdem enabelst du in der Initialisierung rxcie0 und txcie0, aber es
gibt keine zugehörigen Interrupt-Handler.
Ich habe es mal korrigiert:
Sobald das Programm in der finalen "hreset_loop" landet wird die ISR
aufgerufen. Nachdem zwei Bytes in UDR0 abgelegt wurden, geht das UDRE0
Flag brav auf 0.
Das kann man so im Debugger/Simulator ganz bequem durch steppen. Habe
ich mit dem AVR Studio 4.19 getestet.
Hallo Stefan,
Tolle Arbeit.
Wenn ich die Zeile durch
ldi r16, (1<<rxen0)|(1<<txen0)|(1<<rxcie0)|(1<<txcie0)|(1<<udrie0)
sts ucsr0b, r16 ; starte übertragung
dann klappt es.
Habe wie du richtig gesagt hast alle Bits auf 0 gesetzt.
Danke Dir!!!
Wenn die Zeile wirklich nur "starte übertragung" machen soll, dann
solltest du zuerst das Register lesen, nur das eine relevante Bit ändern
und es dann wieder zurück schreiben.
Auf viele Register kann man auch Bit Operationen (SBI und CBI) anwenden.
Ich habe nicht im Kopf, ob ucsr0b dazu zählt.
"... For I/O registers located in extended I/O map, "IN", "OUT", "SBIS",
"SBIC", "CBI", and "SBI" instructions
must be replaced with instructions that allow access to extended I/O.
Typically "LDS" and "STS" combined with "SBRS", "SBRC", "SBR", and
"CBR"."
Leider nein.
W.S. schrieb:> Bei einigen Cortexen (LPC...) hab ich allerdings schon gesehen, daß dort> der UART erst dann tatsächlich zu senden und Interrups anzufordern> beginnt, wenn wenigstens 2 Bytes ab Initialisierung in den Sender> gestopft wurden. Aber bei den AVR würde ich so eine Sonderlocke nicht> erwarten.
Also, so ist es weder beim AVR8 noch bei den Cortexen. Gesendet wird
natürlich sofort. Aber der Interrupt für die Sendeanforderung wird erst
dann gelöscht, wenn die Hardware "voll" ist. Auch das ist identisch und
logisch.
Der Ablauf ist so:
1) Initial gibt es nichts zu versenden, deswegen ist natürlich der
Interrupt für die Sendeanforderung aktiv. Denn der zeigt an, dass aus
Sicht der Hardware Daten zum Versenden benötigt werden. Weil man aber
hier aus Sicht des Treibers noch keine Daten zum Versenden hat, darf
also die Interruptanforderung noch nicht zum Interrupt führen, denn die
ISR hätte nix, womit sie die Hardware bedienen könnte.
2) Man schreibt ein Byte zum Treiber. Der kann das direkt in die
Hardware schreiben. Es wird dort unmittelbar zum Ausgabe-Shiftregister
durchgeleitet und seine Ausgabe beginnt.
Die Interruptanforderung der Hardware bleibt aber aktiv, denn das
Buffer-Register der Hardware bleibt leer, sie "benötigt" aus ihrer Sicht
also weiterhin Ausgabe-Daten. Wenn aus Sicht des Treiber aber keine
weiteren Bytes zur Ausgabe vorliegen, darf der Interrupt in dieser
Situation immer noch nicht freigeschaltet werden.
3) Noch ein Byte kommt zur Ausgabe. Der Treiber kann auch dies
unmittelbar an die Hardware durchreichen, es landet in deren
Buffer-Register. Jetzt geht erstmalig das Interrupflag aus, weil die
Hardware jetzt "voll" ist, mehr kann sie nicht buffern. Aber der Treiber
darf immer noch nicht den Interrupt scharf schalten, denn es liegen ja
keine weiteren Daten zum Versenden vor, die an eine ISR an die Hardware
verfüttern könnte.
Ab hier wird es "komplizierter", denn es kommt darauf an, was wann an
Daten nachkommt. Nehmen wir erst einmal den Fall: keine. Dann ist der
Drops hier für den Treiber gelutscht, denn alles, was zu versenden war,
ist schon in der Hardware gelandet und die wird es ausgeben, zuerst das
Shift-Register, dann wird das erneut aus dem Buffer-Register gefüllt
(dabei geht dann das UDRE-Flag wieder auf aktiv) und letztlich wird auch
das zweite Byte komplett ausgegeben (was dann zum Schluss das
TXC-Interruptflag setzt).
Kommt aber ein drittes Byte zur Ausgabe beim Treiber an, bevor das erste
komplett ausgegeben ist, dann tritt erstmalig die Situation ein, dass
entweder der Treiber "stallen" muss (bei synchroner Ausgabe) oder halt
der Interrupt erlaubt werden muss und die Daten in einen Softwarebuffer
geschrieben werden müssen, aus dem sie später durch die ISR an die
Hardware verfüttert werden (asynchrone Ausgabe).
Bei den Cortexen (und überhaupt bei jeder UART mit Double-Buffering)
geht das im Prinzip ganz genau so, bloß die Bits und Interrupts heißen
halt immer irgendwie anders.
c-hater schrieb:> oder halt> der Interrupt erlaubt werden muss und die Daten in einen Softwarebuffer> geschrieben werden müssen, aus dem sie später durch die ISR an die> Hardware verfüttert werden (asynchrone Ausgabe).
Nach diesem Abschnitt fehlt natürlich noch ein sehr wichtiger, nämlich
der, in dem die UDRE-ISR auf die Situation trifft, dass ihr
Software-Buffer gerade leergelaufen ist. In dieser Situation muss die
ISR natürlich dann den UDRE-Interrupt wieder "entschärfen".