mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik UART String Problem Atmega8L


Autor: Skywalker1981 (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich habe folgendes Problem.
Wenn ich einen String z.B. 1234567 an den µC (atmega8) schicke erhalte 
ich auf dem Display nur die ersten drei zeichen und das letzte also 
1237.
Bis 4 Zeichen funktioniert alles.

Hat jemand Rat warum er die zwischen Zeichen "verschluckt"???

Als Anhang mein C Programm.

Über eine Hilfe wäre ich sehr dankbar.

Gruß


Denis

Autor: Severino R. (severino)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hat der ATmega8 überhaupt einen Empfangsbuffer? Wenn ja, wie gross?
   do{
      string[i]=UDR;
    //  UDR=0;
    //  while (UCSRA&32);
      wait_ms (100); // Damit alle Zeichen im Buffer angekommen sind
    //lcd_write(UDR);
      i++;
    }
    while(UCSRA&128);

Das wait_ms() ist nicht sehr geschickt, lieber warten, bis ein Zeichen 
empfangen wurde und es dann sofort auslesen. So wie im obigen Code 
könnte ja ein Zeichen verpasst werden.

Leider kenne ich mich mit AVRs nicht so gut aus, aber die Flags für die 
Register sollten doch auch symbolisch definiert sein, oder?
UCSRA&128 und so ist mühsam zum Lesen.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
In deinem Programm ist einiges verbockt.

Ein grundsätzlicher Programmaufbau sieht so aus

int main()
{
   initialisiere die Hardware
   schalte Ports richtig
   initialisiere was auch immer das Programm onst noch
   so braucht, zb UART oder ADC


   while( 1 ) {
     arbeite mit der Hardware
   }
}

Es gibt (zumindest in deinem Programm) keinen Grund, die
Hardware (in deinem Fall die UART) innerhalb der Hauptschleife
immer wieder neu zu initialisieren. Ganz im Gegenteil, das
kann auch kontraproduktiv sein. Was denkst du wird wohl
passieren, wenn die UART gerade ein Zeichen empfängt und
dein Code initialisiert die UART neu.


    do{
      string[i]=UDR;
    //  UDR=0;
    //  while (UCSRA&32);
      wait_ms (100); // Damit alle Zeichen im Buffer angekommen sind
    //lcd_write(UDR);
      i++;
    }

Woher weist du, wenn du das UDR Register ausliest, dass die
UART überhaupt ein Zeichen empfangen hat?
Antwort: Du kannst es nicht wissen. Aber die UART zeigt es
dir mit einem speziellen Bit an. Du musst nur solange warten,
bis dieses Bit den Empfang signalisiert.
Aber: Zuerst auf das Signal warten und dann das Zeichen holen!

    while (!(UCSRA & (1<<RXC)))   // warten bis Zeichen verfuegbar
        ;
    string[i] = UDR;

Das mit dem Warten ist Unsinn, du weist nicht wie schnell dein
Benutzer tippt!

http://www.mikrocontroller.net/articles/AVR-GCC-Tu...

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich würde die UART Initialisierung vor die While-Schleife ziehen, also 
diesen Abschnitt:

    //sprintf(str,"A:%d",UCSRA&32);
    //lcd_write1(str);
    //lcd_write1("A1");
    while(!(UCSRA&128));

    UCSRB |= (1<<TXEN);                // UART TX einschalten
      UCSRB |= (1<<RXEN);      // UART RX einschalten
        UCSRC |= (1<<URSEL)|(3<<UCSZ0);    // Asynchron 8N1


z.B. in initUART() machen.

Und ich würde vor dem Abholen von Zeichen beim UART prüfen, ob Zeichen 
vorhanden sind, also diesen Block umstellen

    do{
      string[i]=UDR;
    //  UDR=0;
    //  while (UCSRA&32);
      wait_ms (100); // Damit alle Zeichen im Buffer angekommen sind
    //lcd_write(UDR);
      i++;
    }
    while(UCSRA&128);


zu

    while(UCSRA & 128)
    {
      string[i] = UDR;
      i++;

      // Jetzt die Übertragungszeit für ca. 1-2 Zeichen warten
      // in der Hoffnung, dass der Sender das nächste Zeichen
      // auf den Weg gegeben hat und die Bedingung der while
      // schleife wieder erfüllt ist (Übertragung nicht zuende)
      // oder nicht erfüllt ist (alle Zeichen gesendet und empfangen)
      //
      // Die Wartezeit ist kritisch, weil bei zu langer Wartezeit
      // Zeichen verloren gehen. 100 ms wie im Originalcode halte
      // ich für deutlich zuviel. Bei 9600 Baud 8N1 liegt die 
      // Übetragungszeit bei 
      // 1 / 9600 Bit_pro_Sekunde / (1+8+1) Bit_pro_Zeichen 
      // = ca. 1.04 ms
      //
      // Sehr stabil wird ein solcher Code mit diesem Quasi-Timeout 
      // aber nicht.

      wait_ms (2); 
    }


Oder besser die bereits vorhandene Funktion getchar() benutzen

    string[i++] = getchar();


Allerdings brauchst du dann auch noch einen Weg um festzustellen, ob 
alle Zeichen empfangen wurden. Welcher Weg das ist, hängt vom Sender ab 
z.B. feste Anzahl Zeichen oder bekanntes Endezeichen oder feste 
Sendedauer...

Autor: Skywalker1981 (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

habe mein Programm etwas umgestaltet aber immer noch kein Erfolg.
Mein String den ich vom PC an den µC Sende z.B. 123456
erhalte ich auf dem LCD Display 1236. Die ersten drei Zeichen sind ok 
und dann schneidet er 45 raus und hängt das letzte an.

Hingegen wenn mein String nur aus max. 4 Zeichen besteht ist alles OK.
z.B. 1234

Hat jemand Rat???

Als Anhang nochmals das C Programm

Gruß

Denis

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du solltest nur dann warten, wenn es nötig ist, d.h. wenn kein Zeichen 
an UART anliegt. Also statt:

    while( !( UCSRA & (1<<RXC)) );;

    i=0;
    do{
      string[i]=UDR;
      i++;
      wait_ms(2);
    }
    while(UCSRA&128);



diesen Code


// Max. Anzahl der 1ms Warteschleifen auf ein Zeichen an UART
#define MAX_TIMEOUT 10
#define ZEICHEN_IST_AN_UART_VORHANDEN() (UCSRA & (1<<RXC))

  // Die Empfangsschleife wird verlassen, wenn der Sender 
  // MAX_TIMEOUT * 1ms lang nichts sendet.

  {
    unsigned char timeout = MAX_TIMEOUT;
    i = 0;

    do 
    {
      // getchar() ist eine wartende Funktion und das warten
      // stört den vorgesehenen Programmablauf (da Anzahl der 
      // Zeichen ist unklar & Endezeichen ist nicht definiert)
      //
      // Deshalb vor dem Aufruf zuerst prüfen, ob ein Zeichen 
      // empfangen wurde.

      if (ZEICHEN_IST_AN_UART_VORHANDEN())
      {
        // UDR auslesen
        char c = UDR;

        // Wenn Puffer noch nicht voll, dann Zeichen speichern
        if (i < (32-1))
          string[i++] = c;

        // Das nächste Zeichen erhält wieder die Chance 
        // auf die komplette Wartezeit

        timeout = MAX_TIMEOUT;
      }
      else
      {
        // Im Moment ist Kein Zeichen in UDR vorhanden
        // Jetzt eine kleine Zeitspanne warten und dann
        // neuer Versuch

        // #include <util/delay.h> muss vorhanden sein
        // F_CPU muss definiert sein
        // Es muss mit Optimierung kompiliert werden

        _delay_ms(1); 

        // Nicht bis zum Nimmerleinstag warten, sondern
        // max. eine vorher festgelegte Anzahl (Zeit) lang

        timeout--;
      }

    } while (timeout);
  }


Autor: David Madl (md2k7)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Aber z.B. bei 115200 Baud könnte es sein, dass mehrere Bytes innerhalb 
der Millisekunde ankommen, die du wartest...

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
David Madl wrote:
> Aber z.B. bei 115200 Baud könnte es sein, dass mehrere Bytes innerhalb
> der Millisekunde ankommen, die du wartest...

Stimmt. Und wie könnte man das berücksichtigen?

Mir würde dazu einfallen, dass die Wartefunktion nicht nur trödelt, 
sondern auch den Status der UART überwacht und vorzeitig das Trödeln 
abbricht, wenn ein Zeichen angekommen ist.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan "stefb" B. wrote:
> David Madl wrote:
>> Aber z.B. bei 115200 Baud könnte es sein, dass mehrere Bytes innerhalb
>> der Millisekunde ankommen, die du wartest...
>
> Stimmt. Und wie könnte man das berücksichtigen?

Wie eigentlich immer, wenn es um eine zeitlich begrenzte
Warterei geht und gleichzeitig etwas überwacht werden muss:
* Timer aufsetzen, der die Zeit runterzählt.
* In der Warteschleife neben dem zu überwachenden Signal auch
  noch abprüfen, ob der Timer angeschlagen hat.

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.