Forum: Compiler & IDEs uart_getc() von fleury liest keine Zeichenkette


von Matschi (Gast)


Lesenswert?

Hallo,
ich möchte mit einem ATmega16 Daten über RS232 von einem PC verarbeiten. 
Die Daten sind Strings, mit einer maximalen Länge von 6 (z.B. 'V12345').
Dazu habe ich folgendes (Unter)Programm geschrieben:
1
void Main_Get_Uart_Analyse(void){
2
unsigned int uiRXZeichen;
3
unsigned int uiCount;
4
char String[10];
5
char s[10];
6
uiCount = 0;
7
uiRXZeichen = uart_getc();
8
if (uiRXZeichen != UART_NO_DATA ){
9
    while( uiRXZeichen != UART_NO_DATA ){
10
      String[uiCount] = uiRXZeichen;
11
      uiCount++;
12
      uiRXZeichen = uart_getc();
13
    }
14
    String[uiCount] = '\0';
15
//    uart_puts(String);      // wegen uart_puts_hex auskommentiert
16
    itoa(uiCount,s,10);
17
    uart_puts(s);
18
    uart_puts_hex(String);    // Siehe Forum http://www.mikrocontroller.net/topic/57429#new
19
  }
20
  else{
21
  }
22
}
Das Programm sollte einen String einlesen, und ihn gleich wieder 
ausgeben.
Sende ich mit Br@y Terminal ein 'A' bekomme ich zurück:
1 41 A   (wie erwartet).
|  | ^--- Gesendetes Zeichen in ASCII  (über Funktion uart_puts_hex)
|  ^----- Gesendetes Zeichen in HEX
^-------- Wert der Zählvariable uiCount

(uart_puts_hex() siehe: Beitrag "anwendung uart_getc() von fleury")

Sende ich aber ein 'AB' bekomme ich '1 41 A 1 42 B' zurück. Erwartet 
hätte ich aber '2 41 A 42 B'. Mir scheint, das mein Programm nur einen 
String der Länge 1 einliest, und beim nächsten Programmdurchlauf das 
nächste Zeichen.

Sende ich 'ABCDE' ist das Verhalten genauso. Ich bekommen '1 41 A 1 42 B 
1 43 C 1 44 D 1 45 E' zurück.

Wie kann ich einen ganzen String einlesen, und nicht n mal 1 Zeichen? 
Ich kann sonst nicht unterscheiden, wann ein String zu ende ist bzw. wo 
der neue anfängt.

Der Mega16 läuft mit 11,0592MHz. Die Boud rate ist 19200. Programmier 
mit 'WinAVR 20070525' und 'AVR Studio 4.12.498 Service Pack 4'

Ich sollte noch erwähnen, dass ich "Gelegenheitsprogrammierer" bin. 
Meine Erfahrung geht daher eher gegen "wenig". Im diesem Forum habe ich 
dieses Problem leider nicht gefunden.

von Christian Schifferle (Gast)


Lesenswert?

Im Vergleich zm Speed deines uC kommen die Daten auf der seriellen 
Schnittstelle viiiieeeeeel langsamer rein. D. h. wenn du ein Zeichen auf 
der Schnittstelle siehst kannst du nicht erwarten, dass alle anderen 
Zeichen auch schon da sind. Du musst also warten.
Am besten liest du wirklich Zeichen für Zeichen und wartest auf ein 
spezielles Endezeichen, z.B. Wagenrücklauf (Hex 0x0d). Dann kannst du 
den empfangenen String abschliessen mit dem NULL-Byte und auswerten.
Du solltes aber sicherheitshalber noch auf Pufferüberlauf testen, damit 
das Programm nicht abstürzen kann wenn mal kein Endezeichen kommt.

von Karl H. (kbuchegg)


Lesenswert?

Matschi wrote:

> Sende ich aber ein 'AB' bekomme ich '1 41 A 1 42 B' zurück. Erwartet
> hätte ich aber '2 41 A 42 B'. Mir scheint, das mein Programm nur einen
> String der Länge 1 einliest, und beim nächsten Programmdurchlauf das
> nächste Zeichen.

Das ist nicht der Fall.
Das Problem ist ein ganz anderes.
Mikroprozessoren können nun mal nicht in die Zukunft sehen.
Woher soll denn uart_getc() wissen, dass da keine Daten *mehr
kommen werden*. uart_getc() kann dir nur mitteilen: Im Moment
sind keine Daten verfügbar. Das heist aber nicht, das keine
mehr kommen werden. Insbesondere dauert die Üermittlung
eines Zeichens ja auch seine Zeit. Wenn du also während
eine Übermittlung eines Zeichens gerade läuft die uart_getc()
aufrufst, so wird dir uart_getc() mit einem UART_NO_DATA
sagen, das zur Zeit kein Zeichen da ist. Und das völlig
zu recht.

