attiny usart Ende der Datenübertragung

OP #5204255
Lesenswert?

Hi,

ich wollte bei einem ATTIny 4313 vor dem sleep_mode() sicherstellen, 
dass die Datenübertragung des USART fertig ist.
Hierzu hatte ich folgende Funktion geschrieben:
1
bool huart_tx_empty()
2
{
3
  if((outfifo.count==0)) {
4
    return (( (UCSRA & (1<<TXC))!=0) && ( (UCSRB & (1<<UDRIE))==0));
5
  };
6
  return (false);
7
}
Diese Funktion wird im Hauptprogramm vor dem Schlafmodus aufgerufen:
1
   if(huart_tx_empty()) {
2
    _delay_ms(1);
3
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
4
      sleep_mode();
5
    };

Leider funktioniert das nur, wenn ich den oben erwähnten  _delay_ms(1) 
mache. Ansonsten kommen beim Empfänger unsinnige Zeichen an. Wie kann 
man denn nun darauf warten, dass wirklich die Übertragung fertig ist?
LG Matthias
OP #5204277
Lesenswert?

Es wird alle 4 sec. eine Zahl abgeschlossen mit CR LF gesendet.
Beim Delay kommt die auch gut an, ohne das delay kommt im Terminal nur 
Zeichensalat (=offenbar Frameerror) an.

Die komplette Routine ist:
1
int main(void)
2
{
3
  uint32_t epoch_neu=0;
4
  uint32_t epoch_alt=0;
5
  char buffer[20];
6
  huart_init();
7
  huart_puts("Starte Watchdog\r\n");
8
  watchdog_enable();
9
  while (1) {
10
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
11
      epoch_neu=epoch;
12
    }
13
    if (epoch_neu != epoch_alt) {
14
      epoch_alt=epoch_neu;
15
      ultoa(epoch_neu,buffer,10);
16
      huart_puts(buffer);
17
      huart_putc(13);
18
    }
19
    if(huart_tx_empty()) {
20
    _delay_ms(1);
21
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
22
      sleep_mode();
23
    };
24
  }
25
}
Gast #5204285
Lesenswert?

Der UARt Sender des AVR hat einen zweistufigen Buffer. Erste Stufe ist 
das ausgehenden UDR Register (hat das eigentlich einen eindeutigen 
Namen?). Dessen Status fragst du ab.

Dahinter kommt ein Schieberegister, dass die Bits nacheinander über den 
TxD Pin raus schiebt. Dieses kontrollierst du nicht.

Wenn du ohne Delay in den Sleep Modus gehst, brichst du das Senden 
dieses letzten Bytes ab.

Der Delay ist also definitiv nötig. Wie lange er wirklich sein muss, 
kannst du ja mal selbst anhand der Baudrate ausrechnen.
Gast #5204292
Lesenswert?

Stefan U. schrieb:
> Der Delay ist also definitiv nötig

in der Doku steht aber:

Bit 6 – TXC: USART Transmit Complete
This flag bit is set when the entire frame in the Transmit Shift 
Register has been shifted out a


außerdem würde es nur das letzte Zeichen betreffen, was defekt ist. Wenn 
aber mehre falsche Zeichen ankommen muss woanders noch ein fehler sein.

@Matthias Taube
Lass das Programm doch mal im Simulator laufen.
Gast #5204322
Lesenswert?

Matthias T. schrieb:
> Nein, beim ATTINY 4313 ist das im UCSRB.
> Ich habe inzwischen vor dem sleep dne USART explizit ausgeschaltet und
> danach wieder ein, aber das hilft auch nichts.

ich bin auch der Meinung es müsste gehen. Ich habe auch Atmels wo ich 
das brauche weil ich damit von senden auf empfangen bei RS485 umschalte 
und ein sleep habe ich dort nicht verwendet.


Wie schon oben geschrieben, darf dadurch aber nicht die komplette 
Übertragung gestört werden, maximal das letzte Zeichen. Deswegen vermute 
ich den Fehler woanders. Aber dafür gibt es ja den Simulator, bei den 
kurzen code ist das doch einfach möglich.
Gast #5204345
Lesenswert?

Das TXC-Bit ist ein Flag-Bit.
Das heißt, wenn irgendwann mal Transmit Shift Register und
Transmit Buffer (UDR) leer geworden sind, ist es gesetzt.
Und es bleibt gesetzt, wenn kein "transmit complete interrupt"
ausgeführt wurde.

    The TXC flag bit is automatically cleared when a transmit
    complete interrupt is executed, or it can be cleared by
    writing a one to its bit location.

Abhilfe:
Wenn du mit dem UDRE-Interrupt arbeitest, musst du vor dem
Senden jeder Zahl mit CR, LF das TXC-Bit löschen.
Sonst musst du vor jedem zu sendenden Byte das TXC-Bit löschen.
- Dann zeigt das TXC-Bit den AKTUELLEN Zustand.

