Forum: Projekte & Code AVR UART senden mit UDRE Interrupt


von Frank J. (frajo)


Angehängte Dateien:

Lesenswert?

Hier mal ein Beispiel wie man mit dem UDRE Interrupt einfach Daten im 
Hintergrund versenden kann.
Der UDRE Interrupt wird immer dann ausgelöst, wenn das Datenregister UDR 
leer ist. Er darf nur eingeschaltet werden wenn Daten zum Senden 
vorhanden sind und muss, wenn alle Daten versand worden sind, wieder 
abgeschaltet werden. Gegenüber dem TXC Interrupt hat er den Vorteil, das 
Stop und Startbit "Rücken an Rücken" übertragen werden (Danke Jörg, für 
die Formulierung).

Was braucht man dazu:

Einen Puffer, in den das Hauptprogramm schnell Daten schreiben kann und 
aus dem der UART gemütlich die Daten versenden kann.

Zwei Zeiger. Einen zum Schreiben, damit das Hauptprogramm weiss, wohin 
mit dem nächsten Byte. Einen zum Lesen für die UART.

Zwei Routienen. Eine um in den Puffer die Daten zu schreiben und eine 
ISR um aus dem Puffer die Daten zu lesen.

Etwas zusätzlichen Code um auch Strings bequem aus dem RAM oder FLASH zu 
senden.

Wie im make file zu sehen, ist die Implementierung für einen Mega32. Für 
andere Typen sind evtl. Registernamen anzupassen.

Wie funktioniert das nun.

Nach der Initialisierung des UART (BAUD Rate, Format 8N1, Sender ein) 
werden die Interrupts global frei gegeben.
UartPutC prüft ob noch Platz im Puffer ist. Das ist die einzigste Stelle 
an der evtl. gewartet wird. Je nach Menge der zu übertragenen Daten muss 
die Puffergröße angepasst werden (TX_BUFFER_SIZE in uart.h). Das 
übergebene Byte kommt in den Puffer und der Schreibzeiger wird erhöht. 
Ist das Pufferende erreicht, geht es wieder am Anfang des Puffers los. 
Nun wird der UDRE Interrupt eingeschaltet.

UDRE ist leer. Also wird die Interruptroutiene aktiv. Sie prüft erst mal 
ob schon alle Daten versand worden sind. Dann muss sie sich selbst 
deaktivieren. Wenn nicht, erstes Byte ins Datenregister und Lesezeiger 
erhöhen. Ist der Zeiger am Ende vom Puffer angelangt, geht es wieder auf 
die erste Position.

In der main.c werden auf einen Schlag 44 Zeichen in den nur 32 Byte 
großen Puffer geschrieben. Alles wird aber fehlerfrei versand. Das 
Warten auf Platz im Puffer und die Zeigerverwaltung funktionieren. 
Bestimmt kann man die Zeigerverwaltung noch optimieren. Wer Ideen für 
einen effektiveren Ringpuffer hat, her damit.

Gruß
Frank, DH8JF

von Frank J. (frajo)


Lesenswert?

Oh, sehe gerade TxWrite in uart.c braucht nicht volatile zu sein, da sie 
ja in der Interruproutine nicht verändert wird.

von Thorsten (Gast)


Lesenswert?

Hallo,

