www.mikrocontroller.net

Forum: Compiler & IDEs UART Sendepuffer


Autor: Laplace (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Forum!

Ich arbeite mit WinAVR auf einem ATmega8 und realisiere gerade eine
MIDI-Anwendung. Über einen Sendepuffer und den UART möchte ich meine
Messages versenden. Leider sendet der UART nichts, wenn ich ihn nutze.

Das ist mein Puffer:
BYTE UART_tx_buffer[16] = {0};  //UART Sendepuffer

Hier hab ich mir noch einen "Descriptor" dazu gebaut:
struct buffer_desc {
  volatile BYTE head:4;
  volatile BYTE tail:4;
} RX_desc = {0},TX_desc = {0};

So beschreibe ich den Puffer:
void UART_tx(BYTE data)
{
  cli();
  UART_tx_buffer[TX_desc.head++] = data; //Wert in den Puffer schreiben
  if(TX_desc.head == TX_desc.tail + 1) //Erster Wert im Puffer?
    UCSRB |= (1<<TXCIE);    //Enable TX-INT
  sei();
}

Und hier die Int-Routine:
SIGNAL(SIG_UART_TRANS)
{
  UDR = UART_tx_buffer[TX_desc.tail++];
  if(TX_desc.head == TX_desc.tail) //Puffer leer?
    UCSRB &= ~(1<<TXCIE);    //Disable TX-INT
}

Wenn ich die Standard-Sendefunktion (warten auf das UCSRA, ohne Puffer
und INT) verwende funktioniert alles wunderbar.

Fällt euch was auf? Hab ich nen Denkfehler?

Grüße
Laplace

Autor: Fritz Ganter (fritzg)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich hab das noch nie gemacht, aber was mir auffällt:

Wozu schaltest du den TX Interrupt aus und nicht wieder ein?
Der Interrupt wird nur dann ausgelöst wenn das Zeichen gesendet wurde,

dann sende einfach das nächste Zeichen und gut is.

Autor: Laplace (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sobald der letzt Wert im Puffer gesendet wurde (head == tail) wird der
TX-INT deaktiviert. (Wenn es nichts zu senden gibt, interessiert es ja
nicht, ob er wieder bereit ist) Wenn ich jetzt ein neues Zeichen in den
Puffer schreibe (head == tail + 1) wird der TX-Int wieder aktiviert.
Jetzt sollte wieder ein TX-INT ausgelöst werden, weil der UART ja
nichts zu senden hat. Er bleibt so lange aktiviert, bis der Puffer leer
ist.

Allerdings bin ich mir nicht sicher, ob allgemein bei den AVRs auch
dann ein TX-INT ausgelöst wird, wenn schon länger nichts zum senden
ansteht und das Transmit Compete Interrupt Enable Bit gesetzt wird.
Weiß das jemand?

Autor: Fritz Ganter (fritzg)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der Interrupt löst erst dann wieder aus, wenn du ihm ein Zeichen zum
senden gegeben hast und er damit fertig ist. Also müsstest du in
UART_tx das erste Zeichen manuell ins UDR schieben, der Rest läuft dann
wieder im Interrupt.

Autor: Laplace (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das scheint auch nicht das Problem zu sein. Ich erzeuge jetzt den ersten
Interrupt manuell aber er sendet trotzdem nicht:

void UART_tx(BYTE data)
{
  cli();
  UART_tx_buffer[TX_desc.head++] = data; //Wert in den Puffer schreiben
  if(TX_desc.head == TX_desc.tail + 1) //Erster Wert im Puffer?
  {
    UCSRB |= (1<<TXCIE);    //Enable TX-INT
    UCSRA |= (1<<TXC); //Ersten Interrupt manuell erzeugen
  }
  sei();
}

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
UCSRA |= (1<<TXC); //Ersten Interrupt manuell erzeugen

Das funktioniert nicht.  Damit löschst du das Flag.  Dieser
Interrupt lässt sich nicht ,,manuell'' erzeugen.

Für deine Vorgehensweise empfiehlt es sich, den UDRE-Interrupt
stattdessen zu benutzen.

Autor: Laplace (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hast recht, hab ich auch grad gelesen...:]
Ich werd morgen mal versuchen, das ganze mit dem UDRE-INT zu
realisieren.

Autor: Patrick Dohmen (oldbug) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ein kleiner Hinweis:
void UART_tx(BYTE data)
{
  cli(); 
  UART_tx_buffer[TX_desc.head++] = data; //Wert in den Puffer
schreiben
  if(TX_desc.head == TX_desc.tail + 1) //Erster Wert im Puffer?
  {
    UCSRB |= (1<<TXCIE);    //Enable TX-INT
    UCSRA |= (1<<TXC); //Ersten Interrupt manuell erzeugen
  }
  sei();
}

Damit versaust Du Dir übrigens die Möglichkeit, 'UART_tx' aus einer
Funktion aufzurufen, die bereits das globale Interruptflag gelöscht
hat. Besser so:
void UART_tx(BYTE data)
{
  unsigned char sreg_buf = SREG;
  cli();

  UART_tx_buffer[TX_desc.head++] = data; //Wert in den Puffer
schreiben
  if(TX_desc.head == TX_desc.tail + 1) //Erster Wert im Puffer?
  {
    UCSRB |= (1<<TXCIE);    //Enable TX-INT
    UCSRA |= (1<<TXC); //Ersten Interrupt manuell erzeugen
  }

  SREG = sreg_buf;
}

Damit wird das globale Interruptfalg nur wieder gesetzt, wenn es vor
dem 'cli' schon gesetzt war.

Autor: Laplace (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für den Hinweis. Werd ich berücksichtigen.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.