RS422 USART letzte 2 Bytes im String werden nicht gesendet

OP #3201713
Lesenswert?

Hallo zusammen!
Ich möchte mit einem ATtiny1634 was über einen RS422-Bus senden. dafür 
nutze ich den USART1 mit 9600 8N1 und einen MAX489 Treiber.

Zum Testen schicke ich einen String raus mit:
1
void RS422_PutStr(unsigned char *s) {
2
  /* enable output*/
3
  PORTB |= (1 << PORTB3);
4
  
5
  USART1_PutStr(s);
6
  
7
  /* disable output */
8
  PORTB &= ~(1 << PORTB3);
9
}
PORTB3 geht hierbei auf den Datenrichtungs-Pin auf dem MAX489.


Selsamerweise werden dabei immer die letzten beiden Zeichen im 
beliebigen String nicht gesendet.
Meine Vermutung war dass der Datenrichtungs-Pin zu schnell wieder auf 
low gezogen wird sobald der ATtiny aus der USART1_PutStr(s) Funktion 
zurückkommt und die letzten beiden Bytes zu dem Zeitpunkt noch in 
irgendwelchen Buffer-Registern hängen und noch nicht rausgeschiftet 
wurden.

Tatsächlich funktioniert das Ganze ohne Probleme wenn ich ein kurzes 
delay einbaue:
1
void RS422_PutStr(unsigned char *s) {
2
  /* enable output*/
3
  PORTB |= (1 << PORTB3);
4
  
5
  USART1_PutStr(s);
6
  
7
  _delay_ms(20);
8
  
9
  /* disable output */
10
  PORTB &= ~(1 << PORTB3);
11
}
Jetzt möchte ich das aber etwas eleganter lösen und einfach auf den 
TXC-Flag warten, denn im Datenblatt heißt es:

"This flag is set when the entire frame in the transmit shift register 
has been shifted out and there is no new data
currently present in the transmit buffer (UDRn)." Also probiere ich das 
mit:
1
void RS422_PutStr(unsigned char *s) {
2
  /* enable output*/
3
  PORTB |= (1 << PORTB3);
4
  
5
  USART1_PutStr(s);
6
  
7
  while (!(UCSR1A & (1 << TXC1)))
8
    ;
9
  
10
  /* disable output */
11
  PORTB &= ~(1 << PORTB3);
12
}

Überraschenderweise werden auch hier wieder die letzten beiden Zeichen 
nicht gesendet! Hat jemand eine Idee was ich falsch mache?

Vielen Dank und viele Grüße!
OP #3201721
Lesenswert?

Karl Heinz Buchegger schrieb:
> Und wie sieht deine USART1_PutStr Funktion aus?
>
> Und wenn die wieder weitere Funktionen aufruft, wie sehen diese aus?

Die sind ganz unspektakulär:
1
void USART1_PutChar(unsigned char c) {
2
  while (!(UCSR1A & (1 << UDRE1)))
3
    ;
4
  UDR1 = c;
5
}
6

7
void USART1_PutStr(unsigned char *s) {
8
  while (*s) {
9
    USART1_PutChar(*s);
10
    s++;
11
  }
12
}

Interrupts habe ich auch keine.
#3201724
Lesenswert?

Das TXC Flag muss händisch gelöscht werden (oder der Interrupt muss 
ausgeführt werden). Das wird deine putstr Funktion vermutlich nicht 
machen. Also bleibt das Bit aktiv mit dem ersten Zeichen.

Wenn du ohne Interrupt sendest und eh deine Zeit mit warten auf 
"nächstes Zeichen kann raus" vertrödelst, warte doch einfach in putstr, 
das alles raus ist.
#3201731
Lesenswert?

Hallo,

so wie ich das aus dem DB lese, muss man das BIT auch wieder löschen, 
wenn es gesetzt wurde und ohne den zugehörigen Interrupt zu verwenden !
1
* Bit 6 - TXCn: USART Transmit Complete
2
This flag is set when the entire frame in the transmit shift register has been shifted out and there is no new data
3
currently present in the transmit buffer (UDRn). The TXCn flag bit is automatically cleared when a transmit complete interrupt is executed,
4
or it can be cleared by writing a one to its bit location.
5
The flag can generate a Transmit Complete interrupt (see TXCIEn bit).
OP #3201741
Lesenswert?

D.h. wenn ich auf interrupts verzichte, muss ich die Abfrage für jedes 
Zeichen innerhalb der USART1_PutChar() Funktion machen und danach das 
Flag durch schreiben einer 1 zurücksetzen.

Also etwa so?
1
void USART1_PutChar(unsigned char c) {
2
  while (!(UCSR1A & (1 << UDRE1)))
3
    ;
4
  UDR1 = c;
5
  
6
  /* wait for Tx compete */
7
  while (!(UCSR1A & (1 << TXC1)))
8
    ;
9
  
10
  /* clear TXC flag */
11
  UCSR1A |= (1 << TXC1);
12
  
13
}
#3201766
Lesenswert?