> Wie kann ich einen ganzen String einlesen, und nicht n mal 1 Zeichen?
> Ich kann sonst nicht unterscheiden, wann ein String zu ende ist bzw. wo
> der neue anfängt.

Du musst dir ein spezielles Zeichen definieren, welches genau
diese Aufgabe übernimmt: dem Empfänger mitzuteilen, daß jetzt
der String zu Ende ist.

von Matschi (Gast)


Lesenswert?

Ihr seid ja richtig schnell gewesen. Danke!
Ich habe jetzt ein 'Endezeichen' definiert auf das ich warte und nun 
funktioniert alles sehr schön.

Habe auch gleich die Prüfung auf 'UART Frame Error', 'UART Overrun 
Error', 'Buffer overflow error' und 'too many characters' eingebaut.
Da schließen sich mir aber gleich zwei neue Fragen an:

1) 'Buffer overflow error' und 'too many characters' kann ich mit 
Br@yTerm simulieren, indem ich eine sehr lange Zeichenkette sende. Wie 
kann ich die anderen Fehler simulieren? (ist nicht ganz so wichtig, nur 
Interesse halber)

2) Was macht man, wenn man einen dieser Fehler erkannt hat? Z.Z. gebe 
ich einfach eine Fehlermeldung über uart_puts_P() aus (wie in dem 
Beispiel von fleury) und fange wieder bei 0 an die nächsten Zeichen 
einzulesen.

Hier die Code Fragmente dazu.
1
....
2
while( (uiUartRXZeichen != UART_NO_DATA) && // War ein Zeichen im Buffer?         
3
       (uiUartRXCount   <      STRINGMAX)){ // ist der String zu lang?
4
....
5
   if ( uiUartRXZeichen & UART_FRAME_ERROR ){  
6
      //
7
      // Framing Error detected
8
      //
9
      uart_puts_P("\nUART Frame Error!");
10
      uiUartRXCount = STRINGMAX; // bei Error Schleife verlassen
11
   }
12
....
13
} // ende while
14
15
if (uiUartRXCount == STRINGMAX){   // Wenn ein Error erkannt wurde
16
   uart_puts_P("Error or too many characters received\n");
17
   uiUartRXCount = 0;              // Zählt die empfangenen Zeichen
18
}
19
....
Es funktioniert soweit. Das System "fängt" sich bei "Overflow" und "too 
manny characters", wenn alle Zeichen ausgelesen wurden. Das scheint mir 
aber nicht optimal zu sein.

von Karl H. (kbuchegg)


Lesenswert?

Matschi wrote:

> 1) 'Buffer overflow error' und 'too many characters' kann ich mit
> Br@yTerm simulieren, indem ich eine sehr lange Zeichenkette sende. Wie
> kann ich die anderen Fehler simulieren? (ist nicht ganz so wichtig, nur
> Interesse halber)

Da müsste man jetzt den Fleury Code studieren unter welchen
Bedingungen diese Fehler gemeldet werden.

Einen UART Frame Error kannst du IMHO so gar nicht simulieren,
der tritt bei einer physikalischen Störung auf (Ev. Kabel
während der Übertragung abziehen), wenn das Stopbit nicht
rechtzeitig in Bezug auf das Startbit vorgefunden wird.

Ein UART Overrun müsste vorkommen, wenn die Software nicht
schnell genug ein Byte aus der UART auslesen kann, so dass
es bereits vom nächsten Zeichen überschrieben wurde. Da die
Fleury Lib, wenn ich mich recht erinnere, über UART Interrupts
die Zeichen holt, könnte das also passieren, wenn die Interrupts
zu lange gesperrt wurden.

> 2) Was macht man, wenn man einen dieser Fehler erkannt hat?

Man verwirft alles was bisher empfangen wurde.
Man verwirft alles was weiterhin empfangen wurde und zwar solange
bis man sein definiertes Endezeichen wiederfindet. Erst dann
erfolgt der Wiedereinstieg.

Schon alleine deshalb ist es wichtig so ein spezielles Zeichen
zu haben, das einem eine erneute Synchronisation im Fehlerfall
ermöglicht.

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.