Forum: Compiler & IDEs Wie funktioniert XON/XOFF


von Philipp (Gast)


Lesenswert?

Guten Morgen,

ich habe den Auftrag bekommen mich mal mit XON/XOFF zu beschäftigen und 
durch meine Internetsuche habe ich auch schon herausgefunden wofür es 
gut ist dennoch habe ich nicht wirklich etwas gefunden was mir bei der 
Programmierung helfen könnte. Mir ist unklar ob XON/XOFF eine feste 
Funktion ist oder ob man sie einbinden muss.

Ich benutze den ATmega 128 und möchte mit einem PC kommunizieren.

Ich bin für jede art von Info über XON/XOFF sehr dankbar.

MFG Philipp


von johnny.m (Gast)


Lesenswert?

Hast Du auch da schon geschaut:
http://de.wikipedia.org/wiki/Xon#Software-Flusskontrolle.2C_Software-Handshake.2C_Software-Protokoll_oder_X-ON.2FX-OFF
Da steht eigentlich ausreichend genau beschrieben, wie es 
funktioniert...

von Philipp (Gast)


Lesenswert?

Ja da war ich auch schon aber es hat mir nicht sehr viel geholfen. Somit 
wusste ich zwar wofür XON/XOFF benötigt wird aber nicht wie man es in 
seinem Programm implementiert!

Könnte mir jemand ein Beispielprogramm in Verbindung mit XON/XOFF 
zukommen lassen?

von Günter R. (galileo14)


Lesenswert?

Philipp wrote:
> Ja da war ich auch schon aber es hat mir nicht sehr viel geholfen. Somit
> wusste ich zwar wofür XON/XOFF benötigt wird aber nicht wie man es in
> seinem Programm implementiert!
>
> Könnte mir jemand ein Beispielprogramm in Verbindung mit XON/XOFF
> zukommen lassen?

Du mußt in Deinem Programm einen Empfangspuffer einrichten, in den die 
empfangenen Zeichen zwischengespeichert werden (z.B. ein Fifo); dort 
mußt Du den "Füllgrad" beobachten, durch eine geeignete Funktion, die 
Dir meldet, wenn der Puffer z.B. zu 95% gefüllt ist; sobald diese 
Situation auftritt, sendest Du das X-Off-Zeichen zum Sender; dieser 
reagiert darauf und unterbindet weiteres Senden, solange, bis Du das 
X-On-Zeichen sendest. Das tust Du z.B. dann, wenn der Füllgrad 80% 
beträgt (hängt aber stark von der Applikation ab). Zu beachten ist die 
Reaktionsgeschwindigkeit des Senders bzw. wieviele Zeichen er noch 
senden könnte, wenn Du X-Off abgeschickt hast. Insofern muß ein 
Empfangspuffer immer noch Platz für einige Zeichen haben, auch wenn das 
X-Off abgeschickt wurde; je nach Sendegeschwindigkeit und Puffergröße 
muß also dieser "95%-Wert" angepaßt werden: je kleiner der Puffer und je 
höher die Bitrate und je größer die Reaktionszeit des Senders, umso 
kleiner dieser Prozentwert.

von Karl H. (kbuchegg)


Lesenswert?

Da ist doch nichts schweres daran:

Wenn dein Programm möchte, dass die Gegenstelle
aufhört zu senden, dann schickt es das Zeichen XOFF
über die Leitung. Wenn dein Programm dann wieder
Zeichen aufnehmen kann, schickt es XON.

Das einzige worauf du aufpassen musst, ist dass nach
dem Senden von XOFF noch ein/zwie Zeichen kommen
können (je nachdem wie schnell die Gegenstelle auf
den XOFF reagiert).

Mehr ist das nicht.


Bsp:
Dein Programm hat einen Input-Buffer in dem es zunächst
die empfangenen Zeichen zwischenspeichert. Jetzt ist
dein Programm anderweitig beschäftig und hat keine
Zeit die Zeichen aus diesem Buffer abzuholen ->
der Buffer füllt sich zusehends. Ist der Buffer
zu 80% gefüllt, so schickt die Empfangsroutine
einen XOFF. Daraufhin stellt die Gegenstelle das Senden
ein und dein Programm läuft nicht Gefahr, dass der Buffer
überläuft. (Dazu implementiert man die Empfangsroutine
am besten in eine ISR, die beim Empfang eines Zeichens
aufgerufen wird).

