Hallo, ich muß Telegramme über die serielle Schnittstelle verschicken. Die Telegramme müssen folgenden Aufbau haben (geschrieben als struct: struct { uint8_t cmd; // Das Kommando uint8_t data[] // Abhängig vom Kommando eine bestimmte Anzahl Bytes uint8_t ck1; // Byte 1 der Fletcher_Prüfsumme der Daten uint8_t ck2; // Byte 2 der Fletcher_Prüfsumme der Daten } meinTelegramm; Die Reihenfolge muß natürlich eingehalten werden. Die Telegramme würde ich gerne im Flash ablegen und um die Bytes in einer Schleife aus dem Flash lesen zu können folglich noch gerne die Gesamtlänge als Byte voran auch im Flash ablegen, also ungefähr so: struct { uint8_t len; // Länge im Flash uint8_t cmd; // Das Kommando uint8_t data[] // Abhängig vom Kommando eine bestimmte Anzahl Bytes uint8_t ck1; // Byte 1 der Fletcher_Prüfsumme der Daten uint8_t ck2; // Byte 2 der Fletcher_Prüfsumme der Daten } meinTelegramm; Da ich viele Telegramme habe, würde ich mir gerne ein Makro bauen, am liebsten natürlich so: #define TELEGRAMM( telegramm_name, kommando, ... ) Problem 1: Der C-Präprozessor kann mir die Fletcher-Prüfsumme nicht ausrechnen. Also dann halt so: #define TELEGRAMM( telegramm_name, kommando, ck1, ck2, daten ) \ struct\ { \ const uint8_t len; \ const uint8_t cmd; \ const uint8_t data[]; \ const uint8_t ck1; \ const uint8_t ck2; \ } telegramm_name PROGMEM = \ { \ .len = sizeof(telegramm_name), \ .cmd = kommando, \ .data = daten, \ .ck1 = ck1, \ .ck2 = ck2, \ } Ein Aufruf sollte dann z.B. so aussehen: TELEGRAMM( Telegramm, 0x22, 0xAB, 0xCD, { 0,1,2,3,4,5 } ); Problem 2.1: Warum schafft es GCC nicht die Größe des Structs zu ermitteln und in len einzutragen? Problem 2.2: Warum nimmt mir der Präprozessor { 0,1,2,3,4,5 } auseinander, statt es als ein Argument ans Makro zu verwenden? Problem 2.3: Warum hat GCC ein Problem damit, da0ß data[] nicht als letztes Element im struct steht? Oder bin ich völlig auf dem Holzweg? Im Kern wie gesagt brauche ich bestimmte Bytes in einer bestimmten Reihenfolge und muß deren Länge kennen. Viele Grüße Joachim
Denk' mal nach: die Länge von data[] ist unbestimmt.
Joachim wrote: > Problem 2.2: Warum nimmt mir der Präprozessor { 0,1,2,3,4,5 } > auseinander, statt es als ein Argument ans Makro zu verwenden? Weil das in der C-Syntax so festgelegt ist, dass er sie trennen muss. > Problem 2.3: Warum hat GCC ein Problem damit, da0ß data[] > nicht als letztes Element im struct steht? Wie soll er denn sonst die Offsets für die Zugriffe auf ck1 und ck2 berechnen?
Jörg Wunsch wrote: >> Problem 2.2: Warum nimmt mir der Präprozessor { 0,1,2,3,4,5 } >> auseinander, statt es als ein Argument ans Makro zu verwenden? > > Weil das in der C-Syntax so festgelegt ist, dass er sie trennen > muss. Geklammerte Argumente nimmt er übrigens nicht auseinander. Sie müssen aber in runden Klammern stehen, das nützt dir hier nicht viel. Was dir aber was nützen könnte ist, wenn du deine data-Liste ans Ende nimmst und als ... deklarierst. Zugreifen kannst du über
1 | __VA_ARGS__ |
(das ist ein C99-Feature).
Hallo, @Jörg: Danke, wenn das andere nicht geht, dann wenigstens das. So ganz kann ich aber nicht nachvollziehen, warum die Größe bei einem Array unbestimmt ist / sein soll. Bei einem struct kann der Comipler ja z.B.: struct { const uint8_t len; const uint8_t cmd; } telegramm_name PROGMEM = { .len = sizeof(telegramm_name), \ }; schluckt er anstandslos. Warum kann er es dann nicht bei einem Array? Einfach erstmal das Array aufbauen, dann die Größe ermitteln und dann initialisieren. Aber ok, Wehklagen hilft nicht. ;-)
Nachtrag: Warum kann der Comiler sowas denn auch nicht? struct { const uint8_t len; const uint8_t cmd; const uint8_t data[sizeof({0,1,2,3})]; } telegramm_name;
So etwas könnte deinen Vorstellungen nahe kommen:
1 | #define TELEGRAMM(telegramm_name, kommando, check1, check2, ...) \
|
2 | struct { \
|
3 | const uint8_t len; \
|
4 | const uint8_t cmd; \
|
5 | const uint8_t data[sizeof (char []){__VA_ARGS__}]; \
|
6 | const uint8_t ck1; \
|
7 | const uint8_t ck2; \
|
8 | } telegramm_name = { \
|
9 | .len = sizeof telegramm_name, \
|
10 | .cmd = kommando, \
|
11 | .data = {__VA_ARGS__}, \
|
12 | .ck1 = check1, \
|
13 | .ck2 = check2 \
|
14 | }
|
15 | |
16 | TELEGRAMM(Telegramm, 0x22, 0xAB, 0xCD, 0, 1, 2, 3, 4, 5); |
Joachim wrote: > Warum kann der Comiler sowas denn auch nicht? > const uint8_t data[sizeof({0,1,2,3})]; Weil {0,1,2,3} weder ein C-regelgerechter Ausdruck noch ein regelgerechter Datentyp ist. Diese beiden sind aber die einzigen zulässigen Argumente für den sizeof-Operator. Dein variable-length array member ist ja am Ende der Struktur gut aufgehoben, und dort funktioniert es auch prima. (Ist ein weiteres C99-Feature.) Der Compiler kann trotzdem alle Offsets berechnen, die Längeninformation für das Array am Ende musst du halt anderweitig mitgeben. Dein Versuch, das Array mit einer festen Größe in der Mitte zu platzieren, würde ja lauter verschiedene zueinander inkompatible Datentypen erzeugen. Das variable-length array am Ende hingegen erzeugt nur einen einzigen Datentyp.
@Jörg: Das mit {1,2,3} sehe ich ein. Sorry. Am Ende des structs nützt mir das data[] Element nichts. Es muß halt mittendrin sein. Ob es der Compiler mit vielen Datentypen (Pro Telegram einer)zu tun bekäme könnte mir doch egal sein. Der Zugriff muß ja ehe immer auf PGM_P gecastet werden. @yalu: Vielen Dank! Ich nehme Deinen Vorschlag. Oder weiss doch noch jemand, wie man dem Präprozessor die Berechnung der Fletcher-Prüfsummen beibringen kann? ;-) Viele Grüße Joachim
Joachim wrote: > Am Ende des structs nützt mir das data[] Element nichts. > Es muß halt mittendrin sein. Warum denn das? Warum muss deine Struktur im Speicher denn mit der physischen Übertragung übereinstimmen? Du wirst die Struktur ja wohl (bzw. deren Adresse) an eine Routine übergeben, die sie dann sendet. Die kann ja ck1 und ck2 nach den Daten senden, obwohl sie physisch im Flash-ROM vor den Daten liegen. > Ob es der Compiler mit vielen Datentypen (Pro Telegram einer)zu tun > bekäme > könnte mir doch egal sein. Nein. Der Compiler hat sonst keine Chance, auf die Elemente ck1 und ck2 der verschiedenen Strukturen zuzugreifen. Das sinnvollste erscheint es mir, dass du ck1 und ck2 gar nicht erst gesondert behandelst, sondern einfach ans Ende des Array mit als Datenbytes dranklebst. Du musst sie ja ohnehin vorberechnen. > Oder weiss doch noch jemand, wie man dem Präprozessor > die Berechnung der Fletcher-Prüfsummen beibringen kann? ;-) Das scheitert schon allein daran, dass der C-Präprozessor keine Schleifen ausführen kann. Du könntest möglicherweise den m4- Präprozessor benutzen. Eine komplett andere alternative ist aber, dass du deine Prüfsumme während der Übertragung zur Laufzeit einfach ausrechnen lässt. Die CPU wird ja wohl dazu in der Lage sein. (Keine Ahnung, was eine Fletcher-Prüfsumme sein soll, hab' auch grad keine Lust zu gugeln.) Dann braucht deine Struktur im Speicher nur die eigentlichen Daten enthalten, und die Routine, die die Daten sendet, weiß, dass sie noch zwei Prüfsummenbytes berechnen und danach übertragen muss.
Hallo Jörg, natürlich gibt's verschiedene Wege zum Ziel zu kommen, jeweils mit Vor- und Nachteilen. Aber der verfolgte Ansatz war halt so wie beschrieben: "Einfach mit bzw. in einer(!) Schleife die Bytes rausschicken. und sich dazu die Daten vorher passend ins Flash legen." Danke Joachim
> "Einfach mit bzw. in einer(!) Schleife die Bytes rausschicken. > und sich dazu die Daten vorher passend ins Flash legen." Wenn du die Daten "nur" rausschicken möchtest und nirgends direkt auf einzelne Elemente der Strukturen zugreifen möchtest, könntest du die einzelnen Bytes des Telegramms auch einfach in einem uint8_t-Array ablegen. Du würdest die Startadressen der Strukturen zum Senden sowieso in einen uint8_t-Pointer casten, warum die Daten also nicht gleich im benötigten Format ablegen? Die Telegrammlänge steht dann entweder im ersten Element des Arrays oder wird als zusätzliches Argument an die Telegrammsendefunktion übergeben. Damit deren Aufruf einfacher wird, kannst du ihn zusätzlich noch in ein Makro verpacken, das nur ein Argument, nämlich das Array, entgegen nimmt und das Längenargument für die Funktion automatisch erzeugt.
@yalu: Klar, aber dann müßte ich ja die Länge des jeweiligen .data auch vorher ermitteln. Bei Deiner Version muß ich das nicht.
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.