Hi! Ich habe ein Problem mit dem Code im Anhang, geschrieben ist er für ein ATMega32, Interrupt gesteuerte RS232 Kommunikation. Das Programm sollte einfach nur das empfangene Zeichen wieder zurück schicken und zwar ein Mal (Echo), aber wenn ich ein Zeichen zum Controller schicke fängt er an andauernd das Zeichen zurück zu schicken, bis ich ein neues Schicke, dann schickt er andauernd das neue Zeichen. Wo ist mein Fehler? Hat jemand vielleicht mehr Informationen zu Interrupt gesteuerter RS232 Kommunikation, als im Tutorial oder im Datenblatt? Vielleicht ein Programmablaufplan, fertiger Code oder so?
Ich kann kein C... Aber: Der Sende-Interrupt tritt auf, wenn das Byte raus ist, also ein weiteres Byte gesendet werden könnte. Dies ist sehr vorteilhaft, wenn man Byres aus einem Ringbuffer in "einem Rutsch" im Hintergrund (Interrupt) senden möchte. Da du nach Empfang eines Bytes dieses Byte "nur einmal" zurücksenden willst, solltest du auf den Sende-Interrupt verzichten. Stattdessen einfach das UDRE-Bit prüfen bis UDR leer ist und dann das Byte in UDR schreiben. Fertig... Wenn du das nicht in der RX-ISR machen willst (ist ja nicht "zukunftsfähig"...), dann solltest du dir ein Flag (Boolean) einrichten, welches in der RX-ISR gesetzt wird. Die Mainloop pollt dann (unter anderem, denn das Prog soll ja später sicherlich noch andere Jobs erledigen) dieses Flag bis ein neues Byte eingetroffen ist (Flag gesetzt). Ist dies der Fall, springt man zur TX-Routine (später dann mal zu der Routine, die das Byte "verarbeiten" soll), in der man das Byte sendet und das Flag löscht (der "Job" ist ja erledigt...). Mit mehreren dieser Flags kann man mehrere verschiedene Jobs "anmelden", kaum ein vernünftiges Programm kommt ohne diese Flags aus. Sollte man sich also unbedingt angewöhnen. ...
@Hannes: Das mit dem Empfangen ist ja kein Problem, und dein Vorschlag mit dem Flag habe ich auch für die Zukunft so vorgesehen, nur mit dem Interrupt gesteuerten Senden hab ich noch Probleme. Nachdem ich den TX-Interrupt auslöse (in der Funktion send()) wird diese ja ausgeführt gleichzeitig müsste der Controller ja das UDRE-Flag löschen da ich ja Daten in UDR einschreibe, somit dürfte der Interrupt nicht wieder ausgeführt werden, oder verstehe ich das Datenblatt da falsch. "When the Data Register empty Interrupt Enable (UDRIE) bit in UCSRB is written to one, the USART Data Register Empty Interrupt will be executed as long as UDRE is set. UDRE is cleared by writing UDR. When interrupt-driven data transmission is used, the DATA Register Empty Interrupt routine must either write new data to UDR in order to clear UDRE or disable the Data Register Empty Interrupt, otherwise a new interrupt will occur once the interrupt routine terminates." Ich hab ja sogar beides gemacht, so dass eigentlich kein weiterer Interrupt auftreten dürfte.
Der Interrupt tritt meiner Meinung nach auf, sobald UDR leer ist, also das eben gesendete Byte abgearbeitet ist. Daher wird das Byte (oder das Nächste, sofern eingetroffen) solange gesendet, bis der Interrupt wieder deaktiviert wird. Daher würde ich zum Senden eines einzelnen Bytes keinen Int nehmen. Wenn man einen String senden möchte (z.B. Ringbuffer voller Bytes), dann aktiviert die Routine, die den Ringbuffer füllt, den UDRE-Interrupt. Die UDRE-ISR prüft nun vor jedem Senden die Pointer des Ringbuffers und deaktiviert den Interrupt, sobald der Lesepointer den Schreibpointer eingeholt hat, also sobald der Ringbuffer leer ist. Auf diese Art kann man ganze Strings senden (man füllt ja nur den Ringbuffer, auch FIFO genannt), ohne auf das Timing der UART Rücksicht zu nehmen. Das einentliche Senden geschieht dann in der ISR, bis bei leerem Buffer der Int deaktiviert wird. Verstanden? Wenn du deine Tests (Echo-Betrieb) unbedingt mit Sende-Interrupt machen möchtest, dann sollte dein Empfang den Sende-Int aktivieren und die Senderoutine nach getaner Arbeit (schreiben in UDR) den Int wieder deaktivieren, damit bei leerem UDR (Byte also fertig gesendet) nich nochmal gesendet wird. Nochmal anders betrachtet: Ein Interrupt ist eine Reaktion auf ein Ereignis von außen. Da das Senden eines Bytes kein äußeres Ereignis ist, sondern die Entscheidung des laufenden Programms, ist hier ein Interrupt fehl am Platze. Anders beim Ringbuffer. Das äußere Ereignis ist das "Leersein" des UDR, also die "Meldung", dass der "Transmitter" bereit ist, ein neues Byte zu senden. Man füllt nun den Ringbuffer mit Daten und gibt den UDRE-Interrupt frei. Nun sorgt das Ereignis "UDR leer" dafür, das die ISR aufgerufen wird. In dieser prüft das Programm, ob überhaupt noch Daten zum Senden anliegen und entscheidet danach, ob es das nächste Byte ausgibt oder ob es (weil nix mehr da ist) den Interrupt sperrt, um Doppelausgaben zu vermeiden. Ich hoffe, es war einigermaßen verständlich formuliert (etwas überspitzt war es ja auch...). Frohes Schaffen noch... ...
Ja hab dich verstanden, allerdings steht es dann wohl falsch im Datenblatt. Es wundert mich dass es dann noch keiner bisher gemerkt/korrigiert hat. Vielleicht weiß jemand anderes noch etwas zu diesem Problem/Thema? @Hannes: Aber trotzdem Danke!
>>Es wundert mich dass es dann noch keiner bisher
gemerkt/korrigiert hat.
ist ja auch kein fehler?
Wenn du den Versand (eines Bytes) unbedingt mit einer ISR machen willst, dann könntest du es so machen: SIGNAL(SIG_UART_DATA){ UCSRB &= ~(1<<UDRIE); // UDRIE-Bit löschen UDR = txData; // Byte senden } Das ist aber nicht wirklich schön... Mach es dann lieber so: SIGNAL(SIG_UART_RECV){ rxData = UDR; UDR = rxData; } Da du sowieso immer nur ein Byte als Echo verschicken willst, kann man das auch gleich in der Empfangs-ISR machen. Die Wahrscheinlichkeit, dass sich noch ein Byte im Sendepuffer befindet, ist sehr gering.
> Da du sowieso immer nur ein Byte als Echo verschicken willst, kann > man das auch gleich in der Empfangs-ISR machen..... Ich nahm allerdings an, dass das Echo nur ein Provisorium ist und der MC später mal einen intelligenteren Dialog mit dem PC führen soll (nicht nur als Papagei). Deshalb auch meine Hinweise in Richtung Ringbuffer zum Senden (ist zum Empfang natürlich auch sehr sinnvoll!). AVR-U(S)ART hat nun leider mal kein FIFO. ...
Hannes, da sein Programm so nicht optimal funktioniert, war das nur ein Lösungsvorschlag. Natürlich ist es sinnvoll, Ringpuffer etc. zu verwenden. Ein Mini-FIFO haben AVR schon: Sie können 2 Bytes zwischenspeichern (zwei Zugriffe auf UDR...) Das einfachste wäre wohl erst mal ein Flag zu setzen und gleichzeitig das UDR in ein Register zu schreiben. Das wäre ja die Vorstufe zum Ringspeicher...
Rahul, richtig... Wenn ich das Blockschaltbild richtig interpretiere, hat RX neben dem Shiftregister zwei Bytes Buffer, TX jedoch nur ein Byte. Dass sich beide (physikalisch völlig getrennte) Buffer über eine Adresse (udr) ansprechen lassen, ist ja klar, es sind schließlich Beides "Einbahnstraßen"... Ich ging bisher davon aus, dass RX auch nur ein Byte puffern kann, danke für die Aufklärung... Bit- & Bytebruch... ...HanneS...
> "When the Data Register empty Interrupt Enable (UDRIE) bit in > UCSRB is written to one, the USART Data Register Empty Interrupt > will be executed as long as UDRE is set. UDRE is cleared by > writing UDR. When interrupt-driven data transmission is used, the > DATA Register Empty Interrupt routine must either write new data > to UDR in order to clear UDRE or disable the Data Register Empty > Interrupt, otherwise a new interrupt will occur once the interrupt > routine terminates." > > Ich hab ja sogar beides gemacht, so dass eigentlich kein weiterer > Interrupt auftreten dürfte. Das hast Du vielleicht vor gehabt, aber gemacht hast Du es nicht :-) > > SIGNAL(SIG_UART_DATA){ > UDR = txData; > UCSRA |= (0<<UDRE); > } So kann man kein Bit loeschen (mit bitweisen ODER). ODER: Bit setzen UND: Bit loeschen Um ein Bit zu loeschen, brauchst Du eine Maske, die an allen Stellen, die nicht geaendert werden sollen eine 1 enthaelt und an allen Stellen die geloescht werden sollen eine 0. Die kriegst Du mit dem ~ Operator, der alle Bits umdreht: UCSRA &= ~( 0<< UDRE ); Jetzt wird das Bit geloescht. Mit UCSRA |= (0<<UDRE); hat sich UCSRA kein bischen veraendert. Denn Alter Wert Neuer Wert ***************************** 1 ODER 0 ist 1 0 ODER 0 ist 0
Müsste aber heißen UCSRA &= ~( 1<< UDRE ); Sonst passiert mit dem Register gar nix Stefan
So der Echo-Teil funktioniert jetzt, nachdem ich einige Fehler beseitigt habe, Code ist im Anhang. Mich würde noch interessieren wann eigentlich der Transmit Complete Interrupt ausgelöst wird, wie stellt der µC fest wann ein ganzes Frame übertragen wurde?
"wann eigentlich der Transmit Complete Interrupt ausgelöst wird" Wenn das Buffer-Register leer ist und das Shift-Register leer läuft. Erfahrungsgemäss allerdings noch während des Stopbits. Braucht mal normalerweise nur bei Halfduplex, z.B. RS485, wenn nach der Übertragung der Transmitter abgeschaltet werden muss. Weshalb man da erst noch ein bischen warten muss, sonst killt man das Stopbit.
So hatte ich das auch verstanden, ich hab dann auch mal meinen Echo-Betrieb abgewandelt, indem ich in die Transmit-Complete-Interrupt-Routine eine Endlosschleife eingebaut habe, aber da ist er wohl nie reingesprungen (weg optimiert war die Endlosschleife nicht). Im AVR-Studio-Simulator hab ich es auch noch nicht geschafft, mmmmh mache wohl irgendwas falsch...
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.