Irgendwann hat dann dein Hauptprogramm wieder Zeit die
empfangenen Zeichen abzuholen. Die Funktion die ein Zeichen
aus dem Buffer holt, überprüft auch noch, ob ein XOFF gesendet
wurde und wenn die Bufferauslastung durch das Abholen von Zeichen
wieder unter 10% gesunken ist, sendet sie einen XON an die
Gegenstelle.

Selbes Spielchen in der umgekehrten Richtung: Wenn die
Empfangsroutine einen XOFF empfängt, heist das, das die
Gegenstelle möchte, dass dein Programm das Senden einstellt.
Kriegt deine Empfangsroutine einen XOFF, dann setzt sie ein
Flag, das der Senderoutine verbietet das nächste Zeichen
zu senden. Empfängt sie ein XON, dann setzt sie dieses
Flag wieder zurück.

Wie das im Detail implementiert wird, hängt stark von deiner
momentanen Architektur ab. Das Prinzip ist aber immer:
Du redest mit deinem Freund. Plötzlich ruft dein Freund:
"Warte mal" (XOFF), macht irgendwas anderes und wenn er wieder
aufnahmebereit ist, ruft er dir zu: "OK, weiter" (XON)

von Philipp (Gast)


Lesenswert?

Soweit so gut aber macht er das automatisch? Also führt er automatisch 
den XOFF befehl aus wenn der Puffer voll ist? Wenn ja kann man dem 
Programm sagen das wenn der Puffer zu 50% voll ist das er dann das XOFF 
zeichen sendet?

Du hast auch gesagt "(Dazu implementiert man die Empfangsroutine
am besten in eine ISR, die beim Empfang eines Zeichens
aufgerufen wird)" kannst du mir das vielleicht etwas genauer erklären?

von Philipp (Gast)


Lesenswert?

Mein hauptsächliches Problem besteht darin das ich nicht weiß WIE ich 
ein XON/XOFF Zeichen sende also die Syntax für diese Funktion.

von OliverSo (Gast)


Lesenswert?

>Soweit so gut aber macht er das automatisch?
...
>Mein hauptsächliches Problem besteht darin das ich nicht weiß WIE ich
>ein XON/XOFF Zeichen sende also die Syntax für diese Funktion.

Es gibt keine "Syntax" für diese Funktion.
Das wirst du komplett selber programmieren müssen.

Oliver

von Christoph db1uq K. (christoph_kessler)


Lesenswert?

Das sind einfach zwei ASCII-Steuerzichen, wie es im Wikipedia-Artikel 
steht, die werden genau wie jeder andere Buchstabe gesendet:

"DC1 (oft als X-ON bezeichnet, engl. für Transmission ON, 
Zeichencodierung 11hex, PC-Tastatur: Strg-Q) und
DC3 (oft als X-OFF bezeichnet, engl. für Transmission OFF, 
Zeichencodierung 13hex PC-Tastatur: Strg-S)."

Ascii-Tabelle hier:
http://de.wikipedia.org/wiki/ASCII

von Philipp (Gast)


Lesenswert?

Vielen Dank erstmal für die schnellen Antworten sie haben mir echt 
geholfen jetzt wäre nur noch ein Beispiel-Programm das I-Töpfelchen!

Mit freundlichen Grüßen und Dank

Philipp

von Karl H. (kbuchegg)


Lesenswert?

Philipp wrote:
> Soweit so gut aber macht er das automatisch? Also führt er automatisch
> den XOFF befehl aus wenn der Puffer voll ist? Wenn ja kann man dem
> Programm sagen das wenn der Puffer zu 50% voll ist das er dann das XOFF
> zeichen sendet?
>
> Du hast auch gesagt "(Dazu implementiert man die Empfangsroutine
> am besten in eine ISR, die beim Empfang eines Zeichens
> aufgerufen wird)" kannst du mir das vielleicht etwas genauer erklären?

Dann solltest du dich mal grundsätzlich mit der Funktion
der UART beschäftigen:
* Wie sendet man Zeichen, welche Möglichkeiten gibt es dafür
* Wie empfängt man Zeichen, welche Möglichkeiten gibt es dafür

In beiden Fällen gibt es die Straightforward Sede und
Empfangsfunktionen, die in das Hauptprogramm eingebaut
werden.
Die etwas ausgefuchstere Version ist es, wenn beides
über Interrupts gesteuert abläuft. Und genau die brauchst
du.

Schau dir mal die UART Library von Peter Fleury an (danach
googeln). Wenn ich mich richtig erinnere arbeitet die
über Interrupts.

