Forum: Mikrocontroller und Digitale Elektronik UART TX mit Interrupt?


von AVRli (Gast)


Lesenswert?

Hi,

ich habe nun meine ersten Zeichen erfolgreich zum PC übertragen.
Das ganze mit einer einfachen Warte Abfrage auf das UCSRA Register und
das UDRE Bit.

Da ich aber lange Texte (bis zu 82 Zeichen...) übertragen möchte und
das leider nur in 4800 Baud würde das ja wohl bedeuten wenn man es mit
der Schleifen MEthode macht das ich mir mein Hauptprogramm abbremse.
;-(

Nun habe ich gelesen das es auch für die TX Seite Interrupt Vektoren
gibt nur steige ich noch nicht ganz dahinter wie es funktionieren
soll.

Ich habe meinen String den ich sende möchte im SRAM abgelegt.
Wie ist denn nun der Ablauf wenn man es mit Hilfe der Interrupts senden
möchte?

RX macht mir keine Probleme das habe ich begriffen... :-)

Für jeden Tip danbar... AVRli...

von Marcus M. (Gast)


Lesenswert?

Hallo AVRLi,

wenn ich das jetzt richtig im Kopf habe, dann mußt Du nur den
Sendebuffer füllen und auf den dazu passenden Interrupt warten.
Der Code sollte irgendwie so aussehen.
Allerdings weiß ich jetzt nicht welchen AVR Du benutzt, somit ist der
Code jetzt etwas allgemeiner gefaßt.

Gruß Marcus

char tx_buffer[82];
char tx_send = 0;
char tx_end = 0;

void init_UART(void)  {

  //Parameter entsprechend Datenblatt einstellen!

}

void init_transmission(void) {

  tx_send = 0; //Die Position, die gesendet werden soll, zum
//start erstmal auf Null stellen
  tx_end = strlen(tx_buffer); // Länge des Buffers ermitteln
  sei();       //interrupts aktivieren

}

SIGNAL (SIG_UART_TRANS) {

   if (tx_send < tx_end) //tx_send ist innerhalb des Datenstrings
    UCSRA = tx_buffer[tx_send++];
   else
      cli();  //andernfalls Interrupt deaktivieren, denn es gibt nix
mehr für die serielle Schnittstelle zu senden
}

von AVRli (Gast)


Lesenswert?

Hi Marcus,

danke für den Tip!
Leider habe ich es mal wieder verpennt zu schreiben was meine
"Spielsachen" sind.

ATmega8 und in Assembler...

Mir ist nicht ganz klar wozu die beiden Interrupts da sind.
Und wie man die eigendlich Übertragung dann anschubst...

Gruß AVRli...

von Thomas Burkhardt (Gast)


Lesenswert?

Hi AVRli

der "transmittierende" Teil des USART am Mega 8 hat zwei mögliche
Interrupts. Der eine wird ausgelöst, wenn ein neues Zeichen ins
Datenregister gepackt werden kann (UDRE). Der zweite, wenn die
Übertragung komplett ist (TXC).

In deinem Fall kannst du in der ISR zum UDRE beispielsweise immer ein
neues Zeichen aus deinem String im Datenregister ablegen. Dieses wird
dann automagisch übertragen.


Viele Grüße

von AVRli (Gast)


Lesenswert?

Hi,

danke... ist es dann so das man wenn ich im SRAM meine Daten abgelegt
habe dann im UCSRB das BIT UDRIE setze um die Übertragung zu starten
und wenn alle Zeichen übertragen sind das BIT wieder lösche?

Im Moment springt es ja nun immer wierde in den Vector rein...
Ist das korekt?

Gruß AVRli...

von Thomas Burkhardt (Gast)


Lesenswert?

Hi.

