Forum: Mikrocontroller und Digitale Elektronik Frage zu Software-Handshake per Serieller Schnittstelle


von Micha (Gast)


Lesenswert?

Ich brüte gerade über folgendem Problem:
Ein Atmega-basiertes Gerät gibt mit 9600 Baud ein Listing zu einer RS232 
Schnittstelle aus. Diese ist nur minimal beschaltet (TxD, RxD, GND). Am 
anderen Ende  der seriellen Leitung möchte ich wahlweise entweder ein 
Terminalprogramm oder eine ebenfalls per Atmel-Controller gesteuerten 
Drucker dranhängen. Das Terminalprogramm wird den Datenstrom ohne 
Probleme schlucken. Der Atmel der den Drucker bedient soll jeweils nach 
Senden einer Zeile die Chance bekommen, per XOFF/XON eine Auszeit zu 
nehmen. Die Atmels an beiden Enden der Leitung sind jeweils mit 18,432 
MHz getaktet.
Die Platine für die Druckersteuerung bekomme ich erst nächste Woche, so 
dass ich vorläufig nur graue Theorie machen kann. Hab mir das so 
gedacht, dass der Sender der Daten am Ende einer gesendeten Textzeile 
jeweils für akzeptabel lange Zeit an der Leitung horcht ob ein XOFF 
kommt. Wenn nichts kommt geht es weiter, falls ein XOFF kommt wird 
danach auf das nachfolgende XON gewartet.
Wollte mal fragen ob der folgende Code so funktionieren könnte:
1
// fuer akzeptable Zeitspanne auf ein XOFF warten,
2
// falls eins ankommt: auf nachfolgendes XON warten
3
// falls keins kommt: normal weitermachen
4
// Mikrocontroller: Atmega 1284P @ 18,432 MHz
5
6
uint8_t waitforXOFF (void)
7
{
8
uint16_t  w;
9
  for (w=0;w<65535;w++) {  
10
    if (UCSR0A & (1<<RXC0)) {  // ein Zeichen ist angekommen
11
      if (UDR0 == XOFF) {    // quittieren (?)
12
        while (!(UCSR0A & (1<<RXC0))) {}  // warten auf
13
        if (UDR0 == XON)  break;    // folg. XON
14
      }
15
    }
16
  }
17
}

von STK500-Besitzer (Gast)


Lesenswert?

UDRs sind nur einmal lesbar.
Du musst den Wert also zwischenspeichern, wenn du ihn mehrfach auswerten 
willst.

von Micha (Gast)


Lesenswert?

Das Lesen des UDR quittiert ein eingegangenes Byte? Verstehe ich auch 
so. Vor dem zweiten Lesen warte ich ja auch auf ein weiteres Byte.

Einen Flüchtigkeitsfehler hatte ich auf jeden Fall in dem Listing oben:
die Function "waitforXOFF" muss den Typ void haben, die gibt ja nichts 
zurück.

von Karl H. (kbuchegg)


Lesenswert?

Deine Schleifenschachtelung stimmt nicht.

Aber: Ich würd das ehrlich gesagt gar nicht so machen.

An der UART wird der Empfangsinterrupt freigegeben und in der ISR 
erfolgt die Auswertung des empfangenen Zeichens bezüglich XON oder XOFF. 
Dazu kommt noch eine globale Variable, die den Sender in die 
Gegenrichtung freigibt.
1
volatile uint8_t stoppedPerHandshake;
2
3
ISR( USART_RXC_vect )
4
{
5
  char c = UDR;
6
7
  if( c == XON )
8
    stoppedPerHandshake = FALSE;
9
10
  else if( c == XOFF )
11
    stoppedPerHandshake = TRUE;
12
}
13
14
int uart_putc(unsigned char c)
15
{
16
  while (!(UCSRA & (1<<UDRE)) || stoppedPerHandshake )  /* warten bis Senden moeglich */
17
  {
18
  }                             
19
 
20
  UDR = c;                      /* sende Zeichen */
21
  return 0;
22
}

