Hallo,
ich stehe hier grad bisl aufn Schlauch, obwohl die Lösung wohl ganz
einfach ist.
Sufu wurde natürlich bemüht. Spuckt auch einiges aus, nur ist mein
Problem leicht anders.
Ich habe eine Funktion die im RX Interrupt eines UART Moduls aufgerufen
wird.
Außerdem liegt im globalen Speicher eine Zeichenkette mit Platz für 19
Zeichen.
char myString[20]
Die Funktion soll mir nun das empfangene Zeichen anhängen. Bei Empfang
eines Abschlusszeichens 'CR' soll der String terminiert werden.
Vom Prinzip her würde das bisher so aussehn:
void myRxIsr(char newChar)
{
if(newChar != 0x0D) //Prüfen auf CR
{
myString++ = newChar;
}
else
{
myString = '\0';
}
}
Nun zeigt aber der Pointer myString durch das hochzählen auf das
Zeichen'\0'.
Andere Funktionen die myString lesen können also den String nicht mehr
benutzen.
Außerdem soll der String bei Empfang einer neuen Zeichenkette
überschrieben werden.
Hier steh ich dann völlig auf dem Schlauch.
Ich denke ich brauche dazu einen Hilfszeiger, richtig? Sonst fehlt mir
durch das Hochzählen ja später die Startadresse des Strings.
static Variablen sind eher unerwünscht. Globale Variablen sind erlaubt
aber sollten auch eher sparsam verwendet werden.
Wie würdet ihr das angehen?
Viele montagmorgendliche Grüße,
Lugge
>Außerdem soll der String bei Empfang einer neuen Zeichenkette>überschrieben werden.
Es scheint sich um RS232 zu handeln. Das erste, was du dir überlegen
musst, ist:
"Wann beginnt ein neuer String?"
Ich mache das immer so, dass über RS232 mehrere Bytes hintereinander
gesendet werden, diese gehören zum selben Frame und landen hinternander
im Rx-Puffer (bei dir myString). Sobald jetzt auf der RX-Leitung eine
Pause grösser zB 2Byte-Zeiten erkannt wird, ist der Empfang des
aktuellen Frames beendet und die Auswertung kann beginnen...
Darüber musst du dir zuerst Gedanken machen...
Nimm doch den Index als Hilfsvariable, dann kannst du auch gleich die
Länge vergleichen.
Zudem würde ich nach jedem empfangenen Zeichen die '\0' ans Ende
schreiben, damit der String auf jeden Fall terminiert ist.
Matthias Lipinsky schrieb:> "Wann beginnt ein neuer String?"
Wenn er vorher '\n' empfangen hat. Sein Problem ist, dass er den Zeiger
auf den Pufferspeicher verändert und somit nicht mehr weiß, wo sein
Speicher anfängt.
Danke DirkB, des hört sich sinnvoll an!
@Matthias Lipinsky: Naja also Gedanken hab ich mir scho gemacht.
Ein String hört eben mit <CR>, <LF> oder <CR><LF> auf. Auf diese Zeichen
wird auch geprüft (in meinem obigen Beispiel momentan nur auf <CR>.
Ich bastl mal weiter, hab jetzt wenigstens nen Ansatz!
Danke!
>Wenn er vorher '\n' empfangen hat.
Ok. Möglich.
>Sein Problem ist, dass er den Zeiger auf den Pufferspeicher verändert und >somit
nicht mehr weiß, wo sein Speicher anfängt.
Dann braucht er zwei Zeiger. Einen zum Empfang, und einen zum Auswerten.
Etwa so:
Also anstatt den Zeiger selber zähle ich nun einen Index rauf:
static uint8 index = 0;
und setze ihn im else-Teil, also bei Empfang eines Abschlusszeichens
wieder auf 0.
Terminieren muss ich im else-Teil nicht mehr, das mache ich jetzt
jedesmal wenn ein neues Zeichen reinkommt sofort. Ist wohl sicherer, in
Bezug auf andere Funktionen die den String lesen. (Wobei ich eigentlich
noch ein Completeflag setze, aber zuviel Sicherheit schadet ja auch
nicht)
Hätte es zwar gerne ohne Hilfszeugs gelöst weils einfach eleganter ist,
aber sollte so klappen. Muss jetzt erstmal ausführlich testen!
Grüße,
Lugge
Lugge schrieb:> Hätte es zwar gerne ohne Hilfszeugs gelöst weils einfach eleganter ist,
Dann musst du jedesmal, wenn du ein Zeichen empfängst, die Stringlänge
feststellen.
Ist das eleganter?
Matthias Lipinsky schrieb:> In meinem Post ist ein kleiner Fehler:
Ja ...
> Vor das return in der ISR muss noch ein u8RxCnt++ dazu.
... aber der ist es nicht.
(Und die Sache mit deinem CntLast und der damit zusammenhängen Erkennung
eines neuen Strings: keine gute Idee)
> char myString[20]>> Die Funktion soll mir nun das empfangene Zeichen anhängen. Bei Empfang> eines Abschlusszeichens 'CR' soll der String terminiert werden.>> Vom Prinzip her würde das bisher so aussehn:>> void myRxIsr(char newChar)> {> if(newChar != 0x0D) //Prüfen auf CR> {> myString++ = newChar;
*myString++ = newChar; // aber siehe Text
> }> else> {> myString = '\0';
*myString = '\0';
> }> }>> Nun zeigt aber der Pointer myString durch das hochzählen auf> das Zeichen'\0'.
Nö. Das liegt daran, dass myString schon mal gar kein Pointer ist. Daher
werden auch alle Operationen, bei denen du einen vermeintlichen Pointer
'myString' manipulieren willst, nicht funktionieren.
myString ist ein Array und kein Pointer.
> Andere Funktionen die myString lesen können also den String> nicht mehr benutzen.
Doch, natürlich können sie das. myString ist ein Array und dort steht
vom Anfang an gerechnet immer der String drinnen.
Möglichkeiten gibt es viele. Was wäre denn zB eine bessere Idee?
PS: du meinst wahrscheinlich das:
>> au8RxData = au8RxBuff;
Aber bei pseudocode sollte es passen.
Karl Heinz Buchegger schrieb:> Nö. Das liegt daran, dass myString schon mal gar kein Pointer ist. Daher> werden auch alle Operationen, bei denen du einen vermeintlichen Pointer> 'myString' manipulieren willst, nicht funktionieren.> myString ist ein Array und kein Pointer.
Wird bei dei Deklaration eines "höheren Datentypes" wie String, Array
oder Struct nicht eigentlich nur ein Pointer auf das erste Element
erzeugt (+der genügend Speicher reserviert)?
Jo so machs ich momentan. Klappt aber nochned 100%ig.
Ich habe ein char * message_ptr[20] definiert.
Kann ich dann (unter Verwendung eines Indexes) schreiben:
*message_ptr[i] = 'c';
i++;
Absicht dahinter: ich dereferenziere den Zeiger um ein Zeichen an die
Speicherstelle zu schreiben. Da ich nicht immer nur das erste Zeichen
verändern will setze ich zusätzlich noch den Index dahinter.
Habe so ein Konstrukt noch nie verwendet, jetzt weis ich nicht ob mein
unerwünschtes Verhalten von der Kombination Sternoperator + Index
kommt...
Achtung! char * message_ptr[20] ist ein Feld aus 20 Zeigern auf char.
Entweder Zeiger oder Array.
Lass den * weg. In beiden Fällen.
Und i ist ein extrem blöder Variablenname für eine global Variable.
Ok, mein Fehler, nicht bedacht!
Aber würde die Zeile
*message_ptr[i] = 'c';
funktionieren? Nur aus Interesse...
Deine Variablendefinition und Initialisierung natürlich vorrausgesetzt!
Ja.
Aber nur wenn bei message_ptr[i] ein Zeiger auf einen gültigen
Speicherbereich hinterlegt wurde.
Z.B durch ein message_ptr[i] = myString; // (Definition von myString
siehe bei dir)
Dann steht in myString das 'c'
Ok ich glaub wir reden aneinander vorbei.
Mit *message_ptr[i] will ich nicht auf ein Array von Zeigern zugreifen
sondern den Wert an der Stelle i verändern.
Ich versuchs mal mit so wenig Zeilen wie möglich auszudrücken:
Ich definiere mir einen String und einen Zeiger darauf.
Außerdem einen Index.
char message[20];
char *message_ptr;
message_ptr = &message;
uint8 i=3;
Hätten dann die beiden folgenden Zeilen den selben Effekt?
message[i] = 'c';
*message_ptr[i] = 'c';
char message[20];
char *message_ptr;
message_ptr = message; // Hier brauchst du keinen Adressoperator (&) ...
uint8 i=3;
Dann haben die beiden folgenden Zeilen den selben Effekt!
message[i] = 'c';
message_ptr[i] = 'c'; // und hier keinen Dereferenzierungsoperator (*)
Das gleicht sich nur, das ist nicht das Selbe.
Ein Unterschied zwischen Array und Zeiger ist, dass message =
message_ptr; nicht geht.
Danke dir!
Es funkt! :)
Kannst du mir evtl. noch kurz erklären, was da genau passiert und was in
meinem obigen Beispiel anders ist? Ich geb zu, 100%ig durchgestiegen bin
ich da jetzt nicht!
Und dann noch eine kleine Erweiterung:
Sagen wir ich definiere mir einen eigenen Typen für 20 Zeichen, ich
ändere also die Zeile:
char message[20];
um in:
typedef char myUsartMsgType[20];
myUsartMsgType message;
Der Rest bleibt.
Was genau ist da dran anders? Wieso funktioniert das nicht?
Lugge schrieb:> Kannst du mir evtl. noch kurz erklären, was da genau passiert und was in> meinem obigen Beispiel anders ist? Ich geb zu, 100%ig durchgestiegen bin> ich da jetzt nicht!
Der wichtigste Punkt:
Ein Array ist kein Pointer!
Ein Array legt in deinem Fall eine Speicherfläche fest in der 20 char
untergebracht werden können. Diese Speicherfläche hat einen Namen und
wird unter diesem Namen angesprochen
Ein Pointer hingegen ist eine eigenständige Variable, deren Inhalt die
Adresse einer Speicherfläche ist. Der Pointer stellt nur den Speicher
für den Verweis dar. Um die tatsächliche Speicherfläche muss man sich
seperat kümmern. Ein Pointer 'kann' auf Speicher zeigen, muss es aber
nicht. Und ein Pointer der gerade erst zur Welt gekommen ist, zeigt
irgendwohin aber nirgends konkretes, wenn ich als Programmierer nicht
dafür sorge.
Array
#####
message
+---+---+---+---+---+---+---+- ... -+---+---+---+
| | | | | | | | | | | |
+---+---+---+---+---+---+---+- ... -+---+---+---+
Pointer
#######
message
+----+
| o---------+
+----+ |
|
v
+---+---+---+---+---+---+---+- ... -+---+---+---+
| | | | | | | | | | | |
+---+---+---+---+---+---+---+- ... -+---+---+---+
Das die beiden Dinge hier syntaktisch gleich aussehen, liegt an 3 Dingen
die zusammenspielen:
* in C wird der Name eines Arrays, wenn er alleine steht (also ohne
Indexoperation) automatisch wie die Startadresse der Speicherfläche
angesehen.
* In C ist die Indexoperation [] in Ausdrücken von Pointerarithmetik
definiert.
p[i] <==> *(p + i)
d.h. man kann jede Array-Indexoperation in eine gleichwertige
Pointeroperation umformen und vice versa. Syntaktisch!
Und genau das tut auch der Compiler, wenn er eine Indexoperation
behandelt. Er wandelt sie als erstes in die gleichwertige
Pointer-Notation.
* Der Ausdruck p + i (also: Pointer + Offset) ist in C so definiert,
dass p auf eine Speicherfläche zeigt, und der Offset i automatisch immer
die Größe das Basistyps des Zeigers mit eingerechnet wird. Wenn p also
ein Int-Pointer ist, dann wird i automatisch mit sizeof(int)
multiplizert, ist p ein Double-Pointer, so wird i mit sizeof(double)
multipliziert usw. Hier drinn steckt die Erklärung, warum die
Pointer-Array_Index Dualität überhaupt funktioniert.
schreibst du also
message[5] = 'c';
dann wandelt sich der COMpiler das intern um in
*(message + 5) = 'c'
hier steht mesage für sich alleine. message fungiert hier also als die
Startdresse der SPeicherfläche:
message
+---+---+---+---+---+---+---+- ... -+---+---+---+
| | | | | | | | | | | |
+---+---+---+---+---+---+---+- ... -+---+---+---+
^
|
Startadresse der Speicherfläche
Zu dieser Speicherfläche werden noch 5 dazugezählt, man landet also beim
5. Kästchen von dieser Startadresse aus gesehen rechts.
+---+---+---+---+---+---+---+- ... -+---+---+---+
| | | | | | | | | | | |
+---+---+---+---+---+---+---+- ... -+---+---+---+
^
|
also hier
und durch den * wird dann hier das Zeichen abgelegt.
+---+---+---+---+---+---+---+- ... -+---+---+---+
| | | | | |'c'| | | | | |
+---+---+---+---+---+---+---+- ... -+---+---+---+
Schereibst du aber
char* msg = message;
dann hast du etwas ganz anderes konstruiert.
msg
+---+
| o |
+-|-+
|
|
+---+
| message
| +---+---+---+---+---+---+---+- ... -+---+---+---+
+-> | | | | | | | | | | | |
+---+---+---+---+---+---+---+- ... -+---+---+---+
die OPeration
*(msg + 5) = 'a';
wird ein wenig anders abgearbeitet. Der Inhalt von msg wird hergenommen,
weil es sich hier ja um eine echte Variable handelt. Und das ist nun mal
ein Pointer auf den Anfang der Speicherfläche (also der grafisch
eingezeichnete Pfeil). Wieder wird dieser Pfeil temporär um 5 Kästchen
nach rechts gerückt und dort wo dieser Pfeil dann hinzeigt, das 'c'
reingeschrieben (durch den *)
msg
+---+
| o |
+-|-+
|
|
+---+
| message
| +---+---+---+---+---+---+---+- ... -+---+---+---+
+-> | | | | | | | | | | | |
+---+---+---+---+---+---+---+- ... -+---+---+---+
^ ^ ^ ^ ^ ^
| | | | | |
| | | | | 5. Verschiebung
| | | | 4. Verschiebung
| | | 3. Verschiebung
| | 2. Verschiebung
| 1. Verschiebung
0. Verschiebung (hier zeigt der Pointer msg hin)
Die Operation ist ähnlich aber doch ganz anders.
Und aufgrund der Tatsache, dass es einen Pointer Array DUalismus gibt
a[i] <==> *(p + i)
kann man die Operation
*(msg + 5) = 'a';
auch so ausdrücken
msg[5] = 'a';
> um in:>> typedef char myUsartMsgType[20];> myUsartMsgType message;>> Der Rest bleibt.> Was genau ist da dran anders?
Nichts.
> Wieso funktioniert das nicht?
Weil du irgendwo anders einen Fehler gemacht hast?
Danke für diese Erklärung! Perfekt! Das muss ich jetzt aber erstmal
wirken lassen :)
//Weil du irgendwo anders einen Fehler gemacht hast?
Wahrscheinlich, ich hab den Code oben stark vereinfach gepostet, denn
eigentlich verteilen sich die defines und Funktionen auf mehreren .h und
.c Files. Da muss ich nochmal drüberschaun!
Edit:
Und schon gefunden!
Ich definiere mir:
myUsartMsgType message;
Und übergebe message dann an eine Funktion mit dem Prototyp:
void getMessage(myUsartMsgType *message_ptr);
Ändere ich" myUsartMsgType" in "char" ab dann gehts.
Um "myUsartMsgType" zu benutzen muss ich den Stern vor message_ptr
weglassen denn myUsartMsgType entspricht ja nicht einem char sondern
einem char[20], was im Prinzip ja schon ein char* ist, richtig?
Ansonsten, Danke nochmal an alle!
Lugge schrieb:> weglassen denn myUsartMsgType entspricht ja nicht einem char sondern> einem char[20], was im Prinzip ja schon ein char* ist, richtig?
An dieser Stelle: ja
Arrays werden an Funktionen immer übergeben, in dem die Startadresse des
Arrays übergeben wird. Ergo braucht die Funktion eine Pointervariable,
in der diese Startadresse gespeichert werden kann.
Und in Funktionsdefinitionen sind die Schreibweisen
void foo( datentyp * argument )
und
void foo( datentyp argument[] )
gleichwertig. Da besteht kein Unterschied. Auch wenn es zunächst nicht
so aussieht, ist auch im 2.ten Fall die Variable "argument" eine
Pointervariable.
In deinem Fall steckt die Arraysyntax durch den typedef im Datentyp
drinnen. Ändert aber nichts daran, dass in der Funktionsdefinition in
der Argumentliste ein Array steckt, welches an dieser Stelle automatisch
in einer Pointervariablen ausgeführt wird.