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:
1 | uint8_t au8RxBuff[20]; |
2 | uint8_t u8RxIdx; |
3 | |
4 | uint8_t au8RxData[20] |
5 | uint8_t u8RxLen; |
6 | uint8_t u8RxCnt; |
7 | uint8_t u8RxCntLast; |
8 | |
9 | ISR (Uart_Receive) |
10 | {
|
11 | uint8_t u8Data = UDR; |
12 | |
13 | //-- Frame ende erkannt --------------
|
14 | if ( u8Data == '/n' ) |
15 | {
|
16 | au8RxBuff[u8RxIdx++] = 0; |
17 | //-- Frame zur Auswertung geben
|
18 | au8RxData = au8RxBuff; |
19 | u8RxLen = u8RxIdx; |
20 | u8RxIdx = 0; |
21 | return; |
22 | }
|
23 | |
24 | //-- nein, nur speichern -------------
|
25 | au8RxBuff[u8RxIdx++] = u8Data ; |
26 | }
|
27 | |
28 | |
29 | void main ( void ) |
30 | {
|
31 | ...
|
32 | |
33 | while ( 1 ) |
34 | {
|
35 | ...
|
36 | if ( u8RxCntLast != u8RxCnt ) |
37 | {
|
38 | u8RxCntLast = u8RxCnt; |
39 | |
40 | // mach was mit:
|
41 | au8RxData; |
42 | u8RxLen; |
43 | }
|
44 | }
|
45 | }
|
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?
In meinem Post ist ein kleiner Fehler: Vor das return in der ISR muss noch ein u8RxCnt++ dazu.
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)?
Sinnvollerweise haelt man sich einen Index, der auf auf das letzte Element zeigt, anstelle der idiotischen Null, die man erst suchen muss.
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.
1 | char myString[20]; |
2 | int myString_i = 0; |
3 | |
4 | void myRxIsr(char newChar) |
5 | {
|
6 | // Fehlt: Überprüfen ob myString_i zu groß ist
|
7 | if(newChar != 0x0D) //Prüfen auf CR |
8 | {
|
9 | myString[myString_i++] = newChar; |
10 | myString[myString_i] = '\0'; |
11 | } else |
12 | { myString_i = 0; // oder Flag setzen. |
13 | }
|
14 | }
|
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.