Da alle anderen Ausgabefunktionen letzten Endes IMMER in der uart_putc 
landen, berücksichtigen sie daher auch automatisch das XON/XOFF 
Protokoll. Der Gegenpart auf der anderen Seite kann damit zu jedem 
beliebigen Zeitpunkt mittels XOFF den Sender kruzfristig zum Schweigen 
bringen, egal ob der gerade eine Zeile ausgibt oder nicht.

Und durch den Interrupt Betrieb ist dann auch sicher gestellt, dass ein 
XON/XOFF auf keinen Fall verloren geht, solange die Interrupts aktiviert 
sind. Selbst dann wenn das Hauptprogramm momentan ganz was anderes 
macht, als eine Zeile zu senden.

Die Schwierigkeit beim XON/XOFF besteht nicht so sehr darin, den Sender 
zum Schweigen zu bringen. Die Schwierigkeit besteht mehr darin, dass der 
Empfnger dies rechtzeitig tut und damit rechnen muss, dass da noch ein 
paar Zeichen nach dem XOFF trotzdem eintrudeln. D.h. der wird sein XOFF 
nicht erst dann senden, wenn seine Empfangsbuffer auf Knirsch 
vollgeschrieben sind, sondern schon etwas früher.

von Micha (Gast)


Lesenswert?

Vielen Dank für diese wirklich gute und ausführliche Erklärung!

von Micha (Gast)


Angehängte Dateien:

Lesenswert?

so schaut das Ding übrigens aus wenn ein Terminal dranhängt (Anhang). 
Mit dem Drucker muss ich mich noch bis nächste Woche gedulden.

Hab bisher gezögert den UART per Interrupt dranzuhängen da auf der Kiste 
nebenbei noch ein Timer-Interrupt läuft. Keine Ahnung ob die beiden sich 
vertragen. So was muss man wohl praktisch ausprobieren ;)

von Hmm (Gast)


Lesenswert?

Na, da hast Du Dir ja hübsch was gebastelt auf Omas Wohnzimmertisch. Und 
erst die Erdbeertischdecke! (Ich liebe Erdbeeren).
Ich sehe auch das Du da einen Lunar-Lander programmiert hast. Hübsch.

Magst Du mal zeigen und ein wenig davon erzählen, was da links genau zu 
sehen ist?

Viel Erfolg noch.

von Micha (Gast)


Lesenswert?

Hihi,

Den Modlander hab ich nicht selber entwickelt, sondern nur abgetippt.
hier gibt es paar mehr Infos zu dem abgebildeten Gerät:

www.k1000uc.de

Ist aber alles noch sehr in Entwicklung...

von Karl H. (kbuchegg)


Lesenswert?

Micha schrieb:

> Hab bisher gezögert den UART per Interrupt dranzuhängen da auf der Kiste
> nebenbei noch ein Timer-Interrupt läuft.

Ah geh.
Ist doch kein Problem.
Wenn du dich an die alte Weisheit gehalten hast: keine überflüssigen 
Dinge und vor allem keine delays in der ISR, dann packt der das mit 
links.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Bei 9600 Baud dauert der Empfang eines Zeichens inklusive Start- und 
Stopbit 1,04 mS.Da der Empfang in der UART komplett in Hardware gelöst 
wird, ist der MC in dieser Zeit frei für andere Sachen. Und in 1040 uS 
kann man schon eine ganze Menge machen. Der Worst Case wäre das Zünden 
der Timer ISR während des Stopbit...
Das könnte man nochmal im Datenblatt nachschauen, wann der MC den UART 
IRQ auslöst - während des Stopbits oder erst nach dem kompletten Bit, 
also bei der steigenden Flanke.
Wenn du aber nicht viel Zeit in der ISR verbrätst, klappt das alles 
bestens. Meine BLDC Steuerungen zünden die Timer ISR 20000 mal in der 
Sekunde, nebenbei läuft die serielle Monitor Konsole und es geht 
trotzdem nichts verloren, obwohl die Timer ISR immer Vorrang hat.

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.