Strings zur Compilezeit in ein ByteArray einfügen (und auffüllen)

OP #1271898
Lesenswert?

Liebe Leute,
ich möchte im Programmspeicher ein Bytearray ablegen, welches ich zur 
Compilezeit "zusammenstelle". Dazu habe ich einige #defines erstelle, 
über die die die Feldinhalte festlege:
1
#define NMEA2000_NMEADATABASEVERSION  1210        //  2Byte
2
#define NMEA2000_MFPRODUCTCODE    61258       //  2Byte
3
#define NMEA2000_MFMODELID    "Meine ID"  // 32Byte ASCII

Zur Compilezeit soll dann alles zusammengesetzt werden:
1
const prog_uint8_t NMEA2000_ProductInformation_Data[] = {
2
  (uint8_t)(NMEA2000_NMEADATABASEVERSION),
3
  (NMEA2000_NMEADATABASEVERSION>>8),
4
  (uint8_t)(NMEA2000_MFPRODUCTCODE),
5
  // hier soll jetzt das "Char-Array" eingefügt werden
6
  // und dann kommen noch weitere Bytes und Words ....
7
};

Das klappt auch bis auf den String "NMEA2000_MFMODELID". Hier weiß ich 
nicht, wie ich den zur Compilezeit geeignet zerlegen lassen kann, so 
dass die einzelnen Buchtaben sukkzessive in den Bytes von 
"NMEA2000_ProductInformation_Data" abgelegt werden.

Außerdem ist in der Spezifikation vorgeschrieben, das 
"NMEA2000_MFMODELID" 32 Byte lang zu sein hat und alle nicht vewendeten 
Bytes mit 0xFF aufgefüllt werden. Kann mann das auch irgendwie geschickt 
mit Präprozessor und Compiler lösen?

Ich hätte gern den fertigen Datensatz im Flash, damit ich im Programm 
dann nurnoch die Daten kopieren muss.

lG Phili
Gast #1271924
Lesenswert?

Philipp Drewes schrieb:
>
1
> const prog_uint8_t NMEA2000_ProductInformation_Data[] = {
2
>   (uint8_t)(NMEA2000_NMEADATABASEVERSION),
3
>   (NMEA2000_NMEADATABASEVERSION>>8),
4
>   (uint8_t)(NMEA2000_MFPRODUCTCODE),
5
>   // hier soll jetzt das "Char-Array" eingefügt werden
6
>   // und dann kommen noch weitere Bytes und Words ....
7
> };
8
>
>
> Das klappt auch bis auf den String "NMEA2000_MFMODELID".
Das klappt auch mit dem Rest nicht.

Wenn du NMEA2000_NMEADATABASEVERSION auf uint8_t castest, wird der Rest 
(obere 8 Bits) abgeschnitten und du hast nen falschen Wert drin.
OP #1271939
Lesenswert?

Hallo Sven,
danke für Deine Antwort.

>> Das klappt auch bis auf den String "NMEA2000_MFMODELID".
> Das klappt auch mit dem Rest nicht.
>
> Wenn du NMEA2000_NMEADATABASEVERSION auf uint8_t castest, wird der Rest
> (obere 8 Bits) abgeschnitten und du hast nen falschen Wert drin.

Mein System erwartet die Daten im Little-Endian Format. Somit müssen die 
Daten für das Byte 0 abgeschnitten werden. Für Byte 1 wird geeignet 
geshiftet.

Hast Du eine Idee für den String?
Geht das überhaupt so?

Phili
#1271948
Lesenswert?

vielleicht sollte es so ähnlich aussehen:
1
#define NMEA2000_NMEADATABASEVERSION   1210        //  2Byte
2
#define NMEA2000_MFPRODUCTCODE        61258        //  2Byte
3
#define NMEA2000_MFMODELID            "Meine ID"                          \
4
                                      "\xff\xff\xff\xff\xff\xff\xff\xff"  \
5
                                      "\xff\xff\xff\xff\xff\xff\xff\xff"  \
6
                                      "\xff\xff\xff\xff\xff\xff\xff\xff"
7

8
const struct
9
{
10
  prog_uint16_t databaseversion;
11
  prog_uint16_t productcode;
12
  prog_uint8_t  modelid[32];
13
}
14
  NMEA2000_ProductInformation_Data  =
15
    {
16
      NMEA2000_NMEADATABASEVERSION,
17
      NMEA2000_MFPRODUCTCODE,
18
      NMEA2000_MFMODELID
19
    };

(bzw. die beiden 16-Bitwerte in je 8 Bit aufgeteilt und getrennt 
initalisiert mit cast bzw. Shift, wenn es sonst mit der Reihhenfolge
kneift)
Gast #1271949
Lesenswert?

Da wäre ich vorsichtig, die Schieberei und so wird m.W.n. arithmetisch 
durchgeführt. Schreibs lieber explizit dazu.