V. Baumann schrieb:
> D.h. wenn ich auf interrupts verzichte, muss ich die Abfrage für jedes
> Zeichen innerhalb der USART1_PutChar() Funktion machen und danach das
> Flag durch schreiben einer 1 zurücksetzen.

Die (gängigere) Alternative wäre, das Flag in PutChar zu löschen (ohne 
Warten), und dann in einer übergeordneten Funktion (bei dir PutStr) zu 
Warten.

Egal welche Variante du benutzt, beim Flag-Löschen gibt es zwei Dinge zu 
beachten:

1) Löschen nach dem Schreiben nach UDR (nicht direkt davor, wie man es 
relativ häufig sieht).

2) Beide Aktionen müssen atomar gekapselt sein.
#3201791
Lesenswert?

Karl Heinz Buchegger schrieb:
>> Nein, eben nicht. Das öffnet ein kleines Fenster für Race Conditions.
>
> Ich bin heut geistig noch nicht ganz da.
> Kannst du das bitte näher ausführen? Ich seh die Race Condition noch
> nicht (zumindest springt sie mir nicht ins Gesicht)

Wenn der Ablauf länger unterbrochen wird (Interrupts), kann der UART 
leer laufen und das Flag wird mitten drin schon gesetzt. Dieses "mitten 
drin" könnte zwischen das Flag-Löschen und UDR-Schreiben fallen, und 
schon funktioniert das Warten auf das gerade nach UDR geschriebene Byte 
nicht mehr. Wenn das nun gerade das letzte Byte der aktuellen 
Übertragung war, wird diese zu früh "abgewürgt".

Zugegeben, ein sehr sehr kleines Fenster, aber eben vorhanden.
#3201914
Lesenswert?

Rufus Τ. Firefly schrieb:
> Das Warten auf "tx complete" muss eigentlich nur einmal erfolgen --
> nämlich unmittelbar vor dem Deaktivieren des RS422-Treibers, also nach
> dem Senden des letzten Zeichens des Strings.

Was ich ja auch als "gängigere" Variante vorgeschlagen habe.

Rufus Τ. Firefly schrieb:
> Wird nach jedem Zeichen gewartet, wird damit die effektive
> Übertragungsrate halbiert.

Also das ja nun nicht gerade. Es entsteht lediglich eine kleine Lücke 
zwischen den Zeichen. Selbst bei maximaler Baudrate (UBRR = 0, U2X = 1) 
käme man kaum auf eine Halbierung.
#3202447
Lesenswert?

Stefan Ernst schrieb:
>> Das Warten auf "tx complete" muss eigentlich nur einmal erfolgen --
>> nämlich unmittelbar vor dem Deaktivieren des RS422-Treibers, also nach
>> dem Senden des letzten Zeichens des Strings.
>
> Was ich ja auch als "gängigere" Variante vorgeschlagen habe.

Was aber zwingend voraussetzt, dass man das Flag während der ersten 
Zeichen "passend" zurücksetzt.

In einer Anwendung ohne Interrupts ist es meist egal,ob ich am Anfang 
der Übertragung eines Zeichens auf das Freiwerden des Datenregisters 
warte oder am Ende auf "Schieberegister leer".
#3202477
Lesenswert?

Georg G. schrieb:
> Was aber zwingend voraussetzt, dass man das Flag während der ersten
> Zeichen "passend" zurücksetzt.

Was meinst du mit "passend"? Man muss doch nur dem Flag stumpf bei jedem 
Schreiben nach UDR "eins über braten".

Georg G. schrieb:
> In einer Anwendung ohne Interrupts ist es meist egal,ob ich am Anfang
> der Übertragung eines Zeichens auf das Freiwerden des Datenregisters
> warte oder am Ende auf "Schieberegister leer".

Es geht hier aber nicht um das "am Anfang der Übertragung eines 
Zeichens", sondern um das "nach der Übertragung aller Zeichen". Und da 
kommst du eben mit "auf das Freiwerden des Datenregisters warten" nicht 
weit.
#3202486
Lesenswert?

Stefan Ernst schrieb:
> Was meinst du mit "passend"? Man muss doch nur dem Flag stumpf bei jedem
> Schreiben nach UDR "eins über braten".

Der UART ist auch sendeseitig doppelt gepuffert. Mit etwas Pech schiebst 
du das zweite Byte rein während das erste noch in Arbeit ist. Dann wird 
das Flag vom Byte 1 gesetzt und Nummer 2 kommt noch - unerwartet und 
geht verloren, weil die Senderichtung schon umgeschaltet wurde.
#3202487
Lesenswert?

Georg G. schrieb:
> Der UART ist auch sendeseitig doppelt gepuffert. Mit etwas Pech schiebst
> du das zweite Byte rein während das erste noch in Arbeit ist. Dann wird
> das Flag vom Byte 1 gesetzt

Nein, wird es eben nicht.

1
This flag bit is set when the entire frame in the Transmit Shift
2
Register has been shifted out and there are no new data currently
3
present in the transmit buffer (UDRn).

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren