Forum: Mikrocontroller und Digitale Elektronik UART String einlesen


von Christoph A. (shadowrunner93)


Angehängte Dateien:

Lesenswert?

Mahlzeit Forum!

Zuerst habe ich mal eine allgemeine Frage:
Ich nutze das Programme Termite zur Übertragung an die RS232 
Schnittstelle. Wenn ich jetzt unter Windows einen langen String 
übertrage, liege ich dann richtg mit der Annahme dass der String zuerst 
beim Sender gebuffert wird und dann erst byteweise an den Empfänger 
gelangt? Der Buffer im ATmega32 ist ja nur 1 Byte groß, wenn ich mich 
jetzt nicht irre.

So, nun zu meinem spezifischen Problem (sh. Code im Anhang):
Ich habe das Stringübertragungsproblem so gelöst, dass der User nach 
jedem Befehl einen Strichpunkt am Ende schreiben muss. Mit Strichpunkt 
passt alles wunderbar.

Aber: Alle Eingaben die hintereinander OHNE Strichpunkt am Ende 
übertragen werden, werden aneinander gehängt bis eine Eingabe mit 
Strichpunkt erfolgt und dann laut meinem Code ausgegeben.

Was ich nun erreichen will ist, dass wenn der User mal einen Strichpunkt 
vergisst, ich den Buffer dann lösche und somit nicht die nächste Eingabe 
verfälsche.

Mein Ansatz wäre: Wenn "lange" kein Zeichen mehr kommt (also während der 
User seinen nächsten String eingibt), werte ich das als Syntaxfehler und 
lösche den Buffer.

Aber ich finde die Methode irgendwie unsauber, fällt jemanden vielleicht 
etwas Eleganteres ein?

Vielen Dank schonmal!
Mfg

von Purzel H. (hacky)


Lesenswert?

Das Arbeiten mit Strings ist eigentlich immer eine Zustandsmaschine. Und 
die muss das Ende richtig detektieren. Eine Metode ist mit einem 
Terminator zu arbeiten, einer Null, oder sonstwas. Ich bevorzuge Stings 
mit der Groesse zuerst. Voraussetzung ist, dass man weiss es folgt nun 
ein String. Falls amn weiss, das es ein sogenannter Kurzstring ist, dh 
die Laenge ist kuerzer als 255 byte, dann sendet man dieses Byte zuerst, 
dann den String. Falls der String lenger sein kann. zB weil's ein 
Textfile ist, dann wuerd ich die Groesse als 2, 3, oder 4 byte 
vorspannen, und den String hinterher.
Auf diese Weise kann man einen Timeout machen, sagen wir 100 byte lang 
kommt nichts, dann weiss der Empfaenger, nun ist was nicht gut, zB das 
Device wurde abgezogen, hat keinen Saft mehr oder sonstwas.

von g457 (Gast)


Lesenswert?

Befehls-Terminierung vergessen -> Syntaxfehler (-> dem User eine 
reinhauen) -> Bearbeitung abbrechen. Alles andere ist Unsinn.

Was Du noch berücksichtigen musst ist die Tatsache, dass Dein Puffer in 
der Größe beschränkt ist (bzw. sein muss, die Hardware gibt halt nur 
endlich viel her). Ergo wenn Du einen Pufferüberlauf erkennst (weil zwei 
Befehle drin stehen), dann erst eine Fehlermeldung ausgeben (, dem User 
eine reinhauen) und dann den Puffer löschen. Alles andere ist Unsinn.

Wenn zufällig zwei Befehle (ohne Terminierung dazwischen) in den Puffer 
passen, dann wirst Du bei der Auswertung feststellen, dass der erste 
fehlerhaft aufgebaut ist (weil hinten noch der zweite Befehl mit 
dranhängt) -> Syntaxfehler, rest wie oben.

Fang nicht an zu viel Intelligenz einzubauen, das einzige worauf man 
sich verlassen kann ist die Tatsache, dass es immer einen noch dümmeren 
User gibt. Deswegen: Syntaxfehler -> Meldung -> nochmal.

von Christoph A. (shadowrunner93)


Angehängte Dateien:

Lesenswert?

Danke, sowas in der Richtung habe ich vermutet. Wäre ja Blödsinn sich 
für immer dümmere User, noch mir Hirnschmalz in die Sache zu 
verschwenden =)

Also, im Anhang der neue Code.

Vielen Dank!
Mfg

von Karl H. (kbuchegg)


Lesenswert?

Das mit dem delay ... das kannst du dir sparen
1
uint8_t  USART_ReveiveTimeDelay(uint8_t delay_ms)
2
{
3
  uint8_t i;
4
  
5
  //Mindestzeit warten
6
  for(i=0; i < delay_ms; i++)
7
    _delay_ms(1);
8
  
9
  //Noch immer keine Daten empfangen?
10
  if( ! UCSRA & (1<<RXC))
11
    return NULL;
12
  
13
  return UDR;
14
}
15
16
char* USART_Gets(void)
17
{
18
  static char string[MAX_RECEIVE_STRINGSIZE];
19
  uint8_t i;
20
  
21
  //Warten bis das erste Zeichen des Strings übermittelt wird
22
  while ( !(UCSRA & (1<<RXC)) );
23
24
  for(i=0; i <= MAX_RECEIVE_STRINGSIZE-1 ; i++)
25
  {
26
    //Maximaler Zeitabstand zwischen zwei Zeichen
27
    string[i] = USART_ReveiveTimeDelay(1);

Kein Mensch kann 1000 Zeichen in der Sekunde tippen, du verlangst hier 
zuviel. Timeouts macht man anders. Spars dir hier aber: Einen Benutzer 
treibt man nicht ohne zwingenden Grund in einen Timeout hinein und wenn 
dann ist der Timout im Minutenbereich, so 10 bis 30 Minuten. Irgendwas 
in der Größenordnung. Dein Benutzer wird dir nämlich schön dankbar sein, 
wenn er gerade mühsam seine Eingabe gemacht hat und kurz bevor er damit 
fertig ist klingelt das Telefon. Wenn du deinem Benutzer etwas gutes tun 
willst, dann ermögliche ihm einfache Editiermöglichkeiten, zb indem du 
Backspace auswertest.

von Christoph A. (shadowrunner93)


Lesenswert?

Ich verlange auch nicht 1000 Zeichen in der Sekunde!

Bei meinem Terminalprogramm gibt der Benutzer zuerst seinen ganzen 
Befehl in einen Zeile und drückt dann auf Enter und es wird gesendet =)
Ich schätze das ist generell bei Terminalprogrammen so üblich.

Also, diese Funktion macht folgendes:
Sie wartet solange bis das erste Zeichen im Empfängerbuffer vorliegt, 
dann liest sie alle 1ms ein weiteres Zeichen aus dem Buffer, was ja kein 
Problem ist, da der ganze Befehl ja schon eingetippt ist. Ich stelle 
hier also nur Forderungen an die Baud-Rate, und nicht an den Benutzer =)

Ich habe sie schon getestet, ich sehe eigentlich keine gravierende 
Einschränkung für den Benutzer.

Mfg
Christoph

von Jobst M. (jobstens-de)


Lesenswert?

Christoph A. schrieb:
> Ich schätze das ist generell bei Terminalprogrammen so üblich.

Nö. Eigentlich wird jede gedrückte Taste sofort über UART 
rausgeschmissen.


Gruß

Jobst

von Christoph A. (shadowrunner93)


Lesenswert?

Jobst M. schrieb:
> Christoph A. schrieb:
>> Ich schätze das ist generell bei Terminalprogrammen so üblich.
>
> Nö. Eigentlich wird jede gedrückte Taste sofort über UART
> rausgeschmissen.
>
>
> Gruß
>
> Jobst

Hmm, dann versuche ich das Problem anders zu lösen.

Ich hätte aber zuerst noch eine Versändnisfrage zu der Datenübertragung 
über die serielle Schnittstelle :

Wenn ich jetzt also ein Wort eingebe und es wird tatsächlich jeder 
Buchstabe unmittelbar nach der Eingabe gesendet, landet der erste 
Buchstabe sofort im UDR-Register des Atmega32, die anderen warten noch 
im Buffer des Senders oder?

Mfg

von Jobst M. (jobstens-de)


Lesenswert?

Christoph A. schrieb:
> Wenn ich jetzt also ein Wort eingebe und es wird tatsächlich jeder
> Buchstabe unmittelbar nach der Eingabe gesendet, landet der erste
> Buchstabe sofort im UDR-Register des Atmega32, die anderen warten noch
> im Buffer des Senders oder?


Vermutlich warten sie dort noch nicht, weil sie noch nicht getippt 
wurden.
Bei 9600Bd z.B. ist das eingetippte Zeichen nach ca. 1ms im UDR


Gruß

Jobst

von Karl H. (kbuchegg)


Lesenswert?

Christoph A. schrieb:
> Ich verlange auch nicht 1000 Zeichen in der Sekunde!

Doch das tust du.
Du gestehst deinem Benutzer gerade einmal
1
    string[i] = USART_ReveiveTimeDelay(1);

1 Millisekunde Timeout Zeit zu.

> Bei meinem Terminalprogramm gibt der Benutzer zuerst seinen ganzen
> Befehl in einen Zeile und drückt dann auf Enter und es wird gesendet =)
> Ich schätze das ist generell bei Terminalprogrammen so üblich.

Ist es nicht.
ABer das hat Jobst schon angesprochen.

Du solltest weniger Annahmen treffen und vor allen Dingen erst einmal 
das lokale Echo in deinem Terminalprogramm abschalten.

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.