Guten Abend an alle Forums Mitglieder, seit ein paar Tagen schon schleppe ich mich mit einem Uart Problem herum, welches ich irgendwie nicht gebacken bekomme. In meinem Programm soll nach Erledigen des eigentlichen Jobs die CPU für 4s zum Schlafen gelegt werden, nach dem Aufwachen den nächsten Job erledigen und dann wieder Heia machen usw. Dies ist zwar mein Hauptproblem aber auf dem Weg dahin kommen mir einige Dinge in die Quere. Um den Programmfluß zu überprüfen, lass ich den Uart in den verschiedenen Bereichen des Programmes einen Buchstaben senden nach dem Motto: 'Hier bin ich gerade'. Da aber z.B. die Uart Bibliothek von Peter Fleury Interrupt gesteuert ist (was ich übersetze mit: 'Ich sende das nächste Byte wenn gerade mal wieder Platz im UDR Register frei wird und in der Zwischenzeit tu' ich was anderes') habe ich dies durch meine eigene Funktion ersetzt die wirklich warten soll bis das Byte raus ist. Dies soll durch Übergabe des zu sendenden Bytes an 'UDR' und anschließendem Warten bis (wirklich) das TXC0 bzw. UDRE0 Bit gesetzt wird, erreicht werden. Das Dumme ist nun dass es zwar grundsätzlich funktioniert aber wenn ein Programmsprung ansteht(?) Buchstaben verloren gehen bzw. falsch gesendet werden. So sollte im angefügten Code in der Endlosschleife die Buchstabenfolge: 'BCDXYZE' enstehen, was ich hingegen wirklich erhalte ist alle 4s 'BC4ZE', d.h anstatt der Teilsequenz 'DXY' erhalte ich schlicht '4', selbst wenn ich noch zusätzlich explizit überprüfe dass UDR0 wieder 0 ist (durch while(UDR0 != 0) {}) Der Uart ist mit 2 Stop bits konfiguriert, noch schlimmer wird's wenn ich nur ein Stop-Bit setze was ich auch nicht recht zu interpretieren weiss. Die anderen Werte sind: CPU Atmega644A, BaudenQuarz: 11.0592MHz, Baud rate: 9600, FTDI Chip zur Verständigung mit dem Compi, AVR-GCC Compiler, die Fuse Bits sind auf maximale Aufwachzeit gesetzt: Low: FF, High: D1, Extended FF, so dass die CPU nicht schlaftrunken irgendwo hinstolpert. Hätte jemand 'ne Idee, i.B. dazu wie ich die CPU dazu zwingen kann so lange nichts anderes zu tun (um sicherzustellen) bis/dass jeder Buchstabe wirklich (korrekt) gesendet wird ? Danke: Hermann
Bei 9600 Baud passiert sowas eigentlich nicht. Was ist denn da auf der anderen Seite des UART? Etwa so ein UART-USB Wandler unserer asiatischen Freunde? Die Vorgehensweise mit den TXC und UDR Flags ist jedenfalls die richtige.
:
Bearbeitet durch User
Hermann E. schrieb: > Motto: 'Hier bin ich gerade'. Da aber z.B. die Uart Bibliothek von Peter > Fleury Interrupt gesteuert ist (was ich übersetze mit: 'Ich sende das > nächste Byte wenn gerade mal wieder Platz im UDR Register frei wird und > in der Zwischenzeit tu' ich was anderes') was mal eigentlich sehr sinnvoll und komfortabel ist! > habe ich dies durch meine > eigene Funktion ersetzt die wirklich warten soll bis das Byte raus ist. > Dies soll durch Übergabe des zu sendenden Bytes an 'UDR' und > anschließendem Warten bis (wirklich) das TXC0 bzw. UDRE0 Bit gesetzt > wird, erreicht werden. Kann man machen, waber warum? Man kann genausogut warten, bis der FIFO der Lib leer ist und das letzte Zeichen gesendet wurde. Aber wenn man das schon selber machen will, dann richtig! So nicht!
1 | void SendByte1(char byte) |
2 | {
|
3 | UDR0 = byte ; |
4 | while (( UCSR0A & (1<<UDRE0)) == 0){}; |
5 | // while(UDR0 != 0) {}
|
6 | }
|
Denn hier wird ja nur gewartet, bis UDRE wieder leer ist! Das ist aber NICHT der Zeitpunkt, an dem das Byte WIRKLICH übertragen wurde! Der UART hat einen Sendepuffer, eben UDR. Der wird nach dem Schreiben sofort ins Schieberegister des Senders kopiert und UDR ist dann wieder frei, sprich, UDRE geht auf 1. Du hast es doch selber gesagt, du willst auf TXC warten. Also muss man TXC VORHER löchen und dann auf das Setzen warten! Eher so
1 | void SendByte1(char byte) |
2 | {
|
3 | UCSR0A = (1<<TXC0); // TXC0 loeschen |
4 | UDR0 = byte ; |
5 | while (!( UCSR0A & (1<<TXC0))); // warte auf Ende der Datenübertragung |
6 | }
|
Matthias S. schrieb: > Die Vorgehensweise mit den TXC und UDR Flags ist jedenfalls die > richtige. Aber nur in der Theorie. Dazu müßte man TXC in der Praxis auch mal auswerten . . .und vorher mal löschen . . .
Das geht so nicht. Im SendByte2 schreibst du einfach was ins Senderegister ohne nachzusehen ob das schon gesendet wurde. Die Watchdog ISR nimmt keine Rücksicht darauf, ob das Sendbyte1 fertig ist oder nicht. Wobei ich mit den ganzen Registern die du da prozessorspezifisch verwendest keine Ahnung hab was was ist.
Hermann E. schrieb: > Das Dumme ist nun dass es zwar grundsätzlich funktioniert aber wenn ein > Programmsprung ansteht(?) Buchstaben verloren gehen bzw. falsch gesendet > werden. Dann sende doch mal zum Test eine paaaaaaaar Buchstaben mehr und prüfe ob immer nur diese letzten beiden verloren gehen? Evtl. ist es auch ein Unterprogramm höherer Priorität, das Dir den Ast absägt?
Ich würde beim Senden zuerst schauen ob das Senderegister leer ist und dann das nächste Zeichen senden. Das geht dann auch schneller als andersrum. Denn so wird das Zeichen dann im Hintergrund verschickt, wozu auch die ganze Hardware ist.
Erst einmal vielen dank für eure Mühe/Rückmeldung ! Punkt für Punkt: @Matthias: Der Wandler ist zwar 'made in China', der Chipsatz aber ein FTDI232, ich arbeite ausschließlich mit diesem... //----------------------------------------------------- B. Falk: "Dazu müßte man TXC in der Praxis auch mal auswerten . . .und vorher mal löschen . . ." Aber genau das tue ich doch in der Fkt "SendByte2(char byte)" (?). Das Ergebnis ist genau das Gleiche (ausprobiert !) //----------------------------------------------------- Nick Müller: "Im SendByte2 schreibst du einfach was ins Senderegister ohne nachzusehen ob das schon gesendet wurde. " Das verstehe ich nicht: Die zwei Terme: "( UCSR0A & (1<<UDRE0)" bzw. "((UCSR0A)|(1<<TXC0)))" dienen doch genau dazu zu überwachen ob UDR gesendet hat. Das Datenblatt meint doch: TXCn: This flag bit is set when the entire frame in the Transmit Shift Register has been shifted out and there are no new data currently present in the transmit buffer (UDRn). bzw: The UDREn Flag indicates if the transmit buffer (UDRn) is ready to receive new data. If UDREn is one, the buffer is empty, and therefore ready to be written. Selbst wenn ich noch explizit den "Füllstand" von UDE überprüfe (siehe auskommentierte Zeile: "while(UDR0 != 0) {}" in Send1 und Send2 ist das Ergebnis immer noch unverändert.
> UCSR0A &= ~(1<<TXC0); "The TXCn Flag bit is automatically cleared when a transmit complete interrupt is executed, or it can be cleared by writing a one to its bit location." > // while(UDR0 != 0) {} Es gibt eigentlich zwei UDR Register. Eines für die Senderichtung, das nur geschrieben werden kann, und eines davon völlig getrenntes für die Empfangsrichtung. In der Sendefunktion das Empfangsregister zu testen ist recht wahrscheinlich nicht das, was du vorhast.
:
Bearbeitet durch User
Nick M. schrieb: > schauen ob das Senderegister leer ist Ich wüßte jetzt nicht so genau wie die Gegenstelle reagiert. Evtl. besteht da auch ein zeitliches Problem am anderen Ende? Testweise Umleitung von Ausgang auf Eingang wäre evtl. der nächste Schritt?
Hermann E. schrieb: > "Im SendByte2 schreibst du einfach was ins > Senderegister ohne nachzusehen ob das schon gesendet wurde. " > Das verstehe ich nicht: Die zwei Terme: "( UCSR0A & SendByte2 wird von einer ISR aufgerufen! Die kann jederzeit aufgerufen werden, auch wenn gerade SendByte1 in der Warte-Schleife ist und das Senderegister noch nicht (ganz) leer ist. Du musst zuerst nachsehen ob das Senderegister leer ist und dann senden. Und das sinnvollerweise in beiden SendByte. So wie du es jetzt machst ist es nochdazu ein blocking send, was nicht sehr sinnvoll ist.
Hermann E. schrieb: > B. Falk: > "Dazu müßte man TXC in der Praxis auch mal auswerten . . .und vorher > mal löschen . . ." > Aber genau das tue ich doch in der Fkt "SendByte2(char byte)" (?). VORHER löschen! >Das > Ergebnis ist genau das Gleiche (ausprobiert !) Quark.
@ Ak Die Aussage: [This flag bit is SET when the entire frame in the Transmit Shift Register has been shifted out ] "or it can be cleared by writing a one to its bit location." ist natürlich richtig, habe ich glatt versemmelt, Danke für den Hinweis. Leider hat aber auch die Umkehrung von: while (( UCSR0A & (1<<UDRE0) && ((UCSR0A)|(1<<TXC0))) == 0){}; UCSR0A &= ~(1<<TXC0); in while (( UCSR0A & (1<<UDRE0) == 0) && ((UCSR0A)|(1<<TXC0) != 0) ){}; UCSR0A |= (1<<TXC0); das Problem nicht gelöst. //-------------------------------------------------------- Nun zu "Es gibt eigentlich zwei UDR Register" Das war mir bislang so nicht klar, danke für den Hinweis! Vielleicht liegt ja sogar hier der Hund begraben weil ich im entsprechenden Abschnitt folgendes sehe: " When data is written to the transmit buffer [UDR], and the Transmitter is enabled, the Transmitter will load the data into the Transmit Shift Register when the Shift Register is empty. Then the data will be serially transmitted on the TxDn pin. D.h. wenn ich das so richtig verstehe wird der Inhalt vom "transmit buffer" in das "Transmit Shift Register" geschrieben (also zwei Buffer !) und dann gesendet. Dies könnte vielleicht zumindest ein Timing Problem erzeugen da ja mit Übergabe des Bytes von einem zum anderen Register das Byte noch nicht 'raus' ist und immer noch gesendet werden muß, also verspätet wird. Ps: hab inzwischen den RS232 Wandler gewechselt, das Problem ist aber immer noch da.... Muß jetzt selbst in die Heia, morgen (Abend) geht's weiter... Nochmals Danke: Hermann
Hermann E. schrieb: > Leider hat aber auch die Umkehrung von: > > while (( UCSR0A & (1<<UDRE0) && ((UCSR0A)|(1<<TXC0))) == 0){}; > > UCSR0A &= ~(1<<TXC0); > > in > > while (( UCSR0A & (1<<UDRE0) == 0) && ((UCSR0A)|(1<<TXC0) != 0) ){}; > > UCSR0A |= (1<<TXC0); > > das Problem nicht gelöst. Bis du merkbefreit? Oder kennst du die Bedeutung des Worts VORHER nicht? Beitrag "Re: Uart sendet falsch wenn er gestresst wird" Und nimm nur EINE Sendefunktion, nämlich SenByte1(). Der Rest deines Programms ist auch rein konzeptionell als auch vom Aufbau her eher fragwürdig. Egal, man schafft es trotzdem, die Daten komplett zu senden, bevor man in den Sleep Mode geht.
ja, und den Ausdruck in der Bedingung der While-Schleife so klammern, wie man ihn ausgeführt haben möchte. Sonst wird es keine "Warteschleife".
Die UART ist nicht reentrant, wenn 2 Tasks (Main, Interrupt) darauf zugreifen, muß es schief gehen. Lösung 1: Die Interrupttask schreibt in einen Puffer, die Maintask sendet ihn, wenn die UART frei ist. Lösung2: Der UART Interrupt verwaltet 2 Puffer, je einen für jede Task und sendet sie abwechselnd.
@Nick: "So wie du es jetzt machst ist es noch dazu ein blocking send, was nicht sehr sinnvoll ist" Das war ja gerade die Vorgabe, ich wollte ja den Prozessablauf solange stoppen bis das Byte gesendet wurde ! //--------------------------------------------------------------- @ B. Falk: Danke, das war i.d.T. die Lösung, also: void SendByte(char byte) { UCSR0A |= (1<<TXC0); UDR0 = byte ; while (!( UCSR0A & (1<<TXC0))); } blockiert, wie erhofft, solange den Programmablauf bis das Byte [wirklich] raus ist. Deine wirklich hilfreiche und kompetente Antwort ist zwar etwas "brummelig" bei mir angekommen, aber die Kompetenz ist unleugbar vorhanden. Unsere Posts haben sich vorgestern Abend gekreuzt so dass ich deinen zweiten Kommentar noch nicht gelesen hatte. Nochmals Daumen hoch an alle Beteiligten, das hat mir echt weiter geholfen. Grüße aus dem warmen Südfrankreich: Hermann
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.