mikrocontroller.net

Forum: Projekte & Code AVR UART senden mit UDRE Interrupt


Autor: Frank Jonischkies (frajo)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Frank Jonischkies (frajo)
Datum:

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

Autor: Thorsten (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.



Autor: Frank Jonischkies (frajo)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Wolfram Quehl (quehl)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Frank Jonischkies (frajo)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.
TxBuff[TxWrite++] = data;
kann man auch als
{
 TxBuff[TxWrite] = data;
 TxWrite++;
}
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.
while(TxWrite != TxRead);
Eigentlich gehört hier noch ein leerer Block hin
while(TxWrite != TxRead)
   {
      ;
   }

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

Autor: Frank Jonischkies (frajo)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hier noch mal ohne Optimierung. Vielleicht besser für Dich zu lesen.

Autor: Quehl (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Frank Jonischkies (frajo)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.

Autor: Wolfram Quehl (quehl)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Wolfram Quehl (quehl)
Datum:

Bewertung
0 lesenswert
nicht 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



Autor: quehl (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Hans (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ;-)

Autor: Frank Jonischkies (frajo)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.