Ein Stringliteral à la "blablabla" zerfällt normalerweise in einen 
Zeiger. Lediglich im Kontext einer Vektorinitialisierung zerfällts in 
einzelne Zeichen:
1
char vektor[] = "abc";

Ich wüsste jetzt nicht, wie man dein Problem ohne gescheite Strukturen 
so lösen könnte :-(
#1272004
Lesenswert?

ach und noch ein Tipp:

falls die ID von Fall zu Fall unterschiedlich lang ist, kann
man auch einfach soviele \xFF zum Auffüllen nehmen, daß es
auch für eine kurze ID sicher reicht (also am sichersten 32 hier).

Das erzeugt dann zwar eine Warnung, ist sonst aber nicht weiter 
schädlich.

Der Compiler weiß ja, daß das Feld nur 32 Byte hat und ignoriert
die überzähligen Bytes der Initialisierung.
OP #1272719
Lesenswert?

Klaus Falser schrieb:
> Warum eigentlich mit 0xFF auffüllen?
> Bei einem String wird laut C immer ein 0x00 angehängt.
> Die Initialisierung eines character Feldes kann auch kürzer sein, nur
> das abschließende 0x00 wird von C gebraucht.

Hallo Klaus,
die Daten werden über einen CAN-Bus versendet. Die Spezifikation des 
verwendeten Protokolls erwartet, dass mit 0xFF aufgefüllt wird
OP #1272730
Lesenswert?

Klaus Wachtler schrieb:
> ach und noch ein Tipp:
>
> falls die ID von Fall zu Fall unterschiedlich lang ist, kann
> man auch einfach soviele \xFF zum Auffüllen nehmen, daß es
> auch für eine kurze ID sicher reicht (also am sichersten 32 hier).
>
> Das erzeugt dann zwar eine Warnung, ist sonst aber nicht weiter
> schädlich.
>
> Der Compiler weiß ja, daß das Feld nur 32 Byte hat und ignoriert
> die überzähligen Bytes der Initialisierung.

Und noch eine weitere Anmerkung / Frage hierzu. Damit die Strings auch 
im Flash landen, muss das folgende Vorgehen verfolgt werden:

Beitrag "[AVR-GCC] struct-Initialisierung. PROGMEM und Strings"

Da ich dann im Struct ja aber den Pointer habe, muss ich die Länge von 
32 Byte direkt in der String-Definition vorgeben. Hier der aktuelle 
Code-Ausschnitt
1
typedef struct {
2
  uint16_t    databaseversion;
3
  uint16_t    productcode;
4
  const char  *pmodelid;
5
  const char  *psoftwareversion;
6
  const char  *pmodelversion;
7
  const char  *pmodelserialcode;
8
  uint8_t     certificationlevel;
9
  uint8_t     loadequivalancy;  
10
} tNMEA2000_ProductInformation;
11

12
const char PROGMEM modelid[32] = NMEA2000_MODELID;
13
const char PROGMEM softwareversion[32] = NMEA2000_SOFTWAREVERSIONCODE;
14
const char PROGMEM modelversion[32] = NMEA2000_MODELVERSION;
15
const char PROGMEM modelserialcode[32] = NMEA2000_MODELSERIALCODE;
16

17
const tNMEA2000_ProductInformation PROGMEM NMEA2000_ProductInformation = {
18
    NMEA2000_DATABASEVERSION,
19
    NMEA2000_PRODUCTCODE,
20
    modelid,    
21
    softwareversion,
22
    modelversion,
23
    modelserialcode,
24
    NMEA2000_CERTIFICATIONLEVEL,
25
    NMEA2000_LOADEQUIVALENCY
26
};
OP #1272840
Lesenswert?

Und dann ergibt sich damit das Problem, dass ich auf die uints mit Hilfe 
der "pgm_read_xxx"-Routinen zugreifen kann. Für die Strings benötige ich 
aber "geschachtelte" "pgm_read_word" und "pgm_read_char" Routinen um 
erst die Start-Adresse des Strings zu holen und dann den Inhalt lesen zu 
können.

Ich meiner Implementation würde ich später aber gern nur mit "memcpy_P" 
arbeiten und keine weiteren "i"-angängigen Entscheidungen treffen.
Hier ein Code-Ausschnitt.
1
for (uint8_t i=0; i<18; i++){
2
    message.data[0] = fast_packet_index++;
3
    memcpy_P(message.data+1, &NMEA2000_ClaimAddress+6+7*i, 7);
4
    mcp2515_send_message(&message);
5
}

Meine Frage ist also weiterhin:
Wie erhalte ich eine sequentielle Datenstruktur im Flash, die aus 
unsigned Integers verschiedener Größe und mehreren Strings besteht, zur 
Compilezeit zusammengebaut wird und später sukzessive mit Hilfe von 
"memcpy_P" gelesen werden kann??

Danke noch einmal für alle bisherigen Vorschläge, Nachfragen und 
Anregungen!!

Euer Phili

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren