mikrocontroller.net

Forum: Compiler & IDEs Text senden über UART im Interruptbetrieb


Autor: KlausF (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi, ich versuche mich gerade an dem Aufbau einer seriellen komunikation 
zwischen einem atmega88 und dem rechner.

einzelne zeichen zu senden war nicht das problem. Bei dem versuch eine 
Zeile zu senden stoße ich auf probleme.

ziel ist es, den String in einen puffer zu kopieren und den dann nach 
und nach zu senden. dass soll alles im hintergrund geschehen, gesteuert 
durch den interrupt vector "USART_TX_vect".

mein code sieht wie folgt aus:
#ifndef F_CPU
#define F_CPU 12000000UL  // Systemtakt in Hz - Definition als unsigned long beachten 
                         // Ohne ergeben sich unten Fehler in der Berechnung
#endif
 
#define BAUDRATE 9600UL      // Baudrate
 
// Berechnungen
#define BAUD_PRESCALE (F_CPU/(16*BAUDRATE)-1)       
#define BAUD_REAL (F_CPU/(16*(BAUD_PRESCALE+1)))    // Reale Baudrate
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUDRATE)      // Fehler in Promille, 1000 = kein Fehler.

#if ((BAUD_ERROR<990) || (BAUD_ERROR>1010))
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! 
#endif

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

char    inbuffer[32];              //einganspuffer
char     *inbufferpos=&inbuffer[0];          //positions zeiger
char    outbuffer[32];              //ausgangspuffer
char    *outbufferpos=&outbuffer[0];          //positionszeiger

void send(char * pstr){
  while ((1<<TXCIE0)& UCSR0B);    //warten bis Senderoutine fertig ist
  outbufferpos=&outbuffer[0];      //Zeiger auf Anfang setzen
  while (*pstr!=0x00){        //prüfe auf String Ende
    *outbufferpos = *pstr;      //kopiere Zeichen
    outbufferpos++;          //zeiger incrementieren
    pstr++;
  }
  *outbufferpos=0x00;          //String-Ende in Puffer schreiben

  outbufferpos=&outbuffer[0];      //Zeiger auf Anfang setzen
  
  UCSR0B |= (1<<TXCIE0);        //Erfolgreich gesendet interrupt aktiviren

  UDR0= *outbuffer;          //erstes Zeichen senden
  outbufferpos++;            //zeiger auf 2. Zeichen setzen
}

void long_delay(uint16_t ms) {
    for(; ms>0; ms--) _delay_ms(1);
}

int main()
{
//USART Initialisieren
UCSR0B |= (1 << RXEN0) | (1 << TXEN0);     // Turn on the transmission and reception circuitry 
UCSR0C |= (1 << UCSZ00) | (1 << UCSZ01);   // Use 8-bit character sizes 
UBRR0L = BAUD_PRESCALE;           // Load lower 8-bits of the baud rate value into the low byte of the UBRR register 
UBRR0H = (BAUD_PRESCALE >> 8);         // Load upper 8-bits of the baud rate value into the high byte of the UBRR register 
UCSR0B |= (1 << RXCIE0);           // Enable the USART Recieve Complete interrupt (USART_RXC)

sei();
char str[]="Hallo Welt";

while (1) {

  while (!(UCSR0A & (1<<UDRE0)));
  send(& str[0]);

  long_delay(500);
  PORTB |= (1<<PORTB0);
  long_delay(500);
  PORTB &=~(1<<PORTB0);

  }
}

ISR(USART_TX_vect)
{
  
  if (*outbufferpos!=0x00)
  {  
    while (!(UCSR0A & (1<<UDRE0)));
    UDR0=*outbufferpos;
    outbufferpos++;
  } 
  else 
  {
    while (!(UCSR0A & (1<<UDRE0)));
    UDR0=0x0a;
    while (!(UCSR0A & (1<<UDRE0)));
    UDR0=0x0d;
    while (!(UCSR0A & (1<<UDRE0)));
    UCSR0B &= ~(1<<TXCIE0);
  
  }
}


Nach dem Reset wird genau einmal "Hallo Welt" übertragen. Dannach 
erhalte ich nur noch das "H" im sekundentakt.
also laut dem internen debugger von avr-studio funktioniert die 
angelegenheit, auch mehrmals, aber die realität sieht leider anders aus
Jemand ne idee, wo mein fehler liegt?

grüße klaus

Autor: Uwe ... (uwegw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wieso pollst du im Interrupt das UDRE0-Bit? Das ist doch gerade der Witz 
an der Sache mit den Interrupts, dass man eben nicht mehr pollen und auf 
irgendwas warten muss..
Generell ist es absolut tabu, in einer ISR auf irgendwelche Ereignisse 
zu warten! Eine ISR muss so schnell wie möglich verlassen werden, und 
darf auf keinen Fall das System länger blockieren.

Außerdem wäre ein Fifo als Zwischenspeicher wesentlich flexibler. Bei 
dir kannst du immer nur 32 Zeichen am Stück schreiben, und musst dann 
warten, bis alle gesendet wurden. Wenn du nun zB zweimal 4 Zeichen 
senden willst, musst du nach dem ersten Mal warten, bis diese Zeichen 
gesendet wurden. Bei nem richtigen Fifo könntest du fortlaufend 
schreiben, solange noch Platz im Fifo ist.

Autor: KlausF (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hi, schon mal Danke für die Antwort.
Über dieses FiFO bin ich auch schon drübergestolpert. Es war mir nur 
viel zu kompliziert undunübersichtlich.

Das "pollen" ist eigendlich nur nochmal ne Sicherheitsvorkehrung, in der 
regel werden sehr wenig Daten übertragen.

andere ideen?

grüße

Autor: Lutz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Für Zeichen würd ich lieber unsigned char statt char nehmen; um ganz 
sicher zu gehen uint8_t.
>while (*pstr!=0x00){        //prüfe auf String Ende
würde ich generell '\0' nehmen     //prüfe auf String Ende

Aber das wird nichts an dem Problem ändern.

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> UCSR0B |= (1 << RXCIE0);           // Enable the USART Recieve Complete interrupt (USART_RXC)

Wo ist die Interrupt-Routine dafür?  Wenn es keine gibt (und alles 
deutet darauf hin), mach das mal weg.

Autor: KlausF (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
danke für den hinweis, aber auch das war nicht des Räzels Lösung.
Ich habe die zwei Steuerzeichen im 'else' Teil der Interruptroutine mit 
in den Puffer geschrieben, und alle pollings aus der Interruptroutine 
entfernt. Jetzt läuft es.
danke an alle!!

grüße

Autor: Uwe ... (uwegw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn du das Programm nicht bloß schreibst, um was dabei zu lernen, 
sondern einfach nur die Schnittstelle zum Laufen bringen willst, dann 
würde ich dir die fertige uart-lib von Peter Fleury empfehlen:
http://homepage.hispeed.ch/peterfleury/avr-software.html
Die arbeitet mit nem Fifo, und läuft bei mir seit Jahren in sämtlichen 
AVR-Projekten ohne Probleme...

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dein Problem ist das:
    while (!(UCSR0A & (1<<UDRE0)));
    UDR0=0x0a;
    while (!(UCSR0A & (1<<UDRE0)));
    UDR0=0x0d;
    while (!(UCSR0A & (1<<UDRE0)));
    UCSR0B &= ~(1<<TXCIE0);
Du stopfst Daten in den UART und schaltest dann den TX-Interrupt ab. Das 
hat zur Folge, dass beim nächsten "UCSR0B |= (1<<TXCIE0);" in send das 
Interrupt-Flag noch von der letzten Übertragung gesetzt ist, und daher 
sofort ein TX-Interrupt ausgelöst wird. Da outbufferpos nicht volatile 
ist, ist der geänderte Wert in send noch nicht zurück gespeichert, 
weshalb für diesen sofortigen TX-Interrupt der Pointer immer noch auf 
die Null-Terminierung zeigt, und er sofort wieder in den else-Zweig 
läuft (wo der Interrupt gleich wieder abgeschaltet wird).

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.