ja, sehr effiziente Methode.
In Peter Fleurys UART Lib (http://jump.to/fleury) ist das auch so 
gemacht.
Es hakt nur, wenn man in einer ISR in den Buffer schreibt und der Buffer 
voll wird. Dann wird nix abgearbeitet, da die Interrupts ja deaktiviert 
sind und das putc wartet sich tot.



von Frank J. (frajo)


Lesenswert?

Thorsten wrote:
> Dann wird nix abgearbeitet, da die Interrupts ja deaktiviert
> sind und das putc wartet sich tot.

Ja, das stimmt. Ich habe mir abgewöhnt in ISR's irgend was großes zu 
machen. Da werden nur noch Flags gesetzt. Da die langsamen Sachen wie 
UART und auch TWI jetzt im Hintergrund mit Interruptsteuerung laufen, 
kann die Hauptschleife viel öfters die Flags testen und zeitnah 
reagieren. Füher hat meine Uhrzeit ISR die Zeit auf's Display 
geschrieben oder per UART versand. Heute wird nur noch ein Flag gesetzt 
und die Hauptschleife muß den Rest erledigen.

von Wolfram Q. (quehl)


Lesenswert?

ich hatte solch ein Programm in Assembler auch geschrieben, leider 
funktioniert das bei mir noch nicht. Kann ich evtl. die übersetzte 
Assembler Liste bekommen, da ich C nicht kann und Schwierigkeiten habe, 
das Programm zu lesen.
Wahrscheinlich liegt mein Fehler an den Zeigern. Die beiden Zeiger 
können nur gleich oder ungleich sein. Mehr konnte ich aus dem Programm 
auch nicht erkennen. Es gibt aber 3 Zustände des Puffers. Puffer voll, 
Puffer leer und Normalbetrieb. Wie können denn da die 3 Zustände den 2 
Zeigern zugeordnet werden?

mfg

Quehl ett gmx punkt de

von Frank J. (frajo)


Angehängte Dateien:

Lesenswert?

Die beiden Zeiger TxWrite und TxRead werden immer größer (TxWrite++) 
oder am Ende des Puffers wieder auf den Anfang gesetzt (TxWrite = 0;). 
Beim Schreiben in den Puffer muß Platz für das zu schreibende Byte sein. 
Also TxWrite != TxRead, so dass der Schreibzeiger ungleich (größer oder 
kleiner) TxRead ist. Die Zeiger werden immer nach einer Operation 
erhöht.
1
TxBuff[TxWrite++] = data;
kann man auch als
1
{
2
 TxBuff[TxWrite] = data;
3
 TxWrite++;
4
}
schreiben.
Wenn viele Daten in den Puffer geschrieben werden, kommt der 
Schreibzeiger irgendwann an das Ende des Puffers. Dann wird er wieder 
auf die erste Position gestetzt. Da der UART im Hintergrund sehr langsam 
arbeitet, wird der Schreibzeiger den Lesezeiger einholen. Damit dann 
keine Daten im Puffer überschrieben werden, wartet UartPutC bis der 
Lesezeiger eine Position weiter gerückt ist.
1
while(TxWrite != TxRead);
Eigentlich gehört hier noch ein leerer Block hin
1
while(TxWrite != TxRead)
2
   {
3
      ;
4
   }

Im Anhang mal mein main.lss File für einen Mega8 übersetzt.
Gruß
Frank.

von Frank J. (frajo)


Angehängte Dateien:

Lesenswert?

Hier noch mal ohne Optimierung. Vielleicht besser für Dich zu lesen.

von Quehl (Gast)


Lesenswert?

danke, ob ichs richtig verstehe, muß ich noch sehen.
Ich hatte so programmiert, daß Read ungleich write der normale 
Schreibzyklus ist und read gleich write ist voll. Das war wohl insofern 
verkehrt, weil in der Initialisierung read gleich write war und deshalb 
als voll erkannt wurde.
Ich sehe das daher so, daß Read gleich write leerer Puffer ist und write 
gleich Read-1 ist, dann ist voll. Nur das -1 hatte ich in Deinem 
Programm nicht gefunden.

mfg

von Frank J. (frajo)


Angehängte Dateien:

Lesenswert?

Ja, Du hast recht. In der obigen Version wird der Puffer nicht genutzt. 
Man muß es dann so machen wie Peter es in seiner UART Lib gezeigt hat. 
Ohne einen Dritten Zeiger geht es nicht. Meine Version im Anhang.
Gruß
Frank.

von Wolfram Q. (quehl)


Angehängte Dateien:

Lesenswert?

ich habe mal meine Datei angehängt, die die wesentlichen Informationen 
enthält. Geschrieben in Assembler. Geladen mit Ponyprog auf Pollinbord 
und getestet mit Hterm.

Wenn es funktionieren würde, würde ich dieses noch für geringfügig 
besser halten. Auf die Variable der Pufferlänge habe ich verzichtet, 
weil die Adressen zum Speichern und Lesen des Pufferspeichers sowieso 
geladen werden müssen.
Kann da jemand erkennen, warum mit Hterm nichts angezeigt wird?
Einen kleinen Fehler habe ich zwar noch gefunden, der dürfte aber das 
Senden nicht verhindern. (Push temp)

mfg

von Peter D. (peda)


Lesenswert?

Thorsten wrote:

> Es hakt nur, wenn man in einer ISR in den Buffer schreibt und der Buffer
> voll wird. Dann wird nix abgearbeitet, da die Interrupts ja deaktiviert
> sind und das putc wartet sich tot.

Da darf es ruhig haken, weil das nämlich völlig sinnfrei ist.

Ich hatte sowas mal versehentlich gemacht, also die UART ausm Main und 
nem Interrupt gefüttert.

Sieht dann schon recht lustig aus, wie der Main-Text und der 
Interrupt-Text miteinander vermanscht werden.

Blöd war nur, daß ich das nicht angesehen hatte sondern nem Parser zur 
Auswertung übergeben habe und der ist dann völlig ausgetillt.

Hat also einiges an Debugzeit verbraucht, da es ja nicht immer auftritt.


Blockdevices dürfen immer nur von einer Task gleichzeitig verwendet 
werden !!!


Peter

von Wolfram Q. (quehl)


Lesenswert?

da müßte man wohl beim Schreiben aus dem Main den Interrupt sperren, 
damit das nicht vermanscht wird.

Gegen das Totwarten wird es sicherlich auch eine Gegenmaßnahme geben.

Zu meinem Problem:

ich dachte schon, daß vielleicht mein Serieller Eingang am PC defekt 
wäre, weil Maus und GPS Empfänger auch nicht gehen. Aber das 
Beispielprogramm von Pollin funktioniert. Eingang ist also OK. Nur das 
Programm von Pollin funktioniert nicht so, wie es programmiert ist. Der 
Puffer wird nicht immer vollständig ausgegeben. Anzahl variiert, und 
Endekennung wird nicht berücksichtigt. Da kann der Bascom Programmierer 
nicht viel machen, weil der Programmierer nur einen Print Befehl machen 
kann. Der Fehler liegt da im Compiler von Bascom.
Aber bei meinem Assembler Programm müßte ich doch den Fehler finden 
können. Warum finde ich das nicht?

mfg



von quehl (Gast)


Angehängte Dateien:

Lesenswert?

ich habe jetzt mal eine verkürzte ablauffähige Version angehängt. Ich 
kann darin keinen Fehler mehr finden. Was kann an dem Programm noch 
falsch sein, daß nichts über den Uart übertragen wird? Hinzu kommt, daß 
der Timer zu langsam läuft. Denke, daß das irgendwie zusammenhängen 
könnte.

mfg

von Hans (Gast)


Lesenswert?

Hallo,

ich hab mir das Programm eben angesehen und es ist als Grundgerüst recht 
brauchbar. Es fehlt allerdings in dem Unterprogramm "void UartPutC(char 
data)" die Deaktivierung des Interrupts. Ansonst kann bei beinahe vollem 
Puffer so macher Blödsinn passieren ;-)

von Frank J. (frajo)


Lesenswert?

Hans wrote:
> Es fehlt allerdings in dem Unterprogramm "void UartPutC(char
> data)" die Deaktivierung des Interrupts. Ansonst kann bei beinahe vollem
> Puffer so macher Blödsinn passieren ;-)

??? Auf welches Programm bezieht sich Deine Aussage? Mein korrigiertes 
Programm ist im Anhang zum Posting vom 13.06.2007 11:13.
Der Interrupt wird immer in der ISR deaktiviert wenn keine Daten im 
Puffer sind. In meinem ersten Programm kann der Puffer nicht überlaufen, 
da er nicht benutzt wird.

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
Noch kein Account? Hier anmelden.