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.
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.
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.