Forum: Mikrocontroller und Digitale Elektronik UART String Problem Atmega8L


von Skywalker1981 (Gast)


Angehängte Dateien:

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

von Severino R. (severino)


Lesenswert?

Hat der ATmega8 überhaupt einen Empfangsbuffer? Wenn ja, wie gross?
1
   do{
2
      string[i]=UDR;
3
    //  UDR=0;
4
    //  while (UCSRA&32);
5
      wait_ms (100); // Damit alle Zeichen im Buffer angekommen sind
6
    //lcd_write(UDR);
7
      i++;
8
    }
9
    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.

von Karl H. (kbuchegg)


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-Tutorial#Der_UART

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Ich würde die UART Initialisierung vor die While-Schleife ziehen, also 
diesen Abschnitt:
1
    //sprintf(str,"A:%d",UCSRA&32);
2
    //lcd_write1(str);
3
    //lcd_write1("A1");
4
    while(!(UCSRA&128));
5
6
    UCSRB |= (1<<TXEN);                // UART TX einschalten
7
      UCSRB |= (1<<RXEN);      // UART RX einschalten
8
        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
1
    do{
2
      string[i]=UDR;
3
    //  UDR=0;
4
    //  while (UCSRA&32);
5
      wait_ms (100); // Damit alle Zeichen im Buffer angekommen sind
6
    //lcd_write(UDR);
7
      i++;
8
    }
9
    while(UCSRA&128);


zu
1
    while(UCSRA & 128)
2
    {
3
      string[i] = UDR;
4
      i++;
5
6
      // Jetzt die Übertragungszeit für ca. 1-2 Zeichen warten
7
      // in der Hoffnung, dass der Sender das nächste Zeichen
8
      // auf den Weg gegeben hat und die Bedingung der while
9
      // schleife wieder erfüllt ist (Übertragung nicht zuende)
10
      // oder nicht erfüllt ist (alle Zeichen gesendet und empfangen)
11
      //
12
      // Die Wartezeit ist kritisch, weil bei zu langer Wartezeit
13
      // Zeichen verloren gehen. 100 ms wie im Originalcode halte
14
      // ich für deutlich zuviel. Bei 9600 Baud 8N1 liegt die 
15
      // Übetragungszeit bei 
16
      // 1 / 9600 Bit_pro_Sekunde / (1+8+1) Bit_pro_Zeichen 
17
      // = ca. 1.04 ms
18
      //
19
      // Sehr stabil wird ein solcher Code mit diesem Quasi-Timeout 
20
      // aber nicht.
21
22
      wait_ms (2); 
23
    }


Oder besser die bereits vorhandene Funktion getchar() benutzen
1
    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...

von Skywalker1981 (Gast)


Angehängte Dateien:

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

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Du solltest nur dann warten, wenn es nötig ist, d.h. wenn kein Zeichen 
an UART anliegt. Also statt:
1
    while( !( UCSRA & (1<<RXC)) );;
2
3
    i=0;
4
    do{
5
      string[i]=UDR;
6
      i++;
7
      wait_ms(2);
8
    }
9
    while(UCSRA&128);

diesen Code
1
// Max. Anzahl der 1ms Warteschleifen auf ein Zeichen an UART
2
#define MAX_TIMEOUT 10
3
#define ZEICHEN_IST_AN_UART_VORHANDEN() (UCSRA & (1<<RXC))
4
5
  // Die Empfangsschleife wird verlassen, wenn der Sender 
6
  // MAX_TIMEOUT * 1ms lang nichts sendet.
7
8
  {
9
    unsigned char timeout = MAX_TIMEOUT;
10
    i = 0;
11
12
    do 
13
    {
14
      // getchar() ist eine wartende Funktion und das warten
15
      // stört den vorgesehenen Programmablauf (da Anzahl der 
16
      // Zeichen ist unklar & Endezeichen ist nicht definiert)
17
      //
18
      // Deshalb vor dem Aufruf zuerst prüfen, ob ein Zeichen 
19
      // empfangen wurde.
20
21
      if (ZEICHEN_IST_AN_UART_VORHANDEN())
22
      {
23
        // UDR auslesen
24
        char c = UDR;
25
26
        // Wenn Puffer noch nicht voll, dann Zeichen speichern
27
        if (i < (32-1))
28
          string[i++] = c;
29
30
        // Das nächste Zeichen erhält wieder die Chance 
31
        // auf die komplette Wartezeit
32
33
        timeout = MAX_TIMEOUT;
34
      }
35
      else
36
      {
37
        // Im Moment ist Kein Zeichen in UDR vorhanden
38
        // Jetzt eine kleine Zeitspanne warten und dann
39
        // neuer Versuch
40
41
        // #include <util/delay.h> muss vorhanden sein
42
        // F_CPU muss definiert sein
43
        // Es muss mit Optimierung kompiliert werden
44
45
        _delay_ms(1); 
46
47
        // Nicht bis zum Nimmerleinstag warten, sondern
48
        // max. eine vorher festgelegte Anzahl (Zeit) lang
49
50
        timeout--;
51
      }
52
53
    } while (timeout);
54
  }

von David M. (md2k7)


Lesenswert?

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

von Stefan B. (stefan) Benutzerseite


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.

von Karl H. (kbuchegg)


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.

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.