von OliverSo (Gast)


Lesenswert?

Oder die procyon avrlib. Die bietet interrupts und buffer, nur eben kein 
xon/xoff. Das musst du dann noch selber dranbasteln.

Oliver

von Karl H. (kbuchegg)


Lesenswert?

Allerdings:
Wenn man eine bestehende Lib studieren muss, ist es wohl
besser das ganze neu zu schreiben. Bis man raus hat, wo mann
eingreifen muss, hat man das auch schon neu gemacht und dann
weiss man was man hat :-)

Eine bestehende Lib studieren, wie die Dinge gemacht werden
ist aber immer eine gute Idee.

von Philipp (Gast)


Lesenswert?

Hat nicht jemand ein beispiel Programm für mich?

von Karl H. (kbuchegg)


Lesenswert?

Christoph Db1uq wrote:
> Das sind einfach zwei ASCII-Steuerzichen, wie es im Wikipedia-Artikel
> steht, die werden genau wie jeder andere Buchstabe gesendet

Wobei ich mich immer schon mal gefragt habe:
Wie wird das gehandhabt?
Angenommen mein Gegenüber schickt einen XOFF, möchte
also von mir nicht 'belästigt' werden. Jetzt komm
ich aber selber in Zeit/Speichernot und will ihm einen
XOFF schicken. Darf ich das dann? Sein XOFF steht ja immer
noch und eigentlich besteht ja Sendeverbot.

Mir ist schon klar, dass die Antwort logischerweise
nur 'ja, ich darf senden' lauten kann. Aber
eigenartigerweise wird diese Thematik nie irgendwo
erwähnt :-)

Das Senden von XON, XOFF muss immer den kurzen Weg
durch alle Buffer nehmen und auch ein bestehender
XOFF von der Gegenstelle wird ignoriert. Ansonsten
könnte man sich leicht in einen Deadlock hineinmanövrieren.

von OliverSo (Gast)


Lesenswert?

>Darf ich das dann? Sein XOFF steht ja immer
>noch und eigentlich besteht ja Sendeverbot.

Es besteht nur ein Sendeverbot für Daten. xon/xoff selber sind aber 
keine Daten, sondern Steuerzeichen.

So würde ich das interpretieren ...
Das braucht aber heutzutage eh keiner mehr.

Oliver

von Karl H. (kbuchegg)


Lesenswert?

Philipp wrote:
> Hat nicht jemand ein beispiel Programm für mich?


Sag blos du hast in den vergangenen Stunden die 3 Variablen
und 5 Zeilen zusätzlichen Code nicht hingekriegt?

Eine (ungetestete) Implementierung einer gebufferten UART
Empfangseinheit ohne XON/XOFF könnte zb. so aussehen:
1
#define BUFFER_LEN 20
2
3
char Buffer[BUFFER_LEN];
4
5
volatile uint8_t NextWriteBufferPos;
6
volatile uint8_t NextReadBufferPos;
7
volatile uint8_t CharsInBuffer;
8
9
void SendImmidate( char c )
10
{
11
    while (!(UCSRA & (1<<UDRE)))
12
        ;
13
14
    UDR = c;
15
}
16
17
// wird aufgerufen, wenn ein Zeichen von der UART empfangen wurde
18
ISR( USART_RXC_vect )
19
{
20
  Buffer[NextBufferPos] = UDR;
21
22
  NextWriteBufferPos = NextWriteBufferPos + 1 ) % BUFFER_LEN;
23
  CharsInBuffer++;
24
}
25
26
char GetUartChar()
27
{
28
  if( CharsInBuffer == 0 )
29
      return 0;
30
31
  char NextChar = Buffer[NextReadBufferPos];
32
  NextReadBufferPos = ( NextReadBufferPos + 1 ) % BUFFER_LEN;
33
  CharsInBuffer--;
34
35
  return NextChar;
36
}

Wird ein Zeichen empfangen, wird die Interrupt Funktion aufgerufen
welche das Zeichen in einen Buffer stellt.
Das Hauptprogramm nutzt die Funktion GetUartChar() um sich die
Zeichen dann in aller Gemütsruhe aus diesem Buffer zu holen.

