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?
c-hater schrieb: > wenn die synchrone Befriedigung der Ausgabe-Anforderung nicht > mehr möglich ist. Geht das auch verständlich?
PCB schrieb: > Geht das auch verständlich? Sicher. Wieviel bezahlst du für Nachhilfe-Lehrer pro Stunde?
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.
Und warum werden die Daten dann nicht automatisch gesendet, wenn ich das Register beschreiben kann ??
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.
1 | .include "m2560def.inc" |
2 | .listmac |
3 | |
4 | .org 0x000 rjmp hreset ; 01 - External Pin, Power-on Reset, Brown-out Reset, Watchdog Reset and JTAG AVR Reset |
5 | .org 0x0034 rjmp huartdre0 ; 27 - USART0 Data Register Empty |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | hreset: |
12 | |
13 | ; init stack ptr |
14 | ldi r16, high(ramend) |
15 | out sph, r16 |
16 | ldi r16, low(ramend) |
17 | out spl, r16 |
18 | |
19 | |
20 | ; uart0 setup |
21 | ldi r16, 0 |
22 | sts ubrr0h, r16 |
23 | |
24 | ldi r16, 59 |
25 | sts ubrr0l, r16 |
26 | |
27 | ldi r16, (1<<rxen0)|(1<<txen0)|(1<<rxcie0)|(1<<txcie0) |
28 | sts ucsr0b, r16 |
29 | |
30 | ldi xh, high(sram_start) |
31 | ldi xl, low(sram_start) |
32 | clr r16 |
33 | hreset_memclr: |
34 | st x+, r16 |
35 | cpi xl, low(ramend) |
36 | brne hreset_memclr |
37 | cpi xh, high(ramend) |
38 | brne hreset_memclr |
39 | |
40 | sei ; interrupts global zulassen |
41 | |
42 | hreset_loop:; infinitive loop |
43 | ldi r16, (1<<udrie0) ; initiate transmittion |
44 | sts ucsr0b, r16 |
45 | rjmp hreset_loop |
46 | |
47 | |
48 | |
49 | reti |
50 | ;-------------- |
51 | huartdre0: |
52 | ldi r16, (0<<udrie0) ; stop transmittion |
53 | sts ucsr0b, r16 |
54 | |
55 | sts UDR0, r16 |
56 | sts UDR0, r16 |
57 | |
58 | reti |
und einmal das andere Beispiel:
1 | .include "m2560def.inc" |
2 | .listmac |
3 | |
4 | .org 0x000 rjmp hreset ; 01 - External Pin, Power-on Reset, Brown-out Reset, Watchdog Reset and JTAG AVR Reset |
5 | |
6 | hreset: |
7 | |
8 | ; init stack ptr |
9 | ldi r16, high(ramend) |
10 | out sph, r16 |
11 | ldi r16, low(ramend) |
12 | out spl, r16 |
13 | |
14 | |
15 | ; uart0 setup |
16 | ldi r16, 0 |
17 | sts ubrr0h, r16 |
18 | |
19 | ldi r16, 59 |
20 | sts ubrr0l, r16 |
21 | |
22 | ldi r16, (1<<rxen0)|(1<<txen0)|(1<<rxcie0)|(1<<txcie0) |
23 | sts ucsr0b, r16 |
24 | |
25 | ldi xh, high(sram_start) |
26 | ldi xl, low(sram_start) |
27 | clr r16 |
28 | hreset_memclr: |
29 | st x+, r16 |
30 | cpi xl, low(ramend) |
31 | brne hreset_memclr |
32 | cpi xh, high(ramend) |
33 | brne hreset_memclr |
34 | |
35 | sei ; interrupts global zulassen |
36 | |
37 | hreset_loop:; infinitive loop |
38 | sts UDR0, r16 |
39 | sts UDR0, r16 |
40 | rjmp hreset_loop |
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.
diese Zeilen finde ich merkwürdig:
1 | ldi r16, (1<<udrie0) ; initiate transmittion |
2 | sts ucsr0b, r16 |
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:
1 | .include "m2560def.inc" |
2 | .listmac |
3 | .org 0x000 rjmp hreset ; 01 - External Pin, Power-on Reset, Brown-out Reset, Watchdog Reset and JTAG AVR Reset |
4 | .org 0x0034 rjmp huartdre0 ; 27 - USART0 Data Register Empty |
5 | hreset: |
6 | ; init stack ptr |
7 | ldi r16, high(ramend) |
8 | out sph, r16 |
9 | ldi r16, low(ramend) |
10 | out spl, r16 |
11 | ; uart0 setup |
12 | ldi r16, 0 |
13 | sts ubrr0h, r16 |
14 | ldi r16, 59 |
15 | sts ubrr0l, r16 |
16 | ldi r16, (1<<rxen0)|(1<<txen0)|(1<<udrie0) |
17 | sts ucsr0b, r16 |
18 | ldi xh, high(sram_start) |
19 | ldi xl, low(sram_start) |
20 | clr r16 |
21 | hreset_memclr: |
22 | st x+, r16 |
23 | cpi xl, low(ramend) |
24 | brne hreset_memclr |
25 | cpi xh, high(ramend) |
26 | brne hreset_memclr |
27 | sei ; interrupts global zulassen |
28 | |
29 | hreset_loop:; infinitive loop |
30 | rjmp hreset_loop |
31 | |
32 | huartdre0: |
33 | sts UDR0, r16 |
34 | sts UDR0, r16 |
35 | reti |
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".
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.