Ansonsten, wenn das Programm eh nicht viel zu tun hat, nimm
halt den passenden (!) Delay-Aufruf...
Gast #5204349
Lesenswert?

Jakob schrieb:
> Wenn du mit dem UDRE-Interrupt arbeitest, musst du vor dem
> Senden jeder Zahl mit CR, LF das TXC-Bit löschen.
> Sonst musst du vor jedem zu sendenden Byte das TXC-Bit löschen.
> - Dann zeigt das TXC-Bit den AKTUELLEN Zustand.

nein. Es muss es nur einmal auf 0 setzen, wenn er es abgefragt hat und 
es auf 1 steht.
#5204614
Lesenswert?

Da niemand Deine huart_xxx Funktionen kennt, ist wohl Hellsehen nötig.

Das TXC muß irgendwie gelöscht werden, sonst bleibt es auf ewig gesetzt. 
Z.B. direkt nach dem Schreiben auf UDR.

Hier mal ein Beispiel für die UART als SPI:
1
void max7221_out( uint8_t val )
2
{
3
  while( (UCSR1A & 1<<UDRE1) == 0 );
4
  UDR1 = val;
5
}
6

7
void max7221_last_out( uint8_t val )
8
{
9
  while( (UCSR1A & 1<<UDRE1) == 0 );
10
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
11
    UDR1 = val;
12
    UCSR1A = 1<<TXC1;                           // clear tx complete flag
13
  }
14
  while( (UCSR1A & 1<<TXC1) == 0 );             // until tx completed
15
}

Man beachte die atomare Kapselung, damit nicht die Daten schon gesendet 
wurden, weil ein langer Interrupt dazwischen kommt.
Auch kann ja bei jedem vorherigen Byte durch einen langen Interrupt der 
Puffer leer gelaufen sein, d.h. TXC gesetzt werden.
Nur Löschen nach dem Test ist also nicht sicher.
Gast #5204651
Lesenswert?

> Bit 6 – TXC: USART Transmit Complete
> This flag bit is set when the entire frame in the Transmit Shift
> Register has been shifted out a

Das ist eindeutig. Dann hatte ich es falsch in Erinnerung.
OP #5205437
Lesenswert?

Jakob schrieb:

> Sonst musst du vor jedem zu sendenden Byte das TXC-Bit löschen.
> - Dann zeigt das TXC-Bit den AKTUELLEN Zustand.

Vielen Dank. Ich lösche nun das TXC-Bit beim Schreiben auf das 
Datenregister und damit funktioniert alles wie es soll.

Es ist ein wenig verwirrend, dass man das Bit durch schreiben von 1 auf 
den Zustand 0 setzt, aber das wird schon seinen Grund haben.

LG
Matthias
Gast #5205506
Lesenswert?

Matthias T. schrieb:

> Es ist ein wenig verwirrend, dass man das Bit durch schreiben von 1 auf
> den Zustand 0 setzt, aber das wird schon seinen Grund haben.

Das ist ein sog. "Strobe-Bit". Das findest du in vieler Hardware.

Dir fehlt halt einfach nur die Erfahrung und die Hardware-Kenntnisse... 
Wäre es anders, würde dir die Sache völlig logisch erscheinen...

Die Sache beruht darauf, das die Hardware weiss, ob es sich um einen 
Schreib- oder Lesezugriff handelt. Beim Lesezugriff wird der Status des 
Ausgangs eines Flipflops geliefert, beim Schreibzugriff hingegen für 
einen Takt das übergebene Signal auf den Reset-Eingang desselben 
Flipflops ausgegen.

Beide Zugriffe erfolgen über das gleiche Registerbit, hantieren mit 
demselben Flipflop, betreffen aber eben nicht das gleiche Signal dieses 
Flipflops.

Man hätte statt dessen natürlich auch ein TCXRST-Bit in irgendeinem 
zusätzlichen IO-Register definieren können. Aber wozu? Hätte nur den 
Aufwand für die Hardware erhöht...
Gast #5205529
Lesenswert?

Peter D. schrieb:

> Man hätte es auch einfach logisch machen können, wie es z.B. beim 8051
> der Fall ist. D.h. 0 setzen löscht das Bit, 1 setzen setzt es. Man kann
> daher beim 8051 Interrupts auch per Befehl auslösen. Ich hab das auch
> schonmal benötigt.

Das ist natürlich auch möglich, erfordert aber signifikant zusätzliche 
Logik. Der Schreibzugriff muss dann nämlich abhängig vom Pegel entweder 
auf RESET oder SET des FF gerouted werden. Das ist in synchroner Logik 
in einem Takt nur mit relativ hohem Zusatzaufwand zu schaffen.

Und: es könnte blöde Nebeneffekte geben. Ungewolltes Abwürgen eines noch 
nicht bearbeiteten IRQ z.B... Das könnte man nur dadurch vermeiden, die 
Zahl der Hardwareregister massiv zu erhöhen. Im Prinzip wäre dann wohl 
ein Register pro Strobe-Bit fällig.

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