du musst erstmal TXEN und RXEN setzen, aber das musstest du für die
Interruptlose Kommunikation ja auch. Dann setzt du noch UDRIE, was
bewirkt, dass immer wenn ein Byte fort ist (d. h. aus dem UDR Register
raus) dieser Interrupt ausgelöst wird. Wenn alle Zeichen übertragen
wurden, wird das UDRIE wieder gelöscht, sonst wird die ISR immer wieder
aufgerufen...


Grüße

von Stefan Kleinwort (Gast)


Lesenswert?

Hi AVRli,

Du solltest Dir erstmal überlegen, woran Du das Ende Deiner
Zeichenfolge erkennen willst. Es gibt 2 Möglichkeiten:

1. Counter
2. Null am Ende des Strings

In Deiner Sende-IR-Routine sendest Du also einfach solange Zeichen, bis
die Ende-Bedingung erfüllt ist. Dann schaltest Du den TXD-IR ab mit:
    UCSRB &= ~_BV(UDRIE);  // disable the transmitter-IR

Wenn Du einen String senden willst, schreibst Du den Ptr auf den String
in die IR-Ptr-Variable und gibst den TXD-IR frei:
    UCSRB |= _BV(UDRIE);  // enable the transmitter-IR

Bevor das alles so einfach funktioniert, musst Du den UART
initialisieren. Und da geht das Henne-Ei-Problem los: Das Flag UDRE ist
nach dem Reset Null und kann auch nicht manuell gesetzt werden. Die
einzige Möglichkeit, die ich kenne: ein erstes Byte manuell (ohne IR)
in das TXD-Register schreiben.

@Marcus:
Mit cli und sei beeinflusst Du alle IRs gleichzeitig. Wenn Du also auch
andere IR benutzen willst, solltest Du unbedingt das spezifische
IR-Enable-Register der entsprechenden Hardware (in diesem Fall UDRIE)
benutzen.

Stefan

von Thomas Burkhardt (Gast)


Lesenswert?

Hi Stefan,

"Das Flag UDRE ist nach dem Reset Null und kann auch nicht manuell
gesetzt werden. Die einzige Möglichkeit, die ich kenne: ein erstes Byte
manuell (ohne IR) in das TXD-Register schreiben."

Da das so nicht im Datenblatt steht, habe ich das grad' auch noch mal
ausprobiert ;->
Und es ist wirklich so, dass der UDRE - data register empty interrupt -
sofort ausgelöst wird, sobald UDRIE und natürlich das globale
Interruptflag gesetzt sind. Man muss also das erste Byte nicht selbst
schreiben...

Viele Grüße

von Stefan Kleinwort (Gast)


Lesenswert?

Hallo Thomas,

danke für den Tip.
Ich verwende bei meinem eigenen Code nicht das UDRIE-Flag, sondern das
TXCIE-Flag. Bei dem läuft es so wie ich beschrieben habe, und ich habe
das dem UDRIE-Flag ohne zu testen auch unterstellt.

Normalerweise ist die Verwendung des UDRIE besser. Weil ich bei meiner
Appl. aber XON/XOFF-Handshake benutze, verwende ich das TXCIE. Dadurch
ist der Ausgangspuffer des UART beim Senden immer leer (doppelte
Pufferung). Der Recieve-IR kann also XOFF-Bytes direkt in den
Transmitter schreiben, ohne eine laufende Ausgabe zu stören.

Stefan

von Thomas Burkhardt (Gast)


Lesenswert?

Hi Stefan,

die Verwendung des UDRIE hat halt den einen Vorteil, dass man schon ein
neues Byte zum Transfer abgeben kann, während das andere noch durch die
serielle Übertragung geschoben wird. Aber das war's doch auch schon?!
:)

Viele Grüße

von Stefan Kleinwort (Gast)


Lesenswert?

Hi Thomas,

bei manchen Sachen sind die beiden unterschiedlichen Flags schon ganz
hilfreich, z.B. bei RS485: der letzte macht das Licht aus ;-)

Viele Grüße, Stefan

von AVRli (Gast)


Lesenswert?

Hi,

also ich habe es mit dem UDRE Interrupt gelöst.
Alles super so! ;-)

Also meine Daten liegen im SRAM und enden mit der 0.
Wenn die Daten im SRAM gespeichert sind wird das
UDRIE Bit gesetzt.

In dem Interrupt wird nun immer das nächste Zeichen geleaden und
ausgesendet. Ist er bei der 0 angekommen dann wird das
UDRIE Bit wieder gelöscht.

Funktioniert wirklich gut.

Ich danke für die Infos.
Es macht es schon leichter wenn man sich austauscht.

Gruß AVRli...

von AVRli (Gast)


Lesenswert?

Hi,

nun bin ich dabei die Anwndersoft auf dem PC zu schreiben und da
ist mir aufgefallen das ich was in der Software vom ATMEL (ATmega8,
Assembler) was übersehen haben muss.

Ich schicke meine Abfragen zyklisch zum AVR... kurze Antworten von 3
Zeichen kommen richtig an zum PC.
Längere die dazwischen sind werden nach halber aussendung von der
Antwort der nächsten Anfrage überschrieben.

Mit einer Pause zwischen den Befehlen kann ich nicht leben...
Also Sende 1 Sek warten sende ...

Wie würdet ihr sowas lösen?

Die Möglichkeiten die mir einfallen sind wohl nicht ideal.
1. Doch nicht im Interrupt aussenden und das ganze AVR Programm
abbremsen
2. keine Zeichen annehemen solange nicht die komplette Antwort raus
ist. (hier gehen dann Zeichen verloren oder)

Gruß AVRli...

von Thomas Burkhardt (Gast)


Lesenswert?

Hi AVRli,

kannst du mir das nochmal anders erklären :->
irgendwie stehe ich aufm Schlauch und kapier nicht so recht, was du
meinst.

Wenn deine Abfragen nicht schneller kommen, als die Übertragung des
Strings anhand seiner Länge und Baudrate dauert, leuchtet mir nicht
ein, warum nur ein Teil übertragen werden sollte... Mir wird nicht
klar, warum dies ein Problem der Übertragung mittels Interrupt sein
soll.

von Stefan Kleinwort (Gast)


Lesenswert?

Hi AVRli,

das macht es etwas komplizierter. Du brauchst einen Ausgangs-Puffer,
analog zum Eingangspuffer des RXD-Teils. Zum Verschicken reicht es dann
nicht mehr, einfach den Pointer des Strings zu kopieren, sondern Du
musst alle Zeichen aus dem String in den Puffer kopieren. Nicht
vergessen: Beim Kopieren in den Puffer und auch beim Auslesen die obere
Puffergrenze und den Puffer-Füllstand kontrollieren.

Du brauchst also:
* den Puffer selbst, Größe je nach Anforderung
* einen Puffer-Index für die Füll-Routine
* einen Puffer-Index für den TXD-IR

Viele Grüße, Stefan

von AVRli (Gast)


Lesenswert?

Hi,

@Thomas
Ja das Problem ist folgendes...
Ein Befehl PC>AVR sieht bei mir so aus das er mit einem CR endet.
Also so...

SET1

wenn alle Zeichen im AVR sind dann schreibt der in den SRAM
die Zeichen rein die er aussenden soll.
Soweit ok...

Sendet man nun aber

SET1
SET2
SET3

hintereinander zum AVR dann bekommt man nur die Antwort vom letztem
Befehl richtig zum PC gesendet.
Grund dafür dürfte sein das die Antwort auf den letzten Befehl noch
nicht fertig ist und dann die neue einfach begonnen wird. :-(

Ok?

@Stefan

Danke für den Tip... werde erstmal darüber schlafen.... :-D

Gruß AVRli...

von Tobi (Gast)


Lesenswert?

du brauchst einfach einen eingangspuffer von was weiss ich wievielen
befehlen den dein programm dann der reihe nach abarbeitet

von Thomas Burkhardt (Gast)


Lesenswert?

Hi AVRli

also gut, dann wirds wohl ein Eingangspuffer im AVR tun oder noch
einfacher: dafür sorgen, dass der PC keine Kommandos schickt, solange
die Abarbeitung eines anderen Befehls noch läuft.

von AVRli (Gast)


Lesenswert?

Hi,

> also gut, dann wirds wohl ein Eingangspuffer im AVR tun
Ja habe ich hinbekommen so eine Art "Ringbuffer"...
Die Antwort vom AVR wird nun temporär im SRAM abgelegt diese
darf max. 82 Zeichen haben.
Anschließend werden die neuen Zeichen in den Ringbuffer (255 Zeichen)
geladen.
Nun wird der TX int. aktiviert.
Und er sendet fleißig los.
Kommt nun einen neue Anfrage dann wird diese im Ringbuffer angehangen.
Das ganze funktioniert so lange einwandfrei bis man ein Dauerfeuer an
Kommandos gibt.
Der Ringbuffer ist hier nun 255 Byte groß.
Da knallt es wenn man also 4 mal eine Antwort mit 80 Zeichen bekommen
will... aber das ist total nebensächlich !!!! ;-)
Das kommt in der Praxis nie vor !!!
In der Praxis werden alle 500ms eine Antwort von ca. 50 Zeichen
erwartet.

> oder noch einfacher: dafür sorgen, dass der PC keine Kommandos
> schickt, solange die Abarbeitung eines anderen Befehls noch läuft.

Ja die Lösung hatte ich auch in Betracht gezogen da man ja einen neuen
Befehl hätte blöcken können solange nicht die ganze Antwort da ist...
;-I
Aber geschickt ist das nicht... hahahahaha
Kostet unnötig Resourcen...

Ich freue mich riesig das ich es, dank eurer Hilfe, auf die Reihe
bekommen habe.

Gruß AVRli...

von Thomas Burkhardt (Gast)


Lesenswert?

Hi,

>> oder noch einfacher: dafür sorgen, dass der PC keine Kommandos
>> schickt, solange die Abarbeitung eines anderen Befehls noch läuft.

>Aber geschickt ist das nicht... hahahahaha
>Kostet unnötig Resourcen...

Wie jetzt, was ist daran ungeschickt :-P
Und was kostet das für Resourcen? Du sollst da ja an dem Steuerrechner
kein "Busy-Wait" draus machen :)

von AVRli (Gast)


Lesenswert?

Hi Thomas,

ja na ungeschickt in der Form das man auf der PC Seite damit
beschäftigt ist ständig zu schauen ob die Schaltung fertig ist mit der
Abarbeitung.

Nun kann ich ganz sorglos meine Befehle an einem Stück absenden und tu
dies nun alle 500ms der Atmel hingt zwar etwas hinter her aber das
stört nicht weiter.

Die Befehle hat er locker nach 500ms beantwortet. ;-)
Ohne nur eine Antwort zu zerstückeln.

Finde es eleganter und das wird es leichter machen
wenn Software anderer Entwickler mit meiner Schaltung "spielen"
wollen.

Gruß AVRli...

von Thomas Burkhardt (Gast)


Lesenswert?

Hi,

der PC hat ja auch sowas wie Interrupts :) muss man also nicht
ununterbrochen pollen, um rauszufinden, was der µC tut.
Aber es ist schon richtig, ein Puffer im µC schadet nicht. Nur muss man
sich der Grenzen trotzdem bewusst sein... irgendwann ist der nämlich
auch voll.

Grüße

von Stefan Kleinwort (Gast)


Lesenswert?

übrigens:
Baudrate hochsetzen verringert solche Probleme oft extrem. Bei 115kbaud
brauchen 80 Zeichen keine 8ms mehr.

Happy praogramming, Stefan

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.