Forum: Mikrocontroller und Digitale Elektronik attiny usart Ende der Datenübertragung


von Matthias T. (mati123)


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

von Peter II (Gast)


Lesenswert?

Matthias T. schrieb:
> Ansonsten kommen beim Empfänger unsinnige Zeichen an.

zusätzlicher oder statt der erwarteten Daten?

Was kommt genau an, und was wird erwartet?

von Matthias T. (mati123)


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
}

von Stefan F. (Gast)


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.

von Peter II (Gast)


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.

von Heinz (Gast)


Lesenswert?

Ich hab leider nur das Datenblatt von einem ATMega, aber bei dem ist 
UDRIE auch im UCSRA, nicht UCSRB

von Matthias T. (mati123)


Lesenswert?

Heinz schrieb:
> Ich hab leider nur das Datenblatt von einem ATMega, aber bei dem ist
> UDRIE auch im UCSRA, nicht UCSRB

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.

von Peter II (Gast)


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.

von Peter II (Gast)


Lesenswert?

Nachtrag:

Da du kein Interupt hast, muss du das TXC flag auch noch löschen, sonst 
bleibt es ja auf "1".

> or it can be cleared by writing a one to its bit location.

von Jakob (Gast)


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...

von Peter II (Gast)


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.

von Peter D. (peda)


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.

: Bearbeitet durch User
von Stefan F. (Gast)


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.

von Matthias T. (mati123)


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

von c-hater (Gast)


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...

von Peter D. (peda)


Lesenswert?

c-hater schrieb:
> Man hätte statt dessen natürlich auch ein TCXRST-Bit in irgendeinem
> zusätzlichen IO-Register definieren können.

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.

von c-hater (Gast)


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.

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.