Hallo an alle!
Hab da ein Problem und ich komm nicht drauf, wieso das nicht
funktioniert:
Und zwar will ich aus einem String eine Zahl herausfiltern.
Der String sieht z.B. so aus:
> char string[]="PARAM#123";
nun will ich die Zahl 123 herausfiltern. Ich habe es bisher so probiert:
> volatile int stringPosition=0;> while(string[stringPosition]!='#')> {> stringPosition++;> }> for(i=stringPosition, j=0; i<(stringPosition+3); i++, j++)> {> zeitString[j]=string[i+1];> }> zeit1=atoi(zeitString);
wenn der String jetzt z.B. "PARAM#001" ist, ist zeit1=1, aber wenn im
String jetzt "PARAM#010" steht, dann ist zeit1=0
Wieso liest er das nicht alle drei Stellen ein???
Bitte um Hilfe!
lg Peter
Hast du deinen zeitString richtig initialisiert (z.B. char
zeitString[4]) und terminiert (mit einem 0-Byte, z.B. so:
zeitString[4]=0).
Könnte sein, dass atoi da drüber stolpert.
@Bartholomäus Steinmayr
jep,
> xdata volatile char zeitString[4];
dürfte normal passen, oder muss ich
> xdata volatile char zeitString[4]=0; schreiben?
Peter H. wrote:
> @ peter>> kannst du mir erklären, was das genau macht?
strchr sucht ein Zeichen im String und gibt den Zeiger darauf zurück.
Deine Zahl beginnt dahinter, also "+1".
Und dann noch den Zeiger an atoi übergeben.
Peter
@ peter
danke, d.h ich brauch die ganzen schleifen gar nicht und brauch nur
> zeit1 = atoi( strchr(string, '#') + 1);
zu schreiben?
Wenn ja, was ist, wenn der String so aussieht: "PARAM#123#456"
werden in zeit1 dann nur die zeichen bis zum nächsten # gespeichert,
oder muss ich das dann anders machen?
Das '#' ist keine gültige Ziffer und daher bricht atoi dort ab.
Der korrekte Weg ist allerdings, erstmal nachzusehen, ob überhaupt ein
'#' im String ist, z.B.:
Hallo noch einmal an alle!
Hab jetzt mein Programm mit folgender Variante die letzte Zeit laufen
gehabt:
Also der String sieht so aus:
>"PARAM#123*456+789:"
Und so lese ich das ein:
>zahl1=atoi(strchr(string,'#')+1);>zahl2=atoi(strchr(string,'*')+1);>zahl3=(atoi(strchr(string,'+')+1));
Nach längerem Testen stellte sich nun heraus, dass diese Art nicht immer
zuverlässig ist. D.h. manchmal steht in den Variablen eine andere Zahl
als im String und manchmal gar nichts.
Vielleicht sollte ich noch erwähnen, dass ich den String über die
serielle Schnittstelle einlese, aber das Einlesen funktioniert.
Hat vielleicht einer eine Idee oder eine andere sichere Variante?
Bin für jeden Tip dankbar!
lg Peter
Das ist ja alles ziemlich vage. Ich würde drauf tippen, daß da
irgendwelcher Schrott eingelesen wird, der Deinen Parser aus dem Gleis
haut.
Nur ohne gesichertes Wissen über die Eingangsdaten kommst Du nicht
weiter... interessant ist nicht, was die Gegenseite der seriellen
Leitung abgeschickt hat, sondern was tatsächlich angekommen ist.
Peter H. wrote:
> Hallo noch einmal an alle!>> Hab jetzt mein Programm mit folgender Variante die letzte Zeit laufen> gehabt:>> Also der String sieht so aus:>>>"PARAM#123*456+789:">> Und so lese ich das ein:>>>zahl1=atoi(strchr(string,'#')+1);>>zahl2=atoi(strchr(string,'*')+1);>>zahl3=(atoi(strchr(string,'+')+1));>> Nach längerem Testen stellte sich nun heraus, dass diese Art nicht immer> zuverlässig ist. D.h. manchmal steht in den Variablen eine andere Zahl> als im String
Beispiel?
> und manchmal gar nichts.
Gar nichts kann nicht sein. Ein int hat immer einen Wert
>> Hat vielleicht einer eine Idee
Übernimm nie das Ergebnis eines strchr ungeprüft.
wenn der NULL zurückliefert weiss niemand was der
atoi für ein Ergebnis liefern wird.
Dann bleieben noch die üblichen Verdächtigen:
Fehler sonstwo, Array Überlauf, String zu klein dimensioniert, ...
Dinge in der Art
> oder eine andere sichere Variante?
Das grundsätzliche Vorgehen ist ok.
@Karl heinz Buchegger
Hallo! Danke erstmal für die schnelle Antwort.
Also wie schon gesagt schicke ich den String über die serielle
Schnittstelle und der passt so, dass ist sicher.
Woran ich merke, dass da was ab und zu nicht stimmt?
Wenn ich bestimmte Werte per serieller Schnittstelle schicke, dann setze
ich Leds um zu überprüfen, ob sie richtig eingelesen wurden. Es
funktioniert ja auch an und ab, aber wie gesagt nicht immer. Und dass
ist das, was ich überhaupt nicht verstehe, wieso das manchmal geht und
wieso nicht.
Der Einlese-String ist auch groß genug, von da her dürfte auch nichts
sein. Wo könnte dann der Fehler sein?
Wo ist ein Fehler, wenn es manchmal geht und dann wieder nicht?
Hoffe auf weitere Antworten wie auch auf etwaige andere
Lösungsvorschläge.
lg Peter
Peter H. wrote:
> @Karl heinz Buchegger>> Hallo! Danke erstmal für die schnelle Antwort.>> Also wie schon gesagt schicke ich den String über die serielle> Schnittstelle und der passt so, dass ist sicher.>> Woran ich merke, dass da was ab und zu nicht stimmt?>> Wenn ich bestimmte Werte per serieller Schnittstelle schicke, dann setze> ich Leds um zu überprüfen, ob sie richtig eingelesen wurden. Es> funktioniert ja auch an und ab, aber wie gesagt nicht immer. Und dass> ist das, was ich überhaupt nicht verstehe, wieso das manchmal geht und> wieso nicht.>> Der Einlese-String ist auch groß genug, von da her dürfte auch nichts> sein. Wo könnte dann der Fehler sein?>> Wo ist ein Fehler, wenn es manchmal geht und dann wieder nicht?
Das ist sehr, sehr schwer zu sagen ohne Source Code.
Selbst mit Source Code ist es schwer :-)
Normalerweise sind solch sporadische Dinge auf
Array Overflows, Stack Overflows oder sonstige Schweinereien
zurückzuführen.
Was ich tun würde:
Ich würde auf jeden Fall mal die Return Werte der strchr
anschauen. Wahrscheinlich würde ich mir auch an dieser
Stelle eine Debug-Strecke über die Serielle zurück aufbauen
um zu sehen:
* Welcher String kommt den da (du bist nicht der erste der
schwört, dass die Eingangswerte in Ordnung sind und wenn man
dann genauer hinschaut, stellt man fest, dass dem nicht so ist :-)
* Welche Teilstrings wurden mit dem strchr identifiziert
* Was ist das Ergebnis vom atoi()
Ohne solche Debug-Mittel ist das ziemlich schwer zu finden.
Ich weiss schon, du willst wahrscheinlich hören, dass strchr oder
atoi manchmal Fehler machen. Schmink dir das gleich wieder ab.
Der Fehler liegt mit 99.9% Sicherheit in deinem Code.
@Karl heinz Buchegger
>Ich weiss schon, du willst wahrscheinlich hören, dass strchr oder>atoi manchmal Fehler machen. Schmink dir das gleich wieder ab.>Der Fehler liegt mit 99.9% Sicherheit in deinem Code.
Du kennst mich ja gut. ;-)
Ja, du hast wahrscheinlich recht, also dann werde ich mal überprüfen,
was strch() genau findet und was atoi() liefert.
Eins wäre vielleicht noch zu sagen:
Die Daten von der seriellen Schnittstelle kommen von einem Java
Programm.
Wenn ich aber nun das Java-Programm mittels Hyperterminal simuliere.
Also per Hand eintippe was kommen soll dann funktioniert es immer.
Vielleicht fällt dir dazu noch was ein?
lg Peter
> Wenn ich aber nun das Java-Programm mittels Hyperterminal simuliere.> Also per Hand eintippe was kommen soll dann funktioniert es immer.
Na, wo wird dann wohl der Fehler liegen????
Peter H. wrote:
> @Karl heinz Buchegger>> Eins wäre vielleicht noch zu sagen:>> Die Daten von der seriellen Schnittstelle kommen von einem Java> Programm.> Wenn ich aber nun das Java-Programm mittels Hyperterminal simuliere.> Also per Hand eintippe was kommen soll dann funktioniert es immer.
Da klingeln bei mir alle Alarmglocken, dass du dir
auf jeden Fall das was von der Schnittstelle kommt in
irgendeiner Form anschauen sollst.
>> Vielleicht fällt dir dazu noch was ein?
Da fällt mir zum Beispiel ein, dass das Java Programm
die Daten wesentlich schneller senden kann als du tippen
kannst -> hast du ein Handshake implementiert?
So, hab jetzt mal ausgiegig getestet.
Ich hab mir den ganzen String gespeichert und dann wieder ausgeben.
Bei den werten Zahlen 2, 50 und 100 sollen leds leuchten.
Jetzt hat z.B. nur die led für 2 geleuchtet, aber der String sah
folgendermaßen aus:
>PARAM#002*050+0100:
D.h. es müsste funktionieren. Also weiter Fehlersuche.
@Uhu Uhuhu
Ich hab ihn wieder zum hyperterminal geschickt!
@Peter Dannegger
Heißt das, ich muss schauen, dass vor der Zahl keine Nullen kommen? Aber
im Hyperterminal hat es auch mit Nullen davor geklappt.
Ach ja, noch was:
Also im String steht:
>PARAM#002*050+0100:
Die leds für zahl1 und zahl2 leuchten, für die zahl3 nicht, obwohl es im
String steht.
Dann hab ich mir die zahlen noch einmal geschickt
zahl1=2
zahle2=50
zahl3=0
???
also kann es doch am Einlesen liegen, oder?
> Führende 0: atoi interpretiert Zahl als oktal
Nein, habe gerade die Definition für atoi bei M$ nachgelesen - hätte
mich auch schwer verblüfft, wenn Du recht hättest...
Wenn Du einen Debugger an Deinen µC hast, dann schreib doch einfach
hinter den Befehl, der die letzte Zahl einliest, sowas:
if (zahl3 == 0)
zahl3 = zahl3;
Auf die bedingt ausgeführte Anweisung - die nichts bewirkt - setzt Du
einen Debugger-Haltepunkt.
Damit Du die Strings ansehen kannst, machst Du was in der Art:
char *S1, *S2, S3;
S1 = strchr(string,'#') + 1;
zahl1=atoi(S1);
...
Dann kannst Du Dir die Strings im Debugger ansehen und mußt nicht
rätselraten... Achte auch darauf, daß die S*-Pointer auch wirklich in
Deinen Puffer zeigen!
@Uhu Uhuhu
Ich muss auch nicht rätselraten, ich hab es zwar nicht mit dem Debugger
gemacht, aber ich weiß, dass im String das Richtige steht.
Das hab ich jetzt ausgiebig getestet.
Und ich weiß jetzt auch immer, was in den Zahlen steht.
Wenn die entsprechende Led nicht leuchtet, dann steht in der Zahl auch
nicht das Richtige.
Ich habe die Zahlen öfter überprüft, nicht nur mit den Leds. Ich hab sie
auch so anggesehen.
Somit muss wohl der Fehler beim Einlesen sein, oder?
Also bei:
>zahl1=atoi(strchr(string,'#')+1);>zahl2=atoi(strchr(string,'*')+1);>zahl3=(atoi(strchr(string,'+')+1));
Glaubst Du wirklich, daß Du auf Anhieb in den Routinen, die vor Dir
tausende von Programmierern erfolgreich benutzt haben und deren korrekte
Implementierung seit 30 Jahren bekannt ist, einen Fehler findest?
Eher fällt der erste Mai auf einen Freitag den 13...
Checks nochmal so nach, wie ich es vorhin beschrieben habe.
hab mir den thread gerade durchgelesen und da stellt sich mir doch eine
frage:
@ Peter H.: Wieso bist du dir so sicher das der string, den du von der
seriellen schnittstelle bekommst tatsächlich dem string entspricht, den
du erwartest?
@Lukas D.
Hallo!
Ich habe das Programm sozusagen angehalten, den String abgespeichtert
und dann wieder über die serielle Schnittstelle zum Hyperterminal
gesendet. D.h. ich habe das Java-Programm geschlossen und dann den
String zum Hyperterminal gesendet. Und der String passt, nur in den
Zahlen steht manchmal das Richtige drinnen und manchmal nicht.
Ich bin immer noch nicht dahinter gekommen wieso.
@all
Mir ist schon klar, dass ich da wo einen Fehler habe. Is ja klar, wer
sonst? Und dass die Funktionen funktionieren will ich auch nicht
anzweifeln, sondern ich denke mir nur, dass ich sie eventuell falsch
verwende.
lg Peter
Nochmal: Machs, wie ich am 04.05.2007 20:07 geschrieben habe. Du mußt
wirklich in die Variablen im µC gucken, alles andere ist
Kaffeesatzleserei.
Es ist z.B. denkbar, daß es sich um ein dynamisches Problem handelt, das
erst dann entsteht, wenn Du den empfangenen String schon zurückgeschickt
hast - z.B. daß während der Verarbeitung des Pufferinhaltes von der
Gegenstelle schon wieder was neues in den Puffer geschrieben wird -
Fachausdruck Racecondition.
Solche Dinger kriegt man am besten mit der Haltepunktfalle, die ich oben
skizziert habe.
@Uhu Uhuhu
Hallo!
Es könnte tatsächlich sein, dass sich da auch noch andere Zeichen in den
String schmuggeln. Weil es werden kurz danach, auch noch andere Zeichen
vom Java aus gesendet (vl ja zeitgleich???). Ich verstehe nur nicht,
wieso dann der String im Hyperterminal passt. Aber ich glaube schon,
dass du recht hast, dass ich nur im µC schauen kann, was wirklich
drinnen steht.
Das Problem ist leider nur, dass ich vom debuggen nicht viel Ahnung habe
und es deswegen damit noch nicht probiert habe.
Also kann es sein, dass obwohl der String im Hyperterminal richtig
steht, er im µC anders gespeichert ist?
> Also kann es sein, dass obwohl der String im Hyperterminal richtig>steht, er im µC anders gespeichert ist?
Ja: Wenn er kurz nach dem Absenden an das HT im µC zerdroschen wurde -
z.B. von der Interrupt-Routine, die für das Datenempfangen auf der
Leitung verantwortlich ist.
Benutzt Du nur einen einzigen Empfangspuffer?
> Das Problem ist leider nur, dass ich vom debuggen nicht viel Ahnung habe> und es deswegen damit noch nicht probiert habe.
Dann ist das die Gelegenheit, es zu lernen...
Ja, ich habe nur einen Empfangsbuffer.
Aber den String schicke ich erst, nachdem ich die Zahlen eingelesen
habe. Und siehe da, in den Zahlen steht nicht das Richtige, aber dann im
Hyperterminal steht der String richtig.
Überleg Dir mal, was passiert, wenn in receive gerade "PAR" steht, wenn
getParam aufgerufen wird...
Ich sehe gerade, daß das nicht vorkommen sollte...
Aber was passiert, wenn Zeichen kommen, bevor getParam cnt = 0
ausgeführt hat?
Ich würde sagen, dann zerklopft die ISR Dir den Speicher...
Wenn das nicht zum Absturz führt, können natürlich trotzdem Daten
überschrieben werden. Das Mindeste ist jedoch, daß der betreffende
Datensatz verloren geht.
Wie gesagt, es handelt sich um Codeauszüge.
Hätte ich aber dazuposten können, mein Fehler:
Also, main noch einmal:
1
voidmain(void)
2
{
3
init();
4
led1=0;
5
6
while(1)
7
{
8
9
if((serflag==1))
10
{
11
serflag=0;
12
if(strstr(receive,"ready"))
13
{
14
getParam();
15
}
16
}
17
18
//Hier arbeite ich mit den Zahlen dann weiter
19
20
}
21
22
}
Aber außerdem wird serflag=1 erst gesetzt, wenn '\n' oder '\r' kommt und
'\n' oder '\r' kommen erst am Ende des Strings, also muss der String
schon komplett sein.
Es ist sogar der Fall, dass bevor cnt=0 gesetzt wird, noch Zeichen
kommen.
Werden die dann nicht einfach hinten drangehängt und somit kann ich den
String ja trotzdem auslesen, oder?
Du nullst den Puffer aus und dann finden die str*-Routinen den hinten
angepappten Datensatz nicht, weil sie vorher auf ein NUL-Zeigen gestoßen
sind.
Wahrscheinlich bekommst Du es folgendermaßen hin:
Wenn die ISR einen vollständigen Satz empfangen hat, kopiert sie ihn in
einen separaten Puffer, der von getParam gelesen wird, setzt cnt selbst
zurück und ist dann sofort wieder Empfangsbereit.
GetParam nullt nur seinen eigenen Puffer aus, wenn sie fertig ist.
Die ISR kopiert keine Daten in den Ausgabepuffer, wenn dessen erstes
Byte != 0 ist und setzt stattdessen ein Overrun-Flag. Dabei geht ein
Satz verloren.
Übrigens: Keiner der beiden Puffer muß volatile sein - überleg Dir
warum...
Meiner Meinung nach müssen receive und receive2 schon volatile sein,
sonst steht ja nicht immer der aktuelle Wert drinnen, oder? Und
übergeben tu ich die Strings ja nie.
Ja, das kommt dem sehr nahe...
Ein paar kleine Optimierungen:
Sieh mal nach, ob Dein Compiler eine Funktion memset, oder setmem kennt
- dann brauchst Du das Rad nicht nochmal zu erfinden.
Aber generell brauchst Du den zweiten Puffer garnicht auszunullen, denn
die ISR benutzt strcpy und die kopiert die abschließende 0 mit.
Das clearString() in der ISR kannst Du Dir auch sparen, wenn Du im Block
if((rec=='\r')||(rec=='\n')) {
receive[cnt]= 0;
...
}
den ersten Puffer ordentlich terminierst.
Zum Thema 'volatile':
Der Puffer, der nur von der ISR zugegriffen wird, muß nicht volatile
sein - es gibt keine konkurrierenden Zugriffe.
Statt den zweiten Puffer volatile zu machen, reicht es aus, serflag
volatile zu definieren; wenn die von der ISR gesetzt ist, greift die ja
nicht mehr darauf zu.
Ein weiteres mögliches Problem: 'if((rec=='\r')||(rec=='\n'))'.
Was wenn die Gegenstelle - wie das unter Windows üblich ist - eine Zeile
mit \r\n abzuschließt?
Mit '\n' und '\r' hast du wahrscheinlich recht.
Ich hab mir nun folgendes überlegt:
In meinem Programm kommen öfter solche Abfragen vor und es funktioniert
immer, bis auf das eine mal, also könnte ich es doch einfach so machen:
1
voidgetParam(void)
2
{
3
if(serflag==1)
4
{
5
serflag=0;
6
7
if(strstr(receive2,"PARAM"))
8
{
9
strcpy(receive2,receive);
10
zahl1=0;
11
zahl2=0;
12
zahl3=0;
13
zahl1=atoi(strchr(receive2,'#')+1);
14
zahl2=atoi(strchr(receive2,'*')+1);
15
zahl3=(atoi(strchr(receive2,'+')+1))*10;
16
17
//Leds zum Testen
18
if(zeit==2)
19
led2=0;
20
21
if(kP==50)
22
led3=0;
23
24
if(maxPegelstand==1000)
25
led4=0;
26
}
27
}
28
}
Also nur bei dieser Abfrage mit receive2 arbeiten, dann kann mir ja auch
nichts mehr reinpfuschen, oder?
Das ist keine gute Idee, denn die Synchronisation zwischen ISR und
Vordergrund wird dadurch gelockert. Das Umkopieren sollte die ISR
machen.
Wenn sich Deine ISR merkt, daß sie gerade den Puffer umkopiert hat,
reicht es, alle Zeichen kleiner ' ' einfach wegzuschmeißen, bis
was >= ' ' kommt und dann wieder in den ersten Puffer kopieren.
Der Vorteil ist, daß Du unabhängig von Betriebssystemgebräuchen
bezüglich Zeilenende wirst - solange \r und/oder \n benutzt werden.