Soweit so gut.
Jetzt kann es natürlich passieren, dass das Hauptprogramm die
Zeichen nicht abholt. D.h. die Empfangsfunktion prüft,
ob der Buffer sich füllt und wenn eine kritische Grenze
überschritten wird, dann schickt sie an die Gegenstelle einen
XOFF.
Die Aufhebung erfolgt in der Zeichen-Abhol-Funktion. Wurde
ein Xoff gesendet und sinkt der Füllgrad wieder unter eine
Grenze, dann wird mit einem XON der Gegenstelle wieder
signalisiert, dass sie weitersenden darf.
Und genau das wird straightforward, so wie es in einigen
Postings vorher und auf dem Wiki Artikel erklärt wurde,
dazu implementiert.
1
#define XON  0x11
2
#define XOFF 0x13
3
4
#define BUFFER_LEN 20
5
#define HIGH_WATER  15
6
#define LOW_WATER  5
7
8
char Buffer[BUFFER_LEN];
9
10
volatile uint8_t NextWriteBufferPos;
11
volatile uint8_t NextReadBufferPos;
12
volatile uint8_t CharsInBuffer;
13
volatile uint8_t XoffSent;
14
15
void SendImmidate( char c )
16
{
17
    while (!(UCSRA & (1<<UDRE)))
18
        ;
19
20
    UDR = c;
21
}
22
23
// wird aufgerufen, wenn ein Zeichen von der UART empfangen wurde
24
ISR( USART_RXC_vect )
25
{
26
  Buffer[NextBufferPos] = UDR;
27
28
  NextWriteBufferPos = NextWriteBufferPos + 1 ) % BUFFER_LEN;
29
  CharsInBuffer++;
30
31
  if( CharsInBuffer == HIGH_WATER ) {
32
      SendImmidate( XOFF );
33
      XoffSent = 1;
34
  }
35
}
36
37
char GetUartChar()
38
{
39
  if( CharsInBuffer == 0 )
40
      return 0;
41
42
  char NextChar = Buffer[NextReadBufferPos];
43
  NextReadBufferPos = ( NextReadBufferPos + 1 ) % BUFFER_LEN;
44
  CharsInBuffer--;
45
46
  if( XoffSent && CharsInBuffer < LOW_WATER ) {
47
      SendImmidiate( XON );
48
      XoffSent = 0;
49
  }
50
51
  return NextChar;
52
}

Die Sendeeinheit ist sogar noch einfacher:
In der Empfangsroutine wird zusätzlich noch überprüft
ob von der Gegenstelle ein XON oder XOFF gekommen ist.
Wenn ja wird einfach nur ein Flag gesetzt.
In der Zeichen-Sende-Funktion wird noch getestet, ob
das Senden zulässig ist, und fertig:
1
#define XON  0x11
2
#define XOFF 0x13
3
4
#define BUFFER_LEN 20
5
#define HIGH_WATER  15
6
#define LOW_WATER  5
7
8
char Buffer[BUFFER_LEN];
9
10
volatile uint8_t NextWriteBufferPos;
11
volatile uint8_t NextReadBufferPos;
12
volatile uint8_t CharsInBuffer;
13
volatile uint8_t XoffSent;
14
volatile uint8_t CanSend;
15
16
void SendImmidate( char c )
17
{
18
    while (!(UCSRA & (1<<UDRE)))
19
        ;
20
21
    UDR = c;
22
}
23
24
// wird aufgerufen, wenn ein Zeichen von der UART empfangen wurde
25
ISR( USART_RXC_vect )
26
{
27
  char c = UDR;
28
29
  if( c == XON ) {
30
      CanSend = 1;
31
      return;
32
  }
33
  if( c == XOFF ) {
34
      CanSend = 0;
35
      return;
36
  }
37
  
38
  Buffer[NextBufferPos] = UDR;
39
40
  NextWriteBufferPos = NextWriteBufferPos + 1 ) % BUFFER_LEN;
41
  CharsInBuffer++;
42
43
  if( CharsInBuffer == HIGH_WATER ) {
44
      SendImmidate( XOFF );
45
      XoffSent = 1;
46
  }
47
}
48
49
char GetUartChar()
50
{
51
  if( CharsInBuffer == 0 )
52
      return 0;
53
54
  char NextChar = Buffer[NextReadBufferPos];
55
  NextReadBufferPos = ( NextReadBufferPos + 1 ) % BUFFER_LEN;
56
  CharsInBuffer--;
57
58
  if( XoffSent && CharsInBuffer < LOW_WATER ) {
59
      SendImmidiate( XON );
60
      XoffSent = 0;
61
  }
62
63
  return NextChar;
64
}
65
66
void SendUartChar( char c )
67
{
68
    while( !CanSend )
69
        ;
70
    SendImmidate( c